diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ff57b270..7b077be04 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,20 +13,24 @@ on: schedule: - cron: '0 5 * * 4' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build_linux: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-18.04, ubuntu-20.04 ] + os: [ ubuntu-18.04, ubuntu-20.04, ubuntu-22.04 ] python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -62,15 +66,15 @@ jobs: - name: Tests run: | pytest --random-order --cov=freqtrade --cov-config=.coveragerc - if: matrix.python-version != '3.9' + if: matrix.python-version != '3.9' || matrix.os != 'ubuntu-22.04' - name: Tests incl. ccxt compatibility tests run: | pytest --random-order --cov=freqtrade --cov-config=.coveragerc --longrun - if: matrix.python-version == '3.9' + if: matrix.python-version == '3.9' && matrix.os == 'ubuntu-22.04' - name: Coveralls - if: (runner.os == 'Linux' && matrix.python-version == '3.8') + if: (runner.os == 'Linux' && matrix.python-version == '3.9') env: # Coveralls token. Not used as secret due to github not providing secrets to forked repositories COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu @@ -78,11 +82,13 @@ jobs: # Allow failure for coveralls coveralls || true - - name: Backtesting + - name: Backtesting (multi) run: | cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data - freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy + freqtrade new-strategy -s AwesomeStrategy + freqtrade new-strategy -s AwesomeStrategyMin --template minimal + freqtrade backtesting --datadir tests/testdata --strategy-list AwesomeStrategy AwesomeStrategyMin -i 5m - name: Hyperopt run: | @@ -121,7 +127,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -157,29 +163,15 @@ jobs: pip install -e . - name: Tests - if: (runner.os != 'Linux' || matrix.python-version != '3.8') run: | pytest --random-order - - name: Tests (with cov) - if: (runner.os == 'Linux' && matrix.python-version == '3.8') - run: | - pytest --random-order --cov=freqtrade --cov-config=.coveragerc - - - name: Coveralls - if: (runner.os == 'Linux' && matrix.python-version == '3.8') - env: - # Coveralls token. Not used as secret due to github not providing secrets to forked repositories - COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu - run: | - # Allow failure for coveralls - coveralls -v || true - - name: Backtesting run: | cp config_examples/config_bittrex.example.json config.json freqtrade create-userdir --userdir user_data - freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy + freqtrade new-strategy -s AwesomeStrategyAdv --template advanced + freqtrade backtesting --datadir tests/testdata --strategy AwesomeStrategyAdv - name: Hyperopt run: | @@ -219,7 +211,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} @@ -271,9 +263,9 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: "3.10" - name: pre-commit dependencies run: | @@ -290,9 +282,9 @@ jobs: ./tests/test_docs.sh - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: "3.10" - name: Documentation build run: | @@ -308,18 +300,6 @@ jobs: details: Freqtrade doc test failed! webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} - cleanup-prior-runs: - permissions: - actions: write # for rokroskar/workflow-run-cleanup-action to obtain workflow name & cancel it - contents: read # for rokroskar/workflow-run-cleanup-action to obtain branch - runs-on: ubuntu-20.04 - steps: - - name: Cleanup previous runs on this branch - uses: rokroskar/workflow-run-cleanup-action@v0.3.3 - if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/stable' && github.repository == 'freqtrade/freqtrade'" - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - # Notify only once - when CI completes (and after deploy) in case it's successfull notify-complete: needs: [ build_linux, build_macos, build_windows, docs_check, mypy_version_check ] @@ -327,7 +307,7 @@ jobs: # Discord notification can't handle schedule events if: (github.event_name != 'schedule') permissions: - repository-projects: read + repository-projects: read steps: - name: Check user permission @@ -356,9 +336,9 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: "3.9" - name: Extract branch name shell: bash @@ -371,7 +351,7 @@ jobs: python setup.py sdist bdist_wheel - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@v1.5.0 if: (github.event_name == 'release') with: user: __token__ @@ -379,7 +359,7 @@ jobs: repository_url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@v1.5.0 if: (github.event_name == 'release') with: user: __token__ @@ -419,7 +399,7 @@ jobs: - name: Discord notification uses: rjstone/discord-webhook-notify@v1 - if: always() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) + if: always() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) && (github.event_name != 'schedule') with: severity: info details: Deploy Succeeded! diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1185028b9..59e7f6894 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,11 +13,11 @@ repos: - id: mypy exclude: build_helpers additional_dependencies: - - types-cachetools==5.0.1 - - types-filelock==3.2.5 - - types-requests==2.27.25 - - types-tabulate==0.8.8 - - types-python-dateutil==2.8.14 + - types-cachetools==5.2.1 + - types-filelock==3.2.7 + - types-requests==2.28.0 + - types-tabulate==0.8.11 + - types-python-dateutil==2.8.18 # stages: [push] - repo: https://github.com/pycqa/isort diff --git a/Dockerfile b/Dockerfile index 8f5b85698..5138ecec9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9.9-slim-bullseye as base +FROM python:3.10.5-slim-bullseye as base # Setup env ENV LANG C.UTF-8 diff --git a/README.md b/README.md index cad39f9ac..881895c9a 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,6 @@ Freqtrade is a free and open source crypto trading bot written in Python. It is ![freqtrade](https://raw.githubusercontent.com/freqtrade/freqtrade/develop/docs/assets/freqtrade-screenshot.png) -## Sponsored promotion - -[![tokenbot-promo](https://raw.githubusercontent.com/freqtrade/freqtrade/develop/docs/assets/TokenBot-Freqtrade-banner.png)](https://tokenbot.com/?utm_source=github&utm_medium=freqtrade&utm_campaign=algodevs) - ## Disclaimer This software is for educational purposes only. Do not risk money which @@ -39,7 +35,7 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even - [X] [OKX](https://okx.com/) (Former OKEX) - [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ -### Experimentally, freqtrade also supports futures on the following exchanges +### Supported Futures Exchanges (experimental) - [X] [Binance](https://www.binance.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 6382e1baf..e2e9a16fd 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -155,7 +155,8 @@ "entry_cancel": "on", "exit_cancel": "on", "protection_trigger": "off", - "protection_trigger_global": "on" + "protection_trigger_global": "on", + "show_candle": "off" }, "reload": true, "balance_dust_level": 0.01 diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index 16f2aebcd..73fc681eb 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -1,4 +1,4 @@ -FROM python:3.9.9-slim-bullseye as base +FROM python:3.9.12-slim-bullseye as base # Setup env ENV LANG C.UTF-8 diff --git a/docker/Dockerfile.custom b/docker/Dockerfile.custom index 3b55fcb0e..6e321f14d 100644 --- a/docker/Dockerfile.custom +++ b/docker/Dockerfile.custom @@ -7,4 +7,5 @@ FROM freqtradeorg/freqtrade:develop # The below dependency - pyti - serves as an example. Please use whatever you need! RUN pip install --user pyti +# Switch back to user (only if you required root above) # USER ftuser diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index 2a484da69..5c2500f18 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -22,50 +22,79 @@ DataFrame of the candles that resulted in buy signals. Depending on how many buy makes, this file may get quite large, so periodically check your `user_data/backtest_results` folder to delete old exports. -To analyze the buy tags, we need to use the `buy_reasons.py` script from -[froggleston's repo](https://github.com/froggleston/freqtrade-buyreasons). Follow the instructions -in their README to copy the script into your `freqtrade/scripts/` folder. - Before running your next backtest, make sure you either delete your old backtest results or run backtesting with the `--cache none` option to make sure no cached results are used. If all goes well, you should now see a `backtest-result-{timestamp}_signals.pkl` file in the `user_data/backtest_results` folder. -Now run the `buy_reasons.py` script, supplying a few options: +To analyze the entry/exit tags, we now need to use the `freqtrade backtesting-analysis` command +with `--analysis-groups` option provided with space-separated arguments (default `0 1 2`): ``` bash -python3 scripts/buy_reasons.py -c -s -t -g0,1,2,3,4 +freqtrade backtesting-analysis -c --analysis-groups 0 1 2 3 4 ``` -The `-g` option is used to specify the various tabular outputs, ranging from the simplest (0) -to the most detailed per pair, per buy and per sell tag (4). More options are available by -running with the `-h` option. +This command will read from the last backtesting results. The `--analysis-groups` option is +used to specify the various tabular outputs showing the profit fo each group or trade, +ranging from the simplest (0) to the most detailed per pair, per buy and per sell tag (4): + +* 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) + +More options are available by running with the `-h` option. + +### Using export-filename + +Normally, `backtesting-analysis` uses the latest backtest results, but if you wanted to go +back to a previous backtest output, you need to supply the `--export-filename` option. +You can supply the same parameter to `backtest-analysis` with the name of the final backtest +output file. This allows you to keep historical versions of backtest results and re-analyse +them at a later date: + +``` bash +freqtrade backtesting -c --timeframe --strategy --timerange= --export=signals --export-filename=/tmp/mystrat_backtest.json +``` + +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 "/tmp/mystrat_backtest-2022-06-14_16-28-32.json" +``` + +You can then use that filename in `backtesting-analysis`: + +``` +freqtrade backtesting-analysis -c --export-filename=/tmp/mystrat_backtest-2022-06-14_16-28-32.json +``` ### Tuning the buy tags and sell tags to display To show only certain buy and sell tags in the displayed output, use the following two options: ``` ---enter_reason_list : Comma separated list of enter signals to analyse. Default: "all" ---exit_reason_list : Comma separated list of exit signals to analyse. Default: "stop_loss,trailing_stop_loss" +--enter-reason-list : Space-separated list of enter signals to analyse. Default: "all" +--exit-reason-list : Space-separated list of exit signals to analyse. Default: "all" ``` For example: ```bash -python3 scripts/buy_reasons.py -c -s -t -g0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss" +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 ``` ### Outputting signal candle indicators -The real power of the buy_reasons.py script comes from the ability to print out the indicator +The real power of `freqtrade backtesting-analysis` comes from the ability to print out the indicator values present on signal candles to allow fine-grained investigation and tuning of buy signal indicators. To print out a column for a given set of indicators, use the `--indicator-list` option: ```bash -python3 scripts/buy_reasons.py -c -s -t -g0,1,2,3,4 --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" +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 ``` The indicators have to be present in your strategy's main DataFrame (either for your main diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index 7f1bd0fed..8a1ebaff3 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -98,6 +98,23 @@ class MyAwesomeStrategy(IStrategy): !!! Note All overrides are optional and can be mixed/matched as necessary. +### 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. + +``` python + +class MyAwesomeStrategy(IStrategy): + + def bot_start(self, **kwargs) -> None: + self.buy_adx = IntParameter(20, 30, default=30, optimize=True) + + # ... +``` + +!!! Warning + Parameters created this way will not show up in the `list-strategies` parameter count. + ### Overriding Base estimator You can define your own estimator for Hyperopt by implementing `generate_estimator()` in the Hyperopt subclass. diff --git a/docs/assets/discord_notification.png b/docs/assets/discord_notification.png new file mode 100644 index 000000000..05a7705d7 Binary files /dev/null and b/docs/assets/discord_notification.png differ diff --git a/docs/backtesting.md b/docs/backtesting.md index 75225b654..50fc96923 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -300,6 +300,7 @@ A backtesting result will look like that: | Absolute profit | 0.00762792 BTC | | Total profit % | 76.2% | | CAGR % | 460.87% | +| Profit factor | 1.11 | | Avg. stake amount | 0.001 BTC | | Total trade volume | 0.429 BTC | | | | @@ -320,6 +321,9 @@ A backtesting result will look like that: | Avg. Duration Loser | 6:55:00 | | Rejected Entry signals | 3089 | | Entry/Exit Timeouts | 0 / 0 | +| Canceled Trade Entries | 34 | +| Canceled Entry Orders | 123 | +| Replaced Entry Orders | 89 | | | | | Min balance | 0.00945123 BTC | | Max balance | 0.01846651 BTC | @@ -396,6 +400,7 @@ It contains some useful key metrics about performance of your strategy on backte | Absolute profit | 0.00762792 BTC | | Total profit % | 76.2% | | CAGR % | 460.87% | +| Profit factor | 1.11 | | Avg. stake amount | 0.001 BTC | | Total trade volume | 0.429 BTC | | | | @@ -416,6 +421,9 @@ It contains some useful key metrics about performance of your strategy on backte | Avg. Duration Loser | 6:55:00 | | Rejected Entry signals | 3089 | | Entry/Exit Timeouts | 0 / 0 | +| Canceled Trade Entries | 34 | +| Canceled Entry Orders | 123 | +| Replaced Entry Orders | 89 | | | | | Min balance | 0.00945123 BTC | | Max balance | 0.01846651 BTC | @@ -438,6 +446,8 @@ It contains some useful key metrics about performance of your strategy on backte - `Final balance`: Final balance - starting balance + absolute profit. - `Absolute profit`: Profit made in stake currency. - `Total profit %`: Total profit. Aligned to the `TOTAL` row's `Tot Profit %` from the first table. Calculated as `(End capital − Starting capital) / Starting capital`. +- `CAGR %`: Compound annual growth rate. +- `Profit factor`: profit / loss. - `Avg. stake amount`: Average stake amount, either `stake_amount` or the average when using dynamic stake amount. - `Total trade volume`: Volume generated on the exchange to reach the above profit. - `Best Pair` / `Worst Pair`: Best and worst performing pair, and it's corresponding `Cum Profit %`. @@ -447,6 +457,9 @@ It contains some useful key metrics about performance of your strategy on backte - `Avg. Duration Winners` / `Avg. Duration Loser`: Average durations for winning and losing trades. - `Rejected Entry signals`: Trade entry signals that could not be acted upon due to `max_open_trades` being reached. - `Entry/Exit Timeouts`: Entry/exit orders which did not fill (only applicable if custom pricing is used). +- `Canceled Trade Entries`: Number of trades that have been canceled by user request via `adjust_entry_price`. +- `Canceled Entry Orders`: Number of entry orders that have been canceled by user request via `adjust_entry_price`. +- `Replaced Entry Orders`: Number of entry orders that have been replaced by user request via `adjust_entry_price`. - `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period. - `Max % of account underwater`: Maximum percentage your account has decreased from the top since the simulation started. Calculated as the maximum of `(Max Balance - Current Balance) / (Max Balance)`. @@ -466,7 +479,7 @@ You can get an overview over daily / weekly or monthly results by using the `--b To visualize daily and weekly breakdowns, you can use the following: ``` bash -freqtrade backtesting --strategy MyAwesomeStrategy --breakdown day month +freqtrade backtesting --strategy MyAwesomeStrategy --breakdown day week ``` ``` output @@ -482,7 +495,7 @@ freqtrade backtesting --strategy MyAwesomeStrategy --breakdown day month ``` -The output will show a table containing the realized absolute Profit (in stake currency) for the given timeperiod, as well as wins, draws and losses that materialized (closed) on this day. +The output will show a table containing the realized absolute Profit (in stake currency) for the given timeperiod, as well as wins, draws and losses that materialized (closed) on this day. Below that there will be a second table for the summarized values of weeks indicated by the date of the closing Sunday. The same would apply to a monthly breakdown indicated by the last day of the month. ### Backtest result caching @@ -521,8 +534,9 @@ Since backtesting lacks some detailed information about what happens within a ca - Exit-reason does not explain if a trade was positive or negative, just what triggered the exit (this can look odd if negative ROI values are used) - Evaluation sequence (if multiple signals happen on the same candle) - Exit-signal - - ROI (if not stoploss) - Stoploss + - ROI + - Trailing stoploss Taking these assumptions, backtesting tries to mirror real trading as closely as possible. However, backtesting will **never** replace running a strategy in dry-run mode. Also, keep in mind that past results don't guarantee future success. diff --git a/docs/bot-basics.md b/docs/bot-basics.md index e45e3d9ca..14823722e 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -20,7 +20,9 @@ All profit calculations of Freqtrade include fees. For Backtesting / Hyperopt / ## Bot execution logic Starting freqtrade in dry-run or live mode (using `freqtrade trade`) will start the bot and start the bot iteration loop. -By default, loop runs every few seconds (`internals.process_throttle_secs`) and does roughly the following in the following sequence: +This will also run the `bot_start()` callback. + +By default, the bot loop runs every few seconds (`internals.process_throttle_secs`) and performs the following actions: * Fetch open trades from persistence. * Calculate current list of tradable pairs. @@ -34,6 +36,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and * Check timeouts for open orders. * Calls `check_entry_timeout()` strategy callback for open entry orders. * Calls `check_exit_timeout()` strategy callback for open exit orders. + * Calls `adjust_entry_price()` strategy callback for open entry orders. * Verifies existing positions and eventually places exit orders. * Considers stoploss, ROI and exit-signal, `custom_exit()` and `custom_stoploss()`. * Determine exit-price based on `exit_pricing` configuration setting or by using the `custom_exit_price()` callback. @@ -53,11 +56,13 @@ This loop will be repeated again and again until the bot is stopped. [backtesting](backtesting.md) or [hyperopt](hyperopt.md) do only part of the above logic, since most of the trading operations are fully simulated. * Load historic data for configured pairlist. +* Calls `bot_start()` once. * Calls `bot_loop_start()` once. * Calculate indicators (calls `populate_indicators()` once per pair). * Calculate entry / exit signals (calls `populate_entry_trend()` and `populate_exit_trend()` once per pair). * Loops per candle simulating entry and exit points. * Check for Order timeouts, either via the `unfilledtimeout` configuration, or via `check_entry_timeout()` / `check_exit_timeout()` strategy callbacks. + * Calls `adjust_entry_price()` strategy callback for open entry orders. * Check for trade entry signals (`enter_long` / `enter_short` columns). * Confirm trade entry / exits (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy). * Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle). diff --git a/docs/configuration.md b/docs/configuration.md index 80cd52c5b..0f3069478 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -140,7 +140,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `dry_run` | **Required.** Define if the bot must be in Dry Run or production mode.
*Defaults to `true`.*
**Datatype:** Boolean | `dry_run_wallet` | Define the starting amount in stake currency for the simulated wallet used by the bot running in Dry Run mode.
*Defaults to `1000`.*
**Datatype:** Float | `cancel_open_orders_on_exit` | Cancel open orders when the `/stop` RPC command is issued, `Ctrl+C` is pressed or the bot dies unexpectedly. When set to `true`, this allows you to use `/stop` to cancel unfilled and partially filled orders in the event of a market crash. It does not impact open positions.
*Defaults to `false`.*
**Datatype:** Boolean -| `process_only_new_candles` | Enable processing of indicators only when new candles arrive. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-the-strategy).
*Defaults to `false`.*
**Datatype:** Boolean +| `process_only_new_candles` | Enable processing of indicators only when new candles arrive. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-the-strategy).
*Defaults to `true`.*
**Datatype:** Boolean | `minimal_roi` | **Required.** Set the threshold as ratio the bot will use to exit a trade. [More information below](#understand-minimal_roi). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict | `stoploss` | **Required.** Value as ratio of the stoploss used by the bot. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Float (as ratio) | `trailing_stop` | Enables trailing stoploss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md#trailing-stop-loss). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Boolean @@ -230,6 +230,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `dataformat_trades` | Data format to use to store historical trades data.
*Defaults to `jsongz`*.
**Datatype:** String | `position_adjustment_enable` | Enables the strategy to use position adjustments (additional buys or sells). [More information here](strategy-callbacks.md#adjust-trade-position).
[Strategy Override](#parameters-in-the-strategy).
*Defaults to `false`.*
**Datatype:** Boolean | `max_entry_position_adjustment` | Maximum additional order(s) for each open trade on top of the first entry Order. Set it to `-1` for unlimited additional orders. [More information here](strategy-callbacks.md#adjust-trade-position).
[Strategy Override](#parameters-in-the-strategy).
*Defaults to `-1`.*
**Datatype:** Positive Integer or -1 +| `futures_funding_rate` | User-specified funding rate to be used when historical funding rates are not available from the exchange. This does not overwrite real historical rates. It is recommended that this be set to 0 unless you are testing a specific coin and you understand how the funding rate will affect freqtrade's profit calculations. [More information here](leverage.md#unavailable-funding-rates)
*Defaults to None.*
**Datatype:** Float ### Parameters in the strategy @@ -583,7 +584,7 @@ Once you will be happy with your bot performance running in the Dry-run mode, yo * Market orders fill based on orderbook volume the moment the order is placed. * Limit orders fill once the price reaches the defined level - or time out based on `unfilledtimeout` settings. * In combination with `stoploss_on_exchange`, the stop_loss price is assumed to be filled. -* Open orders (not trades, which are stored in the database) are reset on bot restart. +* Open orders (not trades, which are stored in the database) are kept open after bot restarts, with the assumption that they were not filled while being offline. ## Switch to production mode diff --git a/docs/developer.md b/docs/developer.md index 185bfc92e..0209d220a 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -314,6 +314,32 @@ The output will show the last entry from the Exchange as well as the current UTC If the day shows the same day, then the last candle can be assumed as incomplete and should be dropped (leave the setting `"ohlcv_partial_candle"` from the exchange-class untouched / True). Otherwise, set `"ohlcv_partial_candle"` to `False` to not drop Candles (shown in the example above). Another way is to run this command multiple times in a row and observe if the volume is changing (while the date remains the same). +### Update binance cached leverage tiers + +Updating leveraged tiers should be done regularly - and requires an authenticated account with futures enabled. + +``` python +import ccxt +import json +from pathlib import Path + +exchange = ccxt.binance({ + 'apiKey': '', + 'secret': '' + 'options': {'defaultType': 'future'} + }) +_ = exchange.load_markets() + +lev_tiers = exchange.fetch_leverage_tiers() + +# Assumes this is running in the root of the repository. +file = Path('freqtrade/exchange/binance_leverage_tiers.json') +json.dump(dict(sorted(lev_tiers.items())), file.open('w'), indent=2) + +``` + +This file should then be contributed upstream, so others can benefit from this, too. + ## Updating example notebooks To keep the jupyter notebooks aligned with the documentation, the following should be ran after updating a example notebook. diff --git a/docs/exchanges.md b/docs/exchanges.md index 18a7af5a1..50ebf9e0a 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -230,6 +230,11 @@ OKX requires a passphrase for each api key, you will therefore need to add this !!! Warning OKX only provides 100 candles per api call. Therefore, the strategy will only have a pretty low amount of data available in backtesting mode. +!!! Warning "Futures" + OKX Futures has the concept of "position mode" - which can be Net or long/short (hedge mode). + Freqtrade supports both modes - but changing the mode mid-trading is not supported and will lead to exceptions and failures to place trades. + OKX also only provides MARK candles for the past ~3 months. Backtesting futures prior to that date will therefore lead to slight deviations, as funding-fees cannot be calculated correctly without this data. + ## Gate.io !!! Tip "Stoploss on Exchange" diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 030d73f4b..55fe8f008 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -271,7 +271,8 @@ The last one we call `trigger` and use it to decide which buy trigger we want to !!! Note "Parameter space assignment" Parameters must either be assigned to a variable named `buy_*` or `sell_*` - or contain `space='buy'` | `space='sell'` to be assigned to a space correctly. - If no parameter is available for a space, you'll receive the error that no space was found when running hyperopt. + If no parameter is available for a space, you'll receive the error that no space was found when running hyperopt. + Parameters with unclear space (e.g. `adx_period = IntParameter(4, 24, default=14)` - no explicit nor implicit space) will not be detected and will therefore be ignored. So let's write the buy strategy using these values: @@ -334,6 +335,7 @@ There are four parameter types each suited for different purposes. ## Optimizing an indicator parameter Assuming you have a simple strategy in mind - a EMA cross strategy (2 Moving averages crossing) - and you'd like to find the ideal parameters for this strategy. +By default, we assume a stoploss of 5% - and a take-profit (`minimal_roi`) of 10% - which means freqtrade will sell the trade once 10% profit has been reached. ``` python from pandas import DataFrame @@ -348,6 +350,9 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib class MyAwesomeStrategy(IStrategy): stoploss = -0.05 timeframe = '15m' + minimal_roi = { + "0": 0.10 + }, # Define the parameter spaces buy_ema_short = IntParameter(3, 50, default=5) buy_ema_long = IntParameter(15, 200, default=50) @@ -382,7 +387,7 @@ class MyAwesomeStrategy(IStrategy): return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - conditions = [] + conditions = [] conditions.append(qtpylib.crossed_above( dataframe[f'ema_long_{self.buy_ema_long.value}'], dataframe[f'ema_short_{self.buy_ema_short.value}'] )) @@ -403,7 +408,7 @@ Using `self.buy_ema_short.range` will return a range object containing all entri In this case (`IntParameter(3, 50, default=5)`), the loop would run for all numbers between 3 and 50 (`[3, 4, 5, ... 49, 50]`). By using this in a loop, hyperopt will generate 48 new columns (`['buy_ema_3', 'buy_ema_4', ... , 'buy_ema_50']`). -Hyperopt itself will then use the selected value to create the buy and sell signals +Hyperopt itself will then use the selected value to create the buy and sell signals. While this strategy is most likely too simple to provide consistent profit, it should serve as an example how optimize indicator parameters. @@ -680,7 +685,7 @@ class MyAwesomeStrategy(IStrategy): !!! Note Values in the configuration file will overwrite Parameter-file level parameters - and both will overwrite parameters within the strategy. - The prevalence is therefore: config > parameter file > strategy + The prevalence is therefore: config > parameter file > strategy `*_params` > parameter default ### Understand Hyperopt ROI results @@ -867,6 +872,22 @@ To combat these, you have multiple options: * reduce the number of parallel processes (`-j `) * Increase the memory of your machine +## The objective has been evaluated at this point before. + +If you see `The objective has been evaluated at this point before.` - then this is a sign that your space has been exhausted, or is close to that. +Basically all points in your space have been hit (or a local minima has been hit) - and hyperopt does no longer find points in the multi-dimensional space it did not try yet. +Freqtrade tries to counter the "local minima" problem by using new, randomized points in this case. + +Example: + +``` python +buy_ema_short = IntParameter(5, 20, default=10, space="buy", optimize=True) +# This is the only parameter in the buy space +``` + +The `buy_ema_short` space has 15 possible values (`5, 6, ... 19, 20`). If you now run hyperopt for the buy space, hyperopt will only have 15 values to try before running out of options. +Your epochs should therefore be aligned to the possible values - or you should be ready to interrupt a run if you norice a lot of `The objective has been evaluated at this point before.` warnings. + ## Show details of Hyperopt results After you run Hyperopt for the desired amount of epochs, you can later list all results for analysis, select only best or profitable once, and show the details for any of the epochs previously evaluated. This can be done with the `hyperopt-list` and `hyperopt-show` sub-commands. The usage of these sub-commands is described in the [Utils](utils.md#list-hyperopt-results) chapter. diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index cec5ceb19..0f55c1b79 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -44,7 +44,7 @@ It uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklis ```json "pairlists": [ {"method": "StaticPairList"} - ], +], ``` By default, only currently enabled pairs are allowed. @@ -160,17 +160,17 @@ This filter allows freqtrade to ignore pairs until they have been listed for at Offsets an incoming pairlist by a given `offset` value. -As an example it can be used in conjunction with `VolumeFilter` to remove the top X volume pairs. Or to split -a larger pairlist on two bot instances. +As an example it can be used in conjunction with `VolumeFilter` to remove the top X volume pairs. Or to split a larger pairlist on two bot instances. -Example to remove the first 10 pairs from the pairlist: +Example to remove the first 10 pairs from the pairlist, and takes the next 20 (taking items 10-30 of the initial list): ```json "pairlists": [ // ... { "method": "OffsetFilter", - "offset": 10 + "offset": 10, + "number_assets": 20 } ], ``` @@ -181,7 +181,7 @@ Example to remove the first 10 pairs from the pairlist: `VolumeFilter`. !!! Note - An offset larger then the total length of the incoming pairlist will result in an empty pairlist. + An offset larger than the total length of the incoming pairlist will result in an empty pairlist. #### PerformanceFilter diff --git a/docs/includes/protections.md b/docs/includes/protections.md index bb4a7eb35..d67924cfe 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -96,6 +96,8 @@ def protections(self): `LowProfitPairs` uses all trades for a pair within `lookback_period` in minutes (or in candles when using `lookback_period_candles`) to determine the overall profit ratio. If that ratio is below `required_profit`, that pair will be locked for `stop_duration` in minutes (or in candles when using `stop_duration_candles`). +For futures bots, setting `only_per_side` will make the bot only consider one side, and will then only lock this one side, allowing for example shorts to continue after a series of long losses. + The below example will stop trading a pair for 60 minutes if the pair does not have a required profit of 2% (and a minimum of 2 trades) within the last 6 candles. ``` python @@ -107,7 +109,8 @@ def protections(self): "lookback_period_candles": 6, "trade_limit": 2, "stop_duration": 60, - "required_profit": 0.02 + "required_profit": 0.02, + "only_per_pair": False, } ] ``` diff --git a/docs/index.md b/docs/index.md index e0a88a381..7c35e92b6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -22,10 +22,6 @@ Freqtrade is a free and open source crypto trading bot written in Python. It is ![freqtrade screenshot](assets/freqtrade-screenshot.png) -## Sponsored promotion - -[![tokenbot-promo](assets/TokenBot-Freqtrade-banner.png)](https://tokenbot.com/?utm_source=github&utm_medium=freqtrade&utm_campaign=algodevs) - ## Features - Develop your Strategy: Write your strategy in python, using [pandas](https://pandas.pydata.org/). Example strategies to inspire you are available in the [strategy repository](https://github.com/freqtrade/freqtrade-strategies). @@ -51,7 +47,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, - [X] [OKX](https://okx.com/) (Former OKEX) - [ ] [potentially many others through ccxt](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ -### Experimentally, freqtrade also supports futures on the following exchanges: +### Supported Futures Exchanges (experimental) - [X] [Binance](https://www.binance.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) diff --git a/docs/leverage.md b/docs/leverage.md index 79d3c9842..491e6eda0 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -64,7 +64,10 @@ You will also have to pick a "margin mode" (explanation below) - with freqtrade ### Margin mode -The possible values are: `isolated`, or `cross`(*currently unavailable*) +On top of `trading_mode` - you will also have to configure your `margin_mode`. +While freqtrade currently only supports one margin mode, this will change, and by configuring it now you're all set for future updates. + +The possible values are: `isolated`, or `cross`(*currently unavailable*). #### Isolated margin mode @@ -82,6 +85,16 @@ One account is used to share collateral between markets (trading pairs). Margin "margin_mode": "cross" ``` +## Set leverage to use + +Different strategies and risk profiles will require different levels of leverage. +While you could configure one static leverage value - freqtrade offers you the flexibility to adjust this via [strategy leverage callback](strategy-callbacks.md#leverage-callback) - which allows you to use different leverages by pair, or based on some other factor benefitting your strategy result. + +If not implemented, leverage defaults to 1x (no leverage). + +!!! Warning + Higher leverage also equals higher risk - be sure you fully understand the implications of using leverage! + ## Understand `liquidation_buffer` *Defaults to `0.05`* @@ -101,6 +114,13 @@ Possible values are any floats between 0.0 and 0.99 !!! Danger "A `liquidation_buffer` of 0.0, or a low `liquidation_buffer` is likely to result in liquidations, and liquidation fees" Currently Freqtrade is able to calculate liquidation prices, but does not calculate liquidation fees. Setting your `liquidation_buffer` to 0.0, or using a low `liquidation_buffer` could result in your positions being liquidated. Freqtrade does not track liquidation fees, so liquidations will result in inaccurate profit/loss results for your bot. If you use a low `liquidation_buffer`, it is recommended to use `stoploss_on_exchange` if your exchange supports this. +## Unavailable funding rates + +For futures data, exchanges commonly provide the futures candles, the marks, and the funding rates. However, it is common that whilst candles and marks might be available, the funding rates are not. This can affect backtesting timeranges, i.e. you may only be able to test recent timeranges and not earlier, experiencing the `No data found. Terminating.` error. To get around this, add the `futures_funding_rate` config option as listed in [configuration.md](configuration.md), and it is recommended that you set this to `0`, unless you know a given specific funding rate for your pair, exchange and timerange. Setting this to anything other than `0` can have drastic effects on your profit calculations within strategy, e.g. within the `custom_exit`, `custom_stoploss`, etc functions. + +!!! Warning "This will mean your backtests are inaccurate." + This will not overwrite funding rates that are available from the exchange, but bear in mind that setting a false funding rate will mean backtesting results will be inaccurate for historical timeranges where funding rates are not available. + ### Developer #### Margin mode diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index c5a4b64b3..22d92c65d 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,5 +1,6 @@ +markdown==3.3.7 mkdocs==1.3.0 -mkdocs-material==8.2.12 +mkdocs-material==8.3.9 mdx_truly_sane_lists==1.2 -pymdown-extensions==9.4 +pymdown-extensions==9.5 jinja2==3.1.2 diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index 49372b002..c42cb5575 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -89,11 +89,12 @@ WHERE id=31; If you'd still like to remove a trade from the database directly, you can use the below query. -```sql -DELETE FROM trades WHERE id = ; -``` +!!! Danger + Some systems (Ubuntu) disable foreign keys in their sqlite3 packaging. When using sqlite - please ensure that foreign keys are on by running `PRAGMA foreign_keys = ON` before the above query. ```sql +DELETE FROM trades WHERE id = ; + DELETE FROM trades WHERE id = 31; ``` @@ -102,13 +103,20 @@ DELETE FROM trades WHERE id = 31; ## Use a different database system +Freqtrade is using SQLAlchemy, which supports multiple different database systems. As such, a multitude of database systems should be supported. +Freqtrade does not depend or install any additional database driver. Please refer to the [SQLAlchemy docs](https://docs.sqlalchemy.org/en/14/core/engines.html#database-urls) on installation instructions for the respective database systems. + +The following systems have been tested and are known to work with freqtrade: + +* sqlite (default) +* PostgreSQL) +* MariaDB + !!! Warning - By using one of the below database systems, you acknowledge that you know how to manage such a system. Freqtrade will not provide any support with setup or maintenance (or backups) of the below database systems. + By using one of the below database systems, you acknowledge that you know how to manage such a system. The freqtrade team will not provide any support with setup or maintenance (or backups) of the below database systems. ### PostgreSQL -Freqtrade supports PostgreSQL by using SQLAlchemy, which supports multiple different database systems. - Installation: `pip install psycopg2-binary` diff --git a/docs/stoploss.md b/docs/stoploss.md index 573fdbd6c..6ddb485a4 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -130,7 +130,7 @@ In summary: The stoploss will be adjusted to be always be -10% of the highest ob ### Trailing stop loss, custom positive loss -It is also possible to have a default stop loss, when you are in the red with your buy (buy - fee), but once you hit positive result the system will utilize a new stop loss, which can have a different value. +You could also have a default stop loss when you are in the red with your buy (buy - fee), but once you hit a positive result (or an offset you define) the system will utilize a new stop loss, which can have a different value. For example, your default stop loss is -10%, but once you have more than 0% profit (example 0.1%) a different trailing stoploss will be used. !!! Note @@ -142,6 +142,8 @@ Both values require `trailing_stop` to be set to true and `trailing_stop_positiv stoploss = -0.10 trailing_stop = True trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0 + trailing_only_offset_is_reached = False # Default - not necessary for this example ``` For example, simplified math: @@ -156,11 +158,31 @@ For example, simplified math: The 0.02 would translate to a -2% stop loss. Before this, `stoploss` is used for the trailing stoploss. +!!! Tip "Use an offset to change your stoploss" + Use `trailing_stop_positive_offset` to ensure that your new trailing stoploss will be in profit by setting `trailing_stop_positive_offset` higher than `trailing_stop_positive`. Your first new stoploss value will then already have locked in profits. + + Example with simplified math: + + ``` python + stoploss = -0.10 + trailing_stop = True + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.03 + ``` + + * the bot buys an asset at a price of 100$ + * the stop loss is defined at -10%, so the stop loss would get triggered once the asset drops below 90$ + * assuming the asset now increases to 102$ + * the stoploss will now be at 91.8$ - 10% below the highest observed rate + * assuming the asset now increases to 103.5$ (above the offset configured) + * the stop loss will now be -2% of 103$ = 101.42$ + * now the asset drops in value to 102\$, the stop loss will still be 101.42$ and would trigger once price breaks below 101.42$ + ### Trailing stop loss only once the trade has reached a certain offset -It is also possible to use a static stoploss until the offset is reached, and then trail the trade to take profits once the market turns. +You can also keep a static stoploss until the offset is reached, and then trail the trade to take profits once the market turns. -If `"trailing_only_offset_is_reached": true` then the trailing stoploss is only activated once the offset is reached. Until then, the stoploss remains at the configured `stoploss`. +If `trailing_only_offset_is_reached = True` then the trailing stoploss is only activated once the offset is reached. Until then, the stoploss remains at the configured `stoploss`. This option can be used with or without `trailing_stop_positive`, but uses `trailing_stop_positive_offset` as offset. ``` python @@ -191,6 +213,18 @@ For example, simplified math: !!! Tip Make sure to have this value (`trailing_stop_positive_offset`) lower than minimal ROI, otherwise minimal ROI will apply first and sell the trade. +## Stoploss and Leverage + +Stoploss should be thought of as "risk on this trade" - so a stoploss of 10% on a 100$ trade means you are willing to lose 10$ (10%) on this trade - which would trigger if the price moves 10% to the downside. + +When using leverage, the same principle is applied - with stoploss defining the risk on the trade (the amount you are willing to lose). + +Therefore, a stoploss of 10% on a 10x trade would trigger on a 1% price move. +If your stake amount (own capital) was 100$ - this trade would be 1000$ at 10x (after leverage). +If price moves 1% - you've lost 10$ of your own capital - therfore stoploss will trigger in this case. + +Make sure to be aware of this, and avoid using too tight stoploss (at 10x leverage, 10% risk may be too little to allow the trade to "breath" a little). + ## Changing stoploss on open trades A stoploss on an open trade can be changed by changing the value in the configuration or strategy and use the `/reload_config` command (alternatively, completely stopping and restarting the bot also works). diff --git a/docs/strategy-advanced.md b/docs/strategy-advanced.md index 374c675a2..a3115bfb2 100644 --- a/docs/strategy-advanced.md +++ b/docs/strategy-advanced.md @@ -224,3 +224,5 @@ for val in self.buy_ema_short.range: # Append columns to existing dataframe merged_frame = pd.concat(frames, axis=1) ``` + +Freqtrade does however also counter this by running `dataframe.copy()` on the dataframe right after the `populate_indicators()` method - so performance implications of this should be low to non-existant. diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 5ff499b01..f584bd1bb 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -17,6 +17,7 @@ Currently available callbacks: * [`confirm_trade_entry()`](#trade-entry-buy-order-confirmation) * [`confirm_trade_exit()`](#trade-exit-sell-order-confirmation) * [`adjust_trade_position()`](#adjust-trade-position) +* [`adjust_entry_price()`](#adjust-entry-price) * [`leverage()`](#leverage-callback) !!! Tip "Callback calling sequence" @@ -45,6 +46,9 @@ class AwesomeStrategy(IStrategy): self.cust_remote_data = requests.get('https://some_remote_source.example.com') ``` + +During hyperopt, this runs only once at startup. + ## Bot loop start A simple callback which is called once at the start of every bot throttling iteration (roughly every 5 seconds, unless configured differently). @@ -78,8 +82,9 @@ Called before entering a trade, makes it possible to manage your position size w ```python class AwesomeStrategy(IStrategy): def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, - entry_tag: Optional[str], side: str, **kwargs) -> float: + proposed_stake: float, min_stake: Optional[float], max_stake: float, + leverage: float, entry_tag: Optional[str], side: str, + **kwargs) -> float: dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) current_candle = dataframe.iloc[-1].squeeze() @@ -545,10 +550,12 @@ class AwesomeStrategy(IStrategy): :param pair: Pair that's about to be bought/shorted. :param order_type: Order type (as configured in order_types). usually limit or market. - :param amount: Amount in target (quote) currency that's going to be traded. - :param rate: Rate that's going to be used when using limit orders + :param amount: Amount in target (base) currency that's going to be traded. + :param rate: Rate that's going to be used when using limit orders + or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param current_time: datetime object, containing the current datetime + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the buy-order is placed on the exchange. @@ -562,6 +569,14 @@ class AwesomeStrategy(IStrategy): `confirm_trade_exit()` can be used to abort a trade exit (sell) at the latest second (maybe because the price is not what we expect). +`confirm_trade_exit()` may be called multiple times within one iteration for the same trade if different exit-reasons apply. +The exit-reasons (if applicable) will be in the following sequence: + +* `exit_signal` / `custom_exit` +* `stop_loss` +* `roi` +* `trailing_stop_loss` + ``` python from freqtrade.persistence import Trade @@ -574,7 +589,7 @@ class AwesomeStrategy(IStrategy): rate: float, time_in_force: str, exit_reason: str, current_time: datetime, **kwargs) -> bool: """ - Called right before placing a regular sell order. + Called right before placing a regular exit order. Timing for this function is critical, so avoid doing heavy computations or network requests in this method. @@ -582,17 +597,19 @@ class AwesomeStrategy(IStrategy): When not implemented by a strategy, returns True (always confirming). - :param pair: Pair that's about to be sold. + :param pair: Pair for trade that's about to be exited. + :param trade: trade object. :param order_type: Order type (as configured in order_types). usually limit or market. - :param amount: Amount in quote currency. + :param amount: Amount in base currency. :param rate: Rate that's going to be used when using limit orders + or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param exit_reason: Exit reason. Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', 'exit_signal', 'force_exit', 'emergency_exit'] :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. - :return bool: When True is returned, then the exit-order is placed on the exchange. + :return bool: When True, then the exit-order is placed on the exchange. False aborts the process """ if exit_reason == 'force_exit' and trade.calc_profit_ratio(rate) < 0: @@ -604,6 +621,9 @@ class AwesomeStrategy(IStrategy): ``` +!!! Warning + `confirm_trade_exit()` can prevent stoploss exits, causing significant losses as this would ignore stoploss exits. + ## Adjust trade position The `position_adjustment_enable` strategy property enables the usage of `adjust_trade_position()` callback in the strategy. @@ -654,16 +674,17 @@ class DigDeeperStrategy(IStrategy): max_dca_multiplier = 5.5 # This is called when placing the initial order (opening trade) - def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, - entry_tag: Optional[str], side: str, **kwargs) -> float: +def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, + leverage: float, entry_tag: Optional[str], side: str, + **kwargs) -> float: # We need to leave most of the funds for possible further DCA orders # This also applies to fixed stakes return proposed_stake / self.max_dca_multiplier def adjust_trade_position(self, trade: Trade, current_time: datetime, - current_rate: float, current_profit: float, min_stake: float, + current_rate: float, current_profit: float, min_stake: Optional[float], max_stake: float, **kwargs): """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. @@ -713,6 +734,69 @@ class DigDeeperStrategy(IStrategy): ``` +## Adjust Entry Price + +The `adjust_entry_price()` callback may be used by strategy developer to refresh/replace limit orders upon arrival of new candles. +Be aware that `custom_entry_price()` is still the one dictating initial entry limit order price target at the time of entry trigger. + +Orders can be cancelled out of this callback by returning `None`. + +Returning `current_order_rate` will keep the order on the exchange "as is". +Returning any other price will cancel the existing order, and replace it with a new order. + +The trade open-date (`trade.open_date_utc`) will remain at the time of the very first order placed. +Please make sure to be aware of this - and eventually adjust your logic in other callbacks to account for this, and use the date of the first filled order instead. + +!!! Warning "Regular timeout" + Entry `unfilledtimeout` mechanism (as well as `check_entry_timeout()`) takes precedence over this. + Entry Orders that are cancelled via the above methods will not have this callback called. Be sure to update timeout values to match your expectations. + +```python +from freqtrade.persistence import Trade +from datetime import timedelta + +class AwesomeStrategy(IStrategy): + + # ... populate_* methods + + def adjust_entry_price(self, trade: Trade, order: Optional[Order], pair: str, + current_time: datetime, proposed_rate: float, current_order_rate: float, + entry_tag: Optional[str], side: str, **kwargs) -> float: + """ + Entry price re-adjustment logic, returning the user desired limit price. + This only executes when a order was already placed, still open (unfilled fully or partially) + and not timed out on subsequent candles after entry trigger. + + When not implemented by a strategy, returns current_order_rate as default. + If current_order_rate is returned then the existing order is maintained. + If None is returned then order gets canceled but not replaced by a new one. + + :param pair: Pair that's currently analyzed + :param trade: Trade object. + :param order: Order object + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. + :param current_order_rate: Rate of the existing order in place. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. + :param side: 'long' or 'short' - indicating the direction of the proposed trade + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New entry price value if provided + + """ + # Limit orders to use and follow SMA200 as price target for the first 10 minutes since entry trigger for BTC/USDT pair. + if pair == 'BTC/USDT' and entry_tag == 'long_sma200' and side == 'long' and (current_time - timedelta(minutes=10) > trade.open_date_utc: + # just cancel the order if it has been filled more than half of the amount + if order.filled > order.remaining: + return None + else: + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + # desired price + return current_candle['sma_200'] + # default: maintain existing order + return current_order_rate +``` + ## Leverage Callback When trading in markets that allow leverage, this method must return the desired Leverage (Defaults to 1 -> No leverage). @@ -724,19 +808,23 @@ For markets / exchanges that don't support leverage, this method is ignored. ``` python class AwesomeStrategy(IStrategy): - def leverage(self, pair: str, current_time: 'datetime', current_rate: float, - proposed_leverage: float, max_leverage: float, side: str, + def leverage(self, pair: str, current_time: datetime, current_rate: float, + proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: """ - Customize leverage for each new trade. + Customize leverage for each new trade. This method is only called in futures mode. :param pair: Pair that's currently analyzed :param current_time: datetime object, containing the current datetime :param current_rate: Rate, calculated based on pricing settings in exit_pricing. :param proposed_leverage: A leverage proposed by the bot. :param max_leverage: Max leverage allowed on this pair + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :return: A leverage amount, which is between 1.0 and max_leverage. """ return 1.0 ``` + +All profit calculations include leverage. Stoploss / ROI also include leverage in their calculation. +Defining a stoploss of 10% at 10x leverage would trigger the stoploss with a 1% move to the downside. diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index ae0c6a6a3..fbfce37d1 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -31,11 +31,13 @@ pair = "BTC/USDT" ```python # Load data using values set above from freqtrade.data.history import load_pair_history +from freqtrade.enums import CandleType candles = load_pair_history(datadir=data_location, timeframe=config["timeframe"], pair=pair, data_format = "hdf5", + candle_type=CandleType.SPOT, ) # Confirm success @@ -93,7 +95,7 @@ from freqtrade.data.btanalysis import load_backtest_data, load_backtest_stats # if backtest_dir points to a directory, it'll automatically load the last backtest file. backtest_dir = config["user_data_dir"] / "backtest_results" -# backtest_dir can also point to a specific file +# backtest_dir can also point to a specific file # backtest_dir = config["user_data_dir"] / "backtest_results/backtest-result-2020-07-01_20-04-22.json" ``` diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index 458e80d0e..471ffa601 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -199,7 +199,7 @@ New string argument `side` - which can be either `"long"` or `"short"`. ``` python hl_lines="4" class AwesomeStrategy(IStrategy): def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], **kwargs) -> float: # ... return proposed_stake @@ -208,7 +208,7 @@ class AwesomeStrategy(IStrategy): ``` python hl_lines="4" class AwesomeStrategy(IStrategy): def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: # ... return proposed_stake diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 27f5f91b6..9853e15c6 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -97,7 +97,8 @@ Example configuration showing the different settings: "entry_fill": "off", "exit_fill": "off", "protection_trigger": "off", - "protection_trigger_global": "on" + "protection_trigger_global": "on", + "show_candle": "off" }, "reload": true, "balance_dust_level": 0.01 @@ -108,7 +109,7 @@ Example configuration showing the different settings: `exit` notifications are sent when the order is placed, while `exit_fill` notifications are sent when the order is filled on the exchange. `*_fill` notifications are off by default and must be explicitly enabled. `protection_trigger` notifications are sent when a protection triggers and `protection_trigger_global` notifications trigger when global protections are triggered. - +`show_candle` - show candle values as part of entry/exit messages. Only possible value is "ohlc". `balance_dust_level` will define what the `/balance` command takes as "dust" - Currencies with a balance below this will be shown. `reload` allows you to disable reload-buttons on selected messages. @@ -171,8 +172,8 @@ official commands. You can ask at any moment for help with `/help`. | `/locks` | Show currently locked pairs. | `/unlock ` | Remove the lock for this pair (or for this lock id). | `/profit []` | Display a summary of your profit/loss from close trades and some stats about your performance, over the last n days (all trades by default) -| `/forceexit ` | Instantly exits the given trade (Ignoring `minimum_roi`). -| `/forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`). +| `/forceexit | /fx ` | Instantly exits the given trade (Ignoring `minimum_roi`). +| `/forceexit all | /fx all` | Instantly exits all open trades (Ignoring `minimum_roi`). | `/fx` | alias for `/forceexit` | `/forcelong [rate]` | Instantly buys the given pair. Rate is optional and only applies to limit orders. (`force_entry_enable` must be set to True) | `/forceshort [rate]` | Instantly shorts the given pair. Rate is optional and only applies to limit orders. This will only work on non-spot markets. (`force_entry_enable` must be set to True) @@ -270,10 +271,15 @@ Return a summary of your profit/loss and performance. > **Latest Trade opened:** `2 minutes ago` > **Avg. Duration:** `2:33:45` > **Best Performing:** `PAY/BTC: 50.23%` +> **Trading volume:** `0.5 BTC` +> **Profit factor:** `1.04` +> **Max Drawdown:** `9.23% (0.01255 BTC)` The relative profit of `1.2%` is the average profit per trade. -The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. -Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. +The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. +Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. +Profit Factor is calculated as gross profits / gross losses - and should serve as an overall metric for the strategy. +Max drawdown corresponds to the backtesting metric `Absolute Drawdown (Account)` - calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`. ### /forceexit @@ -281,6 +287,7 @@ Starting capital is either taken from the `available_capital` setting, or calcul !!! Tip You can get a list of all open trades by calling `/forceexit` without parameter, which will show a list of buttons to simply exit a trade. + This command has an alias in `/fx` - which has the same capabilities, but is faster to type in "emergency" situations. ### /forcelong [rate] | /forceshort [rate] @@ -328,11 +335,11 @@ Per default `/daily` will return the 7 last days. The example below if for `/dai > **Daily Profit over the last 3 days:** ``` -Day Profit BTC Profit USD ----------- -------------- ------------ -2018-01-03 0.00224175 BTC 29,142 USD -2018-01-02 0.00033131 BTC 4,307 USD -2018-01-01 0.00269130 BTC 34.986 USD +Day (count) USDT USD Profit % +-------------- ------------ ---------- ---------- +2022-06-11 (1) -0.746 USDT -0.75 USD -0.08% +2022-06-10 (0) 0 USDT 0.00 USD 0.00% +2022-06-09 (5) 20 USDT 20.10 USD 5.00% ``` ### /weekly @@ -342,11 +349,11 @@ from Monday. The example below if for `/weekly 3`: > **Weekly Profit over the last 3 weeks (starting from Monday):** ``` -Monday Profit BTC Profit USD ----------- -------------- ------------ -2018-01-03 0.00224175 BTC 29,142 USD -2017-12-27 0.00033131 BTC 4,307 USD -2017-12-20 0.00269130 BTC 34.986 USD +Monday (count) Profit BTC Profit USD Profit % +------------- -------------- ------------ ---------- +2018-01-03 (5) 0.00224175 BTC 29,142 USD 4.98% +2017-12-27 (1) 0.00033131 BTC 4,307 USD 0.00% +2017-12-20 (4) 0.00269130 BTC 34.986 USD 5.12% ``` ### /monthly @@ -356,11 +363,11 @@ if for `/monthly 3`: > **Monthly Profit over the last 3 months:** ``` -Month Profit BTC Profit USD ----------- -------------- ------------ -2018-01 0.00224175 BTC 29,142 USD -2017-12 0.00033131 BTC 4,307 USD -2017-11 0.00269130 BTC 34.986 USD +Month (count) Profit BTC Profit USD Profit % +------------- -------------- ------------ ---------- +2018-01 (20) 0.00224175 BTC 29,142 USD 4.98% +2017-12 (5) 0.00033131 BTC 4,307 USD 0.00% +2017-11 (10) 0.00269130 BTC 34.986 USD 5.10% ``` ### /whitelist diff --git a/docs/updating.md b/docs/updating.md index 1839edc4c..8dc7279a4 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -32,4 +32,8 @@ Please ensure that you're also updating dependencies - otherwise things might br ``` bash git pull pip install -U -r requirements.txt +pip install -e . + +# Ensure freqUI is at the latest version +freqtrade install-ui ``` diff --git a/docs/utils.md b/docs/utils.md index 5ef5646c3..0dd88b242 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -119,6 +119,7 @@ This subcommand is useful for finding problems in your environment with loading usage: freqtrade list-strategies [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--userdir PATH] [--strategy-path PATH] [-1] [--no-color] + [--recursive-strategy-search] optional arguments: -h, --help show this help message and exit @@ -126,6 +127,9 @@ optional arguments: -1, --one-column Print output in one column. --no-color Disable colorization of hyperopt results. May be useful if you are redirecting output to a file. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -134,9 +138,10 @@ Common arguments: details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH @@ -549,6 +554,27 @@ Show whitelist when using a [dynamic pairlist](plugins.md#pairlists). freqtrade test-pairlist --config config.json --quote USDT BTC ``` +## Convert database + +`freqtrade convert-db` can be used to convert your database from one system to another (sqlite -> postgres, postgres -> other postgres), migrating all trades, orders and Pairlocks. + +Please refer to the [SQL cheatsheet](sql_cheatsheet.md#use-a-different-database-system) to learn about requirements for different database systems. + +``` +usage: freqtrade convert-db [-h] [--db-url PATH] [--db-url-from PATH] + +optional arguments: + -h, --help show this help message and exit + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for + Dry Run). + --db-url-from PATH Source db url to use when migrating a database. +``` + +!!! Warning + Please ensure to only use this on an empty target database. Freqtrade will perform a regular migration, but may fail if entries already existed. + ## Webserver mode !!! Warning "Experimental" @@ -625,6 +651,61 @@ Common arguments: ``` +## Detailed backtest analysis + +Advanced backtest result analysis. + +More details in the [Backtesting analysis](advanced-backtesting.md#analyze-the-buyentry-and-sellexit-tags) Section. + +``` +usage: freqtrade backtesting-analysis [-h] [-v] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--export-filename PATH] + [--analysis-groups {0,1,2,3,4} [{0,1,2,3,4} ...]] + [--enter-reason-list ENTER_REASON_LIST [ENTER_REASON_LIST ...]] + [--exit-reason-list EXIT_REASON_LIST [EXIT_REASON_LIST ...]] + [--indicator-list INDICATOR_LIST [INDICATOR_LIST ...]] + +optional arguments: + -h, --help show this help message and exit + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --analysis-groups {0,1,2,3,4} [{0,1,2,3,4} ...] + grouping output - 0: simple wins/losses by enter tag, + 1: by enter_tag, 2: by enter_tag and exit_tag, 3: by + pair and enter_tag, 4: by pair, enter_ and exit_tag + (this can get quite large) + --enter-reason-list ENTER_REASON_LIST [ENTER_REASON_LIST ...] + Comma separated list of entry signals to analyse. + Default: all. e.g. 'entry_tag_a,entry_tag_b' + --exit-reason-list EXIT_REASON_LIST [EXIT_REASON_LIST ...] + Comma separated list of exit signals to analyse. + Default: all. e.g. + 'exit_tag_a,roi,stop_loss,trailing_stop_loss' + --indicator-list INDICATOR_LIST [INDICATOR_LIST ...] + Comma separated list of indicators to analyse. e.g. + 'close,rsi,bb_lowerband,profit_abs' + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` + ## List Hyperopt results You can list the hyperoptimization epochs the Hyperopt module evaluated previously with the `hyperopt-list` sub-command. diff --git a/docs/webhook-config.md b/docs/webhook-config.md index 5f5933b47..3677ebe89 100644 --- a/docs/webhook-config.md +++ b/docs/webhook-config.md @@ -239,3 +239,52 @@ Possible parameters are: The fields in `webhook.webhookstatus` are used for regular status messages (Started / Stopped / ...). Parameters are filled using string.format. The only possible value here is `{status}`. + +## Discord + +A special form of webhooks is available for discord. +You can configure this as follows: + +```json +"discord": { + "enabled": true, + "webhook_url": "https://discord.com/api/webhooks/", + "exit_fill": [ + {"Trade ID": "{trade_id}"}, + {"Exchange": "{exchange}"}, + {"Pair": "{pair}"}, + {"Direction": "{direction}"}, + {"Open rate": "{open_rate}"}, + {"Close rate": "{close_rate}"}, + {"Amount": "{amount}"}, + {"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"}, + {"Close date": "{close_date:%Y-%m-%d %H:%M:%S}"}, + {"Profit": "{profit_amount} {stake_currency}"}, + {"Profitability": "{profit_ratio:.2%}"}, + {"Enter tag": "{enter_tag}"}, + {"Exit Reason": "{exit_reason}"}, + {"Strategy": "{strategy}"}, + {"Timeframe": "{timeframe}"}, + ], + "entry_fill": [ + {"Trade ID": "{trade_id}"}, + {"Exchange": "{exchange}"}, + {"Pair": "{pair}"}, + {"Direction": "{direction}"}, + {"Open rate": "{open_rate}"}, + {"Amount": "{amount}"}, + {"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"}, + {"Enter tag": "{enter_tag}"}, + {"Strategy": "{strategy} {timeframe}"}, + ] +} +``` + + +The above represents the default (`exit_fill` and `entry_fill` are optional and will default to the above configuration) - modifications are obviously possible. + +Available fields correspond to the fields for webhooks and are documented in the corresponding webhook sections. + +The notifications will look as follows by default. + +![discord-notification](assets/discord_notification.png) diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index 129836000..d93ed1e09 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -6,10 +6,12 @@ Contains all start-commands, subcommands and CLI Interface creation. Note: Be careful with file-scoped imports in these subfiles. as they are parsed on startup, nothing containing optional modules should be loaded. """ +from freqtrade.commands.analyze_commands import start_analysis_entries_exits from freqtrade.commands.arguments import Arguments from freqtrade.commands.build_config_commands import start_new_config from freqtrade.commands.data_commands import (start_convert_data, start_convert_trades, start_download_data, start_list_data) +from freqtrade.commands.db_commands import start_convert_db from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui, start_new_strategy) from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show diff --git a/freqtrade/commands/analyze_commands.py b/freqtrade/commands/analyze_commands.py new file mode 100755 index 000000000..b6b790788 --- /dev/null +++ b/freqtrade/commands/analyze_commands.py @@ -0,0 +1,69 @@ +import logging +from pathlib import Path +from typing import Any, Dict + +from freqtrade.configuration import setup_utils_configuration +from freqtrade.enums import RunMode +from freqtrade.exceptions import OperationalException + + +logger = logging.getLogger(__name__) + + +def setup_analyze_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]: + """ + Prepare the configuration for the entry/exit reason analysis module + :param args: Cli args from Arguments() + :param method: Bot running mode + :return: Configuration + """ + config = setup_utils_configuration(args, method) + + no_unlimited_runmodes = { + RunMode.BACKTEST: 'backtesting', + } + if method in no_unlimited_runmodes.keys(): + from freqtrade.data.btanalysis import get_latest_backtest_filename + + if 'exportfilename' in config: + if config['exportfilename'].is_dir(): + btfile = Path(get_latest_backtest_filename(config['exportfilename'])) + signals_file = f"{config['exportfilename']}/{btfile.stem}_signals.pkl" + else: + if config['exportfilename'].exists(): + btfile = Path(config['exportfilename']) + signals_file = f"{btfile.parent}/{btfile.stem}_signals.pkl" + else: + raise OperationalException(f"{config['exportfilename']} does not exist.") + else: + raise OperationalException('exportfilename not in config.') + + if (not Path(signals_file).exists()): + raise OperationalException( + (f"Cannot find latest backtest signals file: {signals_file}." + "Run backtesting with `--export signals`.") + ) + + return config + + +def start_analysis_entries_exits(args: Dict[str, Any]) -> None: + """ + Start analysis script + :param args: Cli args from Arguments() + :return: None + """ + from freqtrade.data.entryexitanalysis import process_entry_exit_reasons + + # Initialize configuration + config = setup_analyze_configuration(args, RunMode.BACKTEST) + + logger.info('Starting freqtrade in analysis mode') + + process_entry_exit_reasons(config['exportfilename'], + config['exchange']['pair_whitelist'], + config['analysis_groups'], + config['enter_reason_list'], + config['exit_reason_list'], + config['indicator_list'] + ) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index ff1d16590..1e3e2845a 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -82,7 +82,9 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "timeframe", "plot_auto_open", ] -ARGS_INSTALL_UI = ["erase_ui_only", 'ui_version'] +ARGS_CONVERT_DB = ["db_url", "db_url_from"] + +ARGS_INSTALL_UI = ["erase_ui_only", "ui_version"] ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"] @@ -99,6 +101,9 @@ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperop "print_json", "hyperoptexportfilename", "hyperopt_show_no_header", "disableparamexport", "backtest_breakdown"] +ARGS_ANALYZE_ENTRIES_EXITS = ["exportfilename", "analysis_groups", "enter_reason_list", + "exit_reason_list", "indicator_list"] + NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-pairs", "list-strategies", "list-data", "hyperopt-list", "hyperopt-show", "backtest-filter", @@ -180,8 +185,9 @@ class Arguments: self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') self._build_args(optionlist=['version'], parser=self.parser) - from freqtrade.commands import (start_backtesting, start_backtesting_show, - start_convert_data, start_convert_trades, + from freqtrade.commands import (start_analysis_entries_exits, start_backtesting, + start_backtesting_show, start_convert_data, + start_convert_db, start_convert_trades, start_create_userdir, start_download_data, start_edge, start_hyperopt, start_hyperopt_list, start_hyperopt_show, start_install_ui, start_list_data, start_list_exchanges, @@ -281,6 +287,13 @@ class Arguments: backtesting_show_cmd.set_defaults(func=start_backtesting_show) self._build_args(optionlist=ARGS_BACKTEST_SHOW, parser=backtesting_show_cmd) + # Add backtesting analysis subcommand + analysis_cmd = subparsers.add_parser('backtesting-analysis', + help='Backtest Analysis module.', + parents=[_common_parser]) + analysis_cmd.set_defaults(func=start_analysis_entries_exits) + self._build_args(optionlist=ARGS_ANALYZE_ENTRIES_EXITS, parser=analysis_cmd) + # Add edge subcommand edge_cmd = subparsers.add_parser('edge', help='Edge module.', parents=[_common_parser, _strategy_parser]) @@ -374,6 +387,14 @@ class Arguments: test_pairlist_cmd.set_defaults(func=start_test_pairlist) self._build_args(optionlist=ARGS_TEST_PAIRLIST, parser=test_pairlist_cmd) + # Add db-convert subcommand + convert_db = subparsers.add_parser( + "convert-db", + help="Migrate database to different system", + ) + convert_db.set_defaults(func=start_convert_db) + self._build_args(optionlist=ARGS_CONVERT_DB, parser=convert_db) + # Add install-ui subcommand install_ui_cmd = subparsers.add_parser( 'install-ui', diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 58e208652..3370ce64b 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -106,6 +106,11 @@ AVAILABLE_CLI_OPTIONS = { f'`{constants.DEFAULT_DB_DRYRUN_URL}` for Dry Run).', metavar='PATH', ), + "db_url_from": Arg( + '--db-url-from', + help='Source db url to use when migrating a database.', + metavar='PATH', + ), "sd_notify": Arg( '--sd-notify', help='Notify systemd service manager.', @@ -609,4 +614,37 @@ AVAILABLE_CLI_OPTIONS = { "that do not contain any parameters."), action="store_true", ), + "analysis_groups": Arg( + "--analysis-groups", + help=("grouping output - " + "0: simple wins/losses by enter tag, " + "1: by enter_tag, " + "2: by enter_tag and exit_tag, " + "3: by pair and enter_tag, " + "4: by pair, enter_ and exit_tag (this can get quite large)"), + nargs='+', + default=['0', '1', '2'], + choices=['0', '1', '2', '3', '4'], + ), + "enter_reason_list": Arg( + "--enter-reason-list", + help=("Comma separated list of entry signals to analyse. Default: all. " + "e.g. 'entry_tag_a,entry_tag_b'"), + nargs='+', + default=['all'], + ), + "exit_reason_list": Arg( + "--exit-reason-list", + help=("Comma separated list of exit signals to analyse. Default: all. " + "e.g. 'exit_tag_a,roi,stop_loss,trailing_stop_loss'"), + nargs='+', + default=['all'], + ), + "indicator_list": Arg( + "--indicator-list", + help=("Comma separated list of indicators to analyse. " + "e.g. 'close,rsi,bb_lowerband,profit_abs'"), + nargs='+', + default=[], + ), } diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index a2e2a100a..61a99782e 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -79,6 +79,12 @@ def start_download_data(args: Dict[str, Any]) -> None: data_format_trades=config['dataformat_trades'], ) else: + if not exchange._ft_has.get('ohlcv_has_history', True): + raise OperationalException( + f"Historic klines not available for {exchange.name}. " + "Please use `--dl-trades` instead for this exchange " + "(will unfortunately take a long time)." + ) pairs_not_available = refresh_backtest_ohlcv_data( exchange, pairs=expanded_pairs, timeframes=config['timeframes'], datadir=config['datadir'], timerange=timerange, diff --git a/freqtrade/commands/db_commands.py b/freqtrade/commands/db_commands.py new file mode 100644 index 000000000..618b5cb6e --- /dev/null +++ b/freqtrade/commands/db_commands.py @@ -0,0 +1,55 @@ +import logging +from typing import Any, Dict + +from sqlalchemy import func + +from freqtrade.configuration.config_setup import setup_utils_configuration +from freqtrade.enums.runmode import RunMode + + +logger = logging.getLogger(__name__) + + +def start_convert_db(args: Dict[str, Any]) -> None: + from sqlalchemy.orm import make_transient + + from freqtrade.persistence import Order, Trade, init_db + from freqtrade.persistence.migrations import set_sequence_ids + from freqtrade.persistence.pairlock import PairLock + + config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) + + init_db(config['db_url']) + session_target = Trade._session + init_db(config['db_url_from']) + logger.info("Starting db migration.") + + trade_count = 0 + pairlock_count = 0 + for trade in Trade.get_trades(): + trade_count += 1 + make_transient(trade) + for o in trade.orders: + make_transient(o) + + session_target.add(trade) + + session_target.commit() + + for pairlock in PairLock.query: + pairlock_count += 1 + make_transient(pairlock) + session_target.add(pairlock) + session_target.commit() + + # Update sequences + max_trade_id = session_target.query(func.max(Trade.id)).scalar() + max_order_id = session_target.query(func.max(Order.id)).scalar() + max_pairlock_id = session_target.query(func.max(PairLock.id)).scalar() + + set_sequence_ids(session_target.get_bind(), + trade_id=max_trade_id, + order_id=max_order_id, + pairlock_id=max_pairlock_id) + + logger.info(f"Migrated {trade_count} Trades, and {pairlock_count} Pairlocks.") diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 344828282..19e291ea7 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -24,7 +24,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: print_colorized = config.get('print_colorized', False) print_json = config.get('print_json', False) - export_csv = config.get('export_csv', None) + export_csv = config.get('export_csv') no_details = config.get('hyperopt_list_no_details', False) no_header = False diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index 2a5223917..eb761eeec 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -212,7 +212,7 @@ def start_show_trades(args: Dict[str, Any]) -> None: raise OperationalException("--db-url is required for this command.") logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"') - init_db(config['db_url'], clean_open_orders=False) + init_db(config['db_url']) tfilter = [] if config.get('trade_ids'): diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index fa1f47f9b..2be13ce4f 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -27,7 +27,7 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: return True logger.info("Checking exchange...") - exchange = config.get('exchange', {}).get('name').lower() + exchange = config.get('exchange', {}).get('name', '').lower() if not exchange: raise OperationalException( f'This command requires a configured exchange. You should either use ' diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 80df6fb3f..d46d54cb0 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -95,6 +95,8 @@ class Configuration: self._process_data_options(config) + self._process_analyze_options(config) + # Check if the exchange set by the user is supported check_exchange(config, config.get('experimental', {}).get('block_bad_exchanges', True)) @@ -127,7 +129,7 @@ class Configuration: # Default to in-memory db for dry_run if not specified config['db_url'] = constants.DEFAULT_DB_DRYRUN_URL else: - if not config.get('db_url', None): + if not config.get('db_url'): config['db_url'] = constants.DEFAULT_DB_PROD_URL logger.info('Dry run is disabled') @@ -147,6 +149,9 @@ class Configuration: config.update({'db_url': self.args['db_url']}) logger.info('Parameter --db-url detected ...') + self._args_to_config(config, argname='db_url_from', + logstring='Parameter --db-url-from detected ...') + if config.get('force_entry_enable', False): logger.warning('`force_entry_enable` RPC message enabled.') @@ -177,7 +182,7 @@ class Configuration: config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False) logger.info('Using user-data directory: %s ...', config['user_data_dir']) - config.update({'datadir': create_datadir(config, self.args.get('datadir', None))}) + config.update({'datadir': create_datadir(config, self.args.get('datadir'))}) logger.info('Using data directory: %s ...', config.get('datadir')) if self.args.get('exportfilename'): @@ -216,7 +221,7 @@ class Configuration: if config.get('max_open_trades') == -1: config['max_open_trades'] = float('inf') - if self.args.get('stake_amount', None): + if self.args.get('stake_amount'): # Convert explicitly to float to support CLI argument for both unlimited and value try: self.args['stake_amount'] = float(self.args['stake_amount']) @@ -430,6 +435,19 @@ class Configuration: self._args_to_config(config, argname='candle_types', logstring='Detected --candle-types: {}') + def _process_analyze_options(self, config: Dict[str, Any]) -> None: + self._args_to_config(config, argname='analysis_groups', + logstring='Analysis reason groups: {}') + + self._args_to_config(config, argname='enter_reason_list', + logstring='Analysis enter tag list: {}') + + self._args_to_config(config, argname='exit_reason_list', + logstring='Analysis exit tag list: {}') + + self._args_to_config(config, argname='indicator_list', + logstring='Analysis indicator list: {}') + def _process_runmode(self, config: Dict[str, Any]) -> None: self._args_to_config(config, argname='dry_run', @@ -456,7 +474,7 @@ class Configuration: configuration instead of the content) """ if (argname in self.args and self.args[argname] is not None - and self.args[argname] is not False): + and self.args[argname] is not False): config.update({argname: self.args[argname]}) if logfun: @@ -487,7 +505,8 @@ class Configuration: if not pairs_file.exists(): raise OperationalException(f'No pairs file found with path "{pairs_file}".') config['pairs'] = load_file(pairs_file) - config['pairs'].sort() + if isinstance(config['pairs'], list): + config['pairs'].sort() return if 'config' in self.args and self.args['config']: @@ -498,5 +517,5 @@ class Configuration: pairs_file = config['datadir'] / 'pairs.json' if pairs_file.exists(): config['pairs'] = load_file(pairs_file) - if 'pairs' in config: + if 'pairs' in config and isinstance(config['pairs'], list): config['pairs'].sort() diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 70d29e2bd..e88383785 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -113,7 +113,7 @@ def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None: process_removed_setting(config, 'experimental', 'ignore_roi_if_buy_signal', None, 'ignore_roi_if_entry_signal') - process_removed_setting(config, 'ask_strategy', 'use_sell_signal', None, 'exit_sell_signal') + process_removed_setting(config, 'ask_strategy', 'use_sell_signal', None, 'use_exit_signal') process_removed_setting(config, 'ask_strategy', 'sell_profit_only', None, 'exit_profit_only') process_removed_setting(config, 'ask_strategy', 'sell_profit_offset', None, 'exit_profit_offset') diff --git a/freqtrade/configuration/directory_operations.py b/freqtrade/configuration/directory_operations.py index ca305c260..771fd53cc 100644 --- a/freqtrade/configuration/directory_operations.py +++ b/freqtrade/configuration/directory_operations.py @@ -15,7 +15,7 @@ def create_datadir(config: Dict[str, Any], datadir: Optional[str] = None) -> Pat folder = Path(datadir) if datadir else Path(f"{config['user_data_dir']}/data") if not datadir: # set datadir - exchange_name = config.get('exchange', {}).get('name').lower() + exchange_name = config.get('exchange', {}).get('name', '').lower() folder = folder.joinpath(exchange_name) if not folder.is_dir(): diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 53cae8a8e..ce7c0ff83 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -302,17 +302,21 @@ CONF_SCHEMA = { 'exit_fill': { 'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS, - 'default': 'off' + 'default': 'on' }, 'protection_trigger': { 'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS, - 'default': 'off' + 'default': 'on' }, 'protection_trigger_global': { 'type': 'string', 'enum': TELEGRAM_SETTING_OPTIONS, }, + 'show_candle': { + 'type': 'string', + 'enum': ['off', 'ohlc'], + }, } }, 'reload': {'type': 'boolean'}, @@ -336,6 +340,47 @@ CONF_SCHEMA = { 'webhookstatus': {'type': 'object'}, }, }, + 'discord': { + 'type': 'object', + 'properties': { + 'enabled': {'type': 'boolean'}, + 'webhook_url': {'type': 'string'}, + "exit_fill": { + 'type': 'array', 'items': {'type': 'object'}, + 'default': [ + {"Trade ID": "{trade_id}"}, + {"Exchange": "{exchange}"}, + {"Pair": "{pair}"}, + {"Direction": "{direction}"}, + {"Open rate": "{open_rate}"}, + {"Close rate": "{close_rate}"}, + {"Amount": "{amount}"}, + {"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"}, + {"Close date": "{close_date:%Y-%m-%d %H:%M:%S}"}, + {"Profit": "{profit_amount} {stake_currency}"}, + {"Profitability": "{profit_ratio:.2%}"}, + {"Enter tag": "{enter_tag}"}, + {"Exit Reason": "{exit_reason}"}, + {"Strategy": "{strategy}"}, + {"Timeframe": "{timeframe}"}, + ] + }, + "entry_fill": { + 'type': 'array', 'items': {'type': 'object'}, + 'default': [ + {"Trade ID": "{trade_id}"}, + {"Exchange": "{exchange}"}, + {"Pair": "{pair}"}, + {"Direction": "{direction}"}, + {"Open rate": "{open_rate}"}, + {"Amount": "{amount}"}, + {"Open date": "{open_date:%Y-%m-%d %H:%M:%S}"}, + {"Enter tag": "{enter_tag}"}, + {"Strategy": "{strategy} {timeframe}"}, + ] + }, + } + }, 'api_server': { 'type': 'object', 'properties': { @@ -483,6 +528,8 @@ CANCEL_REASON = { "ALL_CANCELLED": "cancelled (all unfilled and partially filled open orders cancelled)", "CANCELLED_ON_EXCHANGE": "cancelled on exchange", "FORCE_EXIT": "forcesold", + "REPLACE": "cancelled to be replaced by new limit order", + "USER_CANCEL": "user requested order cancel" } # List of pairs with their timeframes @@ -494,3 +541,4 @@ TradeList = List[List] LongShort = Literal['long', 'short'] EntryExit = Literal['entry', 'exit'] +BuySell = Literal['buy', 'sell'] diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index e29d9ebe4..9e38f6833 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -26,7 +26,7 @@ BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date', 'profit_ratio', 'profit_abs', 'exit_reason', 'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs', 'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'enter_tag', - 'is_short' + 'is_short', 'open_timestamp', 'close_timestamp', 'orders' ] @@ -283,6 +283,8 @@ def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = Non if 'enter_tag' not in df.columns: df['enter_tag'] = df['buy_tag'] df = df.drop(['buy_tag'], axis=1) + if 'orders' not in df.columns: + df.loc[:, 'orders'] = None else: # old format - only with lists. @@ -337,7 +339,7 @@ def trade_list_to_dataframe(trades: List[LocalTrade]) -> pd.DataFrame: :param trades: List of trade objects :return: Dataframe with BT_DATA_COLUMNS """ - df = pd.DataFrame.from_records([t.to_json() for t in trades], columns=BT_DATA_COLUMNS) + df = pd.DataFrame.from_records([t.to_json(True) for t in trades], columns=BT_DATA_COLUMNS) if len(df) > 0: df.loc[:, 'close_date'] = pd.to_datetime(df['close_date'], utc=True) df.loc[:, 'open_date'] = pd.to_datetime(df['open_date'], utc=True) @@ -353,7 +355,7 @@ def load_trades_from_db(db_url: str, strategy: Optional[str] = None) -> pd.DataF Can also serve as protection to load the correct result. :return: Dataframe containing Trades """ - init_db(db_url, clean_open_orders=False) + init_db(db_url) filters = [] if strategy: diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py new file mode 100755 index 000000000..b22c3f87e --- /dev/null +++ b/freqtrade/data/entryexitanalysis.py @@ -0,0 +1,227 @@ +import logging +from pathlib import Path +from typing import List, Optional + +import joblib +import pandas as pd +from tabulate import tabulate + +from freqtrade.data.btanalysis import (get_latest_backtest_filename, load_backtest_data, + load_backtest_stats) +from freqtrade.exceptions import OperationalException + + +logger = logging.getLogger(__name__) + + +def _load_signal_candles(backtest_dir: Path): + if backtest_dir.is_dir(): + scpf = Path(backtest_dir, + Path(get_latest_backtest_filename(backtest_dir)).stem + "_signals.pkl" + ) + else: + scpf = Path(backtest_dir.parent / f"{backtest_dir.stem}_signals.pkl") + + try: + scp = open(scpf, "rb") + signal_candles = joblib.load(scp) + logger.info(f"Loaded signal candles: {str(scpf)}") + except Exception as e: + logger.error("Cannot load signal candles from pickled results: ", e) + + return signal_candles + + +def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles): + analysed_trades_dict = {} + analysed_trades_dict[strategy_name] = {} + + try: + logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs") + + for pair in pairlist: + if pair in signal_candles[strategy_name]: + analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators( + pair, + trades, + signal_candles[strategy_name][pair]) + except Exception as e: + print(f"Cannot process entry/exit reasons for {strategy_name}: ", e) + + return analysed_trades_dict + + +def _analyze_candles_and_indicators(pair, trades, signal_candles): + buyf = signal_candles + + if len(buyf) > 0: + buyf = buyf.set_index('date', drop=False) + trades_red = trades.loc[trades['pair'] == pair].copy() + + trades_inds = pd.DataFrame() + + if trades_red.shape[0] > 0 and buyf.shape[0] > 0: + for t, v in trades_red.open_date.items(): + allinds = buyf.loc[(buyf['date'] < v)] + if allinds.shape[0] > 0: + tmp_inds = allinds.iloc[[-1]] + + trades_red.loc[t, 'signal_date'] = tmp_inds['date'].values[0] + trades_red.loc[t, 'enter_reason'] = trades_red.loc[t, 'enter_tag'] + tmp_inds.index.rename('signal_date', inplace=True) + trades_inds = pd.concat([trades_inds, tmp_inds]) + + if 'signal_date' in trades_red: + trades_red['signal_date'] = pd.to_datetime(trades_red['signal_date'], utc=True) + trades_red.set_index('signal_date', inplace=True) + + try: + trades_red = pd.merge(trades_red, trades_inds, on='signal_date', how='outer') + except Exception as e: + raise e + return trades_red + else: + return pd.DataFrame() + + +def _do_group_table_output(bigdf, glist): + for g in glist: + # 0: summary wins/losses grouped by enter tag + if g == "0": + group_mask = ['enter_reason'] + wins = bigdf.loc[bigdf['profit_abs'] >= 0] \ + .groupby(group_mask) \ + .agg({'profit_abs': ['sum']}) + + wins.columns = ['profit_abs_wins'] + loss = bigdf.loc[bigdf['profit_abs'] < 0] \ + .groupby(group_mask) \ + .agg({'profit_abs': ['sum']}) + loss.columns = ['profit_abs_loss'] + + new = bigdf.groupby(group_mask).agg({'profit_abs': [ + 'count', + lambda x: sum(x > 0), + lambda x: sum(x <= 0)]}) + new = pd.concat([new, wins, loss], axis=1).fillna(0) + + new['profit_tot'] = new['profit_abs_wins'] - abs(new['profit_abs_loss']) + new['wl_ratio_pct'] = (new.iloc[:, 1] / new.iloc[:, 0] * 100).fillna(0) + new['avg_win'] = (new['profit_abs_wins'] / new.iloc[:, 1]).fillna(0) + new['avg_loss'] = (new['profit_abs_loss'] / new.iloc[:, 2]).fillna(0) + + new.columns = ['total_num_buys', 'wins', 'losses', 'profit_abs_wins', 'profit_abs_loss', + 'profit_tot', 'wl_ratio_pct', 'avg_win', 'avg_loss'] + + sortcols = ['total_num_buys'] + + _print_table(new, sortcols, show_index=True) + + else: + agg_mask = {'profit_abs': ['count', 'sum', 'median', 'mean'], + 'profit_ratio': ['sum', 'median', 'mean']} + agg_cols = ['num_buys', 'profit_abs_sum', 'profit_abs_median', + 'profit_abs_mean', 'median_profit_pct', 'mean_profit_pct', + 'total_profit_pct'] + sortcols = ['profit_abs_sum', 'enter_reason'] + + # 1: profit summaries grouped by enter_tag + if g == "1": + group_mask = ['enter_reason'] + + # 2: profit summaries grouped by enter_tag and exit_tag + if g == "2": + group_mask = ['enter_reason', 'exit_reason'] + + # 3: profit summaries grouped by pair and enter_tag + if g == "3": + group_mask = ['pair', 'enter_reason'] + + # 4: profit summaries grouped by pair, enter_ and exit_tag (this can get quite large) + if g == "4": + group_mask = ['pair', 'enter_reason', 'exit_reason'] + if group_mask: + new = bigdf.groupby(group_mask).agg(agg_mask).reset_index() + new.columns = group_mask + agg_cols + new['median_profit_pct'] = new['median_profit_pct'] * 100 + new['mean_profit_pct'] = new['mean_profit_pct'] * 100 + new['total_profit_pct'] = new['total_profit_pct'] * 100 + + _print_table(new, sortcols) + else: + logger.warning("Invalid group mask specified.") + + +def _print_results(analysed_trades, stratname, analysis_groups, + enter_reason_list, exit_reason_list, + indicator_list, columns=None): + if columns is None: + columns = ['pair', 'open_date', 'close_date', 'profit_abs', 'enter_reason', 'exit_reason'] + + bigdf = pd.DataFrame() + for pair, trades in analysed_trades[stratname].items(): + bigdf = pd.concat([bigdf, trades], ignore_index=True) + + if bigdf.shape[0] > 0 and ('enter_reason' in bigdf.columns): + if analysis_groups: + _do_group_table_output(bigdf, analysis_groups) + + if enter_reason_list and "all" not in enter_reason_list: + bigdf = bigdf.loc[(bigdf['enter_reason'].isin(enter_reason_list))] + + if exit_reason_list and "all" not in exit_reason_list: + bigdf = bigdf.loc[(bigdf['exit_reason'].isin(exit_reason_list))] + + if "all" in indicator_list: + print(bigdf) + elif indicator_list is not None: + available_inds = [] + for ind in indicator_list: + if ind in bigdf: + available_inds.append(ind) + ilist = ["pair", "enter_reason", "exit_reason"] + available_inds + _print_table(bigdf[ilist], sortcols=['exit_reason'], show_index=False) + else: + print("\\_ No trades to show") + + +def _print_table(df, sortcols=None, show_index=False): + if (sortcols is not None): + data = df.sort_values(sortcols) + else: + data = df + + print( + tabulate( + data, + headers='keys', + tablefmt='psql', + showindex=show_index + ) + ) + + +def process_entry_exit_reasons(backtest_dir: Path, + pairlist: List[str], + analysis_groups: Optional[List[str]] = ["0", "1", "2"], + enter_reason_list: Optional[List[str]] = ["all"], + exit_reason_list: Optional[List[str]] = ["all"], + indicator_list: Optional[List[str]] = []): + try: + backtest_stats = load_backtest_stats(backtest_dir) + for strategy_name, results in backtest_stats['strategy'].items(): + trades = load_backtest_data(backtest_dir, strategy_name) + + if not trades.empty: + signal_candles = _load_signal_candles(backtest_dir) + analysed_trades_dict = _process_candles_and_indicators(pairlist, strategy_name, + trades, signal_candles) + _print_results(analysed_trades_dict, + strategy_name, + analysis_groups, + enter_reason_list, + exit_reason_list, + indicator_list) + + except ValueError as e: + raise OperationalException(e) from e diff --git a/freqtrade/data/history/hdf5datahandler.py b/freqtrade/data/history/hdf5datahandler.py index 23120a4ba..dadc9c7e6 100644 --- a/freqtrade/data/history/hdf5datahandler.py +++ b/freqtrade/data/history/hdf5datahandler.py @@ -40,7 +40,7 @@ class HDF5DataHandler(IDataHandler): return [ ( cls.rebuild_pair_from_filename(match[1]), - match[2], + cls.rebuild_timeframe_from_filename(match[2]), CandleType.from_string(match[3]) ) for match in _tmp if match and len(match.groups()) > 1] @@ -109,7 +109,11 @@ class HDF5DataHandler(IDataHandler): ) if not filename.exists(): - return pd.DataFrame(columns=self._columns) + # Fallback mode for 1M files + filename = self._pair_data_filename( + self._datadir, pair, timeframe, candle_type=candle_type, no_timeframe_modify=True) + if not filename.exists(): + return pd.DataFrame(columns=self._columns) where = [] if timerange: if timerange.starttype == 'date': diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index eb36d2042..c972c841c 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -68,7 +68,8 @@ def load_data(datadir: Path, startup_candles: int = 0, fail_without_data: bool = False, data_format: str = 'json', - candle_type: CandleType = CandleType.SPOT + candle_type: CandleType = CandleType.SPOT, + user_futures_funding_rate: int = None, ) -> Dict[str, DataFrame]: """ Load ohlcv history data for a list of pairs. @@ -100,6 +101,10 @@ def load_data(datadir: Path, ) if not hist.empty: result[pair] = hist + else: + if candle_type is CandleType.FUNDING_RATE and user_futures_funding_rate is not None: + logger.warn(f"{pair} using user specified [{user_futures_funding_rate}]") + result[pair] = DataFrame(columns=["open", "close", "high", "low", "volume"]) if fail_without_data and not result: raise OperationalException("No data found. Terminating.") @@ -216,7 +221,7 @@ def _download_pair_history(pair: str, *, prepend=prepend) logger.info(f'({process}) - Download history data for "{pair}", {timeframe}, ' - f'{candle_type} and store in {datadir}.' + f'{candle_type} and store in {datadir}. ' f'From {format_ms_time(since_ms) if since_ms else "start"} to ' f'{format_ms_time(until_ms) if until_ms else "now"}' ) @@ -277,6 +282,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes pairs_not_available = [] data_handler = get_datahandler(datadir, data_format) candle_type = CandleType.get_default(trading_mode) + process = '' for idx, pair in enumerate(pairs, start=1): if pair not in exchange.markets: pairs_not_available.append(pair) diff --git a/freqtrade/data/history/idatahandler.py b/freqtrade/data/history/idatahandler.py index 2e6b070ca..07dc7c763 100644 --- a/freqtrade/data/history/idatahandler.py +++ b/freqtrade/data/history/idatahandler.py @@ -26,7 +26,7 @@ logger = logging.getLogger(__name__) class IDataHandler(ABC): - _OHLCV_REGEX = r'^([a-zA-Z_-]+)\-(\d+\S)\-?([a-zA-Z_]*)?(?=\.)' + _OHLCV_REGEX = r'^([a-zA-Z_-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)' def __init__(self, datadir: Path) -> None: self._datadir = datadir @@ -193,10 +193,14 @@ class IDataHandler(ABC): datadir: Path, pair: str, timeframe: str, - candle_type: CandleType + candle_type: CandleType, + no_timeframe_modify: bool = False ) -> Path: pair_s = misc.pair_to_filename(pair) candle = "" + if not no_timeframe_modify: + timeframe = cls.timeframe_to_file(timeframe) + if candle_type != CandleType.SPOT: datadir = datadir.joinpath('futures') candle = f"-{candle_type}" @@ -210,6 +214,18 @@ class IDataHandler(ABC): filename = datadir.joinpath(f'{pair_s}-trades.{cls._get_file_extension()}') return filename + @staticmethod + def timeframe_to_file(timeframe: str): + return timeframe.replace('M', 'Mo') + + @staticmethod + def rebuild_timeframe_from_filename(timeframe: str) -> str: + """ + converts timeframe from disk to file + Replaces mo with M (to avoid problems on case-insensitive filesystems) + """ + return re.sub('1mo', '1M', timeframe, flags=re.IGNORECASE) + @staticmethod def rebuild_pair_from_filename(pair: str) -> str: """ diff --git a/freqtrade/data/history/jsondatahandler.py b/freqtrade/data/history/jsondatahandler.py index 23054ac51..83ec183df 100644 --- a/freqtrade/data/history/jsondatahandler.py +++ b/freqtrade/data/history/jsondatahandler.py @@ -41,7 +41,7 @@ class JsonDataHandler(IDataHandler): return [ ( cls.rebuild_pair_from_filename(match[1]), - match[2], + cls.rebuild_timeframe_from_filename(match[2]), CandleType.from_string(match[3]) ) for match in _tmp if match and len(match.groups()) > 1] @@ -103,9 +103,14 @@ class JsonDataHandler(IDataHandler): :param candle_type: Any of the enum CandleType (must match trading mode!) :return: DataFrame with ohlcv data, or empty DataFrame """ - filename = self._pair_data_filename(self._datadir, pair, timeframe, candle_type=candle_type) + filename = self._pair_data_filename( + self._datadir, pair, timeframe, candle_type=candle_type) if not filename.exists(): - return DataFrame(columns=self._columns) + # Fallback mode for 1M files + filename = self._pair_data_filename( + self._datadir, pair, timeframe, candle_type=candle_type, no_timeframe_modify=True) + if not filename.exists(): + return DataFrame(columns=self._columns) try: pairdata = read_json(filename, orient='values') pairdata.columns = self._columns diff --git a/freqtrade/enums/exitchecktuple.py b/freqtrade/enums/exitchecktuple.py index c245a05da..cb6411caf 100644 --- a/freqtrade/enums/exitchecktuple.py +++ b/freqtrade/enums/exitchecktuple.py @@ -15,3 +15,9 @@ class ExitCheckTuple: @property def exit_flag(self): return self.exit_type != ExitType.NONE + + def __eq__(self, other): + return self.exit_type == other.exit_type and self.exit_reason == other.exit_reason + + def __repr__(self): + return f"ExitCheckTuple({self.exit_type}, {self.exit_reason})" diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 69ae5198a..37a3c419d 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -52,12 +52,17 @@ class Binance(Exchange): ordertype = 'stop' if self.trading_mode == TradingMode.FUTURES else 'stop_loss_limit' - return order['type'] == ordertype and ( - (side == "sell" and stop_loss > float(order['info']['stopPrice'])) or - (side == "buy" and stop_loss < float(order['info']['stopPrice'])) - ) + return ( + order.get('stopPrice', None) is None + or ( + order['type'] == ordertype + and ( + (side == "sell" and stop_loss > float(order['stopPrice'])) or + (side == "buy" and stop_loss < float(order['stopPrice'])) + ) + )) - def get_tickers(self, symbols: List[str] = None, cached: bool = False) -> Dict: + def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Dict: tickers = super().get_tickers(symbols=symbols, cached=cached) if self.trading_mode == TradingMode.FUTURES: # Binance's future result has no bid/ask values. @@ -95,7 +100,7 @@ class Binance(Exchange): async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, candle_type: CandleType, is_new_pair: bool = False, raise_: bool = False, - until_ms: int = None + until_ms: Optional[int] = None ) -> Tuple[str, str, str, List]: """ Overwrite to introduce "fast new pair" functionality by detecting the pair's listing date diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index ddffe1250..1cf6ba079 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -1,5321 +1,489 @@ { - "RAY/USDT": [ + "1000LUNC/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, "info": { "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, "info": { "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386950.0" + "cum": "386875.0" } } ], - "SUSHI/USDT": [ + "1000SHIB/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", + "notionalCap": "50000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", + "notionalCap": "150000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", + "notionalCap": "250000", + "notionalFloor": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", + "notionalCap": "500000", + "notionalFloor": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", + "notionalCap": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 50000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } } ], - "CVC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BTS/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "HOT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ZRX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "QTUM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "IOTA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BTC/BUSD": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.004, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.004", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.005", - "cum": "50.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 20, - "info": { - "bracket": "3", - "initialLeverage": "20", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.01", - "cum": "1300.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 7500000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "7500000", - "minNotional": "1000000", - "maintMarginRatio": "0.025", - "cum": "16300.0" - } - }, - { - "tier": 5, - "minNotional": 7500000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 6, - "info": { - "bracket": "5", - "initialLeverage": "6", - "maxNotional": "40000000", - "minNotional": "7500000", - "maintMarginRatio": "0.05", - "cum": "203800.0" - } - }, - { - "tier": 6, - "minNotional": 40000000, - "maxNotional": 100000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "6", - "initialLeverage": "5", - "maxNotional": "100000000", - "minNotional": "40000000", - "maintMarginRatio": "0.1", - "cum": "2203800.0" - } - }, - { - "tier": 7, - "minNotional": 100000000, - "maxNotional": 200000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "7", - "initialLeverage": "4", - "maxNotional": "200000000", - "minNotional": "100000000", - "maintMarginRatio": "0.125", - "cum": "4703800.0" - } - }, - { - "tier": 8, - "minNotional": 200000000, - "maxNotional": 400000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "8", - "initialLeverage": "3", - "maxNotional": "400000000", - "minNotional": "200000000", - "maintMarginRatio": "0.15", - "cum": "9703800.0" - } - }, - { - "tier": 9, - "minNotional": 400000000, - "maxNotional": 600000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "9", - "initialLeverage": "2", - "maxNotional": "600000000", - "minNotional": "400000000", - "maintMarginRatio": "0.25", - "cum": "4.97038E7" - } - }, - { - "tier": 10, - "minNotional": 600000000, - "maxNotional": 1000000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "10", - "initialLeverage": "1", - "maxNotional": "1000000000", - "minNotional": "600000000", - "maintMarginRatio": "0.5", - "cum": "1.997038E8" - } - } - ], - "WAVES/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ADA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "LIT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "NU/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "XTZ/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "BNB/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "AKRO/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.012, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.012", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "65.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "690.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5690.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11940.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386940.0" - } - } - ], - "HNT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ETC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "XMR/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "YFI/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "FTT/BUSD": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.05", - "cum": "2500.0" - } - }, - { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.1", - "cum": "27500.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "4", - "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.15", - "cum": "77500.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.25", - "cum": "277500.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", - "maintMarginRatio": "0.5", - "cum": "1527500.0" - } - } - ], - "BTCUSDT_210326": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "9223372036854775807", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - } - ], - "ETH/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 100, - "info": { - "bracket": "1", - "initialLeverage": "100", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.005", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "2", - "initialLeverage": "75", - "maxNotional": "100000", - "minNotional": "10000", - "maintMarginRatio": "0.0065", - "cum": "15.0" - } - }, - { - "tier": 3, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "3", - "initialLeverage": "50", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.01", - "cum": "365.0" - } - }, - { - "tier": 4, - "minNotional": 500000, - "maxNotional": 1500000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "4", - "initialLeverage": "25", - "maxNotional": "1500000", - "minNotional": "500000", - "maintMarginRatio": "0.02", - "cum": "5365.0" - } - }, - { - "tier": 5, - "minNotional": 1500000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "5", - "initialLeverage": "10", - "maxNotional": "4000000", - "minNotional": "1500000", - "maintMarginRatio": "0.05", - "cum": "50365.0" - } - }, - { - "tier": 6, - "minNotional": 4000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "6", - "initialLeverage": "5", - "maxNotional": "10000000", - "minNotional": "4000000", - "maintMarginRatio": "0.1", - "cum": "250365.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "7", - "initialLeverage": "4", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.125", - "cum": "500365.0" - } - }, - { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "8", - "initialLeverage": "3", - "maxNotional": "40000000", - "minNotional": "20000000", - "maintMarginRatio": "0.15", - "cum": "1000365.0" - } - }, - { - "tier": 9, - "minNotional": 40000000, - "maxNotional": 150000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "9", - "initialLeverage": "2", - "maxNotional": "150000000", - "minNotional": "40000000", - "maintMarginRatio": "0.25", - "cum": "5000365.0" - } - }, - { - "tier": 10, - "minNotional": 150000000, - "maxNotional": 500000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "10", - "initialLeverage": "1", - "maxNotional": "500000000", - "minNotional": "150000000", - "maintMarginRatio": "0.5", - "cum": "4.2500365E7" - } - } - ], - "ALICE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "ALPHA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "SFP/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "REEF/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BAT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "DOGE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "7000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "57000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "107000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.25", - "cum": "732000.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "10000000", - "maintMarginRatio": "0.5", - "cum": "3232000.0" - } - } - ], - "TRX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "RLC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "DOTECOUSDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.012, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.012", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "65.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "690.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5690.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11940.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "9223372036854775807", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386940.0" - } - } - ], - "BTCSTUSDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "9223372036854775807", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "STORJ/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "SNX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ETHUSDT_210625": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "7500.0" - } - }, - { - "tier": 3, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "57500.0" - } - }, - { - "tier": 4, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "107500.0" - } - }, - { - "tier": 5, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "232500.0" - } - }, - { - "tier": 6, - "minNotional": 10000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "9223372036854775807", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1232500.0" - } - } - ], "1000XEC/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "AUDIO/USDT": [ + "1INCH/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.012, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.012", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "65.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "690.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5690.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11940.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "10000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386940.0" + } + } + ], + "AAVE/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "XLM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "BTCBUSD_210129": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.004, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.004", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 15, - "info": { - "bracket": "2", - "initialLeverage": "15", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.005", - "cum": "5.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.01", - "cum": "130.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 7, - "info": { - "bracket": "4", - "initialLeverage": "7", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.025", - "cum": "1630.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 6, - "info": { - "bracket": "5", - "initialLeverage": "6", - "maxNotional": "2000000", - "minNotional": "500000", - "maintMarginRatio": "0.05", - "cum": "14130.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "6", - "initialLeverage": "5", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.1", - "cum": "114130.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "7", - "initialLeverage": "4", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.125", - "cum": "239130.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "8", - "initialLeverage": "3", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.15", - "cum": "489130.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "9", - "initialLeverage": "2", - "maxNotional": "9223372036854775807", - "minNotional": "20000000", - "maintMarginRatio": "0.25", - "cum": "2489130.0" - } - } - ], - "IOTX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "NEO/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "UNFI/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "SAND/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "DASH/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "KAVA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "RUNE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "CTK/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "LINK/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "CELR/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "RSR/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5323,7407 +491,1109 @@ ], "ADA/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "1", "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", + "notionalCap": "100000", + "notionalFloor": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, + "tier": 2.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "2", "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", + "notionalCap": "500000", + "notionalFloor": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, + "tier": 3.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "3", "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", + "notionalCap": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "4", "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, + "tier": 5.0, + "currency": "BUSD", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "BUSD", + "minNotional": 5000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "8000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } } ], - "DGB/USDT": [ + "ADA/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "SKL/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "REN/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "LPT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "TOMO/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "MTL/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "LTC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, + "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", + "notionalCap": "10000", + "notionalFloor": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", + "notionalCap": "50000", + "notionalFloor": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "7", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } } ], - "DODO/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "EGLD/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "KSM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BNB/BUSD": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.05", - "cum": "2500.0" - } - }, - { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.1", - "cum": "27500.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "4", - "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.15", - "cum": "77500.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.25", - "cum": "277500.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", - "maintMarginRatio": "0.5", - "cum": "1527500.0" - } - } - ], - "BTCUSDT_210625": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "7500.0" - } - }, - { - "tier": 3, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "57500.0" - } - }, - { - "tier": 4, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "107500.0" - } - }, - { - "tier": 5, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "232500.0" - } - }, - { - "tier": 6, - "minNotional": 10000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "9223372036854775807", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1232500.0" - } - } - ], - "ONT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "VET/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "TRB/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "MANA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "COTI/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "CHR/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ETHUSDT_210924": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "7500.0" - } - }, - { - "tier": 3, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "57500.0" - } - }, - { - "tier": 4, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "107500.0" - } - }, - { - "tier": 5, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "232500.0" - } - }, - { - "tier": 6, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1232500.0" - } - }, - { - "tier": 7, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6232500.0" - } - } - ], - "BAKE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "GRT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ETHUSDT_220325": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 375000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "375000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 375000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "2000000", - "minNotional": "375000", - "maintMarginRatio": "0.05", - "cum": "11250.0" - } - }, - { - "tier": 3, - "minNotional": 2000000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "4000000", - "minNotional": "2000000", - "maintMarginRatio": "0.1", - "cum": "111250.0" - } - }, - { - "tier": 4, - "minNotional": 4000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "10000000", - "minNotional": "4000000", - "maintMarginRatio": "0.125", - "cum": "211250.0" - } - }, - { - "tier": 5, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.15", - "cum": "461250.0" - } - }, - { - "tier": 6, - "minNotional": 20000000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "40000000", - "minNotional": "20000000", - "maintMarginRatio": "0.25", - "cum": "2461250.0" - } - }, - { - "tier": 7, - "minNotional": 40000000, - "maxNotional": 400000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "400000000", - "minNotional": "40000000", - "maintMarginRatio": "0.5", - "cum": "1.246125E7" - } - } - ], - "FLM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "MASK/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "EOS/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "ETHUSDT_211231": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 375000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "375000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 375000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "2000000", - "minNotional": "375000", - "maintMarginRatio": "0.05", - "cum": "11250.0" - } - }, - { - "tier": 3, - "minNotional": 2000000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "4000000", - "minNotional": "2000000", - "maintMarginRatio": "0.1", - "cum": "111250.0" - } - }, - { - "tier": 4, - "minNotional": 4000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "10000000", - "minNotional": "4000000", - "maintMarginRatio": "0.125", - "cum": "211250.0" - } - }, - { - "tier": 5, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.15", - "cum": "461250.0" - } - }, - { - "tier": 6, - "minNotional": 20000000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "40000000", - "minNotional": "20000000", - "maintMarginRatio": "0.25", - "cum": "2461250.0" - } - }, - { - "tier": 7, - "minNotional": 40000000, - "maxNotional": 400000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "400000000", - "minNotional": "40000000", - "maintMarginRatio": "0.5", - "cum": "1.246125E7" - } - } - ], - "OGN/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "SC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BAL/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "STMX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BTTUSDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "LUNA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, - "info": { - "bracket": "6", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.1665", - "cum": "315500.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 15000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "7", - "initialLeverage": "2", - "maxNotional": "15000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1150500.0" - } - }, - { - "tier": 8, - "minNotional": 15000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "8", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "15000000", - "maintMarginRatio": "0.5", - "cum": "4900500.0" - } - } - ], - "DENT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "1000BTTC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "KNC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "SRM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ENJ/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "C98/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ZEN/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ATOM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "NEAR/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "SOL/BUSD": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.05", - "cum": "2500.0" - } - }, - { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.1", - "cum": "27500.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "4", - "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.15", - "cum": "77500.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.25", - "cum": "277500.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", - "maintMarginRatio": "0.5", - "cum": "1527500.0" - } - } - ], - "ENS/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BCH/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "9", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6233035.0" - } - } - ], - "ATA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "IOST/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "HBAR/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ZEC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "1000SHIB/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "TLM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ANT/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BZRXUSDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ETH/BUSD": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 25000, - "maintenanceMarginRate": 0.004, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "25000", - "minNotional": "0", - "maintMarginRatio": "0.004", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.005", - "cum": "25.0" - } - }, - { - "tier": 3, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 20, - "info": { - "bracket": "3", - "initialLeverage": "20", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.01", - "cum": "525.0" - } - }, - { - "tier": 4, - "minNotional": 500000, - "maxNotional": 1500000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1500000", - "minNotional": "500000", - "maintMarginRatio": "0.025", - "cum": "8025.0" - } - }, - { - "tier": 5, - "minNotional": 1500000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 6, - "info": { - "bracket": "5", - "initialLeverage": "6", - "maxNotional": "4000000", - "minNotional": "1500000", - "maintMarginRatio": "0.05", - "cum": "45525.0" - } - }, - { - "tier": 6, - "minNotional": 4000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "6", - "initialLeverage": "5", - "maxNotional": "10000000", - "minNotional": "4000000", - "maintMarginRatio": "0.1", - "cum": "245525.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "7", - "initialLeverage": "4", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.125", - "cum": "495525.0" - } - }, - { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "8", - "initialLeverage": "3", - "maxNotional": "40000000", - "minNotional": "20000000", - "maintMarginRatio": "0.15", - "cum": "995525.0" - } - }, - { - "tier": 9, - "minNotional": 40000000, - "maxNotional": 150000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "9", - "initialLeverage": "2", - "maxNotional": "150000000", - "minNotional": "40000000", - "maintMarginRatio": "0.25", - "cum": "4995525.0" - } - }, - { - "tier": 10, - "minNotional": 150000000, - "maxNotional": 500000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "10", - "initialLeverage": "1", - "maxNotional": "500000000", - "minNotional": "150000000", - "maintMarginRatio": "0.5", - "cum": "4.2495525E7" - } - } - ], - "GALA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "AAVE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, - "info": { - "bracket": "6", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.1665", - "cum": "315500.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "7", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1150500.0" - } - }, - { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "8", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6150500.0" - } - } - ], - "GTC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], "ALGO/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "ICP/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "BTCUSDT_210924": [ + "ALICE/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "7500.0" - } - }, - { - "tier": 3, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "57500.0" - } - }, - { - "tier": 4, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "107500.0" - } - }, - { - "tier": 5, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "232500.0" - } - }, - { - "tier": 6, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1232500.0" - } - }, - { - "tier": 7, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6232500.0" - } - } - ], - "LRC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], - "AVAX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 750000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "750000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 750000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "750000", - "maintMarginRatio": "0.25", - "cum": "123250.0" - } - }, - { - "tier": 7, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "373250.0" - } - } - ], - "BTCUSDT_220325": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 375000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "375000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 375000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "2000000", - "minNotional": "375000", - "maintMarginRatio": "0.05", - "cum": "11250.0" - } - }, - { - "tier": 3, - "minNotional": 2000000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "4000000", - "minNotional": "2000000", - "maintMarginRatio": "0.1", - "cum": "111250.0" - } - }, - { - "tier": 4, - "minNotional": 4000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "10000000", - "minNotional": "4000000", - "maintMarginRatio": "0.125", - "cum": "211250.0" - } - }, - { - "tier": 5, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.15", - "cum": "461250.0" - } - }, - { - "tier": 6, - "minNotional": 20000000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "40000000", - "minNotional": "20000000", - "maintMarginRatio": "0.25", - "cum": "2461250.0" - } - }, - { - "tier": 7, - "minNotional": 40000000, - "maxNotional": 400000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "400000000", - "minNotional": "40000000", - "maintMarginRatio": "0.5", - "cum": "1.246125E7" - } - } - ], - "ARPA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "CELO/USDT": [ + "ALPHA/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ROSE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "MATIC/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "750.0" + "cum": "75.0" } }, { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "4500.0" + "cum": "700.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", - "cum": "17000.0" + "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 500000, - "maxNotional": 750000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 2.0, "info": { "bracket": "5", - "initialLeverage": "4", - "maxNotional": "750000", - "minNotional": "500000", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", - "cum": "29500.0" + "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 750000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, "info": { "bracket": "6", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "750000", - "maintMarginRatio": "0.25", - "cum": "123250.0" - } - }, - { - "tier": 7, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "373250.0" + "cum": "386950.0" } } ], - "1INCH/USDT": [ + "ANC/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "ANKR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.012, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 100000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "100000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } } ], - "MKR/USDT": [ + "ANT/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "PEOPLE/USDT": [ + "APE/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "APE/USDT": [ + { + "tier": 1.0, + "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, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "API3/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "THETA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, - "info": { - "bracket": "6", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.1665", - "cum": "315500.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "7", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1150500.0" - } - }, - { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "8", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6150500.0" - } - } - ], - "UNI/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, - "info": { - "bracket": "6", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.1665", - "cum": "315500.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "7", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1150500.0" - } - }, - { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "8", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6150500.0" - } - } - ], - "ETHUSDT_210326": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, - "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, - "info": { - "bracket": "1", - "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", - "maintMarginRatio": "0.0065", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "2", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", - "maintMarginRatio": "0.01", - "cum": "35.0" - } - }, - { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "3", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "535.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "4", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8035.0" - } - }, - { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "5", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58035.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "6", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108035.0" - } - }, - { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "7", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.15", - "cum": "233035.0" - } - }, - { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "8", - "initialLeverage": "2", - "maxNotional": "9223372036854775807", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1233035.0" - } - } - ], - "LINA/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -12731,1698 +1601,1011 @@ ], "AR/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "RVN/USDT": [ + "ARPA/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "FIL/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, - "info": { - "bracket": "6", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.1665", - "cum": "315500.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "7", - "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1150500.0" - } - }, - { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "8", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", - "maintMarginRatio": "0.5", - "cum": "6150500.0" - } - } - ], - "NKN/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "KLAY/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "DEFI/USDT": [ + "ATA/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "COMP/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BTCDOM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "SOL/USDT": [ + "ATOM/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "AUDIO/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "AVAX/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "AVAX/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.05", "cum": "7000.0" } }, { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "57000.0" } }, { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", "cum": "107000.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", "cum": "732000.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 50000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", "cum": "3232000.0" } } ], - "BTC/USDT": [ + "AXS/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.004, - "maxLeverage": 125, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "125", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.004", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 100, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "100", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.005", - "cum": "50.0" + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "500.0" } }, { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "50", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.01", - "cum": "1300.0" + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8000.0" } }, { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 7500000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "20", - "maxNotional": "7500000", - "minNotional": "1000000", - "maintMarginRatio": "0.025", - "cum": "16300.0" + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58000.0" } }, { - "tier": 5, - "minNotional": 7500000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "10", - "maxNotional": "40000000", - "minNotional": "7500000", - "maintMarginRatio": "0.05", - "cum": "203800.0" + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108000.0" } }, { - "tier": 6, - "minNotional": 40000000, - "maxNotional": 100000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.1665, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "5", - "maxNotional": "100000000", - "minNotional": "40000000", - "maintMarginRatio": "0.1", - "cum": "2203800.0" + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.1665", + "cum": "315500.0" } }, { - "tier": 7, - "minNotional": 100000000, - "maxNotional": 200000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "4", - "maxNotional": "200000000", - "minNotional": "100000000", - "maintMarginRatio": "0.125", - "cum": "4703800.0" + "initialLeverage": "2", + "notionalCap": "15000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1150500.0" } }, { - "tier": 8, - "minNotional": 200000000, - "maxNotional": 400000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "tier": 8.0, + "currency": "USDT", + "minNotional": 15000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, "info": { "bracket": "8", - "initialLeverage": "3", - "maxNotional": "400000000", - "minNotional": "200000000", - "maintMarginRatio": "0.15", - "cum": "9703800.0" - } - }, - { - "tier": 9, - "minNotional": 400000000, - "maxNotional": 600000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "9", - "initialLeverage": "2", - "maxNotional": "600000000", - "minNotional": "400000000", - "maintMarginRatio": "0.25", - "cum": "4.97038E7" - } - }, - { - "tier": 10, - "minNotional": 600000000, - "maxNotional": 1000000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "10", "initialLeverage": "1", - "maxNotional": "1000000000", - "minNotional": "600000000", + "notionalCap": "20000000", + "notionalFloor": "15000000", "maintMarginRatio": "0.5", - "cum": "1.997038E8" + "cum": "4900500.0" } } ], - "OMG/USDT": [ + "BAKE/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.024, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.024", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "5.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "630.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5630.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11880.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "10000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386880.0" - } - } - ], - "ICX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "BLZ/USDT": [ + "BAL/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "BTCUSDT_211231": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 375000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "375000", - "minNotional": "0", - "maintMarginRatio": "0.02", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 375000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "2000000", - "minNotional": "375000", - "maintMarginRatio": "0.05", - "cum": "11250.0" - } - }, - { - "tier": 3, - "minNotional": 2000000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "4000000", - "minNotional": "2000000", - "maintMarginRatio": "0.1", - "cum": "111250.0" - } - }, - { - "tier": 4, - "minNotional": 4000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "4", - "initialLeverage": "4", - "maxNotional": "10000000", - "minNotional": "4000000", - "maintMarginRatio": "0.125", - "cum": "211250.0" - } - }, - { - "tier": 5, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "5", - "initialLeverage": "3", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.15", - "cum": "461250.0" - } - }, - { - "tier": 6, - "minNotional": 20000000, - "maxNotional": 40000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "40000000", - "minNotional": "20000000", - "maintMarginRatio": "0.25", - "cum": "2461250.0" - } - }, - { - "tier": 7, - "minNotional": 40000000, - "maxNotional": 400000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "400000000", - "minNotional": "40000000", - "maintMarginRatio": "0.5", - "cum": "1.246125E7" - } - } - ], - "FTM/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 750000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "750000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 750000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "750000", - "maintMarginRatio": "0.25", - "cum": "123250.0" - } - }, - { - "tier": 7, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "373250.0" - } - } - ], - "YFII/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "KEEP/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -14430,1589 +2613,2019 @@ ], "BAND/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "BTCBUSD_210226": [ + "BAT/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.004, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.004", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 15, - "info": { - "bracket": "2", - "initialLeverage": "15", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.005", - "cum": "5.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 10, + "maxLeverage": 50.0, "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", - "cum": "130.0" + "cum": "0.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 500000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 7, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "7", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.025", - "cum": "1630.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 6, - "info": { - "bracket": "5", - "initialLeverage": "6", - "maxNotional": "2000000", - "minNotional": "500000", - "maintMarginRatio": "0.05", - "cum": "14130.0" - } - }, - { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "6", "initialLeverage": "5", - "maxNotional": "5000000", - "minNotional": "2000000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", - "cum": "114130.0" + "cum": "5700.0" } }, { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 2.0, "info": { - "bracket": "7", - "initialLeverage": "4", - "maxNotional": "10000000", - "minNotional": "5000000", + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", - "cum": "239130.0" + "cum": "11950.0" } }, { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "8", - "initialLeverage": "3", - "maxNotional": "20000000", - "minNotional": "10000000", - "maintMarginRatio": "0.15", - "cum": "489130.0" - } - }, - { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 9223372036854776000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "9", - "initialLeverage": "2", - "maxNotional": "9223372036854775807", - "minNotional": "20000000", - "maintMarginRatio": "0.25", - "cum": "2489130.0" - } - } - ], - "XRP/BUSD": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.05", - "cum": "2500.0" - } - }, - { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.1", - "cum": "27500.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "4", - "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.15", - "cum": "77500.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.25", - "cum": "277500.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "1527500.0" + "cum": "386950.0" } } ], - "DOGE/BUSD": [ + "BCH/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "maxNotional": "100000", - "minNotional": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "2", - "initialLeverage": "10", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.05", - "cum": "2500.0" - } - }, - { - "tier": 3, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "3", - "initialLeverage": "5", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.1", - "cum": "27500.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3, - "info": { - "bracket": "4", - "initialLeverage": "3", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.15", - "cum": "77500.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.25", - "cum": "277500.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", - "maintMarginRatio": "0.5", - "cum": "1527500.0" - } - } - ], - "XRP/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, + "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", + "notionalCap": "10000", + "notionalFloor": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", + "notionalCap": "50000", + "notionalFloor": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "7", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { - "tier": 9, - "minNotional": 20000000, - "maxNotional": 50000000, + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } } ], - "SXP/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "CRV/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", - "maintMarginRatio": "0.5", - "cum": "654500.0" - } - } - ], "BEL/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "DOT/USDT": [ + "BLZ/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 10000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "BNB/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2500.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.1", + "cum": "27500.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "4", + "initialLeverage": "3", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.15", + "cum": "77500.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "277500.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 5000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1527500.0" + } + } + ], + "BNB/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, + "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "maxNotional": "10000", - "minNotional": "0", + "notionalCap": "10000", + "notionalFloor": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 10000, - "maxNotional": 50000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "10000", + "notionalCap": "50000", + "notionalFloor": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { - "tier": 3, - "minNotional": 50000, - "maxNotional": 250000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { - "tier": 5, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { - "tier": 6, - "minNotional": 2000000, - "maxNotional": 5000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { - "tier": 7, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "7", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { - "tier": 8, - "minNotional": 10000000, - "maxNotional": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "maxNotional": "50000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { - "tier": 9, - "minNotional": 50000000, - "maxNotional": 100000000, + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "maxNotional": "100000000", - "minNotional": "50000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "1.3733035E7" + "cum": "6233035.0" } } ], - "XEM/USDT": [ + "BNX/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "50000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], - "ONE/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", + "notionalCap": "150000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", + "notionalCap": "250000", + "notionalFloor": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", + "notionalCap": "500000", + "notionalFloor": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", + "notionalCap": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 2000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "maxNotional": "2000000", - "minNotional": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { - "tier": 7, - "minNotional": 2000000, - "maxNotional": 30000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "2000000", + "notionalCap": "8000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } } ], - "ZIL/USDT": [ + "BTC/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.004, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.004", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.005", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.01", + "cum": "1300.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "7500000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.025", + "cum": "16300.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 7500000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 6.0, + "info": { + "bracket": "5", + "initialLeverage": "6", + "notionalCap": "40000000", + "notionalFloor": "7500000", + "maintMarginRatio": "0.05", + "cum": "203800.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 40000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "100000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.1", + "cum": "2203800.0" + } + }, + { + "tier": 7.0, + "currency": "BUSD", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "200000000", + "notionalFloor": "100000000", + "maintMarginRatio": "0.125", + "cum": "4703800.0" + } + }, + { + "tier": 8.0, + "currency": "BUSD", + "minNotional": 200000000.0, + "maxNotional": 400000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "8", + "initialLeverage": "3", + "notionalCap": "400000000", + "notionalFloor": "200000000", + "maintMarginRatio": "0.15", + "cum": "9703800.0" + } + }, + { + "tier": 9.0, + "currency": "BUSD", + "minNotional": 400000000.0, + "maxNotional": 600000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "9", + "initialLeverage": "2", + "notionalCap": "600000000", + "notionalFloor": "400000000", + "maintMarginRatio": "0.25", + "cum": "4.97038E7" + } + }, + { + "tier": 10.0, + "currency": "BUSD", + "minNotional": 600000000.0, + "maxNotional": 1000000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "10", + "initialLeverage": "1", + "notionalCap": "1000000000", + "notionalFloor": "600000000", + "maintMarginRatio": "0.5", + "cum": "1.997038E8" + } + } + ], + "BTC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.004, + "maxLeverage": 125.0, + "info": { + "bracket": "1", + "initialLeverage": "125", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.004", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 100.0, + "info": { + "bracket": "2", + "initialLeverage": "100", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.005", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "3", + "initialLeverage": "50", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.01", + "cum": "1300.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "10000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.025", + "cum": "16300.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.05", + "cum": "266300.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "50000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.1", + "cum": "1266300.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 50000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "100000000", + "notionalFloor": "50000000", + "maintMarginRatio": "0.125", + "cum": "2516300.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "8", + "initialLeverage": "3", + "notionalCap": "200000000", + "notionalFloor": "100000000", + "maintMarginRatio": "0.15", + "cum": "5016300.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 200000000.0, + "maxNotional": 300000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "9", + "initialLeverage": "2", + "notionalCap": "300000000", + "notionalFloor": "200000000", + "maintMarginRatio": "0.25", + "cum": "2.50163E7" + } + }, + { + "tier": 10.0, + "currency": "USDT", + "minNotional": 300000000.0, + "maxNotional": 9.223372036854776e+18, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "10", + "initialLeverage": "1", + "notionalCap": "9223372036854775807", + "notionalFloor": "300000000", + "maintMarginRatio": "0.5", + "cum": "1.000163E8" + } + } + ], + "BTCDOM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "AXS/USDT": [ + "BTCSTUSDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25, - "info": { - "bracket": "2", - "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.05", - "cum": "8000.0" - } - }, - { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "2000000", - "minNotional": "1000000", - "maintMarginRatio": "0.1", - "cum": "58000.0" - } - }, - { - "tier": 5, - "minNotional": 2000000, - "maxNotional": 5000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "5000000", - "minNotional": "2000000", - "maintMarginRatio": "0.125", - "cum": "108000.0" - } - }, - { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, - "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, - "info": { - "bracket": "6", - "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", - "maintMarginRatio": "0.1665", - "cum": "315500.0" - } - }, - { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 15000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "7", - "initialLeverage": "2", - "maxNotional": "15000000", - "minNotional": "10000000", - "maintMarginRatio": "0.25", - "cum": "1150500.0" - } - }, - { - "tier": 8, - "minNotional": 15000000, - "maxNotional": 50000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "8", - "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "15000000", - "maintMarginRatio": "0.5", - "cum": "4900500.0" - } - } - ], - "DYDX/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "50000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 150000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", - "maintMarginRatio": "0.025", - "cum": "750.0" - } - }, - { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "250000", - "minNotional": "150000", - "maintMarginRatio": "0.05", - "cum": "4500.0" - } - }, - { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "500000", - "minNotional": "250000", - "maintMarginRatio": "0.1", - "cum": "17000.0" - } - }, - { - "tier": 5, - "minNotional": 500000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4, - "info": { - "bracket": "5", - "initialLeverage": "4", - "maxNotional": "1000000", - "minNotional": "500000", - "maintMarginRatio": "0.125", - "cum": "29500.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 4000000, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2, - "info": { - "bracket": "6", - "initialLeverage": "2", - "maxNotional": "4000000", - "minNotional": "1000000", - "maintMarginRatio": "0.25", - "cum": "154500.0" - } - }, - { - "tier": 7, - "minNotional": 4000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "7", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "4000000", - "maintMarginRatio": "0.5", - "cum": "1154500.0" - } - } - ], - "OCEAN/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50, - "info": { - "bracket": "1", - "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 9.223372036854776e+18, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "9223372036854775807", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "BTCUSDT_220930": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 375000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "375000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 375000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "2000000", + "notionalFloor": "375000", + "maintMarginRatio": "0.05", + "cum": "11250.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.1", + "cum": "111250.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "4", + "initialLeverage": "4", + "notionalCap": "10000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.125", + "cum": "211250.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "5", + "initialLeverage": "3", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.15", + "cum": "461250.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "40000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.25", + "cum": "2461250.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 400000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "400000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.5", + "cum": "1.246125E7" + } + } + ], + "BTS/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "C98/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "CELO/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "CELR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "CHR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -16020,367 +4633,505 @@ ], "CHZ/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.012, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } } ], - "LENDUSDT": [ + "COMP/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 9223372036854776000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "9223372036854775807", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ], - "ANKR/USDT": [ + "COTI/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.012, - "maxLeverage": 50, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.012", - "cum": "0.0" - } - }, - { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "2", - "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.025", - "cum": "65.0" - } - }, - { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10, - "info": { - "bracket": "3", - "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", - "maintMarginRatio": "0.05", - "cum": "690.0" - } - }, - { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5, - "info": { - "bracket": "4", - "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", - "maintMarginRatio": "0.1", - "cum": "5690.0" - } - }, - { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2, - "info": { - "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", - "maintMarginRatio": "0.125", - "cum": "11940.0" - } - }, - { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386940.0" - } - } - ], - "DUSK/USDT": [ - { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, - "info": { - "bracket": "1", - "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "CRV/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "CTK/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -16388,94 +5139,12584 @@ ], "CTSI/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "25000", + "notionalFloor": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "3", "initialLeverage": "10", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "maxNotional": "250000", - "minNotional": "100000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "CVC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DAR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DASH/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DEFI/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DENT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DGB/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DODO/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "DOGE/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2500.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.1", + "cum": "27500.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "4", + "initialLeverage": "3", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.15", + "cum": "77500.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "277500.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 5000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1527500.0" + } + } + ], + "DOGE/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "7000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "57000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "107000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "732000.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.5", + "cum": "3232000.0" + } + } + ], + "DOT/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "DOT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "50000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 50000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "100000000", + "notionalFloor": "50000000", + "maintMarginRatio": "0.5", + "cum": "1.3733035E7" + } + } + ], + "DUSK/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "DYDX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.5", + "cum": "1154500.0" + } + } + ], + "EGLD/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ENJ/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "ENS/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "EOS/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6233035.0" + } + } + ], + "ETC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6233035.0" + } + } + ], + "ETH/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.004, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.004", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.005", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.01", + "cum": "525.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1500000", + "notionalFloor": "500000", + "maintMarginRatio": "0.025", + "cum": "8025.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1500000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 6.0, + "info": { + "bracket": "5", + "initialLeverage": "6", + "notionalCap": "4000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.05", + "cum": "45525.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "10000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.1", + "cum": "245525.0" + } + }, + { + "tier": 7.0, + "currency": "BUSD", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.125", + "cum": "495525.0" + } + }, + { + "tier": 8.0, + "currency": "BUSD", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "8", + "initialLeverage": "3", + "notionalCap": "40000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.15", + "cum": "995525.0" + } + }, + { + "tier": 9.0, + "currency": "BUSD", + "minNotional": 40000000.0, + "maxNotional": 150000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "9", + "initialLeverage": "2", + "notionalCap": "150000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.25", + "cum": "4995525.0" + } + }, + { + "tier": 10.0, + "currency": "BUSD", + "minNotional": 150000000.0, + "maxNotional": 500000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "10", + "initialLeverage": "1", + "notionalCap": "500000000", + "notionalFloor": "150000000", + "maintMarginRatio": "0.5", + "cum": "4.2495525E7" + } + } + ], + "ETH/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 100.0, + "info": { + "bracket": "1", + "initialLeverage": "100", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.005", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "2", + "initialLeverage": "75", + "notionalCap": "100000", + "notionalFloor": "10000", + "maintMarginRatio": "0.0065", + "cum": "15.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "3", + "initialLeverage": "50", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.01", + "cum": "365.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "4", + "initialLeverage": "25", + "notionalCap": "1500000", + "notionalFloor": "500000", + "maintMarginRatio": "0.02", + "cum": "5365.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "4000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.05", + "cum": "50365.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "10000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.1", + "cum": "250365.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.125", + "cum": "500365.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "8", + "initialLeverage": "3", + "notionalCap": "40000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.15", + "cum": "1000365.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 150000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "9", + "initialLeverage": "2", + "notionalCap": "150000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.25", + "cum": "5000365.0" + } + }, + { + "tier": 10.0, + "currency": "USDT", + "minNotional": 150000000.0, + "maxNotional": 500000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "10", + "initialLeverage": "1", + "notionalCap": "500000000", + "notionalFloor": "150000000", + "maintMarginRatio": "0.5", + "cum": "4.2500365E7" + } + } + ], + "ETHUSDT_220930": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 375000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "375000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 375000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "2000000", + "notionalFloor": "375000", + "maintMarginRatio": "0.05", + "cum": "11250.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.1", + "cum": "111250.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "4", + "initialLeverage": "4", + "notionalCap": "10000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.125", + "cum": "211250.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "5", + "initialLeverage": "3", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.15", + "cum": "461250.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "40000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.25", + "cum": "2461250.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 400000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "400000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.5", + "cum": "1.246125E7" + } + } + ], + "FIL/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "500.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.1665, + "maxLeverage": 3.0, + "info": { + "bracket": "6", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.1665", + "cum": "315500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1150500.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6150500.0" + } + } + ], + "FLM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "10000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "FLOW/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "FTM/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "FTM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.5", + "cum": "1154500.0" + } + } + ], + "FTT/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2500.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.1", + "cum": "27500.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "4", + "initialLeverage": "3", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.15", + "cum": "77500.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "277500.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 5000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1527500.0" + } + } + ], + "FTT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "GAL/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "GAL/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "GALA/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "GALA/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "GMT/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "GMT/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "GRT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "GTC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "HBAR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "HNT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "HOT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ICP/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "ICP/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ICX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "IMX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "IOST/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "IOTA/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "IOTX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "JASMY/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "KAVA/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "KLAY/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "KNC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "KSM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "LINA/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "LINK/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "LINK/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6233035.0" + } + } + ], + "LIT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "LPT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "LRC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "LTC/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "LTC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6233035.0" + } + } + ], + "LUNA2/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "MANA/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "MASK/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "MATIC/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "123250.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "373250.0" + } + } + ], + "MKR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "MTL/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "NEAR/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "NEAR/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "NEO/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "NKN/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "OCEAN/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "OGN/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "OMG/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ONE/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ONT/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "OP/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "PEOPLE/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "QTUM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "RAY/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "REEF/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "REN/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "RLC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ROSE/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "RSR/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "RUNE/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "RVN/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SAND/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "SAND/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "SC/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SFP/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SKL/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SNX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SOL/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2500.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.1", + "cum": "27500.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "4", + "initialLeverage": "3", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.15", + "cum": "77500.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "277500.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 5000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1527500.0" + } + } + ], + "SOL/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "7000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "57000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "107000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "732000.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.5", + "cum": "3232000.0" + } + } + ], + "SRM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "STMX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "STORJ/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SUSHI/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "SXP/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "THETA/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "500.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.1665, + "maxLeverage": 3.0, + "info": { + "bracket": "6", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.1665", + "cum": "315500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1150500.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6150500.0" + } + } + ], + "TLM/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "TLM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "TOMO/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "TRB/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "TRX/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "TRX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 22.0, + "info": { + "bracket": "2", + "initialLeverage": "22", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6233035.0" + } + } + ], + "UNFI/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "UNI/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "VET/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "WAVES/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], + "WAVES/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "WOO/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "XEM/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "XLM/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "7000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "57000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "107000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "732000.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.5", + "cum": "3232000.0" + } + } + ], + "XMR/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "7000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "57000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "107000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "732000.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.5", + "cum": "3232000.0" + } + } + ], + "XRP/BUSD": [ + { + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "1", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "2", + "initialLeverage": "10", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2500.0" + } + }, + { + "tier": 3.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "3", + "initialLeverage": "5", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.1", + "cum": "27500.0" + } + }, + { + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "4", + "initialLeverage": "3", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.15", + "cum": "77500.0" + } + }, + { + "tier": 5.0, + "currency": "BUSD", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "277500.0" + } + }, + { + "tier": 6.0, + "currency": "BUSD", + "minNotional": 5000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "8000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1527500.0" + } + } + ], + "XRP/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.0065", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "35.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "535.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "8035.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "58035.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "108035.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "7", + "initialLeverage": "3", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.15", + "cum": "233035.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.25", + "cum": "1233035.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.5", + "cum": "6233035.0" + } + } + ], + "XTZ/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "250000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.05", + "cum": "7000.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "57000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "107000.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "732000.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.5", + "cum": "3232000.0" + } + } + ], + "YFI/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ZEC/USDT": [ + { + "tier": 1.0, + "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, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "150000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "750.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "250000", + "notionalFloor": "150000", + "maintMarginRatio": "0.05", + "cum": "4500.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1", + "cum": "17000.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "29500.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "154500.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "ZEN/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ZIL/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386950.0" + } + } + ], + "ZRX/USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "75.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "700.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5700.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, + "info": { + "bracket": "5", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11950.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } } ] -} +} \ No newline at end of file diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index 484b8b9d3..1c4bb858b 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -29,3 +29,17 @@ class Bybit(Exchange): # (TradingMode.FUTURES, MarginMode.CROSS), # (TradingMode.FUTURES, MarginMode.ISOLATED) ] + + @property + def _ccxt_config(self) -> Dict: + # Parameters to add directly to ccxt sync/async initialization. + # ccxt defaults to swap mode. + config = {} + if self.trading_mode == TradingMode.SPOT: + config.update({ + "options": { + "defaultType": "spot" + } + }) + config.update(super()._ccxt_config) + return config diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 4355662a8..5765dc459 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -2,6 +2,7 @@ import asyncio import logging import time from functools import wraps +from typing import Any, Callable, Optional, TypeVar, cast, overload from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError from freqtrade.mixins import LoggingMixin @@ -11,6 +12,14 @@ logger = logging.getLogger(__name__) __logging_mixin = None +def _reset_logging_mixin(): + """ + Reset global logging mixin - used in tests only. + """ + global __logging_mixin + __logging_mixin = LoggingMixin(logger) + + def _get_logging_mixin(): # Logging-mixin to cache kucoin responses # Only to be used in retrier @@ -37,6 +46,7 @@ MAP_EXCHANGE_CHILDCLASS = { 'binanceje': 'binance', 'binanceusdm': 'binance', 'okex': 'okx', + 'gate': 'gateio', } SUPPORTED_EXCHANGES = [ @@ -54,17 +64,16 @@ EXCHANGE_HAS_REQUIRED = [ 'fetchOrder', 'cancelOrder', 'createOrder', - # 'createLimitOrder', 'createMarketOrder', 'fetchBalance', # Public endpoints - 'loadMarkets', 'fetchOHLCV', ] EXCHANGE_HAS_OPTIONAL = [ # Private 'fetchMyTrades', # Trades for order - fee detection + 'createLimitOrder', 'createMarketOrder', # Either OR for orders # 'setLeverage', # Margin/Futures trading # 'setMarginMode', # Margin/Futures trading # 'fetchFundingHistory', # Futures trading @@ -133,8 +142,22 @@ def retrier_async(f): return wrapper -def retrier(_func=None, retries=API_RETRY_COUNT): - def decorator(f): +F = TypeVar('F', bound=Callable[..., Any]) + + +# Type shenanigans +@overload +def retrier(_func: F) -> F: + ... + + +@overload +def retrier(*, retries=API_RETRY_COUNT) -> Callable[[F], F]: + ... + + +def retrier(_func: Optional[F] = None, *, retries=API_RETRY_COUNT): + def decorator(f: F) -> F: @wraps(f) def wrapper(*args, **kwargs): count = kwargs.pop('count', retries) @@ -155,7 +178,7 @@ def retrier(_func=None, retries=API_RETRY_COUNT): else: logger.warning(msg + 'Giving up.') raise ex - return wrapper + return cast(F, wrapper) # Support both @retrier and @retrier(retries=2) syntax if _func is None: return decorator diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index f0ff7e514..db7a8ce41 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -16,11 +16,10 @@ import arrow import ccxt import ccxt.async_support as ccxt_async from cachetools import TTLCache -from ccxt.base.decimal_to_precision import (ROUND_DOWN, ROUND_UP, TICK_SIZE, TRUNCATE, - decimal_to_precision) +from ccxt import ROUND_DOWN, ROUND_UP, TICK_SIZE, TRUNCATE, Precise, decimal_to_precision from pandas import DataFrame -from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, +from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BuySell, EntryExit, ListPairsWithTimeframes, PairWithTimeframe) from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode @@ -64,6 +63,7 @@ class Exchange: "time_in_force_parameter": "timeInForce", "ohlcv_params": {}, "ohlcv_candle_limit": 500, + "ohlcv_has_history": True, # Some exchanges (Kraken) don't provide history via ohlcv "ohlcv_partial_candle": True, "ohlcv_require_since": False, # Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency @@ -77,7 +77,9 @@ class Exchange: "mark_ohlcv_price": "mark", "mark_ohlcv_timeframe": "8h", "ccxt_futures_name": "swap", + "fee_cost_in_contracts": False, # Fee cost needs contract conversion "needs_trading_fees": False, # use fetch_trading_fees to cache fees + "order_props_in_contracts": ['amount', 'cost', 'filled', 'remaining'], } _ft_has: Dict = {} _ft_has_futures: Dict = {} @@ -92,7 +94,7 @@ class Exchange: it does basic validation whether the specified exchange and pairs are valid. :return: None """ - self._api: ccxt.Exchange = None + self._api: ccxt.Exchange self._api_async: ccxt_async.Exchange = None self._markets: Dict = {} self._trading_fees: Dict[str, Any] = {} @@ -174,23 +176,11 @@ class Exchange: logger.info(f'Using Exchange "{self.name}"') if validate: - # Check if timeframe is available - self.validate_timeframes(config.get('timeframe')) - # Initial markets load self._load_markets() - - # Check if all pairs are available - self.validate_stakecurrency(config['stake_currency']) - if not exchange_config.get('skip_pair_validation'): - self.validate_pairs(config['exchange']['pair_whitelist']) - self.validate_ordertypes(config.get('order_types', {})) - self.validate_order_time_in_force(config.get('order_time_in_force', {})) + self.validate_config(config) self.required_candle_call_count = self.validate_required_startup_candles( config.get('startup_candle_count', 0), config.get('timeframe', '')) - self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode) - self.validate_pricing(config['exit_pricing']) - self.validate_pricing(config['entry_pricing']) # Converts the interval provided in minutes in config to seconds self.markets_refresh_interval: int = exchange_config.get( @@ -198,6 +188,7 @@ class Exchange: if self.trading_mode != TradingMode.SPOT: self.fill_leverage_tiers() + self.additional_exchange_init() def __del__(self): """ @@ -212,6 +203,20 @@ class Exchange: logger.info("Closing async ccxt session.") self.loop.run_until_complete(self._api_async.close()) + def validate_config(self, config): + # Check if timeframe is available + self.validate_timeframes(config.get('timeframe')) + + # Check if all pairs are available + self.validate_stakecurrency(config['stake_currency']) + if not config['exchange'].get('skip_pair_validation'): + self.validate_pairs(config['exchange']['pair_whitelist']) + self.validate_ordertypes(config.get('order_types', {})) + self.validate_order_time_in_force(config.get('order_time_in_force', {})) + self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode) + self.validate_pricing(config['exit_pricing']) + self.validate_pricing(config['entry_pricing']) + def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt, ccxt_kwargs: Dict = {}) -> ccxt.Exchange: """ @@ -290,27 +295,38 @@ class Exchange: return self._markets @property - def precisionMode(self) -> str: + def precisionMode(self) -> int: """exchange ccxt precisionMode""" return self._api.precisionMode + def additional_exchange_init(self) -> None: + """ + Additional exchange initialization logic. + .api will be available at this point. + Must be overridden in child methods if required. + """ + pass + def _log_exchange_response(self, endpoint, response) -> None: """ Log exchange responses """ if self.log_responses: logger.info(f"API {endpoint}: {response}") - def ohlcv_candle_limit(self, timeframe: str) -> int: + def ohlcv_candle_limit( + self, timeframe: str, candle_type: CandleType, since_ms: Optional[int] = None) -> int: """ Exchange ohlcv candle limit Uses ohlcv_candle_limit_per_timeframe if the exchange has different limits per timeframe (e.g. bittrex), otherwise falls back to ohlcv_candle_limit :param timeframe: Timeframe to check + :param candle_type: Candle-type + :param since_ms: Starting timestamp :return: Candle limit as integer """ return int(self._ft_has.get('ohlcv_candle_limit_per_timeframe', {}).get( timeframe, self._ft_has.get('ohlcv_candle_limit'))) - def get_markets(self, base_currencies: List[str] = None, quote_currencies: List[str] = None, + def get_markets(self, base_currencies: List[str] = [], quote_currencies: List[str] = [], spot_only: bool = False, margin_only: bool = False, futures_only: bool = False, tradable_only: bool = True, active_only: bool = False) -> Dict[str, Any]: @@ -375,7 +391,7 @@ class Exchange: and market.get('base', None) is not None and (self.precisionMode != TICK_SIZE # Too low precision will falsify calculations - or market.get('precision', {}).get('price', None) > 1e-11) + or market.get('precision', {}).get('price') > 1e-11) and ((self.trading_mode == TradingMode.SPOT and self.market_is_spot(market)) or (self.trading_mode == TradingMode.MARGIN and self.market_is_margin(market)) or (self.trading_mode == TradingMode.FUTURES and self.market_is_future(market))) @@ -410,7 +426,7 @@ class Exchange: if 'symbol' in order and order['symbol'] is not None: contract_size = self._get_contract_size(order['symbol']) if contract_size != 1: - for prop in ['amount', 'cost', 'filled', 'remaining']: + for prop in self._ft_has.get('order_props_in_contracts', []): if prop in order and order[prop] is not None: order[prop] = order[prop] * contract_size return order @@ -525,7 +541,7 @@ class Exchange: # The internal info array is different for each particular market, # its contents depend on the exchange. # It can also be a string or similar ... so we need to verify that first. - elif (isinstance(self.markets[pair].get('info', None), dict) + elif (isinstance(self.markets[pair].get('info'), dict) and self.markets[pair].get('info', {}).get('prohibitedIn', False)): # Warn users about restricted pairs in whitelist. # We cannot determine reliably if Users are affected. @@ -606,19 +622,28 @@ class Exchange: Checks if required startup_candles is more than ohlcv_candle_limit(). Requires a grace-period of 5 candles - so a startup-period up to 494 is allowed by default. """ - candle_limit = self.ohlcv_candle_limit(timeframe) + + candle_limit = self.ohlcv_candle_limit( + timeframe, self._config['candle_type_def'], + int(date_minus_candles(timeframe, startup_candles).timestamp() * 1000) + if timeframe else None) # Require one more candle - to account for the still open candle. candle_count = startup_candles + 1 # Allow 5 calls to the exchange per pair required_candle_call_count = int( (candle_count / candle_limit) + (0 if candle_count % candle_limit == 0 else 1)) + if self._ft_has['ohlcv_has_history']: - if required_candle_call_count > 5: - # Only allow 5 calls per pair to somewhat limit the impact + if required_candle_call_count > 5: + # Only allow 5 calls per pair to somewhat limit the impact + raise OperationalException( + f"This strategy requires {startup_candles} candles to start, " + "which is more than 5x " + f"the amount of candles {self.name} provides for {timeframe}.") + elif required_candle_call_count > 1: raise OperationalException( - f"This strategy requires {startup_candles} candles to start, which is more than 5x " + f"This strategy requires {startup_candles} candles to start, which is more than " f"the amount of candles {self.name} provides for {timeframe}.") - if required_candle_call_count > 1: logger.warning(f"Using {required_candle_call_count} calls to get OHLCV. " f"This can result in slower operations for the bot. Please check " @@ -682,10 +707,11 @@ class Exchange: # counting_mode=self.precisionMode, # )) if self.precisionMode == TICK_SIZE: - precision = self.markets[pair]['precision']['price'] - missing = price % precision - if missing != 0: - price = round(price - missing + precision, 10) + precision = Precise(str(self.markets[pair]['precision']['price'])) + price_str = Precise(str(price)) + missing = price_str % precision + if not missing == Precise("0"): + price = round(float(str(price_str - missing + precision)), 14) else: symbol_prec = self.markets[pair]['precision']['price'] big_price = price * pow(10, symbol_prec) @@ -818,7 +844,7 @@ class Exchange: 'price': rate, 'average': rate, 'amount': _amount, - 'cost': _amount * rate / leverage, + 'cost': _amount * rate, 'type': ordertype, 'side': side, 'filled': 0, @@ -965,19 +991,26 @@ class Exchange: order = self.check_dry_limit_order_filled(order) return order except KeyError as e: + from freqtrade.persistence import Order + order = Order.order_by_id(order_id) + if order: + ccxt_order = order.to_ccxt_object() + self._dry_run_open_orders[order_id] = ccxt_order + return ccxt_order # Gracefully handle errors with dry-run orders. raise InvalidOrderException( f'Tried to get an invalid dry-run-order (id: {order_id}). Message: {e}') from e # Order handling - def _lev_prep(self, pair: str, leverage: float, side: str): + def _lev_prep(self, pair: str, leverage: float, side: BuySell): if self.trading_mode != TradingMode.SPOT: self.set_margin_mode(pair, self.margin_mode) self._set_leverage(leverage, pair) def _get_params( self, + side: BuySell, ordertype: str, leverage: float, reduceOnly: bool, @@ -996,7 +1029,7 @@ class Exchange: *, pair: str, ordertype: str, - side: str, + side: BuySell, amount: float, rate: float, leverage: float, @@ -1007,7 +1040,7 @@ class Exchange: dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate, leverage) return dry_order - params = self._get_params(ordertype, leverage, reduceOnly, time_in_force) + params = self._get_params(side, ordertype, leverage, reduceOnly, time_in_force) try: # Set the precision for amount and price(rate) as accepted by the exchange @@ -1092,7 +1125,7 @@ class Exchange: @retrier(retries=0) def stoploss(self, pair: str, amount: float, stop_price: float, order_types: Dict, - side: str, leverage: float) -> Dict: + side: BuySell, leverage: float) -> Dict: """ creates a stoploss order. requires `_ft_has['stoploss_order_types']` to be set as a dict mapping limit and market @@ -1169,7 +1202,7 @@ class Exchange: raise OperationalException(e) from e @retrier(retries=API_FETCH_ORDER_RETRY_COUNT) - def fetch_order(self, order_id: str, pair: str, params={}) -> Dict: + def fetch_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: return self.fetch_dry_run_order(order_id) try: @@ -1191,8 +1224,8 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - # Assign method to fetch_stoploss_order to allow easy overriding in other classes - fetch_stoploss_order = fetch_order + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: + return self.fetch_order(order_id, pair, params) def fetch_order_or_stoploss_order(self, order_id: str, pair: str, stoploss_order: bool = False) -> Dict: @@ -1217,7 +1250,7 @@ class Exchange: and order.get('filled') == 0.0) @retrier - def cancel_order(self, order_id: str, pair: str, params={}) -> Dict: + def cancel_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: try: order = self.fetch_dry_run_order(order_id) @@ -1243,8 +1276,8 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - # Assign method to cancel_stoploss_order to allow easy overriding in other classes - cancel_stoploss_order = cancel_order + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: + return self.cancel_order(order_id, pair, params) def is_cancel_order_result_suitable(self, corder) -> bool: if not isinstance(corder, dict): @@ -1356,7 +1389,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def fetch_bids_asks(self, symbols: List[str] = None, cached: bool = False) -> Dict: + def fetch_bids_asks(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Dict: """ :param cached: Allow cached result :return: fetch_tickers result @@ -1384,7 +1417,7 @@ class Exchange: raise OperationalException(e) from e @retrier - def get_tickers(self, symbols: List[str] = None, cached: bool = False) -> Dict: + def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Dict: """ :param cached: Allow cached result :return: fetch_tickers result @@ -1468,6 +1501,23 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e + def _get_price_side(self, side: str, is_short: bool, conf_strategy: Dict) -> str: + price_side = conf_strategy['price_side'] + + if price_side in ('same', 'other'): + price_map = { + ('entry', 'long', 'same'): 'bid', + ('entry', 'long', 'other'): 'ask', + ('entry', 'short', 'same'): 'ask', + ('entry', 'short', 'other'): 'bid', + ('exit', 'long', 'same'): 'ask', + ('exit', 'long', 'other'): 'bid', + ('exit', 'short', 'same'): 'bid', + ('exit', 'short', 'other'): 'ask', + } + price_side = price_map[(side, 'short' if is_short else 'long', price_side)] + return price_side + def get_rate(self, pair: str, refresh: bool, side: EntryExit, is_short: bool) -> float: """ @@ -1494,20 +1544,7 @@ class Exchange: conf_strategy = self._config.get(strat_name, {}) - price_side = conf_strategy['price_side'] - - if price_side in ('same', 'other'): - price_map = { - ('entry', 'long', 'same'): 'bid', - ('entry', 'long', 'other'): 'ask', - ('entry', 'short', 'same'): 'ask', - ('entry', 'short', 'other'): 'bid', - ('exit', 'long', 'same'): 'ask', - ('exit', 'long', 'other'): 'bid', - ('exit', 'short', 'same'): 'bid', - ('exit', 'short', 'other'): 'ask', - } - price_side = price_map[(side, 'short' if is_short else 'long', price_side)] + price_side = self._get_price_side(side, is_short, conf_strategy) price_side_word = price_side.capitalize() @@ -1632,27 +1669,35 @@ class Exchange: and order['fee']['cost'] is not None ) - def calculate_fee_rate(self, order: Dict) -> Optional[float]: + def calculate_fee_rate( + self, fee: Dict, symbol: str, cost: float, amount: float) -> Optional[float]: """ Calculate fee rate if it's not given by the exchange. - :param order: Order or trade (one trade) dict + :param fee: ccxt Fee dict - must contain cost / currency / rate + :param symbol: Symbol of the order + :param cost: Total cost of the order + :param amount: Amount of the order """ - if order['fee'].get('rate') is not None: - return order['fee'].get('rate') - fee_curr = order['fee']['currency'] + if fee.get('rate') is not None: + return fee.get('rate') + fee_curr = fee.get('currency') + if fee_curr is None: + return None + fee_cost = float(fee['cost']) + if self._ft_has['fee_cost_in_contracts']: + # Convert cost via "contracts" conversion + fee_cost = self._contracts_to_amount(symbol, fee['cost']) + # Calculate fee based on order details - if fee_curr in self.get_pair_base_currency(order['symbol']): + if fee_curr == self.get_pair_base_currency(symbol): # Base currency - divide by amount - return round( - order['fee']['cost'] / safe_value_fallback2(order, order, 'filled', 'amount'), 8) - elif fee_curr in self.get_pair_quote_currency(order['symbol']): + return round(fee_cost / amount, 8) + elif fee_curr == self.get_pair_quote_currency(symbol): # Quote currency - divide by cost - return round(self._contracts_to_amount( - order['symbol'], order['fee']['cost']) / order['cost'], - 8) if order['cost'] else None + return round(fee_cost / cost, 8) if cost else None else: # If Fee currency is a different currency - if not order['cost']: + if not cost: # If cost is None or 0.0 -> falsy, return None return None try: @@ -1664,19 +1709,28 @@ class Exchange: fee_to_quote_rate = self._config['exchange'].get('unknown_fee_rate', None) if not fee_to_quote_rate: return None - return round((self._contracts_to_amount( - order['symbol'], order['fee']['cost']) * fee_to_quote_rate) / order['cost'], 8) + return round((fee_cost * fee_to_quote_rate) / cost, 8) - def extract_cost_curr_rate(self, order: Dict) -> Tuple[float, str, Optional[float]]: + def extract_cost_curr_rate(self, fee: Dict, symbol: str, cost: float, + amount: float) -> Tuple[float, str, Optional[float]]: """ Extract tuple of cost, currency, rate. Requires order_has_fee to run first! - :param order: Order or trade (one trade) dict + :param fee: ccxt Fee dict - must contain cost / currency / rate + :param symbol: Symbol of the order + :param cost: Total cost of the order + :param amount: Amount of the order :return: Tuple with cost, currency, rate of the given fee dict """ - return (order['fee']['cost'], - order['fee']['currency'], - self.calculate_fee_rate(order)) + return (float(fee['cost']), + fee['currency'], + self.calculate_fee_rate( + fee, + symbol, + cost, + amount + ) + ) # Historic data @@ -1719,7 +1773,7 @@ class Exchange: async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, candle_type: CandleType, is_new_pair: bool = False, raise_: bool = False, - until_ms: int = None + until_ms: Optional[int] = None ) -> Tuple[str, str, str, List]: """ Download historic ohlcv @@ -1727,7 +1781,8 @@ class Exchange: :param candle_type: Any of the enum CandleType (must match trading mode!) """ - one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe) + one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit( + timeframe, candle_type, since_ms) logger.debug( "one_call: %s msecs (%s)", one_call, @@ -1763,7 +1818,8 @@ class Exchange: if (not since_ms and (self._ft_has["ohlcv_require_since"] or self.required_candle_call_count > 1)): # Multiple calls for one pair - to get more history - one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe) + one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit( + timeframe, candle_type, since_ms) move_to = one_call * self.required_candle_call_count now = timeframe_to_next_date(timeframe) since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000) @@ -1778,7 +1834,7 @@ class Exchange: def refresh_latest_ohlcv(self, pair_list: ListPairsWithTimeframes, *, since_ms: Optional[int] = None, cache: bool = True, - drop_incomplete: bool = None + drop_incomplete: Optional[bool] = None ) -> Dict[PairWithTimeframe, DataFrame]: """ Refresh in-memory OHLCV asynchronously and set `_klines` with the result @@ -1881,7 +1937,9 @@ class Exchange: pair, timeframe, since_ms, s ) params = deepcopy(self._ft_has.get('ohlcv_params', {})) - candle_limit = self.ohlcv_candle_limit(timeframe) + candle_limit = self.ohlcv_candle_limit( + timeframe, candle_type=candle_type, since_ms=since_ms) + if candle_type != CandleType.SPOT: params.update({'price': candle_type}) if candle_type != CandleType.FUNDING_RATE: @@ -2128,10 +2186,11 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - @retrier - def get_market_leverage_tiers(self, symbol) -> List[Dict]: + @retrier_async + async def get_market_leverage_tiers(self, symbol: str) -> Tuple[str, List[Dict]]: try: - return self._api.fetch_market_leverage_tiers(symbol) + tier = await self._api_async.fetch_market_leverage_tiers(symbol) + return symbol, tier except ccxt.DDoSProtection as e: raise DDosProtection(e) from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: @@ -2165,8 +2224,14 @@ class Exchange: f"Initializing leverage_tiers for {len(symbols)} markets. " "This will take about a minute.") - for symbol in sorted(symbols): - tiers[symbol] = self.get_market_leverage_tiers(symbol) + coros = [self.get_market_leverage_tiers(symbol) for symbol in sorted(symbols)] + + for input_coro in chunks(coros, 100): + + results = self.loop.run_until_complete( + asyncio.gather(*input_coro, return_exceptions=True)) + for symbol, res in results: + tiers[symbol] = res logger.info(f"Done initializing {len(symbols)} markets.") @@ -2416,14 +2481,35 @@ class Exchange: ) @staticmethod - def combine_funding_and_mark(funding_rates: DataFrame, mark_rates: DataFrame) -> DataFrame: + def combine_funding_and_mark(funding_rates: DataFrame, mark_rates: DataFrame, + futures_funding_rate: Optional[int] = None) -> DataFrame: """ Combine funding-rates and mark-rates dataframes :param funding_rates: Dataframe containing Funding rates (Type FUNDING_RATE) :param mark_rates: Dataframe containing Mark rates (Type mark_ohlcv_price) + :param futures_funding_rate: Fake funding rate to use if funding_rates are not available """ + if futures_funding_rate is None: + return mark_rates.merge( + funding_rates, on='date', how="inner", suffixes=["_mark", "_fund"]) + else: + if len(funding_rates) == 0: + # No funding rate candles - full fillup with fallback variable + mark_rates['open_fund'] = futures_funding_rate + return mark_rates.rename( + columns={'open': 'open_mark', + 'close': 'close_mark', + 'high': 'high_mark', + 'low': 'low_mark', + 'volume': 'volume_mark'}) - return funding_rates.merge(mark_rates, on='date', how="inner", suffixes=["_fund", "_mark"]) + else: + # Fill up missing funding_rate candles with fallback value + combined = mark_rates.merge( + funding_rates, on='date', how="outer", suffixes=["_mark", "_fund"] + ) + combined['open_fund'] = combined['open_fund'].fillna(futures_funding_rate) + return combined def calculate_funding_fees( self, @@ -2698,9 +2784,10 @@ def timeframe_to_msecs(timeframe: str) -> int: def timeframe_to_prev_date(timeframe: str, date: datetime = None) -> datetime: """ - Use Timeframe and determine last possible candle. + Use Timeframe and determine the candle start date for this date. + Does not round when given a candle start date. :param timeframe: timeframe in string format (e.g. "5m") - :param date: date to use. Defaults to utcnow() + :param date: date to use. Defaults to now(utc) :returns: date of previous candle (with utc timezone) """ if not date: @@ -2715,7 +2802,7 @@ def timeframe_to_next_date(timeframe: str, date: datetime = None) -> datetime: """ Use Timeframe and determine next candle. :param timeframe: timeframe in string format (e.g. "5m") - :param date: date to use. Defaults to utcnow() + :param date: date to use. Defaults to now(utc) :returns: date of next candle (with utc timezone) """ if not date: @@ -2725,6 +2812,23 @@ def timeframe_to_next_date(timeframe: str, date: datetime = None) -> datetime: return datetime.fromtimestamp(new_timestamp, tz=timezone.utc) +def date_minus_candles( + timeframe: str, candle_count: int, date: Optional[datetime] = None) -> datetime: + """ + subtract X candles from a date. + :param timeframe: timeframe in string format (e.g. "5m") + :param candle_count: Amount of candles to subtract. + :param date: date to use. Defaults to now(utc) + + """ + if not date: + date = datetime.now(timezone.utc) + + tf_min = timeframe_to_minutes(timeframe) + new_date = timeframe_to_prev_date(timeframe, date) - timedelta(minutes=tf_min * candle_count) + return new_date + + def market_is_active(market: Dict) -> bool: """ Return True if the market is active. diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index d2dcf84a6..9ee6894f1 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -4,6 +4,7 @@ from typing import Any, Dict, List, Tuple import ccxt +from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, OperationalException, TemporaryError) @@ -44,7 +45,7 @@ class Ftx(Exchange): @retrier(retries=0) def stoploss(self, pair: str, amount: float, stop_price: float, - order_types: Dict, side: str, leverage: float) -> Dict: + order_types: Dict, side: BuySell, leverage: float) -> Dict: """ Creates a stoploss order. depending on order_types.stoploss configuration, uses 'market' or limit order. @@ -103,7 +104,7 @@ class Ftx(Exchange): raise OperationalException(e) from e @retrier(retries=API_FETCH_ORDER_RETRY_COUNT) - def fetch_stoploss_order(self, order_id: str, pair: str) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: return self.fetch_dry_run_order(order_id) @@ -144,7 +145,7 @@ class Ftx(Exchange): raise OperationalException(e) from e @retrier - def cancel_stoploss_order(self, order_id: str, pair: str) -> Dict: + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: if self._config['dry_run']: return {} try: diff --git a/freqtrade/exchange/gateio.py b/freqtrade/exchange/gateio.py index 609cf4901..6df3425d2 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -1,11 +1,13 @@ """ Gate.io exchange subclass """ import logging from datetime import datetime -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple +from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import Exchange +from freqtrade.misc import safe_value_fallback2 logger = logging.getLogger(__name__) @@ -24,12 +26,16 @@ class Gateio(Exchange): _ft_has: Dict = { "ohlcv_candle_limit": 1000, "ohlcv_volume_currency": "quote", + "time_in_force_parameter": "timeInForce", + "order_time_in_force": ['gtc', 'ioc'], "stoploss_order_types": {"limit": "limit"}, "stoploss_on_exchange": True, } _ft_has_futures: Dict = { - "needs_trading_fees": True + "needs_trading_fees": True, + "fee_cost_in_contracts": False, # Set explicitly to false for clarity + "order_props_in_contracts": ['amount', 'filled', 'remaining'], } _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ @@ -40,13 +46,33 @@ class Gateio(Exchange): ] def validate_ordertypes(self, order_types: Dict) -> None: - super().validate_ordertypes(order_types) if self.trading_mode != TradingMode.FUTURES: if any(v == 'market' for k, v in order_types.items()): raise OperationalException( f'Exchange {self.name} does not support market orders.') + def _get_params( + self, + side: BuySell, + ordertype: str, + leverage: float, + reduceOnly: bool, + time_in_force: str = 'gtc', + ) -> Dict: + params = super()._get_params( + side=side, + ordertype=ordertype, + leverage=leverage, + reduceOnly=reduceOnly, + time_in_force=time_in_force, + ) + if ordertype == 'market' and self.trading_mode == TradingMode.FUTURES: + params['type'] = 'market' + param = self._ft_has.get('time_in_force_parameter', '') + params.update({param: 'ioc'}) + return params + def get_trades_for_order(self, order_id: str, pair: str, since: datetime, params: Optional[Dict] = None) -> List: trades = super().get_trades_for_order(order_id, pair, since, params) @@ -61,7 +87,8 @@ class Gateio(Exchange): pair_fees = self._trading_fees.get(pair, {}) if pair_fees: for idx, trade in enumerate(trades): - if trade.get('fee', {}).get('cost') is None: + fee = trade.get('fee', {}) + if fee and fee.get('cost') is None: takerOrMaker = trade.get('takerOrMaker', 'taker') if pair_fees.get(takerOrMaker) is not None: trades[idx]['fee'] = { @@ -71,14 +98,31 @@ class Gateio(Exchange): } return trades - def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: - return self.fetch_order( + def get_order_id_conditional(self, order: Dict[str, Any]) -> str: + if self.trading_mode == TradingMode.FUTURES: + return safe_value_fallback2(order, order, 'id_stop', 'id') + return order['id'] + + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: + order = self.fetch_order( order_id=order_id, pair=pair, params={'stop': True} ) + if self.trading_mode == TradingMode.FUTURES: + if order['status'] == 'closed': + # Places a real order - which we need to fetch explicitly. + new_orderid = order.get('info', {}).get('trade_id') + if new_orderid: + order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=params) + order1['id_stop'] = order1['id'] + order1['id'] = order_id + order1['stopPrice'] = order.get('stopPrice') - def cancel_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + return order1 + return order + + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: return self.cancel_order( order_id=order_id, pair=pair, @@ -90,5 +134,7 @@ class Gateio(Exchange): Verify stop_loss against stoploss-order value (limit or price) Returns True if adjustment is necessary. """ - return ((side == "sell" and stop_loss > float(order['stopPrice'])) or - (side == "buy" and stop_loss < float(order['stopPrice']))) + return (order.get('stopPrice', None) is None or ( + side == "sell" and stop_loss > float(order['stopPrice'])) or + (side == "buy" and stop_loss < float(order['stopPrice'])) + ) diff --git a/freqtrade/exchange/huobi.py b/freqtrade/exchange/huobi.py index 71c4d1cf6..736515dec 100644 --- a/freqtrade/exchange/huobi.py +++ b/freqtrade/exchange/huobi.py @@ -27,7 +27,13 @@ class Huobi(Exchange): Verify stop_loss against stoploss-order value (limit or price) Returns True if adjustment is necessary. """ - return order['type'] == 'stop' and stop_loss > float(order['stopPrice']) + return ( + order.get('stopPrice', None) is None + or ( + order['type'] == 'stop' + and stop_loss > float(order['stopPrice']) + ) + ) def _get_stop_params(self, ordertype: str, stop_price: float) -> Dict: diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 94727afa6..0103e2702 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -6,6 +6,7 @@ from typing import Any, Dict, List, Optional, Tuple import ccxt from pandas import DataFrame +from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, InvalidOrderException, OperationalException, TemporaryError) @@ -22,6 +23,7 @@ class Kraken(Exchange): _ft_has: Dict = { "stoploss_on_exchange": True, "ohlcv_candle_limit": 720, + "ohlcv_has_history": False, "trades_pagination": "id", "trades_pagination_arg": "since", "mark_ohlcv_timeframe": "4h", @@ -43,7 +45,7 @@ class Kraken(Exchange): return (parent_check and market.get('darkpool', False) is False) - def get_tickers(self, symbols: List[str] = None, cached: bool = False) -> Dict: + def get_tickers(self, symbols: Optional[List[str]] = None, cached: bool = False) -> Dict: # Only fetch tickers for current stake currency # Otherwise the request for kraken becomes too large. symbols = list(self.get_markets(quote_currencies=[self._config['stake_currency']])) @@ -95,7 +97,7 @@ class Kraken(Exchange): @retrier(retries=0) def stoploss(self, pair: str, amount: float, stop_price: float, - order_types: Dict, side: str, leverage: float) -> Dict: + order_types: Dict, side: BuySell, leverage: float) -> Dict: """ Creates a stoploss market order. Stoploss market orders is the only stoploss type supported by kraken. @@ -165,12 +167,14 @@ class Kraken(Exchange): def _get_params( self, + side: BuySell, ordertype: str, leverage: float, reduceOnly: bool, time_in_force: str = 'gtc' ) -> Dict: params = super()._get_params( + side=side, ordertype=ordertype, leverage=leverage, reduceOnly=reduceOnly, diff --git a/freqtrade/exchange/kucoin.py b/freqtrade/exchange/kucoin.py index f23189b3c..21eaa4bc3 100644 --- a/freqtrade/exchange/kucoin.py +++ b/freqtrade/exchange/kucoin.py @@ -33,7 +33,10 @@ class Kucoin(Exchange): Verify stop_loss against stoploss-order value (limit or price) Returns True if adjustment is necessary. """ - return order['info'].get('stop') is not None and stop_loss > float(order['stopPrice']) + return ( + order.get('stopPrice', None) is None + or stop_loss > float(order['stopPrice']) + ) def _get_stop_params(self, ordertype: str, stop_price: float) -> Dict: diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index fb7388ee1..afd7a672f 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -1,12 +1,15 @@ import logging -from typing import Dict, List, Tuple +from typing import Dict, List, Optional, Tuple import ccxt +from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode +from freqtrade.enums.candletype import CandleType from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exchange import Exchange from freqtrade.exchange.common import retrier +from freqtrade.exchange.exchange import date_minus_candles logger = logging.getLogger(__name__) @@ -19,12 +22,13 @@ class Okx(Exchange): """ _ft_has: Dict = { - "ohlcv_candle_limit": 300, + "ohlcv_candle_limit": 100, # Warning, special case with data prior to X months "mark_ohlcv_timeframe": "4h", "funding_fee_timeframe": "8h", } _ft_has_futures: Dict = { "tickers_have_quoteVolume": False, + "fee_cost_in_contracts": True, } _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ @@ -34,14 +38,69 @@ class Okx(Exchange): (TradingMode.FUTURES, MarginMode.ISOLATED), ] + net_only = True + + def ohlcv_candle_limit( + self, timeframe: str, candle_type: CandleType, since_ms: Optional[int] = None) -> int: + """ + Exchange ohlcv candle limit + OKX has the following behaviour: + * 300 candles for uptodate data + * 100 candles for historic data + * 100 candles for additional candles (not futures or spot). + :param timeframe: Timeframe to check + :param candle_type: Candle-type + :param since_ms: Starting timestamp + :return: Candle limit as integer + """ + if ( + candle_type in (CandleType.FUTURES, CandleType.SPOT) and + (not since_ms or since_ms > (date_minus_candles(timeframe, 300).timestamp() * 1000)) + ): + return 300 + + return super().ohlcv_candle_limit(timeframe, candle_type, since_ms) + + @retrier + def additional_exchange_init(self) -> None: + """ + Additional exchange initialization logic. + .api will be available at this point. + Must be overridden in child methods if required. + """ + try: + if self.trading_mode == TradingMode.FUTURES and not self._config['dry_run']: + accounts = self._api.fetch_accounts() + if len(accounts) > 0: + self.net_only = accounts[0].get('info', {}).get('posMode') == 'net_mode' + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + f'Could not set leverage due to {e.__class__.__name__}. Message: {e}') from e + except ccxt.BaseError as e: + raise OperationalException(e) from e + + def _get_posSide(self, side: BuySell, reduceOnly: bool): + if self.net_only: + return 'net' + if not reduceOnly: + # Enter + return 'long' if side == 'buy' else 'short' + else: + # Exit + return 'long' if side == 'sell' else 'short' + def _get_params( self, + side: BuySell, ordertype: str, leverage: float, reduceOnly: bool, time_in_force: str = 'gtc', ) -> Dict: params = super()._get_params( + side=side, ordertype=ordertype, leverage=leverage, reduceOnly=reduceOnly, @@ -49,10 +108,11 @@ class Okx(Exchange): ) if self.trading_mode == TradingMode.FUTURES and self.margin_mode: params['tdMode'] = self.margin_mode.value + params['posSide'] = self._get_posSide(side, reduceOnly) return params @retrier - def _lev_prep(self, pair: str, leverage: float, side: str): + def _lev_prep(self, pair: str, leverage: float, side: BuySell): if self.trading_mode != TradingMode.SPOT and self.margin_mode is not None: try: # TODO-lev: Test me properly (check mgnMode passed) @@ -61,7 +121,7 @@ class Okx(Exchange): symbol=pair, params={ "mgnMode": self.margin_mode.value, - # "posSide": "net"", + "posSide": self._get_posSide(side, False), }) except ccxt.DDoSProtection as e: raise DDosProtection(e) from e diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c52ce1b1c..2007f9b4e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -4,7 +4,7 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade() import copy import logging import traceback -from datetime import datetime, time, timezone +from datetime import datetime, time, timedelta, timezone from math import isclose from threading import Lock from typing import Any, Dict, List, Optional, Tuple @@ -13,7 +13,7 @@ from schedule import Scheduler from freqtrade import __version__, constants from freqtrade.configuration import validate_config_consistency -from freqtrade.constants import LongShort +from freqtrade.constants import BuySell, LongShort from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge @@ -22,6 +22,7 @@ from freqtrade.enums import (ExitCheckTuple, ExitType, RPCMessageType, RunMode, from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError, InvalidOrderException, PricingError) from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds +from freqtrade.exchange.exchange import timeframe_to_next_date from freqtrade.misc import safe_value_fallback, safe_value_fallback2 from freqtrade.mixins import LoggingMixin from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db @@ -66,14 +67,12 @@ class FreqtradeBot(LoggingMixin): self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) - init_db(self.config.get('db_url', None), clean_open_orders=self.config['dry_run']) + init_db(self.config['db_url']) self.wallets = Wallets(self.config, self.exchange) PairLocks.timeframe = self.config['timeframe'] - self.protections = ProtectionManager(self.config, self.strategy.protections) - # RPC runs in separate threads, can start handling external commands just after # initialization, even before Freqtradebot has a chance to start its throttling, # so anything in the Freqtradebot instance should be ready (initialized), including @@ -122,7 +121,9 @@ class FreqtradeBot(LoggingMixin): self._schedule.every().day.at(t).do(update) self.last_process = datetime(1970, 1, 1, tzinfo=timezone.utc) - self.strategy.bot_start() + self.strategy.ft_bot_start() + # Initialize protections AFTER bot start - otherwise parameters are not loaded. + self.protections = ProtectionManager(self.config, self.strategy.protections) def notify_status(self, msg: str) -> None: """ @@ -190,8 +191,8 @@ class FreqtradeBot(LoggingMixin): self.strategy.analyze(self.active_pair_whitelist) with self._exit_lock: - # Check and handle any timed out open orders - self.check_handle_timedout() + # Check for exchange cancelations, timeouts and user requested replace + self.manage_open_orders() # Protect from collisions with force_exit. # Without this, freqtrade my try to recreate stoploss_on_exchange orders @@ -226,7 +227,7 @@ class FreqtradeBot(LoggingMixin): Notify the user when the bot is stopped (not reloaded) and there are still open trades active. """ - open_trades = Trade.get_trades([Trade.is_open.is_(True)]).all() + open_trades = Trade.get_open_trades() if len(open_trades) != 0 and self.state != State.RELOAD_CONFIG: msg = { @@ -298,7 +299,17 @@ class FreqtradeBot(LoggingMixin): fo = self.exchange.fetch_order_or_stoploss_order(order.order_id, order.ft_pair, order.ft_order_side == 'stoploss') - self.update_trade_state(order.trade, order.order_id, fo) + self.update_trade_state(order.trade, order.order_id, fo, + stoploss_order=(order.ft_order_side == 'stoploss')) + + except InvalidOrderException as e: + logger.warning(f"Error updating Order {order.order_id} due to {e}.") + if order.order_date_utc - timedelta(days=5) < datetime.now(timezone.utc): + logger.warning( + "Order is older than 5 days. Assuming order was fully cancelled.") + fo = order.to_ccxt_object() + fo['status'] = 'canceled' + self.handle_timedout_order(fo, order.trade) except ExchangeError as e: @@ -321,6 +332,8 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open and not trade.fee_updated(trade.exit_side): # Get sell fee order = trade.select_order(trade.exit_side, False) + if not order: + order = trade.select_order('stoploss', False) if order: logger.info( f"Updating {trade.exit_side}-fee on trade {trade}" @@ -535,7 +548,8 @@ class FreqtradeBot(LoggingMixin): if stake_amount is not None and stake_amount > 0.0: # We should increase our position - self.execute_entry(trade.pair, stake_amount, trade=trade, is_short=trade.is_short) + self.execute_entry(trade.pair, stake_amount, price=current_rate, + trade=trade, is_short=trade.is_short) if stake_amount is not None and stake_amount < 0.0: # We should decrease our position @@ -585,6 +599,7 @@ class FreqtradeBot(LoggingMixin): ordertype: Optional[str] = None, enter_tag: Optional[str] = None, trade: Optional[Trade] = None, + order_adjust: bool = False ) -> bool: """ Executes a limit buy for the given pair @@ -594,12 +609,13 @@ class FreqtradeBot(LoggingMixin): """ time_in_force = self.strategy.order_time_in_force['entry'] - [side, name] = ['sell', 'Short'] if is_short else ['buy', 'Long'] + side: BuySell = 'sell' if is_short else 'buy' + name = 'Short' if is_short else 'Long' trade_side: LongShort = 'short' if is_short else 'long' pos_adjust = trade is not None enter_limit_requested, stake_amount, leverage = self.get_valid_enter_price_and_stake( - pair, price, stake_amount, trade_side, enter_tag, trade) + pair, price, stake_amount, trade_side, enter_tag, trade, order_adjust) if not stake_amount: return False @@ -620,7 +636,7 @@ class FreqtradeBot(LoggingMixin): pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, time_in_force=time_in_force, current_time=datetime.now(timezone.utc), entry_tag=enter_tag, side=trade_side): - logger.info(f"User requested abortion of buying {pair}") + logger.info(f"User denied entry for {pair}.") return False order = self.exchange.create_order( pair=pair, @@ -634,7 +650,7 @@ class FreqtradeBot(LoggingMixin): ) order_obj = Order.parse_from_ccxt_object(order, pair, side) order_id = order['id'] - order_status = order.get('status', None) + order_status = order.get('status') logger.info(f"Order #{order_id} was created for {pair} and status is {order_status}.") # we assume the order is executed at the price requested @@ -744,23 +760,26 @@ class FreqtradeBot(LoggingMixin): self, pair: str, price: Optional[float], stake_amount: float, trade_side: LongShort, entry_tag: Optional[str], - trade: Optional[Trade] + trade: Optional[Trade], + order_adjust: bool, ) -> Tuple[float, float, float]: if price: enter_limit_requested = price else: # Calculate price - proposed_enter_rate = self.exchange.get_rate( + enter_limit_requested = self.exchange.get_rate( pair, side='entry', is_short=(trade_side == 'short'), refresh=True) + if not order_adjust: + # Don't call custom_entry_price in order-adjust scenario custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, - default_retval=proposed_enter_rate)( + default_retval=enter_limit_requested)( pair=pair, current_time=datetime.now(timezone.utc), - proposed_rate=proposed_enter_rate, entry_tag=entry_tag, + proposed_rate=enter_limit_requested, entry_tag=entry_tag, side=trade_side, ) - enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate) + enter_limit_requested = self.get_valid_price(custom_entry_price, enter_limit_requested) if not enter_limit_requested: raise PricingError('Could not determine entry price.') @@ -773,7 +792,7 @@ class FreqtradeBot(LoggingMixin): current_rate=enter_limit_requested, proposed_leverage=1.0, max_leverage=max_leverage, - side=trade_side, + side=trade_side, entry_tag=entry_tag, ) if self.trading_mode != TradingMode.SPOT else 1.0 # Cap leverage between 1.0 and max_leverage. leverage = min(max(leverage, 1.0), max_leverage) @@ -797,7 +816,7 @@ class FreqtradeBot(LoggingMixin): pair=pair, current_time=datetime.now(timezone.utc), current_rate=enter_limit_requested, proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=min(max_stake_amount, stake_available), - entry_tag=entry_tag, side=trade_side + leverage=leverage, entry_tag=entry_tag, side=trade_side ) stake_amount = self.wallets.validate_stake_amount( @@ -829,7 +848,7 @@ class FreqtradeBot(LoggingMixin): 'type': msg_type, 'buy_tag': trade.enter_tag, 'enter_tag': trade.enter_tag, - 'exchange': self.exchange.name.capitalize(), + 'exchange': trade.exchange.capitalize(), 'pair': trade.pair, 'leverage': trade.leverage if trade.leverage else None, 'direction': 'Short' if trade.is_short else 'Long', @@ -859,7 +878,7 @@ class FreqtradeBot(LoggingMixin): 'type': RPCMessageType.ENTRY_CANCEL, 'buy_tag': trade.enter_tag, 'enter_tag': trade.enter_tag, - 'exchange': self.exchange.name.capitalize(), + 'exchange': trade.exchange.capitalize(), 'pair': trade.pair, 'leverage': trade.leverage, 'direction': 'Short' if trade.is_short else 'Long', @@ -942,6 +961,29 @@ class FreqtradeBot(LoggingMixin): logger.debug(f'Found no {exit_signal_type} signal for %s.', trade) return False + def _check_and_execute_exit(self, trade: Trade, exit_rate: float, + enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool: + """ + Check and execute trade exit + """ + exits: List[ExitCheckTuple] = self.strategy.should_exit( + trade, + exit_rate, + datetime.now(timezone.utc), + enter=enter, + exit_=exit_, + force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 + ) + for should_exit in exits: + if should_exit.exit_flag: + exit_tag1 = exit_tag if should_exit.exit_type == ExitType.EXIT_SIGNAL else None + logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}' + f'{f" Tag: {exit_tag1}" if exit_tag1 is not None else ""}') + exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag1) + if exited: + return True + return False + def create_stoploss_order(self, trade: Trade, stop_price: float) -> bool: """ Abstracts creating stoploss orders from the logic. @@ -1011,7 +1053,7 @@ class FreqtradeBot(LoggingMixin): # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') - self._notify_exit(trade, "stoploss") + self._notify_exit(trade, "stoploss", True) return True if trade.open_order_id or not trade.is_open: @@ -1093,34 +1135,13 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") - def _check_and_execute_exit(self, trade: Trade, exit_rate: float, - enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool: + def manage_open_orders(self) -> None: """ - Check and execute trade exit - """ - should_exit: ExitCheckTuple = self.strategy.should_exit( - trade, - exit_rate, - datetime.now(timezone.utc), - enter=enter, - exit_=exit_, - force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 - ) - - if should_exit.exit_flag: - logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}' - f'Tag: {exit_tag if exit_tag is not None else "None"}') - self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag) - return True - return False - - def check_handle_timedout(self) -> None: - """ - Check if any orders are timed out and cancel if necessary - :param timeoutvalue: Number of minutes until order is considered timed out + Management of open orders on exchange. Unfilled orders might be cancelled if timeout + was met or replaced if there's a new candle and user has requested it. + Timeout setting takes priority over limit order adjustment request. :return: None """ - for trade in Trade.get_open_order_trades(): try: if not trade.open_order_id: @@ -1131,33 +1152,88 @@ class FreqtradeBot(LoggingMixin): continue fully_cancelled = self.update_trade_state(trade, trade.open_order_id, order) - is_entering = order['side'] == trade.entry_side not_closed = order['status'] == 'open' or fully_cancelled - max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0) - order_obj = trade.select_order_by_order_id(trade.open_order_id) - if not_closed and (fully_cancelled or (order_obj and self.strategy.ft_check_timed_out( - trade, order_obj, datetime.now(timezone.utc))) - ): - if is_entering: - self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) + if not_closed: + if fully_cancelled or (order_obj and self.strategy.ft_check_timed_out( + trade, order_obj, datetime.now(timezone.utc))): + self.handle_timedout_order(order, trade) else: - canceled = self.handle_cancel_exit( - trade, order, constants.CANCEL_REASON['TIMEOUT']) - canceled_count = trade.get_exit_order_count() - max_timeouts = self.config.get( - 'unfilledtimeout', {}).get('exit_timeout_count', 0) - if canceled and max_timeouts > 0 and canceled_count >= max_timeouts: - logger.warning(f'Emergency exiting trade {trade}, as the exit order ' - f'timed out {max_timeouts} times.') - try: - self.execute_trade_exit( - trade, order.get('price'), - exit_check=ExitCheckTuple(exit_type=ExitType.EMERGENCY_EXIT)) - except DependencyException as exception: - logger.warning( - f'Unable to emergency sell trade {trade.pair}: {exception}') + self.replace_order(order, order_obj, trade) + + def handle_timedout_order(self, order: Dict, trade: Trade) -> None: + """ + Check if current analyzed order timed out and cancel if necessary. + :param order: Order dict grabbed with exchange.fetch_order() + :param trade: Trade object. + :return: None + """ + if order['side'] == trade.entry_side: + self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT']) + else: + canceled = self.handle_cancel_exit( + trade, order, constants.CANCEL_REASON['TIMEOUT']) + canceled_count = trade.get_exit_order_count() + max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0) + if canceled and max_timeouts > 0 and canceled_count >= max_timeouts: + logger.warning(f'Emergency exiting trade {trade}, as the exit order ' + f'timed out {max_timeouts} times.') + try: + self.execute_trade_exit( + trade, order['price'], + exit_check=ExitCheckTuple(exit_type=ExitType.EMERGENCY_EXIT)) + except DependencyException as exception: + logger.warning( + f'Unable to emergency sell trade {trade.pair}: {exception}') + + def replace_order(self, order: Dict, order_obj: Optional[Order], trade: Trade) -> None: + """ + Check if current analyzed entry order should be replaced or simply cancelled. + To simply cancel the existing order(no replacement) adjust_entry_price() should return None + To maintain existing order adjust_entry_price() should return order_obj.price + To replace existing order adjust_entry_price() should return desired price for limit order + :param order: Order dict grabbed with exchange.fetch_order() + :param order_obj: Order object. + :param trade: Trade object. + :return: None + """ + analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, + self.strategy.timeframe) + latest_candle_open_date = analyzed_df.iloc[-1]['date'] if len(analyzed_df) > 0 else None + latest_candle_close_date = timeframe_to_next_date(self.strategy.timeframe, + latest_candle_open_date) + # Check if new candle + if order_obj and latest_candle_close_date > order_obj.order_date_utc: + # New candle + proposed_rate = self.exchange.get_rate( + trade.pair, side='entry', is_short=trade.is_short, refresh=True) + adjusted_entry_price = strategy_safe_wrapper(self.strategy.adjust_entry_price, + default_retval=order_obj.price)( + trade=trade, order=order_obj, pair=trade.pair, + current_time=datetime.now(timezone.utc), proposed_rate=proposed_rate, + current_order_rate=order_obj.price, entry_tag=trade.enter_tag, + side=trade.entry_side) + + replacing = True + cancel_reason = constants.CANCEL_REASON['REPLACE'] + if not adjusted_entry_price: + replacing = False + cancel_reason = constants.CANCEL_REASON['USER_CANCEL'] + if order_obj.price != adjusted_entry_price: + # cancel existing order if new price is supplied or None + self.handle_cancel_enter(trade, order, cancel_reason, + replacing=replacing) + if adjusted_entry_price: + # place new order only if new price is supplied + self.execute_entry( + pair=trade.pair, + stake_amount=(order_obj.remaining * order_obj.price), + price=adjusted_entry_price, + trade=trade, + is_short=trade.is_short, + order_adjust=True, + ) def cancel_all_open_orders(self) -> None: """ @@ -1179,9 +1255,13 @@ class FreqtradeBot(LoggingMixin): self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['ALL_CANCELLED']) Trade.commit() - def handle_cancel_enter(self, trade: Trade, order: Dict, reason: str) -> bool: + def handle_cancel_enter( + self, trade: Trade, order: Dict, reason: str, + replacing: Optional[bool] = False + ) -> bool: """ Buy cancel - cancel order + :param replacing: Replacing order - prevent trade deletion. :return: True if order was fully cancelled """ was_trade_fully_canceled = False @@ -1217,9 +1297,10 @@ class FreqtradeBot(LoggingMixin): # Using filled to determine the filled amount filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled') if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC): - logger.info(f'{side} order fully cancelled. Removing {trade} from database.') # if trade is not partially completed and it's the only order, just delete the trade - if len(trade.orders) <= 1: + open_order_count = len([order for order in trade.orders if order.status == 'open']) + if open_order_count <= 1 and trade.nr_of_successful_entries == 0 and not replacing: + logger.info(f'{side} order fully cancelled. Removing {trade} from database.') trade.delete() was_trade_fully_canceled = True reason += f", {constants.CANCEL_REASON['FULLY_CANCELLED']}" @@ -1227,7 +1308,7 @@ class FreqtradeBot(LoggingMixin): # FIXME TODO: This could possibly reworked to not duplicate the code 15 lines below. self.update_trade_state(trade, trade.open_order_id, corder) trade.open_order_id = None - logger.info(f'Partial {side} order timeout for {trade}.') + logger.info(f'{side} Order timeout for {trade}.') else: # if trade is partially complete, edit the stake details for the trade # and close the order @@ -1339,7 +1420,7 @@ class FreqtradeBot(LoggingMixin): :param trade: Trade instance :param limit: limit rate for the sell order :param exit_check: CheckTuple with signal and reason - :return: True if it succeeds (supported) False (not supported) + :return: True if it succeeds False """ trade.funding_fees = self.exchange.get_funding_fees( pair=trade.pair, @@ -1348,6 +1429,7 @@ class FreqtradeBot(LoggingMixin): open_date=trade.open_date_utc, ) exit_type = 'exit' + exit_reason = exit_tag or exit_check.exit_reason if exit_check.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS): exit_type = 'stoploss' @@ -1365,7 +1447,7 @@ class FreqtradeBot(LoggingMixin): pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc), proposed_rate=proposed_limit_rate, current_profit=current_profit, - exit_tag=exit_check.exit_reason) + exit_tag=exit_reason) limit = self.get_valid_price(custom_exit_price, proposed_limit_rate) @@ -1382,10 +1464,10 @@ class FreqtradeBot(LoggingMixin): if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( pair=trade.pair, trade=trade, order_type=order_type, amount=amount, rate=limit, - time_in_force=time_in_force, exit_reason=exit_check.exit_reason, - sell_reason=exit_check.exit_reason, # sellreason -> compatibility + time_in_force=time_in_force, exit_reason=exit_reason, + sell_reason=exit_reason, # sellreason -> compatibility current_time=datetime.now(timezone.utc)): - logger.info(f"User requested abortion of exiting {trade.pair}") + logger.info(f"User denied exit for {trade.pair}.") return False try: @@ -1412,7 +1494,7 @@ class FreqtradeBot(LoggingMixin): trade.open_order_id = order['id'] trade.exit_order_status = '' trade.close_rate_requested = limit - trade.exit_reason = exit_tag or exit_check.exit_reason + trade.exit_reason = exit_reason # Lock pair for one candle to prevent immediate re-trading self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), @@ -1462,7 +1544,7 @@ class FreqtradeBot(LoggingMixin): 'open_date': trade.open_date, 'close_date': trade.close_date or datetime.utcnow(), 'stake_currency': self.config['stake_currency'], - 'fiat_currency': self.config.get('fiat_display_currency', None), + 'fiat_currency': self.config.get('fiat_display_currency'), } if 'fiat_display_currency' in self.config: @@ -1573,12 +1655,11 @@ class FreqtradeBot(LoggingMixin): if order['status'] in constants.NON_OPEN_EXCHANGE_STATES: # If a entry order was closed, force update on stoploss on exchange - if order.get('side', None) == trade.entry_side: + if order.get('side') == trade.entry_side: trade = self.cancel_stoploss_on_exchange(trade) # TODO: Margin will need to use interest_rate as well. # interest_rate = self.exchange.get_interest_rate() trade.set_isolated_liq(self.exchange.get_liquidation_price( - leverage=trade.leverage, pair=trade.pair, amount=trade.amount, @@ -1597,7 +1678,7 @@ class FreqtradeBot(LoggingMixin): if send_msg and not stoploss_order and not trade.open_order_id: self._notify_exit(trade, '', True) self.handle_protections(trade.pair, trade.trade_direction) - elif send_msg and not trade.open_order_id: + elif send_msg and not trade.open_order_id and not stoploss_order: # Enter fill self._notify_enter(trade, order, fill=True) @@ -1663,7 +1744,8 @@ class FreqtradeBot(LoggingMixin): trade_base_currency = self.exchange.get_pair_base_currency(trade.pair) # use fee from order-dict if possible if self.exchange.order_has_fee(order): - fee_cost, fee_currency, fee_rate = self.exchange.extract_cost_curr_rate(order) + fee_cost, fee_currency, fee_rate = self.exchange.extract_cost_curr_rate( + order['fee'], order['symbol'], order['cost'], order_obj.safe_filled) logger.info(f"Fee for Trade {trade} [{order_obj.ft_order_side}]: " f"{fee_cost:.8g} {fee_currency} - rate: {fee_rate}") if fee_rate is None or fee_rate < 0.02: @@ -1701,7 +1783,15 @@ class FreqtradeBot(LoggingMixin): for exectrade in trades: amount += exectrade['amount'] if self.exchange.order_has_fee(exectrade): - fee_cost_, fee_currency, fee_rate_ = self.exchange.extract_cost_curr_rate(exectrade) + # Prefer singular fee + fees = [exectrade['fee']] + else: + fees = exectrade.get('fees', []) + for fee in fees: + + fee_cost_, fee_currency, fee_rate_ = self.exchange.extract_cost_curr_rate( + fee, exectrade['symbol'], exectrade['cost'], exectrade['amount'] + ) fee_cost += fee_cost_ if fee_rate_ is not None: fee_rate_array.append(fee_rate_) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 937ad43de..da28a8d93 100755 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -87,7 +87,7 @@ class Backtesting: self.exchange = ExchangeResolver.load_exchange(self._exchange_name, self.config) self.dataprovider = DataProvider(self.config, self.exchange) - if self.config.get('strategy_list', None): + if self.config.get('strategy_list'): for strat in list(self.config['strategy_list']): stratconf = deepcopy(self.config) stratconf['strategy'] = strat @@ -187,7 +187,9 @@ class Backtesting: # since a "perfect" stoploss-exit is assumed anyway # And the regular "stoploss" function would not apply to that case self.strategy.order_types['stoploss_on_exchange'] = False - self.strategy.bot_start() + + self.strategy.ft_bot_start() + strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)() def _load_protections(self, strategy: IStrategy): if self.config.get('enable_protections', False): @@ -275,8 +277,12 @@ class Backtesting: if pair not in self.exchange._leverage_tiers: unavailable_pairs.append(pair) continue - self.futures_data[pair] = funding_rates_dict[pair].merge( - mark_rates_dict[pair], on='date', how="inner", suffixes=["_fund", "_mark"]) + + self.futures_data[pair] = self.exchange.combine_funding_and_mark( + funding_rates=funding_rates_dict[pair], + mark_rates=mark_rates_dict[pair], + futures_funding_rate=self.config.get('futures_funding_rate', None), + ) if unavailable_pairs: raise OperationalException( @@ -297,6 +303,9 @@ class Backtesting: self.rejected_trades = 0 self.timedout_entry_orders = 0 self.timedout_exit_orders = 0 + self.canceled_trade_entries = 0 + self.canceled_entry_orders = 0 + self.replaced_entry_orders = 0 self.dataprovider.clear_cache() if enable_protections: self._load_protections(self.strategy) @@ -493,7 +502,8 @@ class Backtesting: stake_available = self.wallets.get_available_stake_amount() stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)( - trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], + trade=trade, # type: ignore[arg-type] + current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX], current_profit=current_profit, min_stake=min_stake, max_stake=min(max_stake, stake_available)) @@ -524,64 +534,76 @@ class Backtesting: if check_adjust_entry: trade = self._get_adjust_trade_entry_for_candle(trade, row) - exit_candle_time: datetime = row[DATE_IDX].to_pydatetime() enter = row[SHORT_IDX] if trade.is_short else row[LONG_IDX] exit_sig = row[ESHORT_IDX] if trade.is_short else row[ELONG_IDX] - exit_ = self.strategy.should_exit( - trade, row[OPEN_IDX], exit_candle_time, # type: ignore + exits = self.strategy.should_exit( + trade, row[OPEN_IDX], row[DATE_IDX].to_pydatetime(), # type: ignore enter=enter, exit_=exit_sig, low=row[LOW_IDX], high=row[HIGH_IDX] ) + for exit_ in exits: + t = self._get_exit_for_signal(trade, row, exit_) + if t: + return t + return None + def _get_exit_for_signal(self, trade: LocalTrade, row: Tuple, + exit_: ExitCheckTuple) -> Optional[LocalTrade]: + + exit_candle_time: datetime = row[DATE_IDX].to_pydatetime() if exit_.exit_flag: trade.close_date = exit_candle_time + exit_reason = exit_.exit_reason trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60) try: - closerate = self._get_close_rate(row, trade, exit_, trade_dur) + close_rate = self._get_close_rate(row, trade, exit_, trade_dur) except ValueError: return None - # call the custom exit price,with default value as previous closerate - current_profit = trade.calc_profit_ratio(closerate) + # call the custom exit price,with default value as previous close_rate + current_profit = trade.calc_profit_ratio(close_rate) order_type = self.strategy.order_types['exit'] if exit_.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT): + # Checks and adds an exit tag, after checking that the length of the + # row has the length for an exit tag column + if( + len(row) > EXIT_TAG_IDX + and row[EXIT_TAG_IDX] is not None + and len(row[EXIT_TAG_IDX]) > 0 + and exit_.exit_type in (ExitType.EXIT_SIGNAL,) + ): + exit_reason = row[EXIT_TAG_IDX] # Custom exit pricing only for exit-signals if order_type == 'limit': - closerate = strategy_safe_wrapper(self.strategy.custom_exit_price, - default_retval=closerate)( - pair=trade.pair, trade=trade, + close_rate = strategy_safe_wrapper(self.strategy.custom_exit_price, + default_retval=close_rate)( + pair=trade.pair, + trade=trade, # type: ignore[arg-type] current_time=exit_candle_time, - proposed_rate=closerate, current_profit=current_profit, - exit_tag=exit_.exit_reason) + proposed_rate=close_rate, current_profit=current_profit, + exit_tag=exit_reason) # We can't place orders lower than current low. # freqtrade does not support this in live, and the order would fill immediately if trade.is_short: - closerate = min(closerate, row[HIGH_IDX]) + close_rate = min(close_rate, row[HIGH_IDX]) else: - closerate = max(closerate, row[LOW_IDX]) + close_rate = max(close_rate, row[LOW_IDX]) # Confirm trade exit: time_in_force = self.strategy.order_time_in_force['exit'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( - pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount, - rate=closerate, + pair=trade.pair, + trade=trade, # type: ignore[arg-type] + order_type='limit', + amount=trade.amount, + rate=close_rate, time_in_force=time_in_force, - sell_reason=exit_.exit_reason, # deprecated - exit_reason=exit_.exit_reason, + sell_reason=exit_reason, # deprecated + exit_reason=exit_reason, current_time=exit_candle_time): return None - trade.exit_reason = exit_.exit_reason - - # Checks and adds an exit tag, after checking that the length of the - # row has the length for an exit tag column - if( - len(row) > EXIT_TAG_IDX - and row[EXIT_TAG_IDX] is not None - and len(row[EXIT_TAG_IDX]) > 0 - and exit_.exit_type in (ExitType.EXIT_SIGNAL,) - ): - trade.exit_reason = row[EXIT_TAG_IDX] + trade.exit_reason = exit_reason self.order_id_counter += 1 order = Order( @@ -597,12 +619,12 @@ class Backtesting: side=trade.exit_side, order_type=order_type, status="open", - price=closerate, - average=closerate, + price=close_rate, + average=close_rate, amount=trade.amount, filled=0, remaining=trade.amount, - cost=trade.amount * closerate, + cost=trade.amount * close_rate, ) trade.orders.append(order) return trade @@ -649,7 +671,7 @@ class Backtesting: return self._get_exit_trade_entry_for_candle(trade, row) def get_valid_price_and_stake( - self, pair: str, row: Tuple, propose_rate: float, stake_amount: Optional[float], + self, pair: str, row: Tuple, propose_rate: float, stake_amount: float, direction: LongShort, current_time: datetime, entry_tag: Optional[str], trade: Optional[LocalTrade], order_type: str ) -> Tuple[float, float, float, float]: @@ -683,7 +705,7 @@ class Backtesting: current_rate=row[OPEN_IDX], proposed_leverage=1.0, max_leverage=max_leverage, - side=direction, + side=direction, entry_tag=entry_tag, ) if self._can_short else 1.0 # Cap leverage between 1.0 and max_leverage. leverage = min(max(leverage, 1.0), max_leverage) @@ -700,7 +722,7 @@ class Backtesting: pair=pair, current_time=current_time, current_rate=propose_rate, proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=min(stake_available, max_stake_amount), - entry_tag=entry_tag, side=direction) + leverage=leverage, entry_tag=entry_tag, side=direction) stake_amount_val = self.wallets.validate_stake_amount( pair=pair, @@ -713,19 +735,26 @@ class Backtesting: def _enter_trade(self, pair: str, row: Tuple, direction: LongShort, stake_amount: Optional[float] = None, - trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]: + trade: Optional[LocalTrade] = None, + requested_rate: Optional[float] = None, + requested_stake: Optional[float] = None) -> Optional[LocalTrade]: current_time = row[DATE_IDX].to_pydatetime() entry_tag = row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None # let's call the custom entry price, using the open price as default price order_type = self.strategy.order_types['entry'] - pos_adjust = trade is not None + pos_adjust = trade is not None and requested_rate is None + stake_amount_ = stake_amount or (trade.stake_amount if trade else 0.0) propose_rate, stake_amount, leverage, min_stake_amount = self.get_valid_price_and_stake( - pair, row, row[OPEN_IDX], stake_amount, direction, current_time, entry_tag, trade, + pair, row, row[OPEN_IDX], stake_amount_, direction, current_time, entry_tag, trade, order_type ) + # replace proposed rate if another rate was requested + propose_rate = requested_rate if requested_rate else propose_rate + stake_amount = requested_stake if requested_stake else stake_amount + if not stake_amount: # In case of pos adjust, still return the original trade # If not pos adjust, trade is None @@ -806,11 +835,11 @@ class Backtesting: remaining=amount, cost=stake_amount + trade.fee_open, ) + trade.orders.append(order) if pos_adjust and self._get_order_filled(order.price, row): - order.close_bt_order(current_time) + order.close_bt_order(current_time, trade) else: trade.open_order_id = str(self.order_id_counter) - trade.orders.append(order) trade.recalc_trade_from_orders() return trade @@ -867,28 +896,90 @@ class Backtesting: self.protections.stop_per_pair(pair, current_time, side) self.protections.global_stop(current_time, side) - def check_order_cancel(self, trade: LocalTrade, current_time) -> bool: + def manage_open_orders(self, trade: LocalTrade, current_time: datetime, row: Tuple) -> bool: """ - Check if an order has been canceled. - Returns True if the trade should be Deleted (initial order was canceled). + Check if any open order needs to be cancelled or replaced. + Returns True if the trade should be deleted. """ for order in [o for o in trade.orders if o.ft_is_open]: + oc = self.check_order_cancel(trade, order, current_time) + if oc: + # delete trade due to order timeout + return True + elif oc is None and self.check_order_replace(trade, order, current_time, row): + # delete trade due to user request + self.canceled_trade_entries += 1 + return True + # default maintain trade + return False - timedout = self.strategy.ft_check_timed_out(trade, order, current_time) - if timedout: - if order.side == trade.entry_side: - self.timedout_entry_orders += 1 - if trade.nr_of_successful_entries == 0: - # Remove trade due to entry timeout expiration. - return True - else: - # Close additional entry order - del trade.orders[trade.orders.index(order)] - if order.side == trade.exit_side: - self.timedout_exit_orders += 1 - # Close exit order and retry exiting on next signal. + def check_order_cancel( + self, trade: LocalTrade, order: Order, current_time: datetime) -> Optional[bool]: + """ + Check if current analyzed order has to be canceled. + Returns True if the trade should be Deleted (initial order was canceled), + False if it's Canceled + None if the order is still active. + """ + timedout = self.strategy.ft_check_timed_out( + trade, # type: ignore[arg-type] + order, current_time) + if timedout: + if order.side == trade.entry_side: + self.timedout_entry_orders += 1 + if trade.nr_of_successful_entries == 0: + # Remove trade due to entry timeout expiration. + return True + else: + # Close additional entry order del trade.orders[trade.orders.index(order)] + trade.open_order_id = None + return False + if order.side == trade.exit_side: + self.timedout_exit_orders += 1 + # Close exit order and retry exiting on next signal. + del trade.orders[trade.orders.index(order)] + trade.open_order_id = None + return False + return None + def check_order_replace(self, trade: LocalTrade, order: Order, current_time, + row: Tuple) -> bool: + """ + Check if current analyzed entry order has to be replaced and do so. + If user requested cancellation and there are no filled orders in the trade will + instruct caller to delete the trade. + Returns True if the trade should be deleted. + """ + # only check on new candles for open entry orders + if order.side == trade.entry_side and current_time > order.order_date_utc: + requested_rate = strategy_safe_wrapper(self.strategy.adjust_entry_price, + default_retval=order.price)( + trade=trade, # type: ignore[arg-type] + order=order, pair=trade.pair, current_time=current_time, + proposed_rate=row[OPEN_IDX], current_order_rate=order.price, + entry_tag=trade.enter_tag, side=trade.trade_direction + ) # default value is current order price + + # cancel existing order whenever a new rate is requested (or None) + if requested_rate == order.price: + # assumption: there can't be multiple open entry orders at any given time + return False + else: + del trade.orders[trade.orders.index(order)] + trade.open_order_id = None + self.canceled_entry_orders += 1 + + # place new order if result was not None + if requested_rate: + self._enter_trade(pair=trade.pair, row=row, trade=trade, + requested_rate=requested_rate, + requested_stake=(order.remaining * order.price), + direction='short' if trade.is_short else 'long') + self.replaced_entry_orders += 1 + else: + # assumption: there can't be multiple open entry orders at any given time + return (trade.nr_of_successful_entries == 0) return False def validate_row( @@ -960,11 +1051,12 @@ class Backtesting: self.dataprovider._set_dataframe_max_index(row_index) for t in list(open_trades[pair]): - # 1. Cancel expired entry/exit orders. - if self.check_order_cancel(t, current_time): - # Close trade due to entry timeout expiration. + # 1. Manage currently open orders of active trades + if self.manage_open_orders(t, current_time, row): + # Close trade open_trade_count -= 1 open_trades[pair].remove(t) + LocalTrade.trades_open.remove(t) self.wallets.update() # 2. Process entries. @@ -988,14 +1080,15 @@ class Backtesting: open_trade_count += 1 # logger.debug(f"{pair} - Emulate creation of new trade: {trade}.") open_trades[pair].append(trade) + LocalTrade.add_bt_trade(trade) + self.wallets.update() for trade in list(open_trades[pair]): # 3. Process entry orders. order = trade.select_order(trade.entry_side, is_open=True) if order and self._get_order_filled(order.price, row): - order.close_bt_order(current_time) + order.close_bt_order(current_time, trade) trade.open_order_id = None - LocalTrade.add_bt_trade(trade) self.wallets.update() # 4. Create exit orders (if any) @@ -1005,6 +1098,7 @@ class Backtesting: # 5. Process exit orders. order = trade.select_order(trade.exit_side, is_open=True) if order and self._get_order_filled(order.price, row): + order.close_bt_order(current_time, trade) trade.open_order_id = None trade.close_date = current_time trade.close(order.price, show_msg=False) @@ -1033,6 +1127,9 @@ class Backtesting: 'rejected_signals': self.rejected_trades, 'timedout_entry_orders': self.timedout_entry_orders, 'timedout_exit_orders': self.timedout_exit_orders, + 'canceled_trade_entries': self.canceled_trade_entries, + 'canceled_entry_orders': self.canceled_entry_orders, + 'replaced_entry_orders': self.replaced_entry_orders, 'final_balance': self.wallets.get_total(self.strategy.config['stake_currency']), } @@ -1044,8 +1141,6 @@ class Backtesting: backtest_start_time = datetime.now(timezone.utc) self._set_strategy(strat) - strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)() - # Use max_open_trades in backtesting, except --disable-max-market-positions is set if self.config.get('use_max_market_positions', True): # Must come from strategy config, as the strategy may modify this setting. @@ -1170,13 +1265,14 @@ class Backtesting: self.results['strategy_comparison'].extend(results['strategy_comparison']) else: self.results = results - + dt_appendix = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") if self.config.get('export', 'none') in ('trades', 'signals'): - store_backtest_stats(self.config['exportfilename'], self.results) + store_backtest_stats(self.config['exportfilename'], self.results, dt_appendix) if (self.config.get('export', 'none') == 'signals' and self.dataprovider.runmode == RunMode.BACKTEST): - store_backtest_signal_candles(self.config['exportfilename'], self.processed_dfs) + store_backtest_signal_candles( + self.config['exportfilename'], self.processed_dfs, dt_appendix) # Results may be mixed up now. Sort them so they follow --strategy-list order. if 'strategy_list' in self.config and len(self.results) > 0: diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index 30eabecd0..aa3b02529 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -44,7 +44,7 @@ class EdgeCli: self.edge._timerange = TimeRange.parse_timerange(None if self.config.get( 'timerange') is None else str(self.config.get('timerange'))) - self.strategy.bot_start() + self.strategy.ft_bot_start() def start(self) -> None: result = self.edge.calculate(self.config['exchange']['pair_whitelist']) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 1dafb483c..566412f29 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -6,6 +6,7 @@ This module contains the hyperopt logic import logging import random +import sys import warnings from datetime import datetime, timezone from math import ceil @@ -17,6 +18,7 @@ import rapidjson from colorama import Fore, Style from colorama import init as colorama_init from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects +from joblib.externals import cloudpickle from pandas import DataFrame from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN @@ -27,8 +29,7 @@ from freqtrade.misc import deep_merge_dicts, file_dump_json, plural from freqtrade.optimize.backtesting import Backtesting # Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules from freqtrade.optimize.hyperopt_auto import HyperOptAuto -from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401 -from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401 +from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer from freqtrade.optimize.optimize_reports import generate_strategy_stats from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver @@ -62,7 +63,6 @@ class Hyperopt: hyperopt = Hyperopt(config) hyperopt.start() """ - custom_hyperopt: IHyperOpt def __init__(self, config: Dict[str, Any]) -> None: self.buy_space: List[Dimension] = [] @@ -77,6 +77,7 @@ class Hyperopt: self.backtesting = Backtesting(self.config) self.pairlist = self.backtesting.pairlists.whitelist + self.custom_hyperopt: HyperOptAuto if not self.config.get('hyperopt'): self.custom_hyperopt = HyperOptAuto(self.config) @@ -88,7 +89,9 @@ class Hyperopt: self.backtesting._set_strategy(self.backtesting.strategylist[0]) self.custom_hyperopt.strategy = self.backtesting.strategy - self.custom_hyperoptloss = HyperOptLossResolver.load_hyperoptloss(self.config) + self.hyperopt_pickle_magic(self.backtesting.strategy.__class__.__bases__) + self.custom_hyperoptloss: IHyperOptLoss = HyperOptLossResolver.load_hyperoptloss( + self.config) self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function time_now = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") strategy = str(self.config['strategy']) @@ -137,6 +140,17 @@ class Hyperopt: logger.info(f"Removing `{p}`.") p.unlink() + def hyperopt_pickle_magic(self, bases) -> None: + """ + Hyperopt magic to allow strategy inheritance across files. + For this to properly work, we need to register the module of the imported class + to pickle as value. + """ + for modules in bases: + if modules.__name__ != 'IStrategy': + cloudpickle.register_pickle_by_value(sys.modules[modules.__module__]) + self.hyperopt_pickle_magic(modules.__bases__) + def _get_params_dict(self, dimensions: List[Dimension], raw_params: List[Any]) -> Dict: # Ensure the number of dimensions match @@ -429,7 +443,7 @@ class Hyperopt: return new_list i = 0 asked_non_tried: List[List[Any]] = [] - is_random: List[bool] = [] + is_random_non_tried: List[bool] = [] while i < 5 and len(asked_non_tried) < n_points: if i < 3: self.opt.cache_ = {} @@ -438,9 +452,9 @@ class Hyperopt: else: asked = unique_list(self.opt.space.rvs(n_samples=n_points * 5)) is_random = [True for _ in range(len(asked))] - is_random += [rand for x, rand in zip(asked, is_random) - if x not in self.opt.Xi - and x not in asked_non_tried] + is_random_non_tried += [rand for x, rand in zip(asked, is_random) + if x not in self.opt.Xi + and x not in asked_non_tried] asked_non_tried += [x for x in asked if x not in self.opt.Xi and x not in asked_non_tried] @@ -449,13 +463,13 @@ class Hyperopt: if asked_non_tried: return ( asked_non_tried[:min(len(asked_non_tried), n_points)], - is_random[:min(len(asked_non_tried), n_points)] + is_random_non_tried[:min(len(asked_non_tried), n_points)] ) else: return self.opt.ask(n_points=n_points), [False for _ in range(n_points)] def start(self) -> None: - self.random_state = self._set_random_state(self.config.get('hyperopt_random_state', None)) + self.random_state = self._set_random_state(self.config.get('hyperopt_random_state')) logger.info(f"Using optimizer random state: {self.random_state}") self.hyperopt_table_header = -1 # Initialize spaces ... diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 0421e6e38..ab6ef013b 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -127,14 +127,14 @@ class HyperoptTools(): 'only_profitable': config.get('hyperopt_list_profitable', False), 'filter_min_trades': config.get('hyperopt_list_min_trades', 0), 'filter_max_trades': config.get('hyperopt_list_max_trades', 0), - 'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None), - 'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None), - 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None), - 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None), - 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None), - 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None), - 'filter_min_objective': config.get('hyperopt_list_min_objective', None), - 'filter_max_objective': config.get('hyperopt_list_max_objective', None), + 'filter_min_avg_time': config.get('hyperopt_list_min_avg_time'), + 'filter_max_avg_time': config.get('hyperopt_list_max_avg_time'), + 'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit'), + 'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit'), + 'filter_min_total_profit': config.get('hyperopt_list_min_total_profit'), + 'filter_max_total_profit': config.get('hyperopt_list_max_total_profit'), + 'filter_min_objective': config.get('hyperopt_list_min_objective'), + 'filter_max_objective': config.get('hyperopt_list_max_objective'), } if not HyperoptTools._test_hyperopt_results_exist(results_file): # No file found. diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 42db366a1..44ac4a5b3 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -4,7 +4,6 @@ from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Any, Dict, List, Union -from numpy import int64 from pandas import DataFrame, to_datetime from tabulate import tabulate @@ -18,21 +17,21 @@ from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename logger = logging.getLogger(__name__) -def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> None: +def store_backtest_stats( + recordfilename: Path, stats: Dict[str, DataFrame], dtappendix: str) -> None: """ Stores backtest results :param recordfilename: Path object, which can either be a filename or a directory. Filenames will be appended with a timestamp right before the suffix while for directories, /backtest-result-.json will be used as filename :param stats: Dataframe containing the backtesting statistics + :param dtappendix: Datetime to use for the filename """ if recordfilename.is_dir(): - filename = (recordfilename / - f'backtest-result-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.json') + filename = (recordfilename / f'backtest-result-{dtappendix}.json') else: filename = Path.joinpath( - recordfilename.parent, - f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}' + recordfilename.parent, f'{recordfilename.stem}-{dtappendix}' ).with_suffix(recordfilename.suffix) # Store metadata separately. @@ -45,7 +44,8 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N file_dump_json(latest_filename, {'latest_backtest': str(filename.name)}) -def store_backtest_signal_candles(recordfilename: Path, candles: Dict[str, Dict]) -> Path: +def store_backtest_signal_candles( + recordfilename: Path, candles: Dict[str, Dict], dtappendix: str) -> Path: """ Stores backtest trade signal candles :param recordfilename: Path object, which can either be a filename or a directory. @@ -53,14 +53,13 @@ def store_backtest_signal_candles(recordfilename: Path, candles: Dict[str, Dict] while for directories, /backtest-result-_signals.pkl will be used as filename :param stats: Dict containing the backtesting signal candles + :param dtappendix: Datetime to use for the filename """ if recordfilename.is_dir(): - filename = (recordfilename / - f'backtest-result-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}_signals.pkl') + filename = (recordfilename / f'backtest-result-{dtappendix}_signals.pkl') else: filename = Path.joinpath( - recordfilename.parent, - f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}_signals.pkl' + recordfilename.parent, f'{recordfilename.stem}-{dtappendix}_signals.pkl' ) file_dump_joblib(filename, candles) @@ -417,9 +416,9 @@ def generate_strategy_stats(pairlist: List[str], key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None worst_pair = min([pair for pair in pair_results if pair['key'] != 'TOTAL'], key=lambda x: x['profit_sum']) if len(pair_results) > 1 else None - if not results.empty: - results['open_timestamp'] = results['open_date'].view(int64) // 1e6 - results['close_timestamp'] = results['close_date'].view(int64) // 1e6 + winning_profit = results.loc[results['profit_abs'] > 0, 'profit_abs'].sum() + losing_profit = results.loc[results['profit_abs'] < 0, 'profit_abs'].sum() + profit_factor = winning_profit / abs(losing_profit) if losing_profit else 0.0 backtest_days = (max_date - min_date).days or 1 strat_stats = { @@ -447,6 +446,7 @@ def generate_strategy_stats(pairlist: List[str], 'profit_total_long_abs': results.loc[~results['is_short'], 'profit_abs'].sum(), 'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(), 'cagr': calculate_cagr(backtest_days, start_balance, content['final_balance']), + 'profit_factor': profit_factor, 'backtest_start': min_date.strftime(DATETIME_PRINT_FORMAT), 'backtest_start_ts': int(min_date.timestamp() * 1000), 'backtest_end': max_date.strftime(DATETIME_PRINT_FORMAT), @@ -468,6 +468,9 @@ def generate_strategy_stats(pairlist: List[str], 'rejected_signals': content['rejected_signals'], 'timedout_entry_orders': content['timedout_entry_orders'], 'timedout_exit_orders': content['timedout_exit_orders'], + 'canceled_trade_entries': content['canceled_trade_entries'], + 'canceled_entry_orders': content['canceled_entry_orders'], + 'replaced_entry_orders': content['replaced_entry_orders'], 'max_open_trades': max_open_trades, 'max_open_trades_setting': (config['max_open_trades'] if config['max_open_trades'] != float('inf') else -1), @@ -498,8 +501,10 @@ def generate_strategy_stats(pairlist: List[str], (drawdown_abs, drawdown_start, drawdown_end, high_val, low_val, max_drawdown) = calculate_max_drawdown( results, value_col='profit_abs', starting_balance=start_balance) + # max_relative_drawdown = Underwater (_, _, _, _, _, max_relative_drawdown) = calculate_max_drawdown( results, value_col='profit_abs', starting_balance=start_balance, relative=True) + strat_stats.update({ 'max_drawdown': max_drawdown_legacy, # Deprecated - do not use 'max_drawdown_account': max_drawdown, @@ -753,6 +758,12 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Drawdown End', strat_results['drawdown_end']), ]) + entry_adjustment_metrics = [ + ('Canceled Trade Entries', strat_results.get('canceled_trade_entries', 'N/A')), + ('Canceled Entry Orders', strat_results.get('canceled_entry_orders', 'N/A')), + ('Replaced Entry Orders', strat_results.get('replaced_entry_orders', 'N/A')), + ] if strat_results.get('canceled_entry_orders', 0) > 0 else [] + # Newly added fields should be ignored if they are missing in strat_results. hyperopt-show # command stores these results and newer version of freqtrade must be able to handle old # results with missing new fields. @@ -772,6 +783,8 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ('Total profit %', f"{strat_results['profit_total']:.2%}"), ('CAGR %', f"{strat_results['cagr']:.2%}" if 'cagr' in strat_results else 'N/A'), + ('Profit factor', f'{strat_results["profit_factor"]:.2f}' if 'profit_factor' + in strat_results else 'N/A'), ('Trades per day', strat_results['trades_per_day']), ('Avg. daily profit %', f"{(strat_results['profit_total'] / strat_results['backtest_days']):.2%}"), @@ -801,6 +814,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Entry/Exit Timeouts', f"{strat_results.get('timedout_entry_orders', 'N/A')} / " f"{strat_results.get('timedout_exit_orders', 'N/A')}"), + *entry_adjustment_metrics, ('', ''), # Empty line to improve readability ('Min balance', round_coin_value(strat_results['csum_min'], diff --git a/freqtrade/persistence/__init__.py b/freqtrade/persistence/__init__.py index d1fcac0ba..f4e7470a7 100644 --- a/freqtrade/persistence/__init__.py +++ b/freqtrade/persistence/__init__.py @@ -1,5 +1,5 @@ # flake8: noqa: F401 -from freqtrade.persistence.models import (LocalTrade, Order, Trade, clean_dry_run_db, cleanup_db, - init_db) +from freqtrade.persistence.models import cleanup_db, init_db from freqtrade.persistence.pairlock_middleware import PairLocks +from freqtrade.persistence.trade_model import LocalTrade, Order, Trade diff --git a/freqtrade/persistence/base.py b/freqtrade/persistence/base.py new file mode 100644 index 000000000..fb2d561e1 --- /dev/null +++ b/freqtrade/persistence/base.py @@ -0,0 +1,7 @@ + +from typing import Any + +from sqlalchemy.orm import declarative_base + + +_DECL_BASE: Any = declarative_base() diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 03f3c3fb9..2a8e34cdf 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -1,9 +1,10 @@ import logging from typing import List -from sqlalchemy import inspect, text +from sqlalchemy import inspect, select, text, tuple_, update from freqtrade.exceptions import OperationalException +from freqtrade.persistence.trade_model import Order, Trade logger = logging.getLogger(__name__) @@ -46,7 +47,7 @@ def get_last_sequence_ids(engine, trade_back_name, order_back_name): return order_id, trade_id -def set_sequence_ids(engine, order_id, trade_id): +def set_sequence_ids(engine, order_id, trade_id, pairlock_id=None): if engine.name == 'postgresql': with engine.begin() as connection: @@ -54,6 +55,9 @@ def set_sequence_ids(engine, order_id, trade_id): connection.execute(text(f"ALTER SEQUENCE orders_id_seq RESTART WITH {order_id}")) if trade_id: connection.execute(text(f"ALTER SEQUENCE trades_id_seq RESTART WITH {trade_id}")) + if pairlock_id: + connection.execute( + text(f"ALTER SEQUENCE pairlocks_id_seq RESTART WITH {pairlock_id}")) def drop_index_on_table(engine, inspector, table_bak_name): @@ -99,7 +103,10 @@ def migrate_trades_and_orders_table( liquidation_price = get_column_def(cols, 'liquidation_price', get_column_def(cols, 'isolated_liq', 'null')) # sqlite does not support literals for booleans - is_short = get_column_def(cols, 'is_short', '0') + if engine.name == 'postgresql': + is_short = get_column_def(cols, 'is_short', 'false') + else: + is_short = get_column_def(cols, 'is_short', '0') # Margin Properties interest_rate = get_column_def(cols, 'interest_rate', '0.0') @@ -195,16 +202,18 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List): ft_fee_base = get_column_def(cols_order, 'ft_fee_base', 'null') average = get_column_def(cols_order, 'average', 'null') + stop_price = get_column_def(cols_order, 'stop_price', 'null') # sqlite does not support literals for booleans with engine.begin() as connection: connection.execute(text(f""" insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status, symbol, order_type, side, price, amount, filled, average, remaining, cost, - order_date, order_filled_date, order_update_date, ft_fee_base) + stop_price, order_date, order_filled_date, order_update_date, ft_fee_base) select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status, symbol, order_type, side, price, amount, filled, {average} average, remaining, - cost, order_date, order_filled_date, order_update_date, {ft_fee_base} ft_fee_base + cost, {stop_price} stop_price, order_date, order_filled_date, + order_update_date, {ft_fee_base} ft_fee_base from {table_back_name} """)) @@ -241,6 +250,35 @@ def set_sqlite_to_wal(engine): connection.execute(text("PRAGMA journal_mode=wal")) +def fix_old_dry_orders(engine): + with engine.begin() as connection: + stmt = update(Order).where( + Order.ft_is_open.is_(True), + tuple_(Order.ft_trade_id, Order.order_id).not_in( + select( + Trade.id, Trade.stoploss_order_id + ).where(Trade.stoploss_order_id.is_not(None)) + ), + Order.ft_order_side == 'stoploss', + Order.order_id.like('dry%'), + + ).values(ft_is_open=False) + connection.execute(stmt) + + stmt = update(Order).where( + Order.ft_is_open.is_(True), + tuple_(Order.ft_trade_id, Order.order_id).not_in( + select( + Trade.id, Trade.open_order_id + ).where(Trade.open_order_id.is_not(None)) + ), + Order.ft_order_side != 'stoploss', + Order.order_id.like('dry%') + + ).values(ft_is_open=False) + connection.execute(stmt) + + def check_migrate(engine, decl_base, previous_tables) -> None: """ Checks if migration is necessary and migrates if necessary @@ -259,9 +297,8 @@ def check_migrate(engine, decl_base, previous_tables) -> None: # Check if migration necessary # Migrates both trades and orders table! - # if ('orders' not in previous_tables - # or not has_column(cols_orders, 'leverage')): - if not has_column(cols_trades, 'base_currency'): + if not has_column(cols_orders, 'stop_price'): + # if not has_column(cols_trades, 'base_currency'): logger.info(f"Running database migration for trades - " f"backup: {table_back_name}, {order_table_bak_name}") migrate_trades_and_orders_table( @@ -282,3 +319,4 @@ def check_migrate(engine, decl_base, previous_tables) -> None: "start with a fresh database.") set_sqlite_to_wal(engine) + fix_old_dry_orders(engine) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 299032bb4..86d2f9f9c 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -2,39 +2,31 @@ This module contains the class to persist trades into SQLite """ import logging -from datetime import datetime, timedelta, timezone -from decimal import Decimal -from typing import Any, Dict, List, Optional -from sqlalchemy import (Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String, - create_engine, desc, func, inspect, or_) +from sqlalchemy import create_engine, inspect from sqlalchemy.exc import NoSuchModuleError -from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session, sessionmaker +from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.pool import StaticPool -from sqlalchemy.sql.schema import UniqueConstraint -from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES, LongShort -from freqtrade.enums import ExitType, TradingMode -from freqtrade.exceptions import DependencyException, OperationalException -from freqtrade.leverage import interest +from freqtrade.exceptions import OperationalException +from freqtrade.persistence.base import _DECL_BASE from freqtrade.persistence.migrations import check_migrate +from freqtrade.persistence.pairlock import PairLock +from freqtrade.persistence.trade_model import Order, Trade logger = logging.getLogger(__name__) -_DECL_BASE: Any = declarative_base() _SQL_DOCS_URL = 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls' -def init_db(db_url: str, clean_open_orders: bool = False) -> None: +def init_db(db_url: str) -> None: """ Initializes this module with the given config, registers all known command handlers and starts polling for message updates :param db_url: Database to use - :param clean_open_orders: Remove open orders from the database. - Useful for dry-run or if all orders have been reset on the exchange. :return: None """ kwargs = {} @@ -70,10 +62,6 @@ def init_db(db_url: str, clean_open_orders: bool = False) -> None: _DECL_BASE.metadata.create_all(engine) check_migrate(engine, decl_base=_DECL_BASE, previous_tables=previous_tables) - # Clean dry_run DB if the db is not in-memory - if clean_open_orders and db_url != 'sqlite://': - clean_dry_run_db() - def cleanup_db() -> None: """ @@ -81,1399 +69,3 @@ def cleanup_db() -> None: :return: None """ Trade.commit() - - -def clean_dry_run_db() -> None: - """ - Remove open_order_id from a Dry_run DB - :return: None - """ - for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all(): - # Check we are updating only a dry_run order not a prod one - if 'dry_run' in trade.open_order_id: - trade.open_order_id = None - Trade.commit() - - -class Order(_DECL_BASE): - """ - Order database model - Keeps a record of all orders placed on the exchange - - One to many relationship with Trades: - - One trade can have many orders - - One Order can only be associated with one Trade - - Mirrors CCXT Order structure - """ - __tablename__ = 'orders' - # Uniqueness should be ensured over pair, order_id - # its likely that order_id is unique per Pair on some exchanges. - __table_args__ = (UniqueConstraint('ft_pair', 'order_id', name="_order_pair_order_id"),) - - id = Column(Integer, primary_key=True) - ft_trade_id = Column(Integer, ForeignKey('trades.id'), index=True) - - trade = relationship("Trade", back_populates="orders") - - # order_side can only be 'buy', 'sell' or 'stoploss' - ft_order_side: str = Column(String(25), nullable=False) - ft_pair: str = Column(String(25), nullable=False) - ft_is_open = Column(Boolean, nullable=False, default=True, index=True) - - order_id: str = Column(String(255), nullable=False, index=True) - status = Column(String(255), nullable=True) - symbol = Column(String(25), nullable=True) - order_type: str = Column(String(50), nullable=True) - side = Column(String(25), nullable=True) - price = Column(Float, nullable=True) - average = Column(Float, nullable=True) - amount = Column(Float, nullable=True) - filled = Column(Float, nullable=True) - remaining = Column(Float, nullable=True) - cost = Column(Float, nullable=True) - order_date = Column(DateTime, nullable=True, default=datetime.utcnow) - order_filled_date = Column(DateTime, nullable=True) - order_update_date = Column(DateTime, nullable=True) - - ft_fee_base = Column(Float, nullable=True) - - @property - def order_date_utc(self) -> datetime: - """ Order-date with UTC timezoneinfo""" - return self.order_date.replace(tzinfo=timezone.utc) - - @property - def safe_price(self) -> float: - return self.average or self.price - - @property - def safe_filled(self) -> float: - return self.filled or self.amount or 0.0 - - @property - def safe_fee_base(self) -> float: - return self.ft_fee_base or 0.0 - - @property - def safe_amount_after_fee(self) -> float: - return self.safe_filled - self.safe_fee_base - - def __repr__(self): - - return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, ' - f'side={self.side}, order_type={self.order_type}, status={self.status})') - - def update_from_ccxt_object(self, order): - """ - Update Order from ccxt response - Only updates if fields are available from ccxt - - """ - if self.order_id != str(order['id']): - raise DependencyException("Order-id's don't match") - - self.status = order.get('status', self.status) - self.symbol = order.get('symbol', self.symbol) - self.order_type = order.get('type', self.order_type) - self.side = order.get('side', self.side) - self.price = order.get('price', self.price) - self.amount = order.get('amount', self.amount) - self.filled = order.get('filled', self.filled) - self.average = order.get('average', self.average) - self.remaining = order.get('remaining', self.remaining) - self.cost = order.get('cost', self.cost) - - if 'timestamp' in order and order['timestamp'] is not None: - self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000, tz=timezone.utc) - - self.ft_is_open = True - if self.status in NON_OPEN_EXCHANGE_STATES: - self.ft_is_open = False - if (order.get('filled', 0.0) or 0.0) > 0: - self.order_filled_date = datetime.now(timezone.utc) - self.order_update_date = datetime.now(timezone.utc) - - def to_json(self, entry_side: str) -> Dict[str, Any]: - return { - 'pair': self.ft_pair, - 'order_id': self.order_id, - 'status': self.status, - 'amount': self.amount, - 'average': round(self.average, 8) if self.average else 0, - 'safe_price': self.safe_price, - 'cost': self.cost if self.cost else 0, - 'filled': self.filled, - 'ft_order_side': self.ft_order_side, - 'is_open': self.ft_is_open, - 'order_date': self.order_date.strftime(DATETIME_PRINT_FORMAT) - if self.order_date else None, - 'order_timestamp': int(self.order_date.replace( - tzinfo=timezone.utc).timestamp() * 1000) if self.order_date else None, - 'order_filled_date': self.order_filled_date.strftime(DATETIME_PRINT_FORMAT) - if self.order_filled_date else None, - 'order_filled_timestamp': int(self.order_filled_date.replace( - tzinfo=timezone.utc).timestamp() * 1000) if self.order_filled_date else None, - 'order_type': self.order_type, - 'price': self.price, - 'ft_is_entry': self.ft_order_side == entry_side, - 'remaining': self.remaining, - } - - def close_bt_order(self, close_date: datetime): - self.order_filled_date = close_date - self.filled = self.amount - self.status = 'closed' - self.ft_is_open = False - - @staticmethod - def update_orders(orders: List['Order'], order: Dict[str, Any]): - """ - Get all non-closed orders - useful when trying to batch-update orders - """ - if not isinstance(order, dict): - logger.warning(f"{order} is not a valid response object.") - return - - filtered_orders = [o for o in orders if o.order_id == order.get('id')] - if filtered_orders: - oobj = filtered_orders[0] - oobj.update_from_ccxt_object(order) - Order.query.session.commit() - else: - logger.warning(f"Did not find order for {order}.") - - @staticmethod - def parse_from_ccxt_object(order: Dict[str, Any], pair: str, side: str) -> 'Order': - """ - Parse an order from a ccxt object and return a new order Object. - """ - o = Order(order_id=str(order['id']), ft_order_side=side, ft_pair=pair) - - o.update_from_ccxt_object(order) - return o - - @staticmethod - def get_open_orders() -> List['Order']: - """ - Retrieve open orders from the database - :return: List of open orders - """ - return Order.query.filter(Order.ft_is_open.is_(True)).all() - - -class LocalTrade(): - """ - Trade database model. - Used in backtesting - must be aligned to Trade model! - - """ - use_db: bool = False - # Trades container for backtesting - trades: List['LocalTrade'] = [] - trades_open: List['LocalTrade'] = [] - total_profit: float = 0 - - id: int = 0 - - orders: List[Order] = [] - - exchange: str = '' - pair: str = '' - base_currency: str = '' - stake_currency: str = '' - is_open: bool = True - fee_open: float = 0.0 - fee_open_cost: Optional[float] = None - fee_open_currency: str = '' - fee_close: float = 0.0 - fee_close_cost: Optional[float] = None - fee_close_currency: str = '' - open_rate: float = 0.0 - open_rate_requested: Optional[float] = None - # open_trade_value - calculated via _calc_open_trade_value - open_trade_value: float = 0.0 - close_rate: Optional[float] = None - close_rate_requested: Optional[float] = None - close_profit: Optional[float] = None - close_profit_abs: Optional[float] = None - stake_amount: float = 0.0 - amount: float = 0.0 - amount_requested: Optional[float] = None - open_date: datetime - close_date: Optional[datetime] = None - open_order_id: Optional[str] = None - # absolute value of the stop loss - stop_loss: float = 0.0 - # percentage value of the stop loss - stop_loss_pct: float = 0.0 - # absolute value of the initial stop loss - initial_stop_loss: float = 0.0 - # percentage value of the initial stop loss - initial_stop_loss_pct: Optional[float] = None - # stoploss order id which is on exchange - stoploss_order_id: Optional[str] = None - # last update time of the stoploss order on exchange - stoploss_last_update: Optional[datetime] = None - # absolute value of the highest reached price - max_rate: float = 0.0 - # Lowest price reached - min_rate: float = 0.0 - exit_reason: str = '' - exit_order_status: str = '' - strategy: str = '' - enter_tag: Optional[str] = None - timeframe: Optional[int] = None - - trading_mode: TradingMode = TradingMode.SPOT - - # Leverage trading properties - liquidation_price: Optional[float] = None - is_short: bool = False - leverage: float = 1.0 - - # Margin trading properties - interest_rate: float = 0.0 - - # Futures properties - funding_fees: Optional[float] = None - - @property - def buy_tag(self) -> Optional[str]: - """ - Compatibility between buy_tag (old) and enter_tag (new) - Consider buy_tag deprecated - """ - return self.enter_tag - - @property - def has_no_leverage(self) -> bool: - """Returns true if this is a non-leverage, non-short trade""" - return ((self.leverage == 1.0 or self.leverage is None) and not self.is_short) - - @property - def borrowed(self) -> float: - """ - The amount of currency borrowed from the exchange for leverage trades - If a long trade, the amount is in base currency - If a short trade, the amount is in the other currency being traded - """ - if self.has_no_leverage: - return 0.0 - elif not self.is_short: - return (self.amount * self.open_rate) * ((self.leverage - 1) / self.leverage) - else: - return self.amount - - @property - def open_date_utc(self): - return self.open_date.replace(tzinfo=timezone.utc) - - @property - def close_date_utc(self): - return self.close_date.replace(tzinfo=timezone.utc) - - @property - def enter_side(self) -> str: - """ DEPRECATED, please use entry_side instead""" - # TODO: Please remove me after 2022.5 - return self.entry_side - - @property - def entry_side(self) -> str: - if self.is_short: - return "sell" - else: - return "buy" - - @property - def exit_side(self) -> str: - if self.is_short: - return "buy" - else: - return "sell" - - @property - def trade_direction(self) -> LongShort: - if self.is_short: - return "short" - else: - return "long" - - @property - def safe_base_currency(self) -> str: - """ - Compatibility layer for asset - which can be empty for old trades. - """ - try: - return self.base_currency or self.pair.split('/')[0] - except IndexError: - return '' - - @property - def safe_quote_currency(self) -> str: - """ - Compatibility layer for asset - which can be empty for old trades. - """ - try: - return self.stake_currency or self.pair.split('/')[1].split(':')[0] - except IndexError: - return '' - - def __init__(self, **kwargs): - for key in kwargs: - setattr(self, key, kwargs[key]) - self.recalc_open_trade_value() - if self.trading_mode == TradingMode.MARGIN and self.interest_rate is None: - raise OperationalException( - f"{self.trading_mode.value} trading requires param interest_rate on trades") - - def __repr__(self): - open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed' - - return ( - f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, ' - f'is_short={self.is_short or False}, leverage={self.leverage or 1.0}, ' - f'open_rate={self.open_rate:.8f}, open_since={open_since})' - ) - - def to_json(self) -> Dict[str, Any]: - filled_orders = self.select_filled_orders() - orders = [order.to_json(self.entry_side) for order in filled_orders] - - return { - 'trade_id': self.id, - 'pair': self.pair, - 'base_currency': self.safe_base_currency, - 'quote_currency': self.safe_quote_currency, - 'is_open': self.is_open, - 'exchange': self.exchange, - 'amount': round(self.amount, 8), - 'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None, - 'stake_amount': round(self.stake_amount, 8), - 'strategy': self.strategy, - 'buy_tag': self.enter_tag, - 'enter_tag': self.enter_tag, - 'timeframe': self.timeframe, - - 'fee_open': self.fee_open, - 'fee_open_cost': self.fee_open_cost, - 'fee_open_currency': self.fee_open_currency, - 'fee_close': self.fee_close, - 'fee_close_cost': self.fee_close_cost, - 'fee_close_currency': self.fee_close_currency, - - 'open_date': self.open_date.strftime(DATETIME_PRINT_FORMAT), - 'open_timestamp': int(self.open_date.replace(tzinfo=timezone.utc).timestamp() * 1000), - 'open_rate': self.open_rate, - 'open_rate_requested': self.open_rate_requested, - 'open_trade_value': round(self.open_trade_value, 8), - - 'close_date': (self.close_date.strftime(DATETIME_PRINT_FORMAT) - if self.close_date else None), - 'close_timestamp': int(self.close_date.replace( - tzinfo=timezone.utc).timestamp() * 1000) if self.close_date else None, - 'close_rate': self.close_rate, - 'close_rate_requested': self.close_rate_requested, - 'close_profit': self.close_profit, # Deprecated - 'close_profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None, - 'close_profit_abs': self.close_profit_abs, # Deprecated - - 'trade_duration_s': (int((self.close_date_utc - self.open_date_utc).total_seconds()) - if self.close_date else None), - 'trade_duration': (int((self.close_date_utc - self.open_date_utc).total_seconds() // 60) - if self.close_date else None), - - 'profit_ratio': self.close_profit, - 'profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None, - 'profit_abs': self.close_profit_abs, - - 'sell_reason': self.exit_reason, # Deprecated - 'exit_reason': self.exit_reason, - 'exit_order_status': self.exit_order_status, - 'stop_loss_abs': self.stop_loss, - 'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None, - 'stop_loss_pct': (self.stop_loss_pct * 100) if self.stop_loss_pct else None, - 'stoploss_order_id': self.stoploss_order_id, - 'stoploss_last_update': (self.stoploss_last_update.strftime(DATETIME_PRINT_FORMAT) - if self.stoploss_last_update else None), - 'stoploss_last_update_timestamp': int(self.stoploss_last_update.replace( - tzinfo=timezone.utc).timestamp() * 1000) if self.stoploss_last_update else None, - 'initial_stop_loss_abs': self.initial_stop_loss, - 'initial_stop_loss_ratio': (self.initial_stop_loss_pct - if self.initial_stop_loss_pct else None), - 'initial_stop_loss_pct': (self.initial_stop_loss_pct * 100 - if self.initial_stop_loss_pct else None), - 'min_rate': self.min_rate, - 'max_rate': self.max_rate, - - 'leverage': self.leverage, - 'interest_rate': self.interest_rate, - 'liquidation_price': self.liquidation_price, - 'is_short': self.is_short, - 'trading_mode': self.trading_mode, - 'funding_fees': self.funding_fees, - 'open_order_id': self.open_order_id, - 'orders': orders, - } - - @staticmethod - def reset_trades() -> None: - """ - Resets all trades. Only active for backtesting mode. - """ - LocalTrade.trades = [] - LocalTrade.trades_open = [] - LocalTrade.total_profit = 0 - - def adjust_min_max_rates(self, current_price: float, current_price_low: float) -> None: - """ - Adjust the max_rate and min_rate. - """ - self.max_rate = max(current_price, self.max_rate or self.open_rate) - self.min_rate = min(current_price_low, self.min_rate or self.open_rate) - - def set_isolated_liq(self, liquidation_price: Optional[float]): - """ - Method you should use to set self.liquidation price. - Assures stop_loss is not passed the liquidation price - """ - if not liquidation_price: - return - self.liquidation_price = liquidation_price - - def _set_stop_loss(self, stop_loss: float, percent: float): - """ - Method you should use to set self.stop_loss. - Assures stop_loss is not passed the liquidation price - """ - if self.liquidation_price is not None: - if self.is_short: - sl = min(stop_loss, self.liquidation_price) - else: - sl = max(stop_loss, self.liquidation_price) - else: - sl = stop_loss - - if not self.stop_loss: - self.initial_stop_loss = sl - self.stop_loss = sl - - self.stop_loss_pct = -1 * abs(percent) - self.stoploss_last_update = datetime.utcnow() - - def adjust_stop_loss(self, current_price: float, stoploss: float, - initial: bool = False) -> None: - """ - This adjusts the stop loss to it's most recently observed setting - :param current_price: Current rate the asset is traded - :param stoploss: Stoploss as factor (sample -0.05 -> -5% below current price). - :param initial: Called to initiate stop_loss. - Skips everything if self.stop_loss is already set. - """ - if initial and not (self.stop_loss is None or self.stop_loss == 0): - # Don't modify if called with initial and nothing to do - return - - leverage = self.leverage or 1.0 - if self.is_short: - new_loss = float(current_price * (1 + abs(stoploss / leverage))) - # If trading with leverage, don't set the stoploss below the liquidation price - if self.liquidation_price: - new_loss = min(self.liquidation_price, new_loss) - else: - new_loss = float(current_price * (1 - abs(stoploss / leverage))) - # If trading with leverage, don't set the stoploss below the liquidation price - if self.liquidation_price: - new_loss = max(self.liquidation_price, new_loss) - - # no stop loss assigned yet - if self.initial_stop_loss_pct is None: - logger.debug(f"{self.pair} - Assigning new stoploss...") - self._set_stop_loss(new_loss, stoploss) - self.initial_stop_loss = new_loss - self.initial_stop_loss_pct = -1 * abs(stoploss) - - # evaluate if the stop loss needs to be updated - else: - - higher_stop = new_loss > self.stop_loss - lower_stop = new_loss < self.stop_loss - - # stop losses only walk up, never down!, - # ? But adding more to a leveraged trade would create a lower liquidation price, - # ? decreasing the minimum stoploss - if (higher_stop and not self.is_short) or (lower_stop and self.is_short): - logger.debug(f"{self.pair} - Adjusting stoploss...") - self._set_stop_loss(new_loss, stoploss) - else: - logger.debug(f"{self.pair} - Keeping current stoploss...") - - logger.debug( - f"{self.pair} - Stoploss adjusted. current_price={current_price:.8f}, " - f"open_rate={self.open_rate:.8f}, max_rate={self.max_rate or self.open_rate:.8f}, " - f"initial_stop_loss={self.initial_stop_loss:.8f}, " - f"stop_loss={self.stop_loss:.8f}. " - f"Trailing stoploss saved us: " - f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.") - - def update_trade(self, order: Order) -> None: - """ - Updates this entity with amount and actual open/close rates. - :param order: order retrieved by exchange.fetch_order() - :return: None - """ - - # Ignore open and cancelled orders - if order.status == 'open' or order.safe_price is None: - return - - logger.info(f'Updating trade (id={self.id}) ...') - - if order.ft_order_side == self.entry_side: - # Update open rate and actual amount - self.open_rate = order.safe_price - self.amount = order.safe_amount_after_fee - if self.is_open: - payment = "SELL" if self.is_short else "BUY" - logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.') - self.open_order_id = None - self.recalc_trade_from_orders() - elif order.ft_order_side == self.exit_side: - if self.is_open: - payment = "BUY" if self.is_short else "SELL" - # * On margin shorts, you buy a little bit more than the amount (amount + interest) - logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.') - self.close(order.safe_price) - elif order.ft_order_side == 'stoploss': - self.stoploss_order_id = None - self.close_rate_requested = self.stop_loss - self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value - if self.is_open: - logger.info(f'{order.order_type.upper()} is hit for {self}.') - self.close(order.safe_price) - else: - raise ValueError(f'Unknown order type: {order.order_type}') - Trade.commit() - - def close(self, rate: float, *, show_msg: bool = True) -> None: - """ - Sets close_rate to the given rate, calculates total profit - and marks trade as closed - """ - self.close_rate = rate - self.close_date = self.close_date or datetime.utcnow() - self.close_profit = self.calc_profit_ratio() - self.close_profit_abs = self.calc_profit() - self.is_open = False - self.exit_order_status = 'closed' - self.open_order_id = None - if show_msg: - logger.info( - 'Marking %s as closed as the trade is fulfilled and found no open orders for it.', - self - ) - - def update_fee(self, fee_cost: float, fee_currency: Optional[str], fee_rate: Optional[float], - side: str) -> None: - """ - Update Fee parameters. Only acts once per side - """ - if self.entry_side == side and self.fee_open_currency is None: - self.fee_open_cost = fee_cost - self.fee_open_currency = fee_currency - if fee_rate is not None: - self.fee_open = fee_rate - # Assume close-fee will fall into the same fee category and take an educated guess - self.fee_close = fee_rate - elif self.exit_side == side and self.fee_close_currency is None: - self.fee_close_cost = fee_cost - self.fee_close_currency = fee_currency - if fee_rate is not None: - self.fee_close = fee_rate - - def fee_updated(self, side: str) -> bool: - """ - Verify if this side (buy / sell) has already been updated - """ - if self.entry_side == side: - return self.fee_open_currency is not None - elif self.exit_side == side: - return self.fee_close_currency is not None - else: - return False - - def update_order(self, order: Dict) -> None: - Order.update_orders(self.orders, order) - - def get_exit_order_count(self) -> int: - """ - Get amount of failed exiting orders - assumes full exits. - """ - return len([o for o in self.orders if o.ft_order_side == self.exit_side]) - - def _calc_open_trade_value(self) -> float: - """ - Calculate the open_rate including open_fee. - :return: Price in of the open trade incl. Fees - """ - open_trade = Decimal(self.amount) * Decimal(self.open_rate) - fees = open_trade * Decimal(self.fee_open) - if self.is_short: - return float(open_trade - fees) - else: - return float(open_trade + fees) - - def recalc_open_trade_value(self) -> None: - """ - Recalculate open_trade_value. - Must be called whenever open_rate, fee_open or is_short is changed. - """ - self.open_trade_value = self._calc_open_trade_value() - - def calculate_interest(self, interest_rate: Optional[float] = None) -> Decimal: - """ - :param interest_rate: interest_charge for borrowing this coin(optional). - If interest_rate is not set self.interest_rate will be used - """ - zero = Decimal(0.0) - # If nothing was borrowed - if self.trading_mode != TradingMode.MARGIN or self.has_no_leverage: - return zero - - open_date = self.open_date.replace(tzinfo=None) - now = (self.close_date or datetime.now(timezone.utc)).replace(tzinfo=None) - sec_per_hour = Decimal(3600) - total_seconds = Decimal((now - open_date).total_seconds()) - hours = total_seconds / sec_per_hour or zero - - rate = Decimal(interest_rate or self.interest_rate) - borrowed = Decimal(self.borrowed) - - return interest(exchange_name=self.exchange, borrowed=borrowed, rate=rate, hours=hours) - - def _calc_base_close(self, amount: Decimal, rate: Optional[float] = None, - fee: Optional[float] = None) -> Decimal: - - close_trade = Decimal(amount) * Decimal(rate or self.close_rate) # type: ignore - fees = close_trade * Decimal(fee or self.fee_close) - - if self.is_short: - return close_trade + fees - else: - return close_trade - fees - - def calc_close_trade_value(self, rate: Optional[float] = None, - fee: Optional[float] = None, - interest_rate: Optional[float] = None) -> float: - """ - Calculate the close_rate including fee - :param fee: fee to use on the close rate (optional). - If rate is not set self.fee will be used - :param rate: rate to compare with (optional). - If rate is not set self.close_rate will be used - :param interest_rate: interest_charge for borrowing this coin (optional). - If interest_rate is not set self.interest_rate will be used - :return: Price in BTC of the open trade - """ - if rate is None and not self.close_rate: - return 0.0 - - amount = Decimal(self.amount) - trading_mode = self.trading_mode or TradingMode.SPOT - - if trading_mode == TradingMode.SPOT: - return float(self._calc_base_close(amount, rate, fee)) - - elif (trading_mode == TradingMode.MARGIN): - - total_interest = self.calculate_interest(interest_rate) - - if self.is_short: - amount = amount + total_interest - return float(self._calc_base_close(amount, rate, fee)) - else: - # Currency already owned for longs, no need to purchase - return float(self._calc_base_close(amount, rate, fee) - total_interest) - - elif (trading_mode == TradingMode.FUTURES): - funding_fees = self.funding_fees or 0.0 - # Positive funding_fees -> Trade has gained from fees. - # Negative funding_fees -> Trade had to pay the fees. - if self.is_short: - return float(self._calc_base_close(amount, rate, fee)) - funding_fees - else: - return float(self._calc_base_close(amount, rate, fee)) + funding_fees - else: - raise OperationalException( - f"{self.trading_mode.value} trading is not yet available using freqtrade") - - def calc_profit(self, rate: Optional[float] = None, - fee: Optional[float] = None, - interest_rate: Optional[float] = None) -> float: - """ - Calculate the absolute profit in stake currency between Close and Open trade - :param fee: fee to use on the close rate (optional). - If fee is not set self.fee will be used - :param rate: close rate to compare with (optional). - If rate is not set self.close_rate will be used - :param interest_rate: interest_charge for borrowing this coin (optional). - If interest_rate is not set self.interest_rate will be used - :return: profit in stake currency as float - """ - close_trade_value = self.calc_close_trade_value( - rate=(rate or self.close_rate), - fee=(fee or self.fee_close), - interest_rate=(interest_rate or self.interest_rate) - ) - - if self.is_short: - profit = self.open_trade_value - close_trade_value - else: - profit = close_trade_value - self.open_trade_value - return float(f"{profit:.8f}") - - def calc_profit_ratio(self, rate: Optional[float] = None, - fee: Optional[float] = None, - interest_rate: Optional[float] = None) -> float: - """ - Calculates the profit as ratio (including fee). - :param rate: rate to compare with (optional). - If rate is not set self.close_rate will be used - :param fee: fee to use on the close rate (optional). - :param interest_rate: interest_charge for borrowing this coin (optional). - If interest_rate is not set self.interest_rate will be used - :return: profit ratio as float - """ - close_trade_value = self.calc_close_trade_value( - rate=(rate or self.close_rate), - fee=(fee or self.fee_close), - interest_rate=(interest_rate or self.interest_rate) - ) - - short_close_zero = (self.is_short and close_trade_value == 0.0) - long_close_zero = (not self.is_short and self.open_trade_value == 0.0) - leverage = self.leverage or 1.0 - - if (short_close_zero or long_close_zero): - return 0.0 - else: - if self.is_short: - profit_ratio = (1 - (close_trade_value / self.open_trade_value)) * leverage - else: - profit_ratio = ((close_trade_value / self.open_trade_value) - 1) * leverage - - return float(f"{profit_ratio:.8f}") - - def recalc_trade_from_orders(self): - # We need at least 2 entry orders for averaging amounts and rates. - # TODO: this condition could probably be removed - if len(self.select_filled_orders(self.entry_side)) < 2: - self.stake_amount = self.amount * self.open_rate / self.leverage - - # Just in case, still recalc open trade value - self.recalc_open_trade_value() - return - - total_amount = 0.0 - total_stake = 0.0 - for o in self.orders: - if (o.ft_is_open or - (o.ft_order_side != self.entry_side) or - (o.status not in NON_OPEN_EXCHANGE_STATES)): - continue - - tmp_amount = o.safe_amount_after_fee - tmp_price = o.average or o.price - if o.filled is not None: - tmp_amount = o.filled - if tmp_amount > 0.0 and tmp_price is not None: - total_amount += tmp_amount - total_stake += tmp_price * tmp_amount - - if total_amount > 0: - # Leverage not updated, as we don't allow changing leverage through DCA at the moment. - self.open_rate = total_stake / total_amount - self.stake_amount = total_stake / (self.leverage or 1.0) - self.amount = total_amount - self.fee_open_cost = self.fee_open * self.stake_amount - self.recalc_open_trade_value() - if self.stop_loss_pct is not None and self.open_rate is not None: - self.adjust_stop_loss(self.open_rate, self.stop_loss_pct) - - def select_order_by_order_id(self, order_id: str) -> Optional[Order]: - """ - Finds order object by Order id. - :param order_id: Exchange order id - """ - for o in self.orders: - if o.order_id == order_id: - return o - return None - - def select_order( - self, order_side: str = None, is_open: Optional[bool] = None) -> Optional[Order]: - """ - Finds latest order for this orderside and status - :param order_side: ft_order_side of the order (either 'buy', 'sell' or 'stoploss') - :param is_open: Only search for open orders? - :return: latest Order object if it exists, else None - """ - orders = self.orders - if order_side: - orders = [o for o in self.orders if o.ft_order_side == order_side] - if is_open is not None: - orders = [o for o in orders if o.ft_is_open == is_open] - if len(orders) > 0: - return orders[-1] - else: - return None - - def select_filled_orders(self, order_side: Optional[str] = None) -> List['Order']: - """ - Finds filled orders for this orderside. - :param order_side: Side of the order (either 'buy', 'sell', or None) - :return: array of Order objects - """ - return [o for o in self.orders if ((o.ft_order_side == order_side) or (order_side is None)) - and o.ft_is_open is False and - (o.filled or 0) > 0 and - o.status in NON_OPEN_EXCHANGE_STATES] - - @property - def nr_of_successful_entries(self) -> int: - """ - Helper function to count the number of entry orders that have been filled. - :return: int count of entry orders that have been filled for this trade. - """ - - return len(self.select_filled_orders(self.entry_side)) - - @property - def nr_of_successful_exits(self) -> int: - """ - Helper function to count the number of exit orders that have been filled. - :return: int count of exit orders that have been filled for this trade. - """ - return len(self.select_filled_orders(self.exit_side)) - - @property - def nr_of_successful_buys(self) -> int: - """ - Helper function to count the number of buy orders that have been filled. - WARNING: Please use nr_of_successful_entries for short support. - :return: int count of buy orders that have been filled for this trade. - """ - - return len(self.select_filled_orders('buy')) - - @property - def nr_of_successful_sells(self) -> int: - """ - Helper function to count the number of sell orders that have been filled. - WARNING: Please use nr_of_successful_exits for short support. - :return: int count of sell orders that have been filled for this trade. - """ - return len(self.select_filled_orders('sell')) - - @property - def sell_reason(self) -> str: - """ DEPRECATED! Please use exit_reason instead.""" - return self.exit_reason - - @staticmethod - def get_trades_proxy(*, pair: str = None, is_open: bool = None, - open_date: datetime = None, close_date: datetime = None, - ) -> List['LocalTrade']: - """ - Helper function to query Trades. - Returns a List of trades, filtered on the parameters given. - In live mode, converts the filter to a database query and returns all rows - In Backtest mode, uses filters on Trade.trades to get the result. - - :return: unsorted List[Trade] - """ - - # Offline mode - without database - if is_open is not None: - if is_open: - sel_trades = LocalTrade.trades_open - else: - sel_trades = LocalTrade.trades - - else: - # Not used during backtesting, but might be used by a strategy - sel_trades = list(LocalTrade.trades + LocalTrade.trades_open) - - if pair: - sel_trades = [trade for trade in sel_trades if trade.pair == pair] - if open_date: - sel_trades = [trade for trade in sel_trades if trade.open_date > open_date] - if close_date: - sel_trades = [trade for trade in sel_trades if trade.close_date - and trade.close_date > close_date] - - return sel_trades - - @staticmethod - def close_bt_trade(trade): - LocalTrade.trades_open.remove(trade) - LocalTrade.trades.append(trade) - LocalTrade.total_profit += trade.close_profit_abs - - @staticmethod - def add_bt_trade(trade): - if trade.is_open: - LocalTrade.trades_open.append(trade) - else: - LocalTrade.trades.append(trade) - - @staticmethod - def get_open_trades() -> List[Any]: - """ - Query trades from persistence layer - """ - return Trade.get_trades_proxy(is_open=True) - - @staticmethod - def stoploss_reinitialization(desired_stoploss): - """ - Adjust initial Stoploss to desired stoploss for all open trades. - """ - for trade in Trade.get_open_trades(): - logger.info("Found open trade: %s", trade) - - # skip case if trailing-stop changed the stoploss already. - if (trade.stop_loss == trade.initial_stop_loss - and trade.initial_stop_loss_pct != desired_stoploss): - # Stoploss value got changed - - logger.info(f"Stoploss for {trade} needs adjustment...") - # Force reset of stoploss - trade.stop_loss = None - trade.initial_stop_loss_pct = None - trade.adjust_stop_loss(trade.open_rate, desired_stoploss) - logger.info(f"New stoploss: {trade.stop_loss}.") - - -class Trade(_DECL_BASE, LocalTrade): - """ - Trade database model. - Also handles updating and querying trades - - Note: Fields must be aligned with LocalTrade class - """ - __tablename__ = 'trades' - - use_db: bool = True - - id = Column(Integer, primary_key=True) - - orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan", lazy="joined") - - exchange = Column(String(25), nullable=False) - pair = Column(String(25), nullable=False, index=True) - base_currency = Column(String(25), nullable=True) - stake_currency = Column(String(25), nullable=True) - is_open = Column(Boolean, nullable=False, default=True, index=True) - fee_open = Column(Float, nullable=False, default=0.0) - fee_open_cost = Column(Float, nullable=True) - fee_open_currency = Column(String(25), nullable=True) - fee_close = Column(Float, nullable=False, default=0.0) - fee_close_cost = Column(Float, nullable=True) - fee_close_currency = Column(String(25), nullable=True) - open_rate: float = Column(Float) - open_rate_requested = Column(Float) - # open_trade_value - calculated via _calc_open_trade_value - open_trade_value = Column(Float) - close_rate: Optional[float] = Column(Float) - close_rate_requested = Column(Float) - close_profit = Column(Float) - close_profit_abs = Column(Float) - stake_amount = Column(Float, nullable=False) - amount = Column(Float) - amount_requested = Column(Float) - open_date = Column(DateTime, nullable=False, default=datetime.utcnow) - close_date = Column(DateTime) - open_order_id = Column(String(255)) - # absolute value of the stop loss - stop_loss = Column(Float, nullable=True, default=0.0) - # percentage value of the stop loss - stop_loss_pct = Column(Float, nullable=True) - # absolute value of the initial stop loss - initial_stop_loss = Column(Float, nullable=True, default=0.0) - # percentage value of the initial stop loss - initial_stop_loss_pct = Column(Float, nullable=True) - # stoploss order id which is on exchange - stoploss_order_id = Column(String(255), nullable=True, index=True) - # last update time of the stoploss order on exchange - stoploss_last_update = Column(DateTime, nullable=True) - # absolute value of the highest reached price - max_rate = Column(Float, nullable=True, default=0.0) - # Lowest price reached - min_rate = Column(Float, nullable=True) - exit_reason = Column(String(100), nullable=True) - exit_order_status = Column(String(100), nullable=True) - strategy = Column(String(100), nullable=True) - enter_tag = Column(String(100), nullable=True) - timeframe = Column(Integer, nullable=True) - - trading_mode = Column(Enum(TradingMode), nullable=True) - - # Leverage trading properties - leverage = Column(Float, nullable=True, default=1.0) - is_short = Column(Boolean, nullable=False, default=False) - liquidation_price = Column(Float, nullable=True) - - # Margin Trading Properties - interest_rate = Column(Float, nullable=False, default=0.0) - - # Futures properties - funding_fees = Column(Float, nullable=True, default=None) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.recalc_open_trade_value() - - def delete(self) -> None: - - for order in self.orders: - Order.query.session.delete(order) - - Trade.query.session.delete(self) - Trade.commit() - - @staticmethod - def commit(): - Trade.query.session.commit() - - @staticmethod - def get_trades_proxy(*, pair: str = None, is_open: bool = None, - open_date: datetime = None, close_date: datetime = None, - ) -> List['LocalTrade']: - """ - Helper function to query Trades.j - Returns a List of trades, filtered on the parameters given. - In live mode, converts the filter to a database query and returns all rows - In Backtest mode, uses filters on Trade.trades to get the result. - - :return: unsorted List[Trade] - """ - if Trade.use_db: - trade_filter = [] - if pair: - trade_filter.append(Trade.pair == pair) - if open_date: - trade_filter.append(Trade.open_date > open_date) - if close_date: - trade_filter.append(Trade.close_date > close_date) - if is_open is not None: - trade_filter.append(Trade.is_open.is_(is_open)) - return Trade.get_trades(trade_filter).all() - else: - return LocalTrade.get_trades_proxy( - pair=pair, is_open=is_open, - open_date=open_date, - close_date=close_date - ) - - @staticmethod - def get_trades(trade_filter=None) -> Query: - """ - Helper function to query Trades using filters. - NOTE: Not supported in Backtesting. - :param trade_filter: Optional filter to apply to trades - Can be either a Filter object, or a List of filters - e.g. `(trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True),])` - e.g. `(trade_filter=Trade.id == trade_id)` - :return: unsorted query object - """ - if not Trade.use_db: - raise NotImplementedError('`Trade.get_trades()` not supported in backtesting mode.') - if trade_filter is not None: - if not isinstance(trade_filter, list): - trade_filter = [trade_filter] - return Trade.query.filter(*trade_filter) - else: - return Trade.query - - @staticmethod - def get_open_order_trades() -> List['Trade']: - """ - Returns all open trades - NOTE: Not supported in Backtesting. - """ - return Trade.get_trades(Trade.open_order_id.isnot(None)).all() - - @staticmethod - def get_open_trades_without_assigned_fees(): - """ - Returns all open trades which don't have open fees set correctly - NOTE: Not supported in Backtesting. - """ - return Trade.get_trades([Trade.fee_open_currency.is_(None), - Trade.orders.any(), - Trade.is_open.is_(True), - ]).all() - - @staticmethod - def get_closed_trades_without_assigned_fees(): - """ - Returns all closed trades which don't have fees set correctly - NOTE: Not supported in Backtesting. - """ - return Trade.get_trades([Trade.fee_close_currency.is_(None), - Trade.orders.any(), - Trade.is_open.is_(False), - ]).all() - - @staticmethod - def get_total_closed_profit() -> float: - """ - Retrieves total realized profit - """ - if Trade.use_db: - total_profit = Trade.query.with_entities( - func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False)).scalar() - else: - total_profit = sum( - t.close_profit_abs for t in LocalTrade.get_trades_proxy(is_open=False)) - return total_profit or 0 - - @staticmethod - def total_open_trades_stakes() -> float: - """ - Calculates total invested amount in open trades - in stake currency - """ - if Trade.use_db: - total_open_stake_amount = Trade.query.with_entities( - func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True)).scalar() - else: - total_open_stake_amount = sum( - t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True)) - return total_open_stake_amount or 0 - - @staticmethod - def get_overall_performance(minutes=None) -> List[Dict[str, Any]]: - """ - Returns List of dicts containing all Trades, including profit and trade count - NOTE: Not supported in Backtesting. - """ - filters = [Trade.is_open.is_(False)] - if minutes: - start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) - filters.append(Trade.close_date >= start_date) - pair_rates = Trade.query.with_entities( - Trade.pair, - func.sum(Trade.close_profit).label('profit_sum'), - func.sum(Trade.close_profit_abs).label('profit_sum_abs'), - func.count(Trade.pair).label('count') - ).filter(*filters)\ - .group_by(Trade.pair) \ - .order_by(desc('profit_sum_abs')) \ - .all() - return [ - { - 'pair': pair, - 'profit_ratio': profit, - 'profit': round(profit * 100, 2), # Compatibility mode - 'profit_pct': round(profit * 100, 2), - 'profit_abs': profit_abs, - 'count': count - } - for pair, profit, profit_abs, count in pair_rates - ] - - @staticmethod - def get_enter_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: - """ - Returns List of dicts containing all Trades, based on buy tag performance - Can either be average for all pairs or a specific pair provided - NOTE: Not supported in Backtesting. - """ - - filters = [Trade.is_open.is_(False)] - if(pair is not None): - filters.append(Trade.pair == pair) - - enter_tag_perf = Trade.query.with_entities( - Trade.enter_tag, - func.sum(Trade.close_profit).label('profit_sum'), - func.sum(Trade.close_profit_abs).label('profit_sum_abs'), - func.count(Trade.pair).label('count') - ).filter(*filters)\ - .group_by(Trade.enter_tag) \ - .order_by(desc('profit_sum_abs')) \ - .all() - - return [ - { - 'enter_tag': enter_tag if enter_tag is not None else "Other", - 'profit_ratio': profit, - 'profit_pct': round(profit * 100, 2), - 'profit_abs': profit_abs, - 'count': count - } - for enter_tag, profit, profit_abs, count in enter_tag_perf - ] - - @staticmethod - def get_exit_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]: - """ - Returns List of dicts containing all Trades, based on exit reason performance - Can either be average for all pairs or a specific pair provided - NOTE: Not supported in Backtesting. - """ - - filters = [Trade.is_open.is_(False)] - if(pair is not None): - filters.append(Trade.pair == pair) - - sell_tag_perf = Trade.query.with_entities( - Trade.exit_reason, - func.sum(Trade.close_profit).label('profit_sum'), - func.sum(Trade.close_profit_abs).label('profit_sum_abs'), - func.count(Trade.pair).label('count') - ).filter(*filters)\ - .group_by(Trade.exit_reason) \ - .order_by(desc('profit_sum_abs')) \ - .all() - - return [ - { - 'exit_reason': exit_reason if exit_reason is not None else "Other", - 'profit_ratio': profit, - 'profit_pct': round(profit * 100, 2), - 'profit_abs': profit_abs, - 'count': count - } - for exit_reason, profit, profit_abs, count in sell_tag_perf - ] - - @staticmethod - def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: - """ - Returns List of dicts containing all Trades, based on entry_tag + exit_reason performance - Can either be average for all pairs or a specific pair provided - NOTE: Not supported in Backtesting. - """ - - filters = [Trade.is_open.is_(False)] - if(pair is not None): - filters.append(Trade.pair == pair) - - mix_tag_perf = Trade.query.with_entities( - Trade.id, - Trade.enter_tag, - Trade.exit_reason, - func.sum(Trade.close_profit).label('profit_sum'), - func.sum(Trade.close_profit_abs).label('profit_sum_abs'), - func.count(Trade.pair).label('count') - ).filter(*filters)\ - .group_by(Trade.id) \ - .order_by(desc('profit_sum_abs')) \ - .all() - - return_list: List[Dict] = [] - for id, enter_tag, exit_reason, profit, profit_abs, count in mix_tag_perf: - enter_tag = enter_tag if enter_tag is not None else "Other" - exit_reason = exit_reason if exit_reason is not None else "Other" - - if(exit_reason is not None and enter_tag is not None): - mix_tag = enter_tag + " " + exit_reason - i = 0 - if not any(item["mix_tag"] == mix_tag for item in return_list): - return_list.append({'mix_tag': mix_tag, - 'profit': profit, - 'profit_pct': round(profit * 100, 2), - 'profit_abs': profit_abs, - 'count': count}) - else: - while i < len(return_list): - if return_list[i]["mix_tag"] == mix_tag: - return_list[i] = { - 'mix_tag': mix_tag, - 'profit': profit + return_list[i]["profit"], - 'profit_pct': round(profit + return_list[i]["profit"] * 100, 2), - 'profit_abs': profit_abs + return_list[i]["profit_abs"], - 'count': 1 + return_list[i]["count"]} - i += 1 - - return return_list - - @staticmethod - def get_best_pair(start_date: datetime = datetime.fromtimestamp(0)): - """ - Get best pair with closed trade. - NOTE: Not supported in Backtesting. - :returns: Tuple containing (pair, profit_sum) - """ - best_pair = Trade.query.with_entities( - Trade.pair, func.sum(Trade.close_profit).label('profit_sum') - ).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date)) \ - .group_by(Trade.pair) \ - .order_by(desc('profit_sum')).first() - return best_pair - - -class PairLock(_DECL_BASE): - """ - Pair Locks database model. - """ - __tablename__ = 'pairlocks' - - id = Column(Integer, primary_key=True) - - pair = Column(String(25), nullable=False, index=True) - # lock direction - long, short or * (for both) - side = Column(String(25), nullable=False, default="*") - reason = Column(String(255), nullable=True) - # Time the pair was locked (start time) - lock_time = Column(DateTime, nullable=False) - # Time until the pair is locked (end time) - lock_end_time = Column(DateTime, nullable=False, index=True) - - active = Column(Boolean, nullable=False, default=True, index=True) - - def __repr__(self): - lock_time = self.lock_time.strftime(DATETIME_PRINT_FORMAT) - lock_end_time = self.lock_end_time.strftime(DATETIME_PRINT_FORMAT) - return ( - f'PairLock(id={self.id}, pair={self.pair}, side={self.side}, lock_time={lock_time}, ' - f'lock_end_time={lock_end_time}, reason={self.reason}, active={self.active})') - - @staticmethod - def query_pair_locks(pair: Optional[str], now: datetime, side: str = '*') -> Query: - """ - Get all currently active locks for this pair - :param pair: Pair to check for. Returns all current locks if pair is empty - :param now: Datetime object (generated via datetime.now(timezone.utc)). - """ - filters = [PairLock.lock_end_time > now, - # Only active locks - PairLock.active.is_(True), ] - if pair: - filters.append(PairLock.pair == pair) - if side != '*': - filters.append(or_(PairLock.side == side, PairLock.side == '*')) - else: - filters.append(PairLock.side == '*') - - return PairLock.query.filter( - *filters - ) - - def to_json(self) -> Dict[str, Any]: - return { - 'id': self.id, - 'pair': self.pair, - 'lock_time': self.lock_time.strftime(DATETIME_PRINT_FORMAT), - 'lock_timestamp': int(self.lock_time.replace(tzinfo=timezone.utc).timestamp() * 1000), - 'lock_end_time': self.lock_end_time.strftime(DATETIME_PRINT_FORMAT), - 'lock_end_timestamp': int(self.lock_end_time.replace(tzinfo=timezone.utc - ).timestamp() * 1000), - 'reason': self.reason, - 'side': self.side, - 'active': self.active, - } diff --git a/freqtrade/persistence/pairlock.py b/freqtrade/persistence/pairlock.py new file mode 100644 index 000000000..926c641b0 --- /dev/null +++ b/freqtrade/persistence/pairlock.py @@ -0,0 +1,70 @@ +from datetime import datetime, timezone +from typing import Any, Dict, Optional + +from sqlalchemy import Boolean, Column, DateTime, Integer, String, or_ +from sqlalchemy.orm import Query + +from freqtrade.constants import DATETIME_PRINT_FORMAT +from freqtrade.persistence.base import _DECL_BASE + + +class PairLock(_DECL_BASE): + """ + Pair Locks database model. + """ + __tablename__ = 'pairlocks' + + id = Column(Integer, primary_key=True) + + pair = Column(String(25), nullable=False, index=True) + # lock direction - long, short or * (for both) + side = Column(String(25), nullable=False, default="*") + reason = Column(String(255), nullable=True) + # Time the pair was locked (start time) + lock_time = Column(DateTime, nullable=False) + # Time until the pair is locked (end time) + lock_end_time = Column(DateTime, nullable=False, index=True) + + active = Column(Boolean, nullable=False, default=True, index=True) + + def __repr__(self): + lock_time = self.lock_time.strftime(DATETIME_PRINT_FORMAT) + lock_end_time = self.lock_end_time.strftime(DATETIME_PRINT_FORMAT) + return ( + f'PairLock(id={self.id}, pair={self.pair}, side={self.side}, lock_time={lock_time}, ' + f'lock_end_time={lock_end_time}, reason={self.reason}, active={self.active})') + + @staticmethod + def query_pair_locks(pair: Optional[str], now: datetime, side: str = '*') -> Query: + """ + Get all currently active locks for this pair + :param pair: Pair to check for. Returns all current locks if pair is empty + :param now: Datetime object (generated via datetime.now(timezone.utc)). + """ + filters = [PairLock.lock_end_time > now, + # Only active locks + PairLock.active.is_(True), ] + if pair: + filters.append(PairLock.pair == pair) + if side != '*': + filters.append(or_(PairLock.side == side, PairLock.side == '*')) + else: + filters.append(PairLock.side == '*') + + return PairLock.query.filter( + *filters + ) + + def to_json(self) -> Dict[str, Any]: + return { + 'id': self.id, + 'pair': self.pair, + 'lock_time': self.lock_time.strftime(DATETIME_PRINT_FORMAT), + 'lock_timestamp': int(self.lock_time.replace(tzinfo=timezone.utc).timestamp() * 1000), + 'lock_end_time': self.lock_end_time.strftime(DATETIME_PRINT_FORMAT), + 'lock_end_timestamp': int(self.lock_end_time.replace(tzinfo=timezone.utc + ).timestamp() * 1000), + 'reason': self.reason, + 'side': self.side, + 'active': self.active, + } diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py new file mode 100644 index 000000000..5f302de71 --- /dev/null +++ b/freqtrade/persistence/trade_model.py @@ -0,0 +1,1377 @@ +""" +This module contains the class to persist trades into SQLite +""" +import logging +from datetime import datetime, timedelta, timezone +from decimal import Decimal +from typing import Any, Dict, List, Optional + +from sqlalchemy import (Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String, + UniqueConstraint, desc, func) +from sqlalchemy.orm import Query, lazyload, relationship + +from freqtrade.constants import DATETIME_PRINT_FORMAT, NON_OPEN_EXCHANGE_STATES, BuySell, LongShort +from freqtrade.enums import ExitType, TradingMode +from freqtrade.exceptions import DependencyException, OperationalException +from freqtrade.leverage import interest +from freqtrade.persistence.base import _DECL_BASE + + +logger = logging.getLogger(__name__) + + +class Order(_DECL_BASE): + """ + Order database model + Keeps a record of all orders placed on the exchange + + One to many relationship with Trades: + - One trade can have many orders + - One Order can only be associated with one Trade + + Mirrors CCXT Order structure + """ + __tablename__ = 'orders' + # Uniqueness should be ensured over pair, order_id + # its likely that order_id is unique per Pair on some exchanges. + __table_args__ = (UniqueConstraint('ft_pair', 'order_id', name="_order_pair_order_id"),) + + id = Column(Integer, primary_key=True) + ft_trade_id = Column(Integer, ForeignKey('trades.id'), index=True) + + trade = relationship("Trade", back_populates="orders") + + # order_side can only be 'buy', 'sell' or 'stoploss' + ft_order_side: str = Column(String(25), nullable=False) + ft_pair: str = Column(String(25), nullable=False) + ft_is_open = Column(Boolean, nullable=False, default=True, index=True) + + order_id: str = Column(String(255), nullable=False, index=True) + status = Column(String(255), nullable=True) + symbol = Column(String(25), nullable=True) + order_type: str = Column(String(50), nullable=True) + side = Column(String(25), nullable=True) + price = Column(Float, nullable=True) + average = Column(Float, nullable=True) + amount = Column(Float, nullable=True) + filled = Column(Float, nullable=True) + remaining = Column(Float, nullable=True) + cost = Column(Float, nullable=True) + stop_price = Column(Float, nullable=True) + order_date = Column(DateTime, nullable=True, default=datetime.utcnow) + order_filled_date = Column(DateTime, nullable=True) + order_update_date = Column(DateTime, nullable=True) + + ft_fee_base = Column(Float, nullable=True) + + @property + def order_date_utc(self) -> datetime: + """ Order-date with UTC timezoneinfo""" + return self.order_date.replace(tzinfo=timezone.utc) + + @property + def safe_price(self) -> float: + return self.average or self.price + + @property + def safe_filled(self) -> float: + return self.filled if self.filled is not None else self.amount or 0.0 + + @property + def safe_fee_base(self) -> float: + return self.ft_fee_base or 0.0 + + @property + def safe_amount_after_fee(self) -> float: + return self.safe_filled - self.safe_fee_base + + def __repr__(self): + + return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, ' + f'side={self.side}, order_type={self.order_type}, status={self.status})') + + def update_from_ccxt_object(self, order): + """ + Update Order from ccxt response + Only updates if fields are available from ccxt - + """ + if self.order_id != str(order['id']): + raise DependencyException("Order-id's don't match") + + self.status = order.get('status', self.status) + self.symbol = order.get('symbol', self.symbol) + self.order_type = order.get('type', self.order_type) + self.side = order.get('side', self.side) + self.price = order.get('price', self.price) + self.amount = order.get('amount', self.amount) + self.filled = order.get('filled', self.filled) + self.average = order.get('average', self.average) + self.remaining = order.get('remaining', self.remaining) + self.cost = order.get('cost', self.cost) + self.stop_price = order.get('stopPrice', self.stop_price) + + if 'timestamp' in order and order['timestamp'] is not None: + self.order_date = datetime.fromtimestamp(order['timestamp'] / 1000, tz=timezone.utc) + + self.ft_is_open = True + if self.status in NON_OPEN_EXCHANGE_STATES: + self.ft_is_open = False + if (order.get('filled', 0.0) or 0.0) > 0: + self.order_filled_date = datetime.now(timezone.utc) + self.order_update_date = datetime.now(timezone.utc) + + def to_ccxt_object(self) -> Dict[str, Any]: + return { + 'id': self.order_id, + 'symbol': self.ft_pair, + 'price': self.price, + 'average': self.average, + 'amount': self.amount, + 'cost': self.cost, + 'type': self.order_type, + 'side': self.ft_order_side, + 'filled': self.filled, + 'remaining': self.remaining, + 'stopPrice': self.stop_price, + 'datetime': self.order_date_utc.strftime('%Y-%m-%dT%H:%M:%S.%f'), + 'timestamp': int(self.order_date_utc.timestamp() * 1000), + 'status': self.status, + 'fee': None, + 'info': {}, + } + + def to_json(self, entry_side: str, minified: bool = False) -> Dict[str, Any]: + resp = { + 'amount': self.amount, + 'safe_price': self.safe_price, + 'ft_order_side': self.ft_order_side, + 'order_filled_timestamp': int(self.order_filled_date.replace( + tzinfo=timezone.utc).timestamp() * 1000) if self.order_filled_date else None, + 'ft_is_entry': self.ft_order_side == entry_side, + } + if not minified: + resp.update({ + 'pair': self.ft_pair, + 'order_id': self.order_id, + 'status': self.status, + 'average': round(self.average, 8) if self.average else 0, + 'cost': self.cost if self.cost else 0, + 'filled': self.filled, + 'is_open': self.ft_is_open, + 'order_date': self.order_date.strftime(DATETIME_PRINT_FORMAT) + if self.order_date else None, + 'order_timestamp': int(self.order_date.replace( + tzinfo=timezone.utc).timestamp() * 1000) if self.order_date else None, + 'order_filled_date': self.order_filled_date.strftime(DATETIME_PRINT_FORMAT) + if self.order_filled_date else None, + 'order_type': self.order_type, + 'price': self.price, + 'remaining': self.remaining, + }) + return resp + + def close_bt_order(self, close_date: datetime, trade: 'LocalTrade'): + self.order_filled_date = close_date + self.filled = self.amount + self.remaining = 0 + self.status = 'closed' + self.ft_is_open = False + if (self.ft_order_side == trade.entry_side + and len(trade.select_filled_orders(trade.entry_side)) == 1): + trade.open_rate = self.price + trade.recalc_open_trade_value() + trade.adjust_stop_loss(trade.open_rate, trade.stop_loss_pct, refresh=True) + + @staticmethod + def update_orders(orders: List['Order'], order: Dict[str, Any]): + """ + Get all non-closed orders - useful when trying to batch-update orders + """ + if not isinstance(order, dict): + logger.warning(f"{order} is not a valid response object.") + return + + filtered_orders = [o for o in orders if o.order_id == order.get('id')] + if filtered_orders: + oobj = filtered_orders[0] + oobj.update_from_ccxt_object(order) + Order.query.session.commit() + else: + logger.warning(f"Did not find order for {order}.") + + @staticmethod + def parse_from_ccxt_object(order: Dict[str, Any], pair: str, side: str) -> 'Order': + """ + Parse an order from a ccxt object and return a new order Object. + """ + o = Order(order_id=str(order['id']), ft_order_side=side, ft_pair=pair) + + o.update_from_ccxt_object(order) + return o + + @staticmethod + def get_open_orders() -> List['Order']: + """ + Retrieve open orders from the database + :return: List of open orders + """ + return Order.query.filter(Order.ft_is_open.is_(True)).all() + + @staticmethod + def order_by_id(order_id: str) -> Optional['Order']: + """ + Retrieve order based on order_id + :return: Order or None + """ + return Order.query.filter(Order.order_id == order_id).first() + + +class LocalTrade(): + """ + Trade database model. + Used in backtesting - must be aligned to Trade model! + + """ + use_db: bool = False + # Trades container for backtesting + trades: List['LocalTrade'] = [] + trades_open: List['LocalTrade'] = [] + total_profit: float = 0 + + id: int = 0 + + orders: List[Order] = [] + + exchange: str = '' + pair: str = '' + base_currency: str = '' + stake_currency: str = '' + is_open: bool = True + fee_open: float = 0.0 + fee_open_cost: Optional[float] = None + fee_open_currency: str = '' + fee_close: float = 0.0 + fee_close_cost: Optional[float] = None + fee_close_currency: str = '' + open_rate: float = 0.0 + open_rate_requested: Optional[float] = None + # open_trade_value - calculated via _calc_open_trade_value + open_trade_value: float = 0.0 + close_rate: Optional[float] = None + close_rate_requested: Optional[float] = None + close_profit: Optional[float] = None + close_profit_abs: Optional[float] = None + stake_amount: float = 0.0 + amount: float = 0.0 + amount_requested: Optional[float] = None + open_date: datetime + close_date: Optional[datetime] = None + open_order_id: Optional[str] = None + # absolute value of the stop loss + stop_loss: float = 0.0 + # percentage value of the stop loss + stop_loss_pct: float = 0.0 + # absolute value of the initial stop loss + initial_stop_loss: float = 0.0 + # percentage value of the initial stop loss + initial_stop_loss_pct: Optional[float] = None + # stoploss order id which is on exchange + stoploss_order_id: Optional[str] = None + # last update time of the stoploss order on exchange + stoploss_last_update: Optional[datetime] = None + # absolute value of the highest reached price + max_rate: float = 0.0 + # Lowest price reached + min_rate: float = 0.0 + exit_reason: str = '' + exit_order_status: str = '' + strategy: str = '' + enter_tag: Optional[str] = None + timeframe: Optional[int] = None + + trading_mode: TradingMode = TradingMode.SPOT + + # Leverage trading properties + liquidation_price: Optional[float] = None + is_short: bool = False + leverage: float = 1.0 + + # Margin trading properties + interest_rate: float = 0.0 + + # Futures properties + funding_fees: Optional[float] = None + + @property + def buy_tag(self) -> Optional[str]: + """ + Compatibility between buy_tag (old) and enter_tag (new) + Consider buy_tag deprecated + """ + return self.enter_tag + + @property + def has_no_leverage(self) -> bool: + """Returns true if this is a non-leverage, non-short trade""" + return ((self.leverage == 1.0 or self.leverage is None) and not self.is_short) + + @property + def borrowed(self) -> float: + """ + The amount of currency borrowed from the exchange for leverage trades + If a long trade, the amount is in base currency + If a short trade, the amount is in the other currency being traded + """ + if self.has_no_leverage: + return 0.0 + elif not self.is_short: + return (self.amount * self.open_rate) * ((self.leverage - 1) / self.leverage) + else: + return self.amount + + @property + def open_date_utc(self): + return self.open_date.replace(tzinfo=timezone.utc) + + @property + def close_date_utc(self): + return self.close_date.replace(tzinfo=timezone.utc) + + @property + def enter_side(self) -> str: + """ DEPRECATED, please use entry_side instead""" + # TODO: Please remove me after 2022.5 + return self.entry_side + + @property + def entry_side(self) -> str: + if self.is_short: + return "sell" + else: + return "buy" + + @property + def exit_side(self) -> BuySell: + if self.is_short: + return "buy" + else: + return "sell" + + @property + def trade_direction(self) -> LongShort: + if self.is_short: + return "short" + else: + return "long" + + @property + def safe_base_currency(self) -> str: + """ + Compatibility layer for asset - which can be empty for old trades. + """ + try: + return self.base_currency or self.pair.split('/')[0] + except IndexError: + return '' + + @property + def safe_quote_currency(self) -> str: + """ + Compatibility layer for asset - which can be empty for old trades. + """ + try: + return self.stake_currency or self.pair.split('/')[1].split(':')[0] + except IndexError: + return '' + + def __init__(self, **kwargs): + for key in kwargs: + setattr(self, key, kwargs[key]) + self.recalc_open_trade_value() + if self.trading_mode == TradingMode.MARGIN and self.interest_rate is None: + raise OperationalException( + f"{self.trading_mode.value} trading requires param interest_rate on trades") + + def __repr__(self): + open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed' + + return ( + f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, ' + f'is_short={self.is_short or False}, leverage={self.leverage or 1.0}, ' + f'open_rate={self.open_rate:.8f}, open_since={open_since})' + ) + + def to_json(self, minified: bool = False) -> Dict[str, Any]: + filled_orders = self.select_filled_or_open_orders() + orders = [order.to_json(self.entry_side, minified) for order in filled_orders] + + return { + 'trade_id': self.id, + 'pair': self.pair, + 'base_currency': self.safe_base_currency, + 'quote_currency': self.safe_quote_currency, + 'is_open': self.is_open, + 'exchange': self.exchange, + 'amount': round(self.amount, 8), + 'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None, + 'stake_amount': round(self.stake_amount, 8), + 'strategy': self.strategy, + 'buy_tag': self.enter_tag, + 'enter_tag': self.enter_tag, + 'timeframe': self.timeframe, + + 'fee_open': self.fee_open, + 'fee_open_cost': self.fee_open_cost, + 'fee_open_currency': self.fee_open_currency, + 'fee_close': self.fee_close, + 'fee_close_cost': self.fee_close_cost, + 'fee_close_currency': self.fee_close_currency, + + 'open_date': self.open_date.strftime(DATETIME_PRINT_FORMAT), + 'open_timestamp': int(self.open_date.replace(tzinfo=timezone.utc).timestamp() * 1000), + 'open_rate': self.open_rate, + 'open_rate_requested': self.open_rate_requested, + 'open_trade_value': round(self.open_trade_value, 8), + + 'close_date': (self.close_date.strftime(DATETIME_PRINT_FORMAT) + if self.close_date else None), + 'close_timestamp': int(self.close_date.replace( + tzinfo=timezone.utc).timestamp() * 1000) if self.close_date else None, + 'close_rate': self.close_rate, + 'close_rate_requested': self.close_rate_requested, + 'close_profit': self.close_profit, # Deprecated + 'close_profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None, + 'close_profit_abs': self.close_profit_abs, # Deprecated + + 'trade_duration_s': (int((self.close_date_utc - self.open_date_utc).total_seconds()) + if self.close_date else None), + 'trade_duration': (int((self.close_date_utc - self.open_date_utc).total_seconds() // 60) + if self.close_date else None), + + 'profit_ratio': self.close_profit, + 'profit_pct': round(self.close_profit * 100, 2) if self.close_profit else None, + 'profit_abs': self.close_profit_abs, + + 'sell_reason': self.exit_reason, # Deprecated + 'exit_reason': self.exit_reason, + 'exit_order_status': self.exit_order_status, + 'stop_loss_abs': self.stop_loss, + 'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None, + 'stop_loss_pct': (self.stop_loss_pct * 100) if self.stop_loss_pct else None, + 'stoploss_order_id': self.stoploss_order_id, + 'stoploss_last_update': (self.stoploss_last_update.strftime(DATETIME_PRINT_FORMAT) + if self.stoploss_last_update else None), + 'stoploss_last_update_timestamp': int(self.stoploss_last_update.replace( + tzinfo=timezone.utc).timestamp() * 1000) if self.stoploss_last_update else None, + 'initial_stop_loss_abs': self.initial_stop_loss, + 'initial_stop_loss_ratio': (self.initial_stop_loss_pct + if self.initial_stop_loss_pct else None), + 'initial_stop_loss_pct': (self.initial_stop_loss_pct * 100 + if self.initial_stop_loss_pct else None), + 'min_rate': self.min_rate, + 'max_rate': self.max_rate, + + 'leverage': self.leverage, + 'interest_rate': self.interest_rate, + 'liquidation_price': self.liquidation_price, + 'is_short': self.is_short, + 'trading_mode': self.trading_mode, + 'funding_fees': self.funding_fees, + 'open_order_id': self.open_order_id, + 'orders': orders, + } + + @staticmethod + def reset_trades() -> None: + """ + Resets all trades. Only active for backtesting mode. + """ + LocalTrade.trades = [] + LocalTrade.trades_open = [] + LocalTrade.total_profit = 0 + + def adjust_min_max_rates(self, current_price: float, current_price_low: float) -> None: + """ + Adjust the max_rate and min_rate. + """ + self.max_rate = max(current_price, self.max_rate or self.open_rate) + self.min_rate = min(current_price_low, self.min_rate or self.open_rate) + + def set_isolated_liq(self, liquidation_price: Optional[float]): + """ + Method you should use to set self.liquidation price. + Assures stop_loss is not passed the liquidation price + """ + if not liquidation_price: + return + self.liquidation_price = liquidation_price + + def _set_stop_loss(self, stop_loss: float, percent: float): + """ + Method you should use to set self.stop_loss. + Assures stop_loss is not passed the liquidation price + """ + if self.liquidation_price is not None: + if self.is_short: + sl = min(stop_loss, self.liquidation_price) + else: + sl = max(stop_loss, self.liquidation_price) + else: + sl = stop_loss + + if not self.stop_loss: + self.initial_stop_loss = sl + self.stop_loss = sl + + self.stop_loss_pct = -1 * abs(percent) + self.stoploss_last_update = datetime.utcnow() + + def adjust_stop_loss(self, current_price: float, stoploss: float, + initial: bool = False, refresh: bool = False) -> None: + """ + This adjusts the stop loss to it's most recently observed setting + :param current_price: Current rate the asset is traded + :param stoploss: Stoploss as factor (sample -0.05 -> -5% below current price). + :param initial: Called to initiate stop_loss. + Skips everything if self.stop_loss is already set. + """ + if initial and not (self.stop_loss is None or self.stop_loss == 0): + # Don't modify if called with initial and nothing to do + return + refresh = True if refresh and self.nr_of_successful_entries == 1 else False + + leverage = self.leverage or 1.0 + if self.is_short: + new_loss = float(current_price * (1 + abs(stoploss / leverage))) + # If trading with leverage, don't set the stoploss below the liquidation price + if self.liquidation_price: + new_loss = min(self.liquidation_price, new_loss) + else: + new_loss = float(current_price * (1 - abs(stoploss / leverage))) + # If trading with leverage, don't set the stoploss below the liquidation price + if self.liquidation_price: + new_loss = max(self.liquidation_price, new_loss) + + # no stop loss assigned yet + if self.initial_stop_loss_pct is None or refresh: + self._set_stop_loss(new_loss, stoploss) + self.initial_stop_loss = new_loss + self.initial_stop_loss_pct = -1 * abs(stoploss) + + # evaluate if the stop loss needs to be updated + else: + + higher_stop = new_loss > self.stop_loss + lower_stop = new_loss < self.stop_loss + + # stop losses only walk up, never down!, + # ? But adding more to a leveraged trade would create a lower liquidation price, + # ? decreasing the minimum stoploss + if (higher_stop and not self.is_short) or (lower_stop and self.is_short): + logger.debug(f"{self.pair} - Adjusting stoploss...") + self._set_stop_loss(new_loss, stoploss) + else: + logger.debug(f"{self.pair} - Keeping current stoploss...") + + logger.debug( + f"{self.pair} - Stoploss adjusted. current_price={current_price:.8f}, " + f"open_rate={self.open_rate:.8f}, max_rate={self.max_rate or self.open_rate:.8f}, " + f"initial_stop_loss={self.initial_stop_loss:.8f}, " + f"stop_loss={self.stop_loss:.8f}. " + f"Trailing stoploss saved us: " + f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.") + + def update_trade(self, order: Order) -> None: + """ + Updates this entity with amount and actual open/close rates. + :param order: order retrieved by exchange.fetch_order() + :return: None + """ + + # Ignore open and cancelled orders + if order.status == 'open' or order.safe_price is None: + return + + logger.info(f'Updating trade (id={self.id}) ...') + + if order.ft_order_side == self.entry_side: + # Update open rate and actual amount + self.open_rate = order.safe_price + self.amount = order.safe_amount_after_fee + if self.is_open: + payment = "SELL" if self.is_short else "BUY" + logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.') + self.open_order_id = None + self.recalc_trade_from_orders() + elif order.ft_order_side == self.exit_side: + if self.is_open: + payment = "BUY" if self.is_short else "SELL" + # * On margin shorts, you buy a little bit more than the amount (amount + interest) + logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.') + self.close(order.safe_price) + elif order.ft_order_side == 'stoploss': + self.stoploss_order_id = None + self.close_rate_requested = self.stop_loss + self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value + if self.is_open: + logger.info(f'{order.order_type.upper()} is hit for {self}.') + self.close(order.safe_price) + else: + raise ValueError(f'Unknown order type: {order.order_type}') + Trade.commit() + + def close(self, rate: float, *, show_msg: bool = True) -> None: + """ + Sets close_rate to the given rate, calculates total profit + and marks trade as closed + """ + self.close_rate = rate + self.close_date = self.close_date or datetime.utcnow() + self.close_profit = self.calc_profit_ratio(rate) + self.close_profit_abs = self.calc_profit(rate) + self.is_open = False + self.exit_order_status = 'closed' + self.open_order_id = None + if show_msg: + logger.info( + 'Marking %s as closed as the trade is fulfilled and found no open orders for it.', + self + ) + + def update_fee(self, fee_cost: float, fee_currency: Optional[str], fee_rate: Optional[float], + side: str) -> None: + """ + Update Fee parameters. Only acts once per side + """ + if self.entry_side == side and self.fee_open_currency is None: + self.fee_open_cost = fee_cost + self.fee_open_currency = fee_currency + if fee_rate is not None: + self.fee_open = fee_rate + # Assume close-fee will fall into the same fee category and take an educated guess + self.fee_close = fee_rate + elif self.exit_side == side and self.fee_close_currency is None: + self.fee_close_cost = fee_cost + self.fee_close_currency = fee_currency + if fee_rate is not None: + self.fee_close = fee_rate + + def fee_updated(self, side: str) -> bool: + """ + Verify if this side (buy / sell) has already been updated + """ + if self.entry_side == side: + return self.fee_open_currency is not None + elif self.exit_side == side: + return self.fee_close_currency is not None + else: + return False + + def update_order(self, order: Dict) -> None: + Order.update_orders(self.orders, order) + + def get_exit_order_count(self) -> int: + """ + Get amount of failed exiting orders + assumes full exits. + """ + return len([o for o in self.orders if o.ft_order_side == self.exit_side]) + + def _calc_open_trade_value(self) -> float: + """ + Calculate the open_rate including open_fee. + :return: Price in of the open trade incl. Fees + """ + open_trade = Decimal(self.amount) * Decimal(self.open_rate) + fees = open_trade * Decimal(self.fee_open) + if self.is_short: + return float(open_trade - fees) + else: + return float(open_trade + fees) + + def recalc_open_trade_value(self) -> None: + """ + Recalculate open_trade_value. + Must be called whenever open_rate, fee_open is changed. + """ + self.open_trade_value = self._calc_open_trade_value() + + def calculate_interest(self) -> Decimal: + """ + Calculate interest for this trade. Only applicable for Margin trading. + """ + zero = Decimal(0.0) + # If nothing was borrowed + if self.trading_mode != TradingMode.MARGIN or self.has_no_leverage: + return zero + + open_date = self.open_date.replace(tzinfo=None) + now = (self.close_date or datetime.now(timezone.utc)).replace(tzinfo=None) + sec_per_hour = Decimal(3600) + total_seconds = Decimal((now - open_date).total_seconds()) + hours = total_seconds / sec_per_hour or zero + + rate = Decimal(self.interest_rate) + borrowed = Decimal(self.borrowed) + + return interest(exchange_name=self.exchange, borrowed=borrowed, rate=rate, hours=hours) + + def _calc_base_close(self, amount: Decimal, rate: float, fee: float) -> Decimal: + + close_trade = amount * Decimal(rate) + fees = close_trade * Decimal(fee) + + if self.is_short: + return close_trade + fees + else: + return close_trade - fees + + def calc_close_trade_value(self, rate: float) -> float: + """ + Calculate the Trade's close value including fees + :param rate: rate to compare with. + :return: value in stake currency of the open trade + """ + if rate is None and not self.close_rate: + return 0.0 + + amount = Decimal(self.amount) + trading_mode = self.trading_mode or TradingMode.SPOT + + if trading_mode == TradingMode.SPOT: + return float(self._calc_base_close(amount, rate, self.fee_close)) + + elif (trading_mode == TradingMode.MARGIN): + + total_interest = self.calculate_interest() + + if self.is_short: + amount = amount + total_interest + return float(self._calc_base_close(amount, rate, self.fee_close)) + else: + # Currency already owned for longs, no need to purchase + return float(self._calc_base_close(amount, rate, self.fee_close) - total_interest) + + elif (trading_mode == TradingMode.FUTURES): + funding_fees = self.funding_fees or 0.0 + # Positive funding_fees -> Trade has gained from fees. + # Negative funding_fees -> Trade had to pay the fees. + if self.is_short: + return float(self._calc_base_close(amount, rate, self.fee_close)) - funding_fees + else: + return float(self._calc_base_close(amount, rate, self.fee_close)) + funding_fees + else: + raise OperationalException( + f"{self.trading_mode.value} trading is not yet available using freqtrade") + + def calc_profit(self, rate: float) -> float: + """ + Calculate the absolute profit in stake currency between Close and Open trade + :param rate: close rate to compare with. + :return: profit in stake currency as float + """ + close_trade_value = self.calc_close_trade_value(rate) + + if self.is_short: + profit = self.open_trade_value - close_trade_value + else: + profit = close_trade_value - self.open_trade_value + return float(f"{profit:.8f}") + + def calc_profit_ratio(self, rate: float) -> float: + """ + Calculates the profit as ratio (including fee). + :param rate: rate to compare with. + :return: profit ratio as float + """ + close_trade_value = self.calc_close_trade_value(rate) + + short_close_zero = (self.is_short and close_trade_value == 0.0) + long_close_zero = (not self.is_short and self.open_trade_value == 0.0) + leverage = self.leverage or 1.0 + + if (short_close_zero or long_close_zero): + return 0.0 + else: + if self.is_short: + profit_ratio = (1 - (close_trade_value / self.open_trade_value)) * leverage + else: + profit_ratio = ((close_trade_value / self.open_trade_value) - 1) * leverage + + return float(f"{profit_ratio:.8f}") + + def recalc_trade_from_orders(self): + + total_amount = 0.0 + total_stake = 0.0 + for o in self.orders: + if (o.ft_is_open or + (o.ft_order_side != self.entry_side) or + (o.status not in NON_OPEN_EXCHANGE_STATES)): + continue + + tmp_amount = o.safe_amount_after_fee + tmp_price = o.average or o.price + if tmp_amount > 0.0 and tmp_price is not None: + total_amount += tmp_amount + total_stake += tmp_price * tmp_amount + + if total_amount > 0: + # Leverage not updated, as we don't allow changing leverage through DCA at the moment. + self.open_rate = total_stake / total_amount + self.stake_amount = total_stake / (self.leverage or 1.0) + self.amount = total_amount + self.fee_open_cost = self.fee_open * total_stake + self.recalc_open_trade_value() + if self.stop_loss_pct is not None and self.open_rate is not None: + self.adjust_stop_loss(self.open_rate, self.stop_loss_pct) + + def select_order_by_order_id(self, order_id: str) -> Optional[Order]: + """ + Finds order object by Order id. + :param order_id: Exchange order id + """ + for o in self.orders: + if o.order_id == order_id: + return o + return None + + def select_order(self, order_side: Optional[str] = None, + is_open: Optional[bool] = None) -> Optional[Order]: + """ + Finds latest order for this orderside and status + :param order_side: ft_order_side of the order (either 'buy', 'sell' or 'stoploss') + :param is_open: Only search for open orders? + :return: latest Order object if it exists, else None + """ + orders = self.orders + if order_side: + orders = [o for o in self.orders if o.ft_order_side == order_side] + if is_open is not None: + orders = [o for o in orders if o.ft_is_open == is_open] + if len(orders) > 0: + return orders[-1] + else: + return None + + def select_filled_orders(self, order_side: Optional[str] = None) -> List['Order']: + """ + Finds filled orders for this orderside. + :param order_side: Side of the order (either 'buy', 'sell', or None) + :return: array of Order objects + """ + return [o for o in self.orders if ((o.ft_order_side == order_side) or (order_side is None)) + and o.ft_is_open is False and + (o.filled or 0) > 0 and + o.status in NON_OPEN_EXCHANGE_STATES] + + def select_filled_or_open_orders(self) -> List['Order']: + """ + Finds filled or open orders + :param order_side: Side of the order (either 'buy', 'sell', or None) + :return: array of Order objects + """ + return [o for o in self.orders if + ( + o.ft_is_open is False + and (o.filled or 0) > 0 + and o.status in NON_OPEN_EXCHANGE_STATES + ) + or (o.ft_is_open is True and o.status is not None) + ] + + @property + def nr_of_successful_entries(self) -> int: + """ + Helper function to count the number of entry orders that have been filled. + :return: int count of entry orders that have been filled for this trade. + """ + + return len(self.select_filled_orders(self.entry_side)) + + @property + def nr_of_successful_exits(self) -> int: + """ + Helper function to count the number of exit orders that have been filled. + :return: int count of exit orders that have been filled for this trade. + """ + return len(self.select_filled_orders(self.exit_side)) + + @property + def nr_of_successful_buys(self) -> int: + """ + Helper function to count the number of buy orders that have been filled. + WARNING: Please use nr_of_successful_entries for short support. + :return: int count of buy orders that have been filled for this trade. + """ + + return len(self.select_filled_orders('buy')) + + @property + def nr_of_successful_sells(self) -> int: + """ + Helper function to count the number of sell orders that have been filled. + WARNING: Please use nr_of_successful_exits for short support. + :return: int count of sell orders that have been filled for this trade. + """ + return len(self.select_filled_orders('sell')) + + @property + def sell_reason(self) -> str: + """ DEPRECATED! Please use exit_reason instead.""" + return self.exit_reason + + @staticmethod + def get_trades_proxy(*, pair: str = None, is_open: bool = None, + open_date: datetime = None, close_date: datetime = None, + ) -> List['LocalTrade']: + """ + Helper function to query Trades. + Returns a List of trades, filtered on the parameters given. + In live mode, converts the filter to a database query and returns all rows + In Backtest mode, uses filters on Trade.trades to get the result. + + :return: unsorted List[Trade] + """ + + # Offline mode - without database + if is_open is not None: + if is_open: + sel_trades = LocalTrade.trades_open + else: + sel_trades = LocalTrade.trades + + else: + # Not used during backtesting, but might be used by a strategy + sel_trades = list(LocalTrade.trades + LocalTrade.trades_open) + + if pair: + sel_trades = [trade for trade in sel_trades if trade.pair == pair] + if open_date: + sel_trades = [trade for trade in sel_trades if trade.open_date > open_date] + if close_date: + sel_trades = [trade for trade in sel_trades if trade.close_date + and trade.close_date > close_date] + + return sel_trades + + @staticmethod + def close_bt_trade(trade): + LocalTrade.trades_open.remove(trade) + LocalTrade.trades.append(trade) + LocalTrade.total_profit += trade.close_profit_abs + + @staticmethod + def add_bt_trade(trade): + if trade.is_open: + LocalTrade.trades_open.append(trade) + else: + LocalTrade.trades.append(trade) + + @staticmethod + def get_open_trades() -> List[Any]: + """ + Query trades from persistence layer + """ + return Trade.get_trades_proxy(is_open=True) + + @staticmethod + def stoploss_reinitialization(desired_stoploss): + """ + Adjust initial Stoploss to desired stoploss for all open trades. + """ + for trade in Trade.get_open_trades(): + logger.info("Found open trade: %s", trade) + + # skip case if trailing-stop changed the stoploss already. + if (trade.stop_loss == trade.initial_stop_loss + and trade.initial_stop_loss_pct != desired_stoploss): + # Stoploss value got changed + + logger.info(f"Stoploss for {trade} needs adjustment...") + # Force reset of stoploss + trade.stop_loss = None + trade.initial_stop_loss_pct = None + trade.adjust_stop_loss(trade.open_rate, desired_stoploss) + logger.info(f"New stoploss: {trade.stop_loss}.") + + +class Trade(_DECL_BASE, LocalTrade): + """ + Trade database model. + Also handles updating and querying trades + + Note: Fields must be aligned with LocalTrade class + """ + __tablename__ = 'trades' + + use_db: bool = True + + id = Column(Integer, primary_key=True) + + orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan", lazy="joined") + + exchange = Column(String(25), nullable=False) + pair = Column(String(25), nullable=False, index=True) + base_currency = Column(String(25), nullable=True) + stake_currency = Column(String(25), nullable=True) + is_open = Column(Boolean, nullable=False, default=True, index=True) + fee_open = Column(Float, nullable=False, default=0.0) + fee_open_cost = Column(Float, nullable=True) + fee_open_currency = Column(String(25), nullable=True) + fee_close = Column(Float, nullable=False, default=0.0) + fee_close_cost = Column(Float, nullable=True) + fee_close_currency = Column(String(25), nullable=True) + open_rate: float = Column(Float) + open_rate_requested = Column(Float) + # open_trade_value - calculated via _calc_open_trade_value + open_trade_value = Column(Float) + close_rate: Optional[float] = Column(Float) + close_rate_requested = Column(Float) + close_profit = Column(Float) + close_profit_abs = Column(Float) + stake_amount = Column(Float, nullable=False) + amount = Column(Float) + amount_requested = Column(Float) + open_date = Column(DateTime, nullable=False, default=datetime.utcnow) + close_date = Column(DateTime) + open_order_id = Column(String(255)) + # absolute value of the stop loss + stop_loss = Column(Float, nullable=True, default=0.0) + # percentage value of the stop loss + stop_loss_pct = Column(Float, nullable=True) + # absolute value of the initial stop loss + initial_stop_loss = Column(Float, nullable=True, default=0.0) + # percentage value of the initial stop loss + initial_stop_loss_pct = Column(Float, nullable=True) + # stoploss order id which is on exchange + stoploss_order_id = Column(String(255), nullable=True, index=True) + # last update time of the stoploss order on exchange + stoploss_last_update = Column(DateTime, nullable=True) + # absolute value of the highest reached price + max_rate = Column(Float, nullable=True, default=0.0) + # Lowest price reached + min_rate = Column(Float, nullable=True) + exit_reason = Column(String(100), nullable=True) + exit_order_status = Column(String(100), nullable=True) + strategy = Column(String(100), nullable=True) + enter_tag = Column(String(100), nullable=True) + timeframe = Column(Integer, nullable=True) + + trading_mode = Column(Enum(TradingMode), nullable=True) + + # Leverage trading properties + leverage = Column(Float, nullable=True, default=1.0) + is_short = Column(Boolean, nullable=False, default=False) + liquidation_price = Column(Float, nullable=True) + + # Margin Trading Properties + interest_rate = Column(Float, nullable=False, default=0.0) + + # Futures properties + funding_fees = Column(Float, nullable=True, default=None) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.recalc_open_trade_value() + + def delete(self) -> None: + + for order in self.orders: + Order.query.session.delete(order) + + Trade.query.session.delete(self) + Trade.commit() + + @staticmethod + def commit(): + Trade.query.session.commit() + + @staticmethod + def get_trades_proxy(*, pair: str = None, is_open: bool = None, + open_date: datetime = None, close_date: datetime = None, + ) -> List['LocalTrade']: + """ + Helper function to query Trades.j + Returns a List of trades, filtered on the parameters given. + In live mode, converts the filter to a database query and returns all rows + In Backtest mode, uses filters on Trade.trades to get the result. + + :return: unsorted List[Trade] + """ + if Trade.use_db: + trade_filter = [] + if pair: + trade_filter.append(Trade.pair == pair) + if open_date: + trade_filter.append(Trade.open_date > open_date) + if close_date: + trade_filter.append(Trade.close_date > close_date) + if is_open is not None: + trade_filter.append(Trade.is_open.is_(is_open)) + return Trade.get_trades(trade_filter).all() + else: + return LocalTrade.get_trades_proxy( + pair=pair, is_open=is_open, + open_date=open_date, + close_date=close_date + ) + + @staticmethod + def get_trades(trade_filter=None, include_orders: bool = True) -> Query: + """ + Helper function to query Trades using filters. + NOTE: Not supported in Backtesting. + :param trade_filter: Optional filter to apply to trades + Can be either a Filter object, or a List of filters + e.g. `(trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True),])` + e.g. `(trade_filter=Trade.id == trade_id)` + :return: unsorted query object + """ + if not Trade.use_db: + raise NotImplementedError('`Trade.get_trades()` not supported in backtesting mode.') + if trade_filter is not None: + if not isinstance(trade_filter, list): + trade_filter = [trade_filter] + this_query = Trade.query.filter(*trade_filter) + else: + this_query = Trade.query + if not include_orders: + # Don't load order relations + # Consider using noload or raiseload instead of lazyload + this_query = this_query.options(lazyload(Trade.orders)) + return this_query + + @staticmethod + def get_open_order_trades() -> List['Trade']: + """ + Returns all open trades + NOTE: Not supported in Backtesting. + """ + return Trade.get_trades(Trade.open_order_id.isnot(None)).all() + + @staticmethod + def get_open_trades_without_assigned_fees(): + """ + Returns all open trades which don't have open fees set correctly + NOTE: Not supported in Backtesting. + """ + return Trade.get_trades([Trade.fee_open_currency.is_(None), + Trade.orders.any(), + Trade.is_open.is_(True), + ]).all() + + @staticmethod + def get_closed_trades_without_assigned_fees(): + """ + Returns all closed trades which don't have fees set correctly + NOTE: Not supported in Backtesting. + """ + return Trade.get_trades([Trade.fee_close_currency.is_(None), + Trade.orders.any(), + Trade.is_open.is_(False), + ]).all() + + @staticmethod + def get_total_closed_profit() -> float: + """ + Retrieves total realized profit + """ + if Trade.use_db: + total_profit = Trade.query.with_entities( + func.sum(Trade.close_profit_abs)).filter(Trade.is_open.is_(False)).scalar() + else: + total_profit = sum( + t.close_profit_abs for t in LocalTrade.get_trades_proxy(is_open=False)) + return total_profit or 0 + + @staticmethod + def total_open_trades_stakes() -> float: + """ + Calculates total invested amount in open trades + in stake currency + """ + if Trade.use_db: + total_open_stake_amount = Trade.query.with_entities( + func.sum(Trade.stake_amount)).filter(Trade.is_open.is_(True)).scalar() + else: + total_open_stake_amount = sum( + t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True)) + return total_open_stake_amount or 0 + + @staticmethod + def get_overall_performance(minutes=None) -> List[Dict[str, Any]]: + """ + Returns List of dicts containing all Trades, including profit and trade count + NOTE: Not supported in Backtesting. + """ + filters = [Trade.is_open.is_(False)] + if minutes: + start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) + filters.append(Trade.close_date >= start_date) + pair_rates = Trade.query.with_entities( + Trade.pair, + func.sum(Trade.close_profit).label('profit_sum'), + func.sum(Trade.close_profit_abs).label('profit_sum_abs'), + func.count(Trade.pair).label('count') + ).filter(*filters)\ + .group_by(Trade.pair) \ + .order_by(desc('profit_sum_abs')) \ + .all() + return [ + { + 'pair': pair, + 'profit_ratio': profit, + 'profit': round(profit * 100, 2), # Compatibility mode + 'profit_pct': round(profit * 100, 2), + 'profit_abs': profit_abs, + 'count': count + } + for pair, profit, profit_abs, count in pair_rates + ] + + @staticmethod + def get_enter_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: + """ + Returns List of dicts containing all Trades, based on buy tag performance + Can either be average for all pairs or a specific pair provided + NOTE: Not supported in Backtesting. + """ + + filters = [Trade.is_open.is_(False)] + if(pair is not None): + filters.append(Trade.pair == pair) + + enter_tag_perf = Trade.query.with_entities( + Trade.enter_tag, + func.sum(Trade.close_profit).label('profit_sum'), + func.sum(Trade.close_profit_abs).label('profit_sum_abs'), + func.count(Trade.pair).label('count') + ).filter(*filters)\ + .group_by(Trade.enter_tag) \ + .order_by(desc('profit_sum_abs')) \ + .all() + + return [ + { + 'enter_tag': enter_tag if enter_tag is not None else "Other", + 'profit_ratio': profit, + 'profit_pct': round(profit * 100, 2), + 'profit_abs': profit_abs, + 'count': count + } + for enter_tag, profit, profit_abs, count in enter_tag_perf + ] + + @staticmethod + def get_exit_reason_performance(pair: Optional[str]) -> List[Dict[str, Any]]: + """ + Returns List of dicts containing all Trades, based on exit reason performance + Can either be average for all pairs or a specific pair provided + NOTE: Not supported in Backtesting. + """ + + filters = [Trade.is_open.is_(False)] + if(pair is not None): + filters.append(Trade.pair == pair) + + sell_tag_perf = Trade.query.with_entities( + Trade.exit_reason, + func.sum(Trade.close_profit).label('profit_sum'), + func.sum(Trade.close_profit_abs).label('profit_sum_abs'), + func.count(Trade.pair).label('count') + ).filter(*filters)\ + .group_by(Trade.exit_reason) \ + .order_by(desc('profit_sum_abs')) \ + .all() + + return [ + { + 'exit_reason': exit_reason if exit_reason is not None else "Other", + 'profit_ratio': profit, + 'profit_pct': round(profit * 100, 2), + 'profit_abs': profit_abs, + 'count': count + } + for exit_reason, profit, profit_abs, count in sell_tag_perf + ] + + @staticmethod + def get_mix_tag_performance(pair: Optional[str]) -> List[Dict[str, Any]]: + """ + Returns List of dicts containing all Trades, based on entry_tag + exit_reason performance + Can either be average for all pairs or a specific pair provided + NOTE: Not supported in Backtesting. + """ + + filters = [Trade.is_open.is_(False)] + if(pair is not None): + filters.append(Trade.pair == pair) + + mix_tag_perf = Trade.query.with_entities( + Trade.id, + Trade.enter_tag, + Trade.exit_reason, + func.sum(Trade.close_profit).label('profit_sum'), + func.sum(Trade.close_profit_abs).label('profit_sum_abs'), + func.count(Trade.pair).label('count') + ).filter(*filters)\ + .group_by(Trade.id) \ + .order_by(desc('profit_sum_abs')) \ + .all() + + return_list: List[Dict] = [] + for id, enter_tag, exit_reason, profit, profit_abs, count in mix_tag_perf: + enter_tag = enter_tag if enter_tag is not None else "Other" + exit_reason = exit_reason if exit_reason is not None else "Other" + + if(exit_reason is not None and enter_tag is not None): + mix_tag = enter_tag + " " + exit_reason + i = 0 + if not any(item["mix_tag"] == mix_tag for item in return_list): + return_list.append({'mix_tag': mix_tag, + 'profit': profit, + 'profit_pct': round(profit * 100, 2), + 'profit_abs': profit_abs, + 'count': count}) + else: + while i < len(return_list): + if return_list[i]["mix_tag"] == mix_tag: + return_list[i] = { + 'mix_tag': mix_tag, + 'profit': profit + return_list[i]["profit"], + 'profit_pct': round(profit + return_list[i]["profit"] * 100, 2), + 'profit_abs': profit_abs + return_list[i]["profit_abs"], + 'count': 1 + return_list[i]["count"]} + i += 1 + + return return_list + + @staticmethod + def get_best_pair(start_date: datetime = datetime.fromtimestamp(0)): + """ + Get best pair with closed trade. + NOTE: Not supported in Backtesting. + :returns: Tuple containing (pair, profit_sum) + """ + best_pair = Trade.query.with_entities( + Trade.pair, func.sum(Trade.close_profit).label('profit_sum') + ).filter(Trade.is_open.is_(False) & (Trade.close_date >= start_date)) \ + .group_by(Trade.pair) \ + .order_by(desc('profit_sum')).first() + return best_pair + + @staticmethod + def get_trading_volume(start_date: datetime = datetime.fromtimestamp(0)) -> float: + """ + Get Trade volume based on Orders + NOTE: Not supported in Backtesting. + :returns: Tuple containing (pair, profit_sum) + """ + trading_volume = Order.query.with_entities( + func.sum(Order.cost).label('volume') + ).filter( + Order.order_filled_date >= start_date, + Order.status == 'closed' + ).scalar() + return trading_volume diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 37758d05f..a64281156 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -633,7 +633,8 @@ def load_and_plot_trades(config: Dict[str, Any]): exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config) IStrategy.dp = DataProvider(config, exchange) - strategy.bot_start() + strategy.ft_bot_start() + strategy.bot_loop_start() plot_elements = init_plotscript(config, list(exchange.markets), strategy.startup_candle_count) timerange = plot_elements['timerange'] trades = plot_elements['trades'] diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index bb6f75012..786f32e88 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -30,20 +30,21 @@ class AgeFilter(IPairList): self._symbolsCheckFailed = PeriodicCache(maxsize=1000, ttl=86_400) self._min_days_listed = pairlistconfig.get('min_days_listed', 10) - self._max_days_listed = pairlistconfig.get('max_days_listed', None) + self._max_days_listed = pairlistconfig.get('max_days_listed') + candle_limit = exchange.ohlcv_candle_limit('1d', self._config['candle_type_def']) if self._min_days_listed < 1: raise OperationalException("AgeFilter requires min_days_listed to be >= 1") - if self._min_days_listed > exchange.ohlcv_candle_limit('1d'): + if self._min_days_listed > candle_limit: raise OperationalException("AgeFilter requires min_days_listed to not exceed " "exchange max request size " - f"({exchange.ohlcv_candle_limit('1d')})") + f"({candle_limit})") if self._max_days_listed and self._max_days_listed <= self._min_days_listed: raise OperationalException("AgeFilter max_days_listed <= min_days_listed not permitted") - if self._max_days_listed and self._max_days_listed > exchange.ohlcv_candle_limit('1d'): + if self._max_days_listed and self._max_days_listed > candle_limit: raise OperationalException("AgeFilter requires max_days_listed to not exceed " "exchange max request size " - f"({exchange.ohlcv_candle_limit('1d')})") + f"({candle_limit})") @property def needstickers(self) -> bool: diff --git a/freqtrade/plugins/pairlist/OffsetFilter.py b/freqtrade/plugins/pairlist/OffsetFilter.py index 573a573a6..e0f8414ef 100644 --- a/freqtrade/plugins/pairlist/OffsetFilter.py +++ b/freqtrade/plugins/pairlist/OffsetFilter.py @@ -19,6 +19,7 @@ class OffsetFilter(IPairList): super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) self._offset = pairlistconfig.get('offset', 0) + self._number_pairs = pairlistconfig.get('number_assets', 0) if self._offset < 0: raise OperationalException("OffsetFilter requires offset to be >= 0") @@ -36,7 +37,9 @@ class OffsetFilter(IPairList): """ Short whitelist method description - used for startup-messages """ - return f"{self.name} - Offseting pairs by {self._offset}." + if self._number_pairs: + return f"{self.name} - Taking {self._number_pairs} Pairs, starting from {self._offset}." + return f"{self.name} - Offsetting pairs by {self._offset}." def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: """ @@ -50,5 +53,9 @@ class OffsetFilter(IPairList): self.log_once(f"Offset of {self._offset} is larger than " + f"pair count of {len(pairlist)}", logger.warning) pairs = pairlist[self._offset:] + if self._number_pairs: + pairs = pairs[:self._number_pairs] + self.log_once(f"Searching {len(pairs)} pairs: {pairs}", logger.info) + return pairs diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 5b02a47ab..8e0b407c3 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -21,7 +21,7 @@ class PerformanceFilter(IPairList): super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) self._minutes = pairlistconfig.get('minutes', 0) - self._min_profit = pairlistconfig.get('min_profit', None) + self._min_profit = pairlistconfig.get('min_profit') @property def needstickers(self) -> bool: diff --git a/freqtrade/plugins/pairlist/SpreadFilter.py b/freqtrade/plugins/pairlist/SpreadFilter.py index d1f88d2a5..43856b451 100644 --- a/freqtrade/plugins/pairlist/SpreadFilter.py +++ b/freqtrade/plugins/pairlist/SpreadFilter.py @@ -50,7 +50,7 @@ class SpreadFilter(IPairList): :param ticker: ticker dict as returned from ccxt.fetch_tickers() :return: True if the pair can stay, false if it should be removed """ - if 'bid' in ticker and 'ask' in ticker and ticker['ask']: + if 'bid' in ticker and 'ask' in ticker and ticker['ask'] and ticker['bid']: spread = 1 - ticker['bid'] / ticker['ask'] if spread > self._max_spread_ratio: self.log_once(f"Removed {pair} from whitelist, because spread " diff --git a/freqtrade/plugins/pairlist/VolatilityFilter.py b/freqtrade/plugins/pairlist/VolatilityFilter.py index 6aa857c2c..bab44bdd1 100644 --- a/freqtrade/plugins/pairlist/VolatilityFilter.py +++ b/freqtrade/plugins/pairlist/VolatilityFilter.py @@ -38,12 +38,12 @@ class VolatilityFilter(IPairList): self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) + candle_limit = exchange.ohlcv_candle_limit('1d', self._config['candle_type_def']) if self._days < 1: raise OperationalException("VolatilityFilter requires lookback_days to be >= 1") - if self._days > exchange.ohlcv_candle_limit('1d'): + if self._days > candle_limit: raise OperationalException("VolatilityFilter requires lookback_days to not " - "exceed exchange max request size " - f"({exchange.ohlcv_candle_limit('1d')})") + f"exceed exchange max request size ({candle_limit})") @property def needstickers(self) -> bool: diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 26e7d45be..cd16a46a3 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -84,12 +84,13 @@ class VolumePairList(IPairList): raise OperationalException( f'key {self._sort_key} not in {SORT_VALUES}') + candle_limit = exchange.ohlcv_candle_limit( + self._lookback_timeframe, self._config['candle_type_def']) if self._lookback_period < 0: raise OperationalException("VolumeFilter requires lookback_period to be >= 0") - if self._lookback_period > exchange.ohlcv_candle_limit(self._lookback_timeframe): + if self._lookback_period > candle_limit: raise OperationalException("VolumeFilter requires lookback_period to not " - "exceed exchange max request size " - f"({exchange.ohlcv_candle_limit(self._lookback_timeframe)})") + f"exceed exchange max request size ({candle_limit})") @property def needstickers(self) -> bool: diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index c9edfd13d..f3e7bc0d6 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -27,18 +27,18 @@ class RangeStabilityFilter(IPairList): self._days = pairlistconfig.get('lookback_days', 10) self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01) - self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', None) + self._max_rate_of_change = pairlistconfig.get('max_rate_of_change') self._refresh_period = pairlistconfig.get('refresh_period', 1440) self._def_candletype = self._config['candle_type_def'] self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) + candle_limit = exchange.ohlcv_candle_limit('1d', self._config['candle_type_def']) if self._days < 1: raise OperationalException("RangeStabilityFilter requires lookback_days to be >= 1") - if self._days > exchange.ohlcv_candle_limit('1d'): + if self._days > candle_limit: raise OperationalException("RangeStabilityFilter requires lookback_days to not " - "exceed exchange max request size " - f"({exchange.ohlcv_candle_limit('1d')})") + f"exceed exchange max request size ({candle_limit})") @property def needstickers(self) -> bool: diff --git a/freqtrade/plugins/pairlistmanager.py b/freqtrade/plugins/pairlistmanager.py index 2ae67a157..3ddad4a5e 100644 --- a/freqtrade/plugins/pairlistmanager.py +++ b/freqtrade/plugins/pairlistmanager.py @@ -28,7 +28,7 @@ class PairListManager(LoggingMixin): self._blacklist = self._config['exchange'].get('pair_blacklist', []) self._pairlist_handlers: List[IPairList] = [] self._tickers_needed = False - for pairlist_handler_config in self._config.get('pairlists', None): + for pairlist_handler_config in self._config.get('pairlists', []): pairlist_handler = PairListResolver.load_pairlist( pairlist_handler_config['method'], exchange=exchange, diff --git a/freqtrade/plugins/protections/low_profit_pairs.py b/freqtrade/plugins/protections/low_profit_pairs.py index 7d5d6054d..099242b8d 100644 --- a/freqtrade/plugins/protections/low_profit_pairs.py +++ b/freqtrade/plugins/protections/low_profit_pairs.py @@ -21,6 +21,7 @@ class LowProfitPairs(IProtection): self._trade_limit = protection_config.get('trade_limit', 1) self._required_profit = protection_config.get('required_profit', 0.0) + self._only_per_side = protection_config.get('only_per_side', False) def short_desc(self) -> str: """ @@ -36,7 +37,8 @@ class LowProfitPairs(IProtection): return (f'{profit} < {self._required_profit} in {self.lookback_period_str}, ' f'locking for {self.stop_duration_str}.') - def _low_profit(self, date_now: datetime, pair: str) -> Optional[ProtectionReturn]: + def _low_profit( + self, date_now: datetime, pair: str, side: LongShort) -> Optional[ProtectionReturn]: """ Evaluate recent trades for pair """ @@ -54,7 +56,10 @@ class LowProfitPairs(IProtection): # Not enough trades in the relevant period return None - profit = sum(trade.close_profit for trade in trades if trade.close_profit) + profit = sum( + trade.close_profit for trade in trades if trade.close_profit + and (not self._only_per_side or trade.trade_direction == side) + ) if profit < self._required_profit: self.log_once( f"Trading for {pair} stopped due to {profit:.2f} < {self._required_profit} " @@ -65,6 +70,7 @@ class LowProfitPairs(IProtection): lock=True, until=until, reason=self._reason(profit), + lock_side=(side if self._only_per_side else '*') ) return None @@ -86,4 +92,4 @@ class LowProfitPairs(IProtection): :return: Tuple of [bool, until, reason]. If true, this pair will be locked with until """ - return self._low_profit(date_now, pair=pair) + return self._low_profit(date_now, pair=pair, side=side) diff --git a/freqtrade/plugins/protections/stoploss_guard.py b/freqtrade/plugins/protections/stoploss_guard.py index f9fe039d6..713a2da07 100644 --- a/freqtrade/plugins/protections/stoploss_guard.py +++ b/freqtrade/plugins/protections/stoploss_guard.py @@ -38,8 +38,8 @@ class StoplossGuard(IProtection): return (f'{self._trade_limit} stoplosses in {self._lookback_period} min, ' f'locking for {self._stop_duration} min.') - def _stoploss_guard( - self, date_now: datetime, pair: Optional[str], side: str) -> Optional[ProtectionReturn]: + def _stoploss_guard(self, date_now: datetime, pair: Optional[str], + side: LongShort) -> Optional[ProtectionReturn]: """ Evaluate recent trades """ diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 44d590b67..8b01980ce 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -47,26 +47,7 @@ class StrategyResolver(IResolver): strategy: IStrategy = StrategyResolver._load_strategy( strategy_name, config=config, extra_dir=config.get('strategy_path')) - - if strategy._ft_params_from_file: - # Set parameters from Hyperopt results file - params = strategy._ft_params_from_file - strategy.minimal_roi = params.get('roi', getattr(strategy, 'minimal_roi', {})) - - strategy.stoploss = params.get('stoploss', {}).get( - 'stoploss', getattr(strategy, 'stoploss', -0.1)) - trailing = params.get('trailing', {}) - strategy.trailing_stop = trailing.get( - 'trailing_stop', getattr(strategy, 'trailing_stop', False)) - strategy.trailing_stop_positive = trailing.get( - 'trailing_stop_positive', getattr(strategy, 'trailing_stop_positive', None)) - strategy.trailing_stop_positive_offset = trailing.get( - 'trailing_stop_positive_offset', - getattr(strategy, 'trailing_stop_positive_offset', 0)) - strategy.trailing_only_offset_is_reached = trailing.get( - 'trailing_only_offset_is_reached', - getattr(strategy, 'trailing_only_offset_is_reached', 0.0)) - + strategy.ft_load_params_from_file() # Set attributes # Check if we need to override configuration # (Attribute name, default, subkey) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 41712632b..06f04729b 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -1,6 +1,7 @@ import asyncio import logging from copy import deepcopy +from datetime import datetime from typing import Any, Dict, List from fastapi import APIRouter, BackgroundTasks, Depends @@ -102,7 +103,10 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac min_date=min_date, max_date=max_date) if btconfig.get('export', 'none') == 'trades': - store_backtest_stats(btconfig['exportfilename'], ApiServer._bt.results) + store_backtest_stats( + btconfig['exportfilename'], ApiServer._bt.results, + datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + ) logger.info("Backtest finished.") @@ -172,6 +176,7 @@ def api_delete_backtest(ws_mode=Depends(is_webserver_mode)): "status_msg": "Backtest running", } if ApiServer._bt: + ApiServer._bt.cleanup() del ApiServer._bt ApiServer._bt = None del ApiServer._bt_data diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index d78ea8b78..333f2fe6e 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -104,6 +104,10 @@ class Profit(BaseModel): best_pair_profit_ratio: float winning_trades: int losing_trades: int + profit_factor: float + max_drawdown: float + max_drawdown_abs: float + trading_volume: Optional[float] class SellReason(BaseModel): @@ -120,6 +124,8 @@ class Stats(BaseModel): class DailyRecord(BaseModel): date: date abs_profit: float + rel_profit: float + starting_balance: float fiat_value: float trade_count: int @@ -166,7 +172,7 @@ class ShowConfig(BaseModel): trailing_stop_positive: Optional[float] trailing_stop_positive_offset: Optional[float] trailing_only_offset_is_reached: Optional[bool] - unfilledtimeout: UnfilledTimeout + unfilledtimeout: Optional[UnfilledTimeout] # Empty in webserver mode order_types: Optional[OrderTypes] use_custom_stoploss: Optional[bool] timeframe: Optional[str] @@ -256,6 +262,7 @@ class TradeSchema(BaseModel): leverage: Optional[float] interest_rate: Optional[float] + liquidation_price: Optional[float] funding_fees: Optional[float] trading_mode: Optional[TradingMode] @@ -276,6 +283,7 @@ class OpenTradeSchema(TradeSchema): class TradeResponse(BaseModel): trades: List[TradeSchema] trades_count: int + offset: int total_trades: int diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index a8b9873d7..b3506409d 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -36,7 +36,8 @@ logger = logging.getLogger(__name__) # versions 2.xx -> futures/short branch # 2.14: Add entry/exit orders to trade response # 2.15: Add backtest history endpoints -API_VERSION = 2.15 +# 2.16: Additional daily metrics +API_VERSION = 2.16 # Public API, requires no auth. router_public = APIRouter() @@ -86,8 +87,8 @@ def stats(rpc: RPC = Depends(get_rpc)): @router.get('/daily', response_model=Daily, tags=['info']) def daily(timescale: int = 7, rpc: RPC = Depends(get_rpc), config=Depends(get_config)): - return rpc._rpc_daily_profit(timescale, config['stake_currency'], - config.get('fiat_display_currency', '')) + return rpc._rpc_timeunit_profit(timescale, config['stake_currency'], + config.get('fiat_display_currency', '')) @router.get('/status', response_model=List[OpenTradeSchema], tags=['info']) @@ -281,7 +282,7 @@ def get_strategy(strategy: str, config=Depends(get_config)): def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Optional[str] = None, candletype: Optional[CandleType] = None, config=Depends(get_config)): - dh = get_datahandler(config['datadir'], config.get('dataformat_ohlcv', None)) + 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) diff --git a/freqtrade/rpc/discord.py b/freqtrade/rpc/discord.py new file mode 100644 index 000000000..5991f7126 --- /dev/null +++ b/freqtrade/rpc/discord.py @@ -0,0 +1,59 @@ +import logging +from typing import Any, Dict + +from freqtrade.enums.rpcmessagetype import RPCMessageType +from freqtrade.rpc import RPC +from freqtrade.rpc.webhook import Webhook + + +logger = logging.getLogger(__name__) + + +class Discord(Webhook): + def __init__(self, rpc: 'RPC', config: Dict[str, Any]): + # super().__init__(rpc, config) + self.rpc = rpc + self.config = config + self.strategy = config.get('strategy', '') + self.timeframe = config.get('timeframe', '') + + self._url = self.config['discord']['webhook_url'] + self._format = 'json' + self._retries = 1 + self._retry_delay = 0.1 + + def cleanup(self) -> None: + """ + Cleanup pending module resources. + This will do nothing for webhooks, they will simply not be called anymore + """ + pass + + def send_msg(self, msg) -> None: + logger.info(f"Sending discord message: {msg}") + + if msg['type'].value in self.config['discord']: + + msg['strategy'] = self.strategy + msg['timeframe'] = self.timeframe + fields = self.config['discord'].get(msg['type'].value) + color = 0x0000FF + if msg['type'] in (RPCMessageType.EXIT, RPCMessageType.EXIT_FILL): + profit_ratio = msg.get('profit_ratio') + color = (0x00FF00 if profit_ratio > 0 else 0xFF0000) + + embeds = [{ + 'title': f"Trade: {msg['pair']} {msg['type'].value}", + 'color': color, + 'fields': [], + + }] + for f in fields: + for k, v in f.items(): + v = v.format(**msg) + embeds[0]['fields'].append( # type: ignore + {'name': k, 'value': v, 'inline': True}) + + # Send the message to discord channel + payload = {'embeds': embeds} + self._send_msg(payload) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 12adc34d1..e6948c9e2 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -18,6 +18,7 @@ from freqtrade import __version__ from freqtrade.configuration.timerange import TimeRange from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT from freqtrade.data.history import load_data +from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, State, TradingMode) from freqtrade.exceptions import ExchangeError, PricingError @@ -96,7 +97,7 @@ class RPC: """ self._freqtrade = freqtrade self._config: Dict[str, Any] = freqtrade.config - if self._config.get('fiat_display_currency', None): + if self._config.get('fiat_display_currency'): self._fiat_converter = CryptoToFiatConverter() @staticmethod @@ -177,16 +178,19 @@ class RPC: current_rate = NAN else: current_rate = trade.close_rate - current_profit = trade.calc_profit_ratio(current_rate) - current_profit_abs = trade.calc_profit(current_rate) - current_profit_fiat: Optional[float] = None - # Calculate fiat profit - if self._fiat_converter: - current_profit_fiat = self._fiat_converter.convert_amount( - current_profit_abs, - self._freqtrade.config['stake_currency'], - self._freqtrade.config['fiat_display_currency'] - ) + if len(trade.select_filled_orders(trade.entry_side)) > 0: + current_profit = trade.calc_profit_ratio(current_rate) + current_profit_abs = trade.calc_profit(current_rate) + current_profit_fiat: Optional[float] = None + # Calculate fiat profit + if self._fiat_converter: + current_profit_fiat = self._fiat_converter.convert_amount( + current_profit_abs, + self._freqtrade.config['stake_currency'], + self._freqtrade.config['fiat_display_currency'] + ) + else: + current_profit = current_profit_abs = current_profit_fiat = 0.0 # Calculate guaranteed profit (in case of trailing stop) stoploss_entry_dist = trade.calc_profit(trade.stop_loss) @@ -235,8 +239,12 @@ class RPC: trade.pair, side='exit', is_short=trade.is_short, refresh=False) except (PricingError, ExchangeError): current_rate = NAN - trade_profit = trade.calc_profit(current_rate) - profit_str = f'{trade.calc_profit_ratio(current_rate):.2%}' + if len(trade.select_filled_orders(trade.entry_side)) > 0: + trade_profit = trade.calc_profit(current_rate) + profit_str = f'{trade.calc_profit_ratio(current_rate):.2%}' + else: + trade_profit = 0.0 + profit_str = f'{0.0:.2f}' direction_str = ('S' if trade.is_short else 'L') if nonspot else '' if self._fiat_converter: fiat_profit = self._fiat_converter.convert_amount( @@ -244,7 +252,7 @@ class RPC: stake_currency, fiat_display_currency ) - if fiat_profit and not isnan(fiat_profit): + if not isnan(fiat_profit): profit_str += f" ({fiat_profit:.2f})" fiat_profit_sum = fiat_profit if isnan(fiat_profit_sum) \ else fiat_profit_sum + fiat_profit @@ -276,33 +284,57 @@ class RPC: columns.append('# Entries') return trades_list, columns, fiat_profit_sum - def _rpc_daily_profit( + def _rpc_timeunit_profit( self, timescale: int, - stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]: - today = datetime.now(timezone.utc).date() - profit_days: Dict[date, Dict] = {} + stake_currency: str, fiat_display_currency: str, + timeunit: str = 'days') -> Dict[str, Any]: + """ + :param timeunit: Valid entries are 'days', 'weeks', 'months' + """ + start_date = datetime.now(timezone.utc).date() + if timeunit == 'weeks': + # weekly + start_date = start_date - timedelta(days=start_date.weekday()) # Monday + if timeunit == 'months': + start_date = start_date.replace(day=1) + + def time_offset(step: int): + if timeunit == 'months': + return relativedelta(months=step) + return timedelta(**{timeunit: step}) if not (isinstance(timescale, int) and timescale > 0): raise RPCException('timescale must be an integer greater than 0') + profit_units: Dict[date, Dict] = {} + daily_stake = self._freqtrade.wallets.get_total_stake_amount() + for day in range(0, timescale): - profitday = today - timedelta(days=day) - trades = Trade.get_trades(trade_filter=[ + profitday = start_date - time_offset(day) + # Only query for necessary columns for performance reasons. + trades = Trade.query.session.query(Trade.close_profit_abs).filter( Trade.is_open.is_(False), Trade.close_date >= profitday, - Trade.close_date < (profitday + timedelta(days=1)) - ]).order_by(Trade.close_date).all() + Trade.close_date < (profitday + time_offset(1)) + ).order_by(Trade.close_date).all() + curdayprofit = sum( trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None) - profit_days[profitday] = { + # Calculate this periods starting balance + daily_stake = daily_stake - curdayprofit + profit_units[profitday] = { 'amount': curdayprofit, - 'trades': len(trades) + 'daily_stake': daily_stake, + 'rel_profit': round(curdayprofit / daily_stake, 8) if daily_stake > 0 else 0, + 'trades': len(trades), } data = [ { - 'date': key, + 'date': f"{key.year}-{key.month:02d}" if timeunit == 'months' else key, 'abs_profit': value["amount"], + 'starting_balance': value["daily_stake"], + 'rel_profit': value["rel_profit"], 'fiat_value': self._fiat_converter.convert_amount( value['amount'], stake_currency, @@ -310,92 +342,7 @@ class RPC: ) if self._fiat_converter else 0, 'trade_count': value["trades"], } - for key, value in profit_days.items() - ] - return { - 'stake_currency': stake_currency, - 'fiat_display_currency': fiat_display_currency, - 'data': data - } - - def _rpc_weekly_profit( - self, timescale: int, - stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]: - today = datetime.now(timezone.utc).date() - first_iso_day_of_week = today - timedelta(days=today.weekday()) # Monday - profit_weeks: Dict[date, Dict] = {} - - if not (isinstance(timescale, int) and timescale > 0): - raise RPCException('timescale must be an integer greater than 0') - - for week in range(0, timescale): - profitweek = first_iso_day_of_week - timedelta(weeks=week) - trades = Trade.get_trades(trade_filter=[ - Trade.is_open.is_(False), - Trade.close_date >= profitweek, - Trade.close_date < (profitweek + timedelta(weeks=1)) - ]).order_by(Trade.close_date).all() - curweekprofit = sum( - trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None) - profit_weeks[profitweek] = { - 'amount': curweekprofit, - 'trades': len(trades) - } - - data = [ - { - 'date': key, - 'abs_profit': value["amount"], - 'fiat_value': self._fiat_converter.convert_amount( - value['amount'], - stake_currency, - fiat_display_currency - ) if self._fiat_converter else 0, - 'trade_count': value["trades"], - } - for key, value in profit_weeks.items() - ] - return { - 'stake_currency': stake_currency, - 'fiat_display_currency': fiat_display_currency, - 'data': data - } - - def _rpc_monthly_profit( - self, timescale: int, - stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]: - first_day_of_month = datetime.now(timezone.utc).date().replace(day=1) - profit_months: Dict[date, Dict] = {} - - if not (isinstance(timescale, int) and timescale > 0): - raise RPCException('timescale must be an integer greater than 0') - - for month in range(0, timescale): - profitmonth = first_day_of_month - relativedelta(months=month) - trades = Trade.get_trades(trade_filter=[ - Trade.is_open.is_(False), - Trade.close_date >= profitmonth, - Trade.close_date < (profitmonth + relativedelta(months=1)) - ]).order_by(Trade.close_date).all() - curmonthprofit = sum( - trade.close_profit_abs for trade in trades if trade.close_profit_abs is not None) - profit_months[profitmonth] = { - 'amount': curmonthprofit, - 'trades': len(trades) - } - - data = [ - { - 'date': f"{key.year}-{key.month:02d}", - 'abs_profit': value["amount"], - 'fiat_value': self._fiat_converter.convert_amount( - value['amount'], - stake_currency, - fiat_display_currency - ) if self._fiat_converter else 0, - 'trade_count': value["trades"], - } - for key, value in profit_months.items() + for key, value in profit_units.items() ] return { 'stake_currency': stake_currency, @@ -418,6 +365,7 @@ class RPC: return { "trades": output, "trades_count": len(output), + "offset": offset, "total_trades": Trade.get_trades([Trade.is_open.is_(False)]).count(), } @@ -432,7 +380,7 @@ class RPC: return 'losses' else: return 'draws' - trades: List[Trade] = Trade.get_trades([Trade.is_open.is_(False)]) + trades: List[Trade] = Trade.get_trades([Trade.is_open.is_(False)], include_orders=False) # Sell reason exit_reasons = {} for trade in trades: @@ -460,7 +408,8 @@ class RPC: """ Returns cumulative profit statistics """ trade_filter = ((Trade.is_open.is_(False) & (Trade.close_date >= start_date)) | Trade.is_open.is_(True)) - trades: List[Trade] = Trade.get_trades(trade_filter).order_by(Trade.id).all() + trades: List[Trade] = Trade.get_trades( + trade_filter, include_orders=False).order_by(Trade.id).all() profit_all_coin = [] profit_all_ratio = [] @@ -469,6 +418,8 @@ class RPC: durations = [] winning_trades = 0 losing_trades = 0 + winning_profit = 0.0 + losing_profit = 0.0 for trade in trades: current_rate: float = 0.0 @@ -484,8 +435,10 @@ class RPC: profit_closed_ratio.append(profit_ratio) if trade.close_profit >= 0: winning_trades += 1 + winning_profit += trade.close_profit_abs else: losing_trades += 1 + losing_profit += trade.close_profit_abs else: # Get current rate try: @@ -501,6 +454,7 @@ class RPC: profit_all_ratio.append(profit_ratio) best_pair = Trade.get_best_pair(start_date) + trading_volume = Trade.get_trading_volume(start_date) # Prepare data to display profit_closed_coin_sum = round(sum(profit_closed_coin), 8) @@ -524,6 +478,21 @@ class RPC: profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance + profit_factor = winning_profit / abs(losing_profit) if losing_profit else float('inf') + + trades_df = DataFrame([{'close_date': trade.close_date.strftime(DATETIME_PRINT_FORMAT), + 'profit_abs': trade.close_profit_abs} + for trade in trades if not trade.is_open]) + max_drawdown_abs = 0.0 + max_drawdown = 0.0 + if len(trades_df) > 0: + try: + (max_drawdown_abs, _, _, _, _, max_drawdown) = calculate_max_drawdown( + trades_df, value_col='profit_abs', starting_balance=starting_balance) + except ValueError: + # ValueError if no losing trade. + pass + profit_all_fiat = self._fiat_converter.convert_amount( profit_all_coin_sum, stake_currency, @@ -562,11 +531,15 @@ class RPC: 'best_pair_profit_ratio': best_pair[1] if best_pair else 0, 'winning_trades': winning_trades, 'losing_trades': losing_trades, + 'profit_factor': profit_factor, + 'max_drawdown': max_drawdown, + 'max_drawdown_abs': max_drawdown_abs, + 'trading_volume': trading_volume, } def _rpc_balance(self, stake_currency: str, fiat_display_currency: str) -> Dict: """ Returns current account balance per crypto """ - currencies = [] + currencies: List[Dict] = [] total = 0.0 try: tickers = self._freqtrade.exchange.get_tickers(cached=True) @@ -593,7 +566,7 @@ class RPC: else: try: pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency) - rate = tickers.get(pair, {}).get('last', None) + rate = tickers.get(pair, {}).get('last') if rate: if pair.startswith(stake_currency) and not pair.endswith(stake_currency): rate = 1.0 / rate @@ -601,13 +574,12 @@ class RPC: except (ExchangeError): logger.warning(f" Could not get rate for pair {coin}.") continue - total = total + (est_stake or 0) + total = total + est_stake currencies.append({ 'currency': coin, - # TODO: The below can be simplified if we don't assign None to values. - 'free': balance.free if balance.free is not None else 0, - 'balance': balance.total if balance.total is not None else 0, - 'used': balance.used if balance.used is not None else 0, + 'free': balance.free, + 'balance': balance.total, + 'used': balance.used, 'est_stake': est_stake or 0, 'stake': stake_currency, 'side': 'long', @@ -637,7 +609,6 @@ class RPC: total, stake_currency, fiat_display_currency) if self._fiat_converter else 0 trade_count = len(Trade.get_trades_proxy()) - starting_capital_ratio = 0.0 starting_capital_ratio = (total / starting_capital) - 1 if starting_capital else 0.0 starting_cap_fiat_ratio = (value / starting_cap_fiat) - 1 if starting_cap_fiat else 0.0 @@ -925,7 +896,7 @@ class RPC: else: errors[pair] = { 'error_msg': f"Pair {pair} is not in the current blacklist." - } + } resp = self._rpc_blacklist() resp['errors'] = errors return resp diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index d97d1df5f..66e84029f 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -27,6 +27,12 @@ class RPCManager: from freqtrade.rpc.telegram import Telegram self.registered_modules.append(Telegram(self._rpc, config)) + # Enable discord + if config.get('discord', {}).get('enabled', False): + logger.info('Enabling rpc.discord ...') + from freqtrade.rpc.discord import Discord + self.registered_modules.append(Discord(self._rpc, config)) + # Enable Webhook if config.get('webhook', {}).get('enabled', False): logger.info('Enabling rpc.webhook ...') diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 1a9be4503..2aff1d210 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -6,6 +6,7 @@ This module manage Telegram communication import json import logging import re +from dataclasses import dataclass from datetime import date, datetime, timedelta from functools import partial from html import escape @@ -37,6 +38,15 @@ logger.debug('Included module rpc.telegram ...') MAX_TELEGRAM_MESSAGE_LENGTH = 4096 +@dataclass +class TimeunitMappings: + header: str + message: str + message2: str + callback: str + default: int + + def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]: """ Decorator to check if the message comes from the correct chat_id @@ -225,6 +235,30 @@ class Telegram(RPCHandler): # This can take up to `timeout` from the call to `start_polling`. self._updater.stop() + def _exchange_from_msg(self, msg: Dict[str, Any]) -> str: + """ + Extracts the exchange name from the given message. + :param msg: The message to extract the exchange name from. + :return: The exchange name. + """ + return f"{msg['exchange']}{' (dry)' if self._config['dry_run'] else ''}" + + def _add_analyzed_candle(self, pair: str) -> str: + candle_val = self._config['telegram'].get( + 'notification_settings', {}).get('show_candle', 'off') + if candle_val != 'off': + if candle_val == 'ohlc': + analyzed_df, _ = self._rpc._freqtrade.dataprovider.get_analyzed_dataframe( + pair, self._config['timeframe']) + candle = analyzed_df.iloc[-1].squeeze() if len(analyzed_df) > 0 else None + if candle is not None: + return ( + f"*Candle OHLC*: `{candle['open']}, {candle['high']}, " + f"{candle['low']}, {candle['close']}`\n" + ) + + return '' + def _format_entry_msg(self, msg: Dict[str, Any]) -> str: if self._rpc._fiat_converter: msg['stake_amount_fiat'] = self._rpc._fiat_converter.convert_amount( @@ -237,11 +271,12 @@ class Telegram(RPCHandler): entry_side = ({'enter': 'Long', 'entered': 'Longed'} if msg['direction'] == 'Long' else {'enter': 'Short', 'entered': 'Shorted'}) message = ( - f"{emoji} *{msg['exchange']}:*" + f"{emoji} *{self._exchange_from_msg(msg)}:*" f" {entry_side['entered'] if is_fill else entry_side['enter']} {msg['pair']}" f" (#{msg['trade_id']})\n" ) - message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag', None) else "" + message += self._add_analyzed_candle(msg['pair']) + message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag') else "" message += f"*Amount:* `{msg['amount']:.8f}`\n" if msg.get('leverage') and msg.get('leverage', 1.0) != 1.0: message += f"*Leverage:* `{msg['leverage']}`\n" @@ -254,7 +289,7 @@ class Telegram(RPCHandler): message += f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" - if msg.get('fiat_currency', None): + if msg.get('fiat_currency'): message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" message += ")`" @@ -270,7 +305,7 @@ class Telegram(RPCHandler): msg['enter_tag'] = msg['enter_tag'] if "enter_tag" in msg.keys() else None msg['emoji'] = self._get_sell_emoji(msg) msg['leverage_text'] = (f"*Leverage:* `{msg['leverage']:.1f}`\n" - if msg.get('leverage', None) and msg.get('leverage', 1.0) != 1.0 + if msg.get('leverage') and msg.get('leverage', 1.0) != 1.0 else "") # Check if all sell properties are available. @@ -286,8 +321,9 @@ class Telegram(RPCHandler): msg['profit_extra'] = '' is_fill = msg['type'] == RPCMessageType.EXIT_FILL message = ( - f"{msg['emoji']} *{msg['exchange']}:* " + f"{msg['emoji']} *{self._exchange_from_msg(msg)}:* " f"{'Exited' if is_fill else 'Exiting'} {msg['pair']} (#{msg['trade_id']})\n" + f"{self._add_analyzed_candle(msg['pair'])}" f"*{'Profit' if is_fill else 'Unrealized Profit'}:* " f"`{msg['profit_ratio']:.2%}{msg['profit_extra']}`\n" f"*Enter Tag:* `{msg['enter_tag']}`\n" @@ -316,33 +352,33 @@ class Telegram(RPCHandler): elif msg_type in (RPCMessageType.ENTRY_CANCEL, RPCMessageType.EXIT_CANCEL): msg['message_side'] = 'enter' if msg_type in [RPCMessageType.ENTRY_CANCEL] else 'exit' - message = ("\N{WARNING SIGN} *{exchange}:* " - "Cancelling {message_side} Order for {pair} (#{trade_id}). " - "Reason: {reason}.".format(**msg)) + message = (f"\N{WARNING SIGN} *{self._exchange_from_msg(msg)}:* " + f"Cancelling {msg['message_side']} Order for {msg['pair']} " + f"(#{msg['trade_id']}). Reason: {msg['reason']}.") elif msg_type == RPCMessageType.PROTECTION_TRIGGER: message = ( - "*Protection* triggered due to {reason}. " - "`{pair}` will be locked until `{lock_end_time}`." - ).format(**msg) + f"*Protection* triggered due to {msg['reason']}. " + f"`{msg['pair']}` will be locked until `{msg['lock_end_time']}`." + ) elif msg_type == RPCMessageType.PROTECTION_TRIGGER_GLOBAL: message = ( - "*Protection* triggered due to {reason}. " - "*All pairs* will be locked until `{lock_end_time}`." - ).format(**msg) + f"*Protection* triggered due to {msg['reason']}. " + f"*All pairs* will be locked until `{msg['lock_end_time']}`." + ) elif msg_type == RPCMessageType.STATUS: - message = '*Status:* `{status}`'.format(**msg) + message = f"*Status:* `{msg['status']}`" elif msg_type == RPCMessageType.WARNING: - message = '\N{WARNING SIGN} *Warning:* `{status}`'.format(**msg) + message = f"\N{WARNING SIGN} *Warning:* `{msg['status']}`" elif msg_type == RPCMessageType.STARTUP: - message = '{status}'.format(**msg) + message = f"{msg['status']}" else: - raise NotImplementedError('Unknown message type: {}'.format(msg_type)) + raise NotImplementedError(f"Unknown message type: {msg_type}") return message def send_msg(self, msg: Dict[str, Any]) -> None: @@ -396,7 +432,7 @@ class Telegram(RPCHandler): first_avg = filled_orders[0]["safe_price"] for x, order in enumerate(filled_orders): - if not order['ft_is_entry']: + if not order['ft_is_entry'] or order['is_open'] is True: continue cur_entry_datetime = arrow.get(order["order_filled_date"]) cur_entry_amount = order["amount"] @@ -563,6 +599,60 @@ class Telegram(RPCHandler): except RPCException as e: self._send_msg(str(e)) + @authorized_only + def _timeunit_stats(self, update: Update, context: CallbackContext, unit: str) -> None: + """ + Handler for /daily + Returns a daily profit (in BTC) over the last n days. + :param bot: telegram bot + :param update: message update + :return: None + """ + + vals = { + 'days': TimeunitMappings('Day', 'Daily', 'days', 'update_daily', 7), + 'weeks': TimeunitMappings('Monday', 'Weekly', 'weeks (starting from Monday)', + 'update_weekly', 8), + 'months': TimeunitMappings('Month', 'Monthly', 'months', 'update_monthly', 6), + } + val = vals[unit] + + stake_cur = self._config['stake_currency'] + fiat_disp_cur = self._config.get('fiat_display_currency', '') + try: + timescale = int(context.args[0]) if context.args else val.default + except (TypeError, ValueError, IndexError): + timescale = val.default + try: + stats = self._rpc._rpc_timeunit_profit( + timescale, + stake_cur, + fiat_disp_cur, + unit + ) + stats_tab = tabulate( + [[f"{period['date']} ({period['trade_count']})", + f"{round_coin_value(period['abs_profit'], stats['stake_currency'])}", + f"{period['fiat_value']:.2f} {stats['fiat_display_currency']}", + f"{period['rel_profit']:.2%}", + ] for period in stats['data']], + headers=[ + f"{val.header} (count)", + f'{stake_cur}', + f'{fiat_disp_cur}', + 'Profit %', + 'Trades', + ], + tablefmt='simple') + message = ( + f'{val.message} Profit over the last {timescale} {val.message2}:\n' + f'
{stats_tab}
' + ) + self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, + callback_path=val.callback, query=update.callback_query) + except RPCException as e: + self._send_msg(str(e)) + @authorized_only def _daily(self, update: Update, context: CallbackContext) -> None: """ @@ -572,35 +662,7 @@ class Telegram(RPCHandler): :param update: message update :return: None """ - stake_cur = self._config['stake_currency'] - fiat_disp_cur = self._config.get('fiat_display_currency', '') - try: - timescale = int(context.args[0]) if context.args else 7 - except (TypeError, ValueError, IndexError): - timescale = 7 - try: - stats = self._rpc._rpc_daily_profit( - timescale, - stake_cur, - fiat_disp_cur - ) - stats_tab = tabulate( - [[day['date'], - f"{round_coin_value(day['abs_profit'], stats['stake_currency'])}", - f"{day['fiat_value']:.3f} {stats['fiat_display_currency']}", - f"{day['trade_count']} trades"] for day in stats['data']], - headers=[ - 'Day', - f'Profit {stake_cur}', - f'Profit {fiat_disp_cur}', - 'Trades', - ], - tablefmt='simple') - message = f'Daily Profit over the last {timescale} days:\n
{stats_tab}
' - self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, - callback_path="update_daily", query=update.callback_query) - except RPCException as e: - self._send_msg(str(e)) + self._timeunit_stats(update, context, 'days') @authorized_only def _weekly(self, update: Update, context: CallbackContext) -> None: @@ -611,36 +673,7 @@ class Telegram(RPCHandler): :param update: message update :return: None """ - stake_cur = self._config['stake_currency'] - fiat_disp_cur = self._config.get('fiat_display_currency', '') - try: - timescale = int(context.args[0]) if context.args else 8 - except (TypeError, ValueError, IndexError): - timescale = 8 - try: - stats = self._rpc._rpc_weekly_profit( - timescale, - stake_cur, - fiat_disp_cur - ) - stats_tab = tabulate( - [[week['date'], - f"{round_coin_value(week['abs_profit'], stats['stake_currency'])}", - f"{week['fiat_value']:.3f} {stats['fiat_display_currency']}", - f"{week['trade_count']} trades"] for week in stats['data']], - headers=[ - 'Monday', - f'Profit {stake_cur}', - f'Profit {fiat_disp_cur}', - 'Trades', - ], - tablefmt='simple') - message = f'Weekly Profit over the last {timescale} weeks ' \ - f'(starting from Monday):\n
{stats_tab}
' - self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, - callback_path="update_weekly", query=update.callback_query) - except RPCException as e: - self._send_msg(str(e)) + self._timeunit_stats(update, context, 'weeks') @authorized_only def _monthly(self, update: Update, context: CallbackContext) -> None: @@ -651,36 +684,7 @@ class Telegram(RPCHandler): :param update: message update :return: None """ - stake_cur = self._config['stake_currency'] - fiat_disp_cur = self._config.get('fiat_display_currency', '') - try: - timescale = int(context.args[0]) if context.args else 6 - except (TypeError, ValueError, IndexError): - timescale = 6 - try: - stats = self._rpc._rpc_monthly_profit( - timescale, - stake_cur, - fiat_disp_cur - ) - stats_tab = tabulate( - [[month['date'], - f"{round_coin_value(month['abs_profit'], stats['stake_currency'])}", - f"{month['fiat_value']:.3f} {stats['fiat_display_currency']}", - f"{month['trade_count']} trades"] for month in stats['data']], - headers=[ - 'Month', - f'Profit {stake_cur}', - f'Profit {fiat_disp_cur}', - 'Trades', - ], - tablefmt='simple') - message = f'Monthly Profit over the last {timescale} months' \ - f':\n
{stats_tab}
' - self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True, - callback_path="update_monthly", query=update.callback_query) - except RPCException as e: - self._send_msg(str(e)) + self._timeunit_stats(update, context, 'months') @authorized_only def _profit(self, update: Update, context: CallbackContext) -> None: @@ -744,12 +748,18 @@ class Telegram(RPCHandler): f"*Total Trade Count:* `{trade_count}`\n" f"*{'First Trade opened' if not timescale else 'Showing Profit since'}:* " f"`{first_trade_date}`\n" - f"*Latest Trade opened:* `{latest_trade_date}\n`" + f"*Latest Trade opened:* `{latest_trade_date}`\n" f"*Win / Loss:* `{stats['winning_trades']} / {stats['losing_trades']}`" ) if stats['closed_trade_count'] > 0: - markdown_msg += (f"\n*Avg. Duration:* `{avg_duration}`\n" - f"*Best Performing:* `{best_pair}: {best_pair_profit_ratio:.2%}`") + markdown_msg += ( + f"\n*Avg. Duration:* `{avg_duration}`\n" + f"*Best Performing:* `{best_pair}: {best_pair_profit_ratio:.2%}`\n" + f"*Trading volume:* `{round_coin_value(stats['trading_volume'], stake_cur)}`\n" + f"*Profit factor:* `{stats['profit_factor']:.2f}`\n" + f"*Max Drawdown:* `{stats['max_drawdown']:.2%} " + f"({round_coin_value(stats['max_drawdown_abs'], stake_cur)})`" + ) self._send_msg(markdown_msg, reload_able=True, callback_path="update_profit", query=update.callback_query) @@ -785,7 +795,7 @@ class Telegram(RPCHandler): headers=['Exit Reason', 'Exits', 'Wins', 'Losses'] ) if len(exit_reasons_tabulate) > 25: - self._send_msg(exit_reasons_msg, ParseMode.MARKDOWN) + self._send_msg(f"```\n{exit_reasons_msg}```", ParseMode.MARKDOWN) exit_reasons_msg = '' durations = stats['durations'] @@ -889,7 +899,7 @@ class Telegram(RPCHandler): :return: None """ msg = self._rpc._rpc_start() - self._send_msg('Status: `{status}`'.format(**msg)) + self._send_msg(f"Status: `{msg['status']}`") @authorized_only def _stop(self, update: Update, context: CallbackContext) -> None: @@ -901,7 +911,7 @@ class Telegram(RPCHandler): :return: None """ msg = self._rpc._rpc_stop() - self._send_msg('Status: `{status}`'.format(**msg)) + self._send_msg(f"Status: `{msg['status']}`") @authorized_only def _reload_config(self, update: Update, context: CallbackContext) -> None: @@ -913,7 +923,7 @@ class Telegram(RPCHandler): :return: None """ msg = self._rpc._rpc_reload_config() - self._send_msg('Status: `{status}`'.format(**msg)) + self._send_msg(f"Status: `{msg['status']}`") @authorized_only def _stopbuy(self, update: Update, context: CallbackContext) -> None: @@ -925,7 +935,7 @@ class Telegram(RPCHandler): :return: None """ msg = self._rpc._rpc_stopbuy() - self._send_msg('Status: `{status}`'.format(**msg)) + self._send_msg(f"Status: `{msg['status']}`") @authorized_only def _force_exit(self, update: Update, context: CallbackContext) -> None: @@ -1087,9 +1097,9 @@ class Telegram(RPCHandler): trade_id = int(context.args[0]) msg = self._rpc._rpc_delete(trade_id) self._send_msg(( - '`{result_msg}`\n' + f"`{msg['result_msg']}`\n" 'Please make sure to take care of this asset on the exchange manually.' - ).format(**msg)) + )) except RPCException as e: self._send_msg(str(e)) @@ -1410,14 +1420,14 @@ class Telegram(RPCHandler): "Optionally takes a rate at which to sell " "(only applies to limit orders).` \n") message = ( - "_BotControl_\n" + "_Bot Control_\n" "------------\n" "*/start:* `Starts the trader`\n" "*/stop:* Stops the trader\n" "*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" "*/forceexit |all:* `Instantly exits the given trade or all trades, " "regardless of profit`\n" - "*/fe |all:* `Alias to /forceexit`" + "*/fx |all:* `Alias to /forceexit`\n" f"{force_enter_text if self._config.get('force_entry_enable', False) else ''}" "*/delete :* `Instantly delete the given trade in the database`\n" "*/whitelist:* `Show current whitelist` \n" diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py index a2edcbc85..1b39a29b7 100644 --- a/freqtrade/rpc/webhook.py +++ b/freqtrade/rpc/webhook.py @@ -45,21 +45,21 @@ class Webhook(RPCHandler): try: whconfig = self._config['webhook'] if msg['type'] in [RPCMessageType.ENTRY]: - valuedict = whconfig.get('webhookentry', None) + valuedict = whconfig.get('webhookentry') elif msg['type'] in [RPCMessageType.ENTRY_CANCEL]: - valuedict = whconfig.get('webhookentrycancel', None) + valuedict = whconfig.get('webhookentrycancel') elif msg['type'] in [RPCMessageType.ENTRY_FILL]: - valuedict = whconfig.get('webhookentryfill', None) + valuedict = whconfig.get('webhookentryfill') elif msg['type'] == RPCMessageType.EXIT: - valuedict = whconfig.get('webhookexit', None) + valuedict = whconfig.get('webhookexit') elif msg['type'] == RPCMessageType.EXIT_FILL: - valuedict = whconfig.get('webhookexitfill', None) + valuedict = whconfig.get('webhookexitfill') elif msg['type'] == RPCMessageType.EXIT_CANCEL: - valuedict = whconfig.get('webhookexitcancel', None) + valuedict = whconfig.get('webhookexitcancel') elif msg['type'] in (RPCMessageType.STATUS, RPCMessageType.STARTUP, RPCMessageType.WARNING): - valuedict = whconfig.get('webhookstatus', None) + valuedict = whconfig.get('webhookstatus') else: raise NotImplementedError('Unknown message type: {}'.format(msg['type'])) if not valuedict: diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index 2ea0ad2b4..2d23bcd4d 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -1,9 +1,9 @@ # flake8: noqa: F401 from freqtrade.exchange import (timeframe_to_minutes, timeframe_to_msecs, timeframe_to_next_date, timeframe_to_prev_date, timeframe_to_seconds) -from freqtrade.strategy.hyper import (BooleanParameter, CategoricalParameter, DecimalParameter, - IntParameter, RealParameter) from freqtrade.strategy.informative_decorator import informative from freqtrade.strategy.interface import IStrategy +from freqtrade.strategy.parameters import (BooleanParameter, CategoricalParameter, DecimalParameter, + IntParameter, RealParameter) from freqtrade.strategy.strategy_helper import (merge_informative_pair, stoploss_from_absolute, stoploss_from_open) diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index 278954bb2..47377f238 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -3,295 +3,18 @@ IHyperStrategy interface, hyperoptable Parameter class. This module defines a base class for auto-hyperoptable strategies. """ import logging -from abc import ABC, abstractmethod -from contextlib import suppress from pathlib import Path -from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union +from typing import Any, Dict, Iterator, List, Tuple, Type, Union +from freqtrade.exceptions import OperationalException from freqtrade.misc import deep_merge_dicts, json_load from freqtrade.optimize.hyperopt_tools import HyperoptTools - - -with suppress(ImportError): - from skopt.space import Integer, Real, Categorical - from freqtrade.optimize.space import SKDecimal - -from freqtrade.enums import RunMode -from freqtrade.exceptions import OperationalException +from freqtrade.strategy.parameters import BaseParameter logger = logging.getLogger(__name__) -class BaseParameter(ABC): - """ - Defines a parameter that can be optimized by hyperopt. - """ - category: Optional[str] - default: Any - value: Any - in_space: bool = False - name: str - - def __init__(self, *, default: Any, space: Optional[str] = None, - optimize: bool = True, load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable parameter. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter field - name is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.(Integer|Real|Categorical). - """ - if 'name' in kwargs: - raise OperationalException( - 'Name is determined by parameter field name and can not be specified manually.') - self.category = space - self._space_params = kwargs - self.value = default - self.optimize = optimize - self.load = load - - def __repr__(self): - return f'{self.__class__.__name__}({self.value})' - - @abstractmethod - def get_space(self, name: str) -> Union['Integer', 'Real', 'SKDecimal', 'Categorical']: - """ - Get-space - will be used by Hyperopt to get the hyperopt Space - """ - - -class NumericParameter(BaseParameter): - """ Internal parameter used for Numeric purposes """ - float_or_int = Union[int, float] - default: float_or_int - value: float_or_int - - def __init__(self, low: Union[float_or_int, Sequence[float_or_int]], - high: Optional[float_or_int] = None, *, default: float_or_int, - space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable numeric parameter. - Cannot be instantiated, but provides the validation for other numeric parameters - :param low: Lower end (inclusive) of optimization space or [low, high]. - :param high: Upper end (inclusive) of optimization space. - Must be none of entire range is passed first parameter. - :param default: A default value. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter fieldname is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.*. - """ - if high is not None and isinstance(low, Sequence): - raise OperationalException(f'{self.__class__.__name__} space invalid.') - if high is None or isinstance(low, Sequence): - if not isinstance(low, Sequence) or len(low) != 2: - raise OperationalException(f'{self.__class__.__name__} space must be [low, high]') - self.low, self.high = low - else: - self.low = low - self.high = high - - super().__init__(default=default, space=space, optimize=optimize, - load=load, **kwargs) - - -class IntParameter(NumericParameter): - default: int - value: int - - def __init__(self, low: Union[int, Sequence[int]], high: Optional[int] = None, *, default: int, - space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable integer parameter. - :param low: Lower end (inclusive) of optimization space or [low, high]. - :param high: Upper end (inclusive) of optimization space. - Must be none of entire range is passed first parameter. - :param default: A default value. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter fieldname is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.Integer. - """ - - super().__init__(low=low, high=high, default=default, space=space, optimize=optimize, - load=load, **kwargs) - - def get_space(self, name: str) -> 'Integer': - """ - Create skopt optimization space. - :param name: A name of parameter field. - """ - return Integer(low=self.low, high=self.high, name=name, **self._space_params) - - @property - def range(self): - """ - Get each value in this space as list. - Returns a List from low to high (inclusive) in Hyperopt mode. - Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid - calculating 100ds of indicators. - """ - if self.in_space and self.optimize: - # Scikit-optimize ranges are "inclusive", while python's "range" is exclusive - return range(self.low, self.high + 1) - else: - return range(self.value, self.value + 1) - - -class RealParameter(NumericParameter): - default: float - value: float - - def __init__(self, low: Union[float, Sequence[float]], high: Optional[float] = None, *, - default: float, space: Optional[str] = None, optimize: bool = True, - load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable floating point parameter with unlimited precision. - :param low: Lower end (inclusive) of optimization space or [low, high]. - :param high: Upper end (inclusive) of optimization space. - Must be none if entire range is passed first parameter. - :param default: A default value. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter fieldname is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.Real. - """ - super().__init__(low=low, high=high, default=default, space=space, optimize=optimize, - load=load, **kwargs) - - def get_space(self, name: str) -> 'Real': - """ - Create skopt optimization space. - :param name: A name of parameter field. - """ - return Real(low=self.low, high=self.high, name=name, **self._space_params) - - -class DecimalParameter(NumericParameter): - default: float - value: float - - def __init__(self, low: Union[float, Sequence[float]], high: Optional[float] = None, *, - default: float, decimals: int = 3, space: Optional[str] = None, - optimize: bool = True, load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable decimal parameter with a limited precision. - :param low: Lower end (inclusive) of optimization space or [low, high]. - :param high: Upper end (inclusive) of optimization space. - Must be none if entire range is passed first parameter. - :param default: A default value. - :param decimals: A number of decimals after floating point to be included in testing. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter fieldname is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.Integer. - """ - self._decimals = decimals - default = round(default, self._decimals) - - super().__init__(low=low, high=high, default=default, space=space, optimize=optimize, - load=load, **kwargs) - - def get_space(self, name: str) -> 'SKDecimal': - """ - Create skopt optimization space. - :param name: A name of parameter field. - """ - return SKDecimal(low=self.low, high=self.high, decimals=self._decimals, name=name, - **self._space_params) - - @property - def range(self): - """ - Get each value in this space as list. - Returns a List from low to high (inclusive) in Hyperopt mode. - Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid - calculating 100ds of indicators. - """ - if self.in_space and self.optimize: - low = int(self.low * pow(10, self._decimals)) - high = int(self.high * pow(10, self._decimals)) + 1 - return [round(n * pow(0.1, self._decimals), self._decimals) for n in range(low, high)] - else: - return [self.value] - - -class CategoricalParameter(BaseParameter): - default: Any - value: Any - opt_range: Sequence[Any] - - def __init__(self, categories: Sequence[Any], *, default: Optional[Any] = None, - space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable parameter. - :param categories: Optimization space, [a, b, ...]. - :param default: A default value. If not specified, first item from specified space will be - used. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter field - name is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.Categorical. - """ - if len(categories) < 2: - raise OperationalException( - 'CategoricalParameter space must be [a, b, ...] (at least two parameters)') - self.opt_range = categories - super().__init__(default=default, space=space, optimize=optimize, - load=load, **kwargs) - - def get_space(self, name: str) -> 'Categorical': - """ - Create skopt optimization space. - :param name: A name of parameter field. - """ - return Categorical(self.opt_range, name=name, **self._space_params) - - @property - def range(self): - """ - Get each value in this space as list. - Returns a List of categories in Hyperopt mode. - Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid - calculating 100ds of indicators. - """ - if self.in_space and self.optimize: - return self.opt_range - else: - return [self.value] - - -class BooleanParameter(CategoricalParameter): - - def __init__(self, *, default: Optional[Any] = None, - space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): - """ - Initialize hyperopt-optimizable Boolean Parameter. - It's a shortcut to `CategoricalParameter([True, False])`. - :param default: A default value. If not specified, first item from specified space will be - used. - :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if - parameter field - name is prefixed with 'buy_' or 'sell_'. - :param optimize: Include parameter in hyperopt optimizations. - :param load: Load parameter value from {space}_params. - :param kwargs: Extra parameters to skopt.space.Categorical. - """ - - categories = [True, False] - super().__init__(categories=categories, default=default, space=space, optimize=optimize, - load=load, **kwargs) - - class HyperStrategyMixin: """ A helper base class which allows HyperOptAuto class to reuse implementations of buy/sell @@ -307,7 +30,10 @@ class HyperStrategyMixin: self.ft_sell_params: List[BaseParameter] = [] self.ft_protection_params: List[BaseParameter] = [] - self._load_hyper_params(config.get('runmode') == RunMode.HYPEROPT) + params = self.load_params_from_file() + params = params.get('params', {}) + self._ft_params_from_file = params + # Init/loading of parameters is done as part of ft_bot_start(). def enumerate_parameters(self, category: str = None) -> Iterator[Tuple[str, BaseParameter]]: """ @@ -327,28 +53,13 @@ class HyperStrategyMixin: for par in params: yield par.name, par - @classmethod - def detect_parameters(cls, category: str) -> Iterator[Tuple[str, BaseParameter]]: - """ Detect all parameters for 'category' """ - for attr_name in dir(cls): - if not attr_name.startswith('__'): # Ignore internals, not strictly necessary. - attr = getattr(cls, attr_name) - if issubclass(attr.__class__, BaseParameter): - if (attr_name.startswith(category + '_') - and attr.category is not None and attr.category != category): - raise OperationalException( - f'Inconclusive parameter name {attr_name}, category: {attr.category}.') - if (category == attr.category or - (attr_name.startswith(category + '_') and attr.category is None)): - yield attr_name, attr - @classmethod def detect_all_parameters(cls) -> Dict: """ Detect all parameters and return them as a list""" - params: Dict = { - 'buy': list(cls.detect_parameters('buy')), - 'sell': list(cls.detect_parameters('sell')), - 'protection': list(cls.detect_parameters('protection')), + params: Dict[str, Any] = { + 'buy': list(detect_parameters(cls, 'buy')), + 'sell': list(detect_parameters(cls, 'sell')), + 'protection': list(detect_parameters(cls, 'protection')), } params.update({ 'count': len(params['buy'] + params['sell'] + params['protection']) @@ -356,21 +67,49 @@ class HyperStrategyMixin: return params - def _load_hyper_params(self, hyperopt: bool = False) -> None: + def ft_load_params_from_file(self) -> None: + """ + Load Parameters from parameter file + Should/must run before config values are loaded in strategy_resolver. + """ + if self._ft_params_from_file: + # Set parameters from Hyperopt results file + params = self._ft_params_from_file + self.minimal_roi = params.get('roi', getattr(self, 'minimal_roi', {})) + + self.stoploss = params.get('stoploss', {}).get( + 'stoploss', getattr(self, 'stoploss', -0.1)) + trailing = params.get('trailing', {}) + self.trailing_stop = trailing.get( + 'trailing_stop', getattr(self, 'trailing_stop', False)) + self.trailing_stop_positive = trailing.get( + 'trailing_stop_positive', getattr(self, 'trailing_stop_positive', None)) + self.trailing_stop_positive_offset = trailing.get( + 'trailing_stop_positive_offset', + getattr(self, 'trailing_stop_positive_offset', 0)) + self.trailing_only_offset_is_reached = trailing.get( + 'trailing_only_offset_is_reached', + getattr(self, 'trailing_only_offset_is_reached', 0.0)) + + def ft_load_hyper_params(self, hyperopt: bool = False) -> None: """ Load Hyperoptable parameters + Prevalence: + * Parameters from parameter file + * Parameters defined in parameters objects (buy_params, sell_params, ...) + * Parameter defaults """ - params = self.load_params_from_file() - params = params.get('params', {}) - self._ft_params_from_file = params - buy_params = deep_merge_dicts(params.get('buy', {}), getattr(self, 'buy_params', {})) - sell_params = deep_merge_dicts(params.get('sell', {}), getattr(self, 'sell_params', {})) - protection_params = deep_merge_dicts(params.get('protection', {}), + + buy_params = deep_merge_dicts(self._ft_params_from_file.get('buy', {}), + getattr(self, 'buy_params', {})) + sell_params = deep_merge_dicts(self._ft_params_from_file.get('sell', {}), + getattr(self, 'sell_params', {})) + protection_params = deep_merge_dicts(self._ft_params_from_file.get('protection', {}), getattr(self, 'protection_params', {})) - self._load_params(buy_params, 'buy', hyperopt) - self._load_params(sell_params, 'sell', hyperopt) - self._load_params(protection_params, 'protection', hyperopt) + self._ft_load_params(buy_params, 'buy', hyperopt) + self._ft_load_params(sell_params, 'sell', hyperopt) + self._ft_load_params(protection_params, 'protection', hyperopt) def load_params_from_file(self) -> Dict: filename_str = getattr(self, '__file__', '') @@ -393,7 +132,7 @@ class HyperStrategyMixin: return {} - def _load_params(self, params: Dict, space: str, hyperopt: bool = False) -> None: + def _ft_load_params(self, params: Dict, space: str, hyperopt: bool = False) -> None: """ Set optimizable parameter values. :param params: Dictionary with new parameter values. @@ -402,7 +141,7 @@ class HyperStrategyMixin: logger.info(f"No params for {space} found, using default values.") param_container: List[BaseParameter] = getattr(self, f"ft_{space}_params") - for attr_name, attr in self.detect_parameters(space): + for attr_name, attr in detect_parameters(self, space): attr.name = attr_name attr.in_space = hyperopt and HyperoptTools.has_space(self.config, space) if not attr.category: @@ -424,7 +163,7 @@ class HyperStrategyMixin: """ Returns list of Parameters that are not part of the current optimize job """ - params = { + params: Dict[str, Dict] = { 'buy': {}, 'sell': {}, 'protection': {}, @@ -433,3 +172,26 @@ class HyperStrategyMixin: if not p.optimize or not p.in_space: params[p.category][name] = p.value return params + + +def detect_parameters( + obj: Union[HyperStrategyMixin, Type[HyperStrategyMixin]], + category: str + ) -> Iterator[Tuple[str, BaseParameter]]: + """ + Detect all parameters for 'category' for "obj" + :param obj: Strategy object or class + :param category: category - usually `'buy', 'sell', 'protection',... + """ + for attr_name in dir(obj): + if not attr_name.startswith('__'): # Ignore internals, not strictly necessary. + attr = getattr(obj, attr_name) + if issubclass(attr.__class__, BaseParameter): + if (attr_name.startswith(category + '_') + and attr.category is not None and attr.category != category): + raise OperationalException( + f'Inconclusive parameter name {attr_name}, category: {attr.category}.') + + if (category == attr.category or + (attr_name.startswith(category + '_') and attr.category is None)): + yield attr_name, attr diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 51ed1ba48..c60817c99 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -14,11 +14,10 @@ from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, SignalTagType, SignalType, TradingMode) +from freqtrade.enums.runmode import RunMode from freqtrade.exceptions import OperationalException, StrategyError -from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds -from freqtrade.exchange.exchange import timeframe_to_next_date -from freqtrade.persistence import PairLocks, Trade -from freqtrade.persistence.models import LocalTrade, Order +from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds +from freqtrade.persistence import Order, PairLocks, Trade from freqtrade.strategy.hyper import HyperStrategyMixin from freqtrade.strategy.informative_decorator import (InformativeData, PopulateIndicators, _create_and_merge_informative_pair, @@ -84,7 +83,7 @@ class IStrategy(ABC, HyperStrategyMixin): } # run "populate_indicators" only for new candle - process_only_new_candles: bool = False + process_only_new_candles: bool = True use_exit_signal: bool exit_profit_only: bool @@ -146,6 +145,15 @@ class IStrategy(ABC, HyperStrategyMixin): informative_data.candle_type = config['candle_type_def'] self._ft_informative.append((informative_data, cls_method)) + def ft_bot_start(self, **kwargs) -> None: + """ + Strategy init - runs after dataprovider has been added. + Must call bot_start() + """ + strategy_safe_wrapper(self.bot_start)() + + self.ft_load_hyper_params(self.config.get('runmode') == RunMode.HYPEROPT) + @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ @@ -279,8 +287,9 @@ class IStrategy(ABC, HyperStrategyMixin): :param pair: Pair that's about to be bought/shorted. :param order_type: Order type (as configured in order_types). usually limit or market. - :param amount: Amount in target (quote) currency that's going to be traded. + :param amount: Amount in target (base) currency that's going to be traded. :param rate: Rate that's going to be used when using limit orders + or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param current_time: datetime object, containing the current datetime :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. @@ -306,8 +315,9 @@ class IStrategy(ABC, HyperStrategyMixin): :param pair: Pair for trade that's about to be exited. :param trade: trade object. :param order_type: Order type (as configured in order_types). usually limit or market. - :param amount: Amount in quote currency. + :param amount: Amount in base currency. :param rate: Rate that's going to be used when using limit orders + or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param exit_reason: Exit reason. Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', @@ -431,8 +441,9 @@ class IStrategy(ABC, HyperStrategyMixin): return self.custom_sell(pair, trade, current_time, current_rate, current_profit, **kwargs) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, - entry_tag: Optional[str], side: str, **kwargs) -> float: + proposed_stake: float, min_stake: Optional[float], max_stake: float, + leverage: float, entry_tag: Optional[str], side: str, + **kwargs) -> float: """ Customize stake size for each new trade. @@ -442,6 +453,7 @@ class IStrategy(ABC, HyperStrategyMixin): :param proposed_stake: A stake amount proposed by the bot. :param min_stake: Minimal stake size allowed by exchange. :param max_stake: Balance available for trading. + :param leverage: Leverage selected for this trade. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :return: A stake size, which is between min_stake and max_stake. @@ -449,8 +461,9 @@ class IStrategy(ABC, HyperStrategyMixin): return proposed_stake def adjust_trade_position(self, trade: Trade, current_time: datetime, - current_rate: float, current_profit: float, min_stake: float, - max_stake: float, **kwargs) -> Optional[float]: + current_rate: float, current_profit: float, + min_stake: Optional[float], max_stake: float, + **kwargs) -> Optional[float]: """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. This means extra buy orders with additional fees. @@ -471,9 +484,37 @@ class IStrategy(ABC, HyperStrategyMixin): """ return None + def adjust_entry_price(self, trade: Trade, order: Optional[Order], pair: str, + current_time: datetime, proposed_rate: float, current_order_rate: float, + entry_tag: Optional[str], side: str, **kwargs) -> float: + """ + Entry price re-adjustment logic, returning the user desired limit price. + This only executes when a order was already placed, still open (unfilled fully or partially) + and not timed out on subsequent candles after entry trigger. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/ + + When not implemented by a strategy, returns current_order_rate as default. + If current_order_rate is returned then the existing order is maintained. + If None is returned then order gets canceled but not replaced by a new one. + + :param pair: Pair that's currently analyzed + :param trade: Trade object. + :param order: Order object + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. + :param current_order_rate: Rate of the existing order in place. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. + :param side: 'long' or 'short' - indicating the direction of the proposed trade + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New entry price value if provided + + """ + return current_order_rate + def leverage(self, pair: str, current_time: datetime, current_rate: float, - proposed_leverage: float, max_leverage: float, side: str, - **kwargs) -> float: + proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], + side: str, **kwargs) -> float: """ Customize leverage for each new trade. This method is only called in futures mode. @@ -482,6 +523,7 @@ class IStrategy(ABC, HyperStrategyMixin): :param current_rate: Rate, calculated based on pricing settings in exit_pricing. :param proposed_leverage: A leverage proposed by the bot. :param max_leverage: Max leverage allowed on this pair + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :return: A leverage amount, which is between 1.0 and max_leverage. """ @@ -852,16 +894,16 @@ class IStrategy(ABC, HyperStrategyMixin): def should_exit(self, trade: Trade, rate: float, current_time: datetime, *, enter: bool, exit_: bool, low: float = None, high: float = None, - force_stoploss: float = 0) -> ExitCheckTuple: + force_stoploss: float = 0) -> List[ExitCheckTuple]: """ This function evaluates if one of the conditions required to trigger an exit order has been reached, which can either be a stop-loss, ROI or exit-signal. :param low: Only used during backtesting to simulate (long)stoploss/(short)ROI :param high: Only used during backtesting, to simulate (short)stoploss/(long)ROI :param force_stoploss: Externally provided stoploss - :return: True if trade should be exited, False otherwise + :return: List of exit reasons - or empty list. """ - + exits: List[ExitCheckTuple] = [] current_rate = rate current_profit = trade.calc_profit_ratio(current_rate) @@ -891,19 +933,20 @@ class IStrategy(ABC, HyperStrategyMixin): if exit_ and not enter: exit_signal = ExitType.EXIT_SIGNAL else: - custom_reason = strategy_safe_wrapper(self.custom_exit, default_retval=False)( + reason_cust = strategy_safe_wrapper(self.custom_exit, default_retval=False)( pair=trade.pair, trade=trade, current_time=current_time, current_rate=current_rate, current_profit=current_profit) - if custom_reason: + if reason_cust: exit_signal = ExitType.CUSTOM_EXIT - if isinstance(custom_reason, str): - if len(custom_reason) > CUSTOM_EXIT_MAX_LENGTH: + if isinstance(reason_cust, str): + custom_reason = reason_cust + if len(reason_cust) > CUSTOM_EXIT_MAX_LENGTH: logger.warning(f'Custom exit reason returned from ' f'custom_exit is too long and was trimmed' f'to {CUSTOM_EXIT_MAX_LENGTH} characters.') - custom_reason = custom_reason[:CUSTOM_EXIT_MAX_LENGTH] + custom_reason = reason_cust[:CUSTOM_EXIT_MAX_LENGTH] else: - custom_reason = None + custom_reason = '' if ( exit_signal == ExitType.CUSTOM_EXIT or (exit_signal == ExitType.EXIT_SIGNAL @@ -912,24 +955,29 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug(f"{trade.pair} - Sell signal received. " f"exit_type=ExitType.{exit_signal.name}" + (f", custom_reason={custom_reason}" if custom_reason else "")) - return ExitCheckTuple(exit_type=exit_signal, exit_reason=custom_reason) + exits.append(ExitCheckTuple(exit_type=exit_signal, exit_reason=custom_reason)) # Sequence: # Exit-signal - # ROI (if not stoploss) # Stoploss - if roi_reached and stoplossflag.exit_type != ExitType.STOP_LOSS: - logger.debug(f"{trade.pair} - Required profit reached. exit_type=ExitType.ROI") - return ExitCheckTuple(exit_type=ExitType.ROI) + # ROI + # Trailing stoploss - if stoplossflag.exit_flag: + if stoplossflag.exit_type == ExitType.STOP_LOSS: logger.debug(f"{trade.pair} - Stoploss hit. exit_type={stoplossflag.exit_type}") - return stoplossflag + exits.append(stoplossflag) - # This one is noisy, commented out... - # logger.debug(f"{trade.pair} - No exit signal.") - return ExitCheckTuple(exit_type=ExitType.NONE) + if roi_reached: + logger.debug(f"{trade.pair} - Required profit reached. exit_type=ExitType.ROI") + exits.append(ExitCheckTuple(exit_type=ExitType.ROI)) + + if stoplossflag.exit_type == ExitType.TRAILING_STOP_LOSS: + + logger.debug(f"{trade.pair} - Trailing stoploss hit.") + exits.append(stoplossflag) + + return exits def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime, current_profit: float, @@ -1044,7 +1092,7 @@ class IStrategy(ABC, HyperStrategyMixin): else: return current_profit > roi - def ft_check_timed_out(self, trade: LocalTrade, order: Order, + def ft_check_timed_out(self, trade: Trade, order: Order, current_time: datetime) -> bool: """ FT Internal method. diff --git a/freqtrade/strategy/parameters.py b/freqtrade/strategy/parameters.py new file mode 100644 index 000000000..83dd41de9 --- /dev/null +++ b/freqtrade/strategy/parameters.py @@ -0,0 +1,289 @@ +""" +IHyperStrategy interface, hyperoptable Parameter class. +This module defines a base class for auto-hyperoptable strategies. +""" +import logging +from abc import ABC, abstractmethod +from contextlib import suppress +from typing import Any, Optional, Sequence, Union + + +with suppress(ImportError): + from skopt.space import Integer, Real, Categorical + from freqtrade.optimize.space import SKDecimal + +from freqtrade.exceptions import OperationalException + + +logger = logging.getLogger(__name__) + + +class BaseParameter(ABC): + """ + Defines a parameter that can be optimized by hyperopt. + """ + category: Optional[str] + default: Any + value: Any + in_space: bool = False + name: str + + def __init__(self, *, default: Any, space: Optional[str] = None, + optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable parameter. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter field + name is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.(Integer|Real|Categorical). + """ + if 'name' in kwargs: + raise OperationalException( + 'Name is determined by parameter field name and can not be specified manually.') + self.category = space + self._space_params = kwargs + self.value = default + self.optimize = optimize + self.load = load + + def __repr__(self): + return f'{self.__class__.__name__}({self.value})' + + @abstractmethod + def get_space(self, name: str) -> Union['Integer', 'Real', 'SKDecimal', 'Categorical']: + """ + Get-space - will be used by Hyperopt to get the hyperopt Space + """ + + +class NumericParameter(BaseParameter): + """ Internal parameter used for Numeric purposes """ + float_or_int = Union[int, float] + default: float_or_int + value: float_or_int + + def __init__(self, low: Union[float_or_int, Sequence[float_or_int]], + high: Optional[float_or_int] = None, *, default: float_or_int, + space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable numeric parameter. + Cannot be instantiated, but provides the validation for other numeric parameters + :param low: Lower end (inclusive) of optimization space or [low, high]. + :param high: Upper end (inclusive) of optimization space. + Must be none of entire range is passed first parameter. + :param default: A default value. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter fieldname is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.*. + """ + if high is not None and isinstance(low, Sequence): + raise OperationalException(f'{self.__class__.__name__} space invalid.') + if high is None or isinstance(low, Sequence): + if not isinstance(low, Sequence) or len(low) != 2: + raise OperationalException(f'{self.__class__.__name__} space must be [low, high]') + self.low, self.high = low + else: + self.low = low + self.high = high + + super().__init__(default=default, space=space, optimize=optimize, + load=load, **kwargs) + + +class IntParameter(NumericParameter): + default: int + value: int + low: int + high: int + + def __init__(self, low: Union[int, Sequence[int]], high: Optional[int] = None, *, default: int, + space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable integer parameter. + :param low: Lower end (inclusive) of optimization space or [low, high]. + :param high: Upper end (inclusive) of optimization space. + Must be none of entire range is passed first parameter. + :param default: A default value. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter fieldname is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.Integer. + """ + + super().__init__(low=low, high=high, default=default, space=space, optimize=optimize, + load=load, **kwargs) + + def get_space(self, name: str) -> 'Integer': + """ + Create skopt optimization space. + :param name: A name of parameter field. + """ + return Integer(low=self.low, high=self.high, name=name, **self._space_params) + + @property + def range(self): + """ + Get each value in this space as list. + Returns a List from low to high (inclusive) in Hyperopt mode. + Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid + calculating 100ds of indicators. + """ + if self.in_space and self.optimize: + # Scikit-optimize ranges are "inclusive", while python's "range" is exclusive + return range(self.low, self.high + 1) + else: + return range(self.value, self.value + 1) + + +class RealParameter(NumericParameter): + default: float + value: float + + def __init__(self, low: Union[float, Sequence[float]], high: Optional[float] = None, *, + default: float, space: Optional[str] = None, optimize: bool = True, + load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable floating point parameter with unlimited precision. + :param low: Lower end (inclusive) of optimization space or [low, high]. + :param high: Upper end (inclusive) of optimization space. + Must be none if entire range is passed first parameter. + :param default: A default value. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter fieldname is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.Real. + """ + super().__init__(low=low, high=high, default=default, space=space, optimize=optimize, + load=load, **kwargs) + + def get_space(self, name: str) -> 'Real': + """ + Create skopt optimization space. + :param name: A name of parameter field. + """ + return Real(low=self.low, high=self.high, name=name, **self._space_params) + + +class DecimalParameter(NumericParameter): + default: float + value: float + + def __init__(self, low: Union[float, Sequence[float]], high: Optional[float] = None, *, + default: float, decimals: int = 3, space: Optional[str] = None, + optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable decimal parameter with a limited precision. + :param low: Lower end (inclusive) of optimization space or [low, high]. + :param high: Upper end (inclusive) of optimization space. + Must be none if entire range is passed first parameter. + :param default: A default value. + :param decimals: A number of decimals after floating point to be included in testing. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter fieldname is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.Integer. + """ + self._decimals = decimals + default = round(default, self._decimals) + + super().__init__(low=low, high=high, default=default, space=space, optimize=optimize, + load=load, **kwargs) + + def get_space(self, name: str) -> 'SKDecimal': + """ + Create skopt optimization space. + :param name: A name of parameter field. + """ + return SKDecimal(low=self.low, high=self.high, decimals=self._decimals, name=name, + **self._space_params) + + @property + def range(self): + """ + Get each value in this space as list. + Returns a List from low to high (inclusive) in Hyperopt mode. + Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid + calculating 100ds of indicators. + """ + if self.in_space and self.optimize: + low = int(self.low * pow(10, self._decimals)) + high = int(self.high * pow(10, self._decimals)) + 1 + return [round(n * pow(0.1, self._decimals), self._decimals) for n in range(low, high)] + else: + return [self.value] + + +class CategoricalParameter(BaseParameter): + default: Any + value: Any + opt_range: Sequence[Any] + + def __init__(self, categories: Sequence[Any], *, default: Optional[Any] = None, + space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable parameter. + :param categories: Optimization space, [a, b, ...]. + :param default: A default value. If not specified, first item from specified space will be + used. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter field + name is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.Categorical. + """ + if len(categories) < 2: + raise OperationalException( + 'CategoricalParameter space must be [a, b, ...] (at least two parameters)') + self.opt_range = categories + super().__init__(default=default, space=space, optimize=optimize, + load=load, **kwargs) + + def get_space(self, name: str) -> 'Categorical': + """ + Create skopt optimization space. + :param name: A name of parameter field. + """ + return Categorical(self.opt_range, name=name, **self._space_params) + + @property + def range(self): + """ + Get each value in this space as list. + Returns a List of categories in Hyperopt mode. + Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid + calculating 100ds of indicators. + """ + if self.in_space and self.optimize: + return self.opt_range + else: + return [self.value] + + +class BooleanParameter(CategoricalParameter): + + def __init__(self, *, default: Optional[Any] = None, + space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): + """ + Initialize hyperopt-optimizable Boolean Parameter. + It's a shortcut to `CategoricalParameter([True, False])`. + :param default: A default value. If not specified, first item from specified space will be + used. + :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if + parameter field + name is prefixed with 'buy_' or 'sell_'. + :param optimize: Include parameter in hyperopt optimizations. + :param load: Load parameter value from {space}_params. + :param kwargs: Extra parameters to skopt.space.Categorical. + """ + + categories = [True, False] + super().__init__(categories=categories, default=default, space=space, optimize=optimize, + load=load, **kwargs) diff --git a/freqtrade/strategy/strategy_wrapper.py b/freqtrade/strategy/strategy_wrapper.py index 9aead8395..8cb0bde15 100644 --- a/freqtrade/strategy/strategy_wrapper.py +++ b/freqtrade/strategy/strategy_wrapper.py @@ -1,5 +1,7 @@ import logging from copy import deepcopy +from functools import wraps +from typing import Any, Callable, TypeVar, cast from freqtrade.exceptions import StrategyError @@ -7,12 +9,16 @@ from freqtrade.exceptions import StrategyError logger = logging.getLogger(__name__) -def strategy_safe_wrapper(f, message: str = "", default_retval=None, supress_error=False): +F = TypeVar('F', bound=Callable[..., Any]) + + +def strategy_safe_wrapper(f: F, message: str = "", default_retval=None, supress_error=False) -> F: """ Wrapper around user-provided methods and functions. Caches all exceptions and returns either the default_retval (if it's not None) or raises a StrategyError exception, which then needs to be handled by the calling method. """ + @wraps(f) def wrapper(*args, **kwargs): try: if 'trade' in kwargs: @@ -37,4 +43,4 @@ def strategy_safe_wrapper(f, message: str = "", default_retval=None, supress_err raise StrategyError(str(error)) from error return default_retval - return wrapper + return cast(F, wrapper) diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index 53237f67d..610a7a96e 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -4,7 +4,9 @@ # --- Do not remove these libs --- import numpy as np # noqa import pandas as pd # noqa -from pandas import DataFrame +from pandas import DataFrame # noqa +from datetime import datetime # noqa +from typing import Optional, Union # noqa from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, IStrategy, IntParameter) @@ -62,7 +64,7 @@ class {{ strategy }}(IStrategy): # trailing_stop_positive_offset = 0.0 # Disabled / not configured # Run "populate_indicators()" only for new candle. - process_only_new_candles = False + process_only_new_candles = True # These values can be overridden in the config. use_exit_signal = True diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index f0ae6c10d..1b375714a 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -62,7 +62,7 @@ class SampleStrategy(IStrategy): timeframe = '5m' # Run "populate_indicators()" only for new candle. - process_only_new_candles = False + process_only_new_candles = True # These values can be overridden in the config. use_exit_signal = True diff --git a/freqtrade/templates/strategy_analysis_example.ipynb b/freqtrade/templates/strategy_analysis_example.ipynb index 93e4b83ae..a7430c225 100644 --- a/freqtrade/templates/strategy_analysis_example.ipynb +++ b/freqtrade/templates/strategy_analysis_example.ipynb @@ -51,11 +51,13 @@ "source": [ "# Load data using values set above\n", "from freqtrade.data.history import load_pair_history\n", + "from freqtrade.enums import CandleType\n", "\n", "candles = load_pair_history(datadir=data_location,\n", " timeframe=config[\"timeframe\"],\n", " pair=pair,\n", " data_format = \"hdf5\",\n", + " candle_type=CandleType.SPOT,\n", " )\n", "\n", "# Confirm success\n", diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index ed40ef509..989f1d37a 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -13,7 +13,7 @@ def bot_loop_start(self, **kwargs) -> None: pass def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: float, - entry_tag: 'Optional[str]', **kwargs) -> float: + entry_tag: 'Optional[str]', side: str, **kwargs) -> float: """ Custom entry price logic, returning the new entry price. @@ -30,6 +30,34 @@ def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: """ return proposed_rate +def adjust_entry_price(self, trade: 'Trade', order: 'Optional[Order]', pair: str, + current_time: datetime, proposed_rate: float, current_order_rate: float, + entry_tag: Optional[str], side: str, **kwargs) -> float: + """ + Entry price re-adjustment logic, returning the user desired limit price. + This only executes when a order was already placed, still open (unfilled fully or partially) + and not timed out on subsequent candles after entry trigger. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/ + + When not implemented by a strategy, returns current_order_rate as default. + If current_order_rate is returned then the existing order is maintained. + If None is returned then order gets canceled but not replaced by a new one. + + :param pair: Pair that's currently analyzed + :param trade: Trade object. + :param order: Order object + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. + :param current_order_rate: Rate of the existing order in place. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. + :param side: 'long' or 'short' - indicating the direction of the proposed trade + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New entry price value if provided + + """ + return current_order_rate + def custom_exit_price(self, pair: str, trade: 'Trade', current_time: 'datetime', proposed_rate: float, current_profit: float, exit_tag: Optional[str], **kwargs) -> float: @@ -51,9 +79,10 @@ def custom_exit_price(self, pair: str, trade: 'Trade', """ return proposed_rate -def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, - proposed_stake: float, min_stake: float, max_stake: float, - side: str, entry_tag: 'Optional[str]', **kwargs) -> float: +def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: Optional[float], max_stake: float, + leverage: float, entry_tag: Optional[str], side: str, + **kwargs) -> float: """ Customize stake size for each new trade. @@ -63,6 +92,7 @@ def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: :param proposed_stake: A stake amount proposed by the bot. :param min_stake: Minimal stake size allowed by exchange. :param max_stake: Balance available for trading. + :param leverage: Leverage selected for this trade. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :return: A stake size, which is between min_stake and max_stake. @@ -118,7 +148,7 @@ def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', curre return None def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, current_time: datetime, entry_tag: 'Optional[str]', + time_in_force: str, current_time: datetime, entry_tag: Optional[str], side: str, **kwargs) -> bool: """ Called right before placing a entry order. @@ -131,8 +161,9 @@ def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: f :param pair: Pair that's about to be bought/shorted. :param order_type: Order type (as configured in order_types). usually limit or market. - :param amount: Amount in target (quote) currency that's going to be traded. + :param amount: Amount in target (base) currency that's going to be traded. :param rate: Rate that's going to be used when using limit orders + or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param current_time: datetime object, containing the current datetime :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. @@ -147,7 +178,7 @@ def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: rate: float, time_in_force: str, exit_reason: str, current_time: 'datetime', **kwargs) -> bool: """ - Called right before placing a regular sell order. + Called right before placing a regular exit order. Timing for this function is critical, so avoid doing heavy computations or network requests in this method. @@ -155,18 +186,19 @@ def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: When not implemented by a strategy, returns True (always confirming). - :param pair: Pair that's currently analyzed + :param pair: Pair for trade that's about to be exited. :param trade: trade object. :param order_type: Order type (as configured in order_types). usually limit or market. - :param amount: Amount in quote currency. + :param amount: Amount in base currency. :param rate: Rate that's going to be used when using limit orders + or current rate for market orders. :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param exit_reason: Exit reason. Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', 'exit_signal', 'force_exit', 'emergency_exit'] :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. - :return bool: When True is returned, then the exit-order is placed on the exchange. + :return bool: When True, then the exit-order is placed on the exchange. False aborts the process """ return True @@ -216,7 +248,7 @@ def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order', return False def adjust_trade_position(self, trade: 'Trade', current_time: 'datetime', - current_rate: float, current_profit: float, min_stake: float, + current_rate: float, current_profit: float, min_stake: Optional[float], max_stake: float, **kwargs) -> 'Optional[float]': """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. @@ -239,8 +271,8 @@ def adjust_trade_position(self, trade: 'Trade', current_time: 'datetime', return None def leverage(self, pair: str, current_time: datetime, current_rate: float, - proposed_leverage: float, max_leverage: float, side: str, - **kwargs) -> float: + proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], + side: str, **kwargs) -> float: """ Customize leverage for each new trade. This method is only called in futures mode. @@ -249,6 +281,7 @@ def leverage(self, pair: str, current_time: datetime, current_rate: float, :param current_rate: Rate, calculated based on pricing settings in exit_pricing. :param proposed_leverage: A leverage proposed by the bot. :param max_leverage: Max leverage allowed on this pair + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param side: 'long' or 'short' - indicating the direction of the proposed trade :return: A leverage amount, which is between 1.0 and max_leverage. """ diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index d93689a0e..14e5a6743 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -131,9 +131,9 @@ class Wallets: if isinstance(balances[currency], dict): self._wallets[currency] = Wallet( currency, - balances[currency].get('free', None), - balances[currency].get('used', None), - balances[currency].get('total', None) + balances[currency].get('free'), + balances[currency].get('used'), + balances[currency].get('total') ) # Remove currencies no longer in get_balances output for currency in deepcopy(self._wallets): @@ -300,7 +300,8 @@ class Wallets: if min_stake_amount is not None and min_stake_amount > max_stake_amount: if self._log: - logger.warning("Minimum stake amount > available balance.") + logger.warning("Minimum stake amount > available balance. " + f"{min_stake_amount} > {max_stake_amount}") return 0 if min_stake_amount is not None and stake_amount < min_stake_amount: if self._log: diff --git a/pyproject.toml b/pyproject.toml index e8d5ed47e..8020b0636 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,28 @@ skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*", "**/user_data/*" [tool.pytest.ini_options] asyncio_mode = "auto" +[tool.mypy] +ignore_missing_imports = true +warn_unused_ignores = true +exclude = [ + '^build_helpers\.py$' +] + +[[tool.mypy.overrides]] +module = "tests.*" +ignore_errors = true + [build-system] requires = ["setuptools >= 46.4.0", "wheel"] build-backend = "setuptools.build_meta" + +[tool.pyright] +include = ["freqtrade"] +exclude = [ + "**/__pycache__", + "build_helpers/*.py", +] +ignore = ["freqtrade/vendor/**"] + +# Align pyright to mypy config +strictParameterNoneValue = false diff --git a/requirements-dev.txt b/requirements-dev.txt index 9458be1ef..f2f77c2ba 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,24 +6,24 @@ coveralls==3.3.1 flake8==4.0.1 -flake8-tidy-imports==4.6.0 -mypy==0.950 -pre-commit==2.18.1 +flake8-tidy-imports==4.8.0 +mypy==0.961 +pre-commit==2.20.0 pytest==7.1.2 pytest-asyncio==0.18.3 pytest-cov==3.0.0 -pytest-mock==3.7.0 +pytest-mock==3.8.2 pytest-random-order==1.0.4 isort==5.10.1 # For datetime mocking -time-machine==2.6.0 +time-machine==2.7.1 # Convert jupyter notebooks to markdown documents nbconvert==6.5.0 # mypy types -types-cachetools==5.0.1 -types-filelock==3.2.5 -types-requests==2.27.25 -types-tabulate==0.8.8 -types-python-dateutil==2.8.14 +types-cachetools==5.2.1 +types-filelock==3.2.7 +types-requests==2.28.0 +types-tabulate==0.8.11 +types-python-dateutil==2.8.18 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 32fc3f4b9..94e59ec15 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -2,8 +2,8 @@ -r requirements.txt # Required for hyperopt -scipy==1.8.0 -scikit-learn==1.0.2 +scipy==1.8.1 +scikit-learn==1.1.1 scikit-optimize==0.9.0 -filelock==3.6.0 +filelock==3.7.1 progressbar2==4.0.0 diff --git a/requirements-plot.txt b/requirements-plot.txt index d9faed301..0f6ae94c2 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,4 +1,4 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.7.0 +plotly==5.9.0 diff --git a/requirements.txt b/requirements.txt index 709408aeb..2bb3b4b75 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,21 @@ -numpy==1.22.3 -pandas==1.4.2 +numpy==1.23.1 +pandas==1.4.3 pandas-ta==0.3.14b -ccxt==1.81.16 +ccxt==1.90.47 # Pin cryptography for now due to rust build errors with piwheels -cryptography==37.0.1 +cryptography==37.0.4 aiohttp==3.8.1 -SQLAlchemy==1.4.36 -python-telegram-bot==13.11 +SQLAlchemy==1.4.39 +python-telegram-bot==13.13 arrow==1.2.2 cachetools==4.2.2 -requests==2.27.1 -urllib3==1.26.9 -jsonschema==4.4.0 +requests==2.28.1 +urllib3==1.26.10 +jsonschema==4.6.2 TA-Lib==0.4.24 technical==1.3.0 -tabulate==0.8.9 +tabulate==0.8.10 pycoingecko==2.2.0 jinja2==3.1.2 tables==3.7.0 @@ -26,25 +26,25 @@ joblib==1.1.0 py_find_1st==1.1.5 # Load ticker files 30% faster -python-rapidjson==1.6 +python-rapidjson==1.8 # Properly format api responses -orjson==3.6.8 +orjson==3.7.7 # Notify systemd sdnotify==0.3.2 # API Server -fastapi==0.75.2 -uvicorn==0.17.6 -pyjwt==2.3.0 +fastapi==0.78.0 +uvicorn==0.18.2 +pyjwt==2.4.0 aiofiles==0.8.0 -psutil==5.9.0 +psutil==5.9.1 # Support for colorized terminal output -colorama==0.4.4 +colorama==0.4.5 # Building config files interactively questionary==1.10.0 -prompt-toolkit==3.0.29 +prompt-toolkit==3.0.30 # Extensions to datetime library python-dateutil==2.8.2 diff --git a/scripts/rest_client.py b/scripts/rest_client.py index ecbb65253..e5d358c98 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -261,7 +261,7 @@ class FtRestClient(): } return self._post("forcebuy", data=data) - def force_enter(self, pair, side, price=None): + def forceenter(self, pair, side, price=None): """Force entering a trade :param pair: Pair to buy (ETH/BTC) @@ -273,7 +273,7 @@ class FtRestClient(): "side": side, "price": price, } - return self._post("force_enter", data=data) + return self._post("forceenter", data=data) def forceexit(self, tradeid): """Force-exit a trade. diff --git a/setup.cfg b/setup.cfg index edbd320c3..d711534d9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ tests_require = pytest-mock packages = find: -python_requires = >=3.6 +python_requires = >=3.8 [options.entry_points] console_scripts = @@ -50,13 +50,3 @@ exclude = .eggs, user_data, -[mypy] -ignore_missing_imports = True -warn_unused_ignores = True -exclude = (?x)( - ^build_helpers\.py$ - ) - - -[mypy-tests.*] -ignore_errors = True diff --git a/setup.py b/setup.py index c5e418d0d..7aa56bf81 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ setup( ], install_requires=[ # from requirements.txt - 'ccxt>=1.79.69', + 'ccxt>=1.83.12', 'SQLAlchemy', 'python-telegram-bot>=13.4', 'arrow>=0.17.0', diff --git a/setup.sh b/setup.sh index dcf6c02c7..202cb70c7 100755 --- a/setup.sh +++ b/setup.sh @@ -25,7 +25,7 @@ function check_installed_python() { exit 2 fi - for v in 9 10 8 + for v in 10 9 8 do PYTHON="python3.${v}" which $PYTHON @@ -87,6 +87,10 @@ function updateenv() { echo "Failed installing Freqtrade" exit 1 fi + + echo "Installing freqUI" + freqtrade install-ui + echo "pip install completed" echo if [[ $dev =~ ^[Yy]$ ]]; then diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 37eeda86a..d6e80675e 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -1,5 +1,6 @@ import json import re +from datetime import datetime from io import BytesIO from pathlib import Path from unittest.mock import MagicMock, PropertyMock @@ -14,11 +15,14 @@ from freqtrade.commands import (start_backtesting_show, start_convert_data, star start_list_exchanges, start_list_markets, start_list_strategies, start_list_timeframes, start_new_strategy, start_show_trades, start_test_pairlist, start_trading, start_webserver) +from freqtrade.commands.db_commands import start_convert_db from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, get_ui_download_url, read_ui_version) from freqtrade.configuration import setup_utils_configuration from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException +from freqtrade.persistence.models import init_db +from freqtrade.persistence.pairlock_middleware import PairLocks from tests.conftest import (CURRENT_TEST_STRATEGY, create_mock_trades, get_args, log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) from tests.conftest_trades import MOCK_TRADE_COUNT @@ -831,6 +835,23 @@ def test_download_data_trades(mocker, caplog): start_download_data(pargs) +def test_download_data_data_invalid(mocker): + patch_exchange(mocker, id="kraken") + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + args = [ + "download-data", + "--exchange", "kraken", + "--pairs", "ETH/BTC", "XRP/BTC", + "--days", "20", + ] + pargs = get_args(args) + pargs['config'] = None + with pytest.raises(OperationalException, match=r"Historic klines not available for .*"): + start_download_data(pargs) + + def test_start_convert_trades(mocker, caplog): convert_mock = mocker.patch('freqtrade.commands.data_commands.convert_trades_to_ohlcv', MagicMock(return_value=[])) @@ -1458,3 +1479,33 @@ def test_backtesting_show(mocker, testdatadir, capsys): assert sbr.call_count == 1 out, err = capsys.readouterr() assert "Pairs for Strategy" in out + + +def test_start_convert_db(mocker, fee, tmpdir, caplog): + db_src_file = Path(f"{tmpdir}/db.sqlite") + db_from = f"sqlite:///{db_src_file}" + db_target_file = Path(f"{tmpdir}/db_target.sqlite") + db_to = f"sqlite:///{db_target_file}" + args = [ + "convert-db", + "--db-url-from", + db_from, + "--db-url", + db_to, + ] + + assert not db_src_file.is_file() + init_db(db_from) + + create_mock_trades(fee) + + PairLocks.timeframe = '5m' + PairLocks.lock_pair('XRP/USDT', datetime.now(), 'Random reason 125', side='long') + assert db_src_file.is_file() + assert not db_target_file.is_file() + + pargs = get_args(args) + pargs['config'] = None + start_convert_db(pargs) + + assert db_target_file.is_file() diff --git a/tests/conftest.py b/tests/conftest.py index cc07de1de..3158e9ede 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,9 +78,21 @@ def get_args(args): # Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines -def get_mock_coro(return_value): +# TODO: This should be replaced with AsyncMock once support for python 3.7 is dropped. +def get_mock_coro(return_value=None, side_effect=None): async def mock_coro(*args, **kwargs): - return return_value + if side_effect: + if isinstance(side_effect, list): + effect = side_effect.pop(0) + else: + effect = side_effect + if isinstance(effect, Exception): + raise effect + if callable(effect): + return effect(*args, **kwargs) + return effect + else: + return return_value return Mock(wraps=mock_coro) @@ -100,11 +112,8 @@ def patch_exchange( mock_supported_modes=True ) -> None: mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.validate_config', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id)) mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title())) mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2)) @@ -325,7 +334,7 @@ def create_mock_trades_with_leverage(fee, use_db: bool = True): Trade.query.session.flush() -def create_mock_trades_usdt(fee, use_db: bool = True): +def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool = True): """ Create some fake trades ... """ @@ -335,26 +344,29 @@ def create_mock_trades_usdt(fee, use_db: bool = True): else: LocalTrade.add_bt_trade(trade) + is_short1 = is_short if is_short is not None else True + is_short2 = is_short if is_short is not None else False + # Simulate dry_run entries - trade = mock_trade_usdt_1(fee) + trade = mock_trade_usdt_1(fee, is_short1) add_trade(trade) - trade = mock_trade_usdt_2(fee) + trade = mock_trade_usdt_2(fee, is_short1) add_trade(trade) - trade = mock_trade_usdt_3(fee) + trade = mock_trade_usdt_3(fee, is_short1) add_trade(trade) - trade = mock_trade_usdt_4(fee) + trade = mock_trade_usdt_4(fee, is_short2) add_trade(trade) - trade = mock_trade_usdt_5(fee) + trade = mock_trade_usdt_5(fee, is_short2) add_trade(trade) - trade = mock_trade_usdt_6(fee) + trade = mock_trade_usdt_6(fee, is_short1) add_trade(trade) - trade = mock_trade_usdt_7(fee) + trade = mock_trade_usdt_7(fee, is_short1) add_trade(trade) if use_db: Trade.commit() @@ -384,7 +396,7 @@ def patch_coingekko(mocker) -> None: @pytest.fixture(scope='function') def init_persistence(default_conf): - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) @pytest.fixture(scope="function") @@ -1616,6 +1628,7 @@ def limit_buy_order_open(): 'datetime': arrow.utcnow().isoformat(), 'price': 0.00001099, 'amount': 90.99181073, + 'average': None, 'filled': 0.0, 'cost': 0.0009999, 'remaining': 90.99181073, @@ -1678,6 +1691,7 @@ def limit_buy_order_old_partial(): 'price': 0.00001099, 'amount': 90.99181073, 'filled': 23.0, + 'cost': 90.99181073 * 23.0, 'remaining': 67.99181073, 'status': 'open' } @@ -3149,60 +3163,46 @@ def leverage_tiers(): "AAVE/USDT": [ { 'min': 0, - 'max': 50000, + 'max': 5000, 'mmr': 0.01, 'lev': 50, 'maintAmt': 0.0 }, { - 'min': 50000, - 'max': 250000, + 'min': 5000, + 'max': 25000, 'mmr': 0.02, 'lev': 25, - 'maintAmt': 500.0 + 'maintAmt': 75.0 + }, + { + 'min': 25000, + 'max': 100000, + 'mmr': 0.05, + 'lev': 10, + 'maintAmt': 700.0 + }, + { + 'min': 100000, + 'max': 250000, + 'mmr': 0.1, + 'lev': 5, + 'maintAmt': 5700.0 }, { 'min': 250000, 'max': 1000000, - 'mmr': 0.05, - 'lev': 10, - 'maintAmt': 8000.0 - }, - { - 'min': 1000000, - 'max': 2000000, - 'mmr': 0.1, - 'lev': 5, - 'maintAmt': 58000.0 - }, - { - 'min': 2000000, - 'max': 5000000, 'mmr': 0.125, - 'lev': 4, - 'maintAmt': 108000.0 - }, - { - 'min': 5000000, - 'max': 10000000, - 'mmr': 0.1665, - 'lev': 3, - 'maintAmt': 315500.0 + 'lev': 2, + 'maintAmt': 11950.0 }, { 'min': 10000000, - 'max': 20000000, - 'mmr': 0.25, - 'lev': 2, - 'maintAmt': 1150500.0 + 'max': 50000000, + 'mmr': 0.5, + 'lev': 1, + 'maintAmt': 386950.0 }, - { - "min": 20000000, - "max": 50000000, - "mmr": 0.5, - "lev": 1, - "maintAmt": 6150500.0 - } ], "ADA/BUSD": [ { diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index 006eab98f..1a8cf3183 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -29,6 +29,7 @@ def mock_order_1(is_short: bool): 'average': 0.123, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -65,6 +66,7 @@ def mock_order_2(is_short: bool): 'price': 0.123, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -79,6 +81,7 @@ def mock_order_2_sell(is_short: bool): 'price': 0.128, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -126,6 +129,7 @@ def mock_order_3(is_short: bool): 'price': 0.05, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -141,6 +145,7 @@ def mock_order_3_sell(is_short: bool): 'average': 0.06, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -186,6 +191,7 @@ def mock_order_4(is_short: bool): 'price': 0.123, 'amount': 123.0, 'filled': 0.0, + 'cost': 15.129, 'remaining': 123.0, } @@ -225,6 +231,7 @@ def mock_order_5(is_short: bool): 'price': 0.123, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -239,6 +246,7 @@ def mock_order_5_stoploss(is_short: bool): 'price': 0.123, 'amount': 123.0, 'filled': 0.0, + 'cost': 0.0, 'remaining': 123.0, } @@ -281,6 +289,7 @@ def mock_order_6(is_short: bool): 'price': 0.15, 'amount': 2.0, 'filled': 2.0, + 'cost': 0.3, 'remaining': 0.0, } @@ -295,6 +304,7 @@ def mock_order_6_sell(is_short: bool): 'price': 0.15 if is_short else 0.20, 'amount': 2.0, 'filled': 0.0, + 'cost': 0.0, 'remaining': 2.0, } @@ -337,6 +347,7 @@ def short_order(): 'price': 0.123, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.129, 'remaining': 0.0, } @@ -351,6 +362,7 @@ def exit_short_order(): 'price': 0.128, 'amount': 123.0, 'filled': 123.0, + 'cost': 15.744, 'remaining': 0.0, } @@ -424,6 +436,7 @@ def leverage_order(): 'amount': 123.0, 'filled': 123.0, 'remaining': 0.0, + 'cost': 15.129, 'leverage': 5.0 } @@ -439,6 +452,7 @@ def leverage_order_sell(): 'amount': 123.0, 'filled': 123.0, 'remaining': 0.0, + 'cost': 15.744, 'leverage': 5.0 } diff --git a/tests/conftest_trades_usdt.py b/tests/conftest_trades_usdt.py index 59e7f0457..41d705c01 100644 --- a/tests/conftest_trades_usdt.py +++ b/tests/conftest_trades_usdt.py @@ -6,47 +6,84 @@ from freqtrade.persistence.models import Order, Trade MOCK_TRADE_COUNT = 6 -def mock_order_usdt_1(): +def entry_side(is_short: bool): + return "sell" if is_short else "buy" + + +def exit_side(is_short: bool): + return "buy" if is_short else "sell" + + +def direc(is_short: bool): + return "short" if is_short else "long" + + +def mock_order_usdt_1(is_short: bool): return { - 'id': '1234', - 'symbol': 'ADA/USDT', + 'id': f'prod_entry_1_{direc(is_short)}', + 'symbol': 'LTC/USDT', 'status': 'closed', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', - 'price': 2.0, - 'amount': 10.0, - 'filled': 10.0, + 'price': 10.0, + 'amount': 2.0, + 'filled': 2.0, 'remaining': 0.0, } -def mock_trade_usdt_1(fee): +def mock_order_usdt_1_exit(is_short: bool): + return { + 'id': f'prod_exit_1_{direc(is_short)}', + 'symbol': 'LTC/USDT', + 'status': 'closed', + 'side': exit_side(is_short), + 'type': 'limit', + 'price': 8.0, + 'amount': 2.0, + 'filled': 2.0, + 'remaining': 0.0, + } + + +def mock_trade_usdt_1(fee, is_short: bool): + """ + Simulate prod entry with open sell order + """ trade = Trade( - pair='ADA/USDT', + pair='LTC/USDT', stake_amount=20.0, - amount=10.0, - amount_requested=10.0, + amount=2.0, + amount_requested=2.0, + open_date=datetime.now(tz=timezone.utc) - timedelta(days=2, minutes=20), + close_date=datetime.now(tz=timezone.utc) - timedelta(days=2, minutes=5), fee_open=fee.return_value, fee_close=fee.return_value, - is_open=True, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17), - open_rate=2.0, + is_open=False, + open_rate=10.0, + close_rate=8.0, + close_profit=-0.2, + close_profit_abs=-4.0, exchange='binance', - open_order_id='dry_run_buy_12345', - strategy='StrategyTestV2', + strategy='SampleStrategy', + open_order_id=f'prod_exit_1_{direc(is_short)}', timeframe=5, + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_1(), 'ADA/USDT', 'buy') + o = Order.parse_from_ccxt_object(mock_order_usdt_1(is_short), 'LTC/USDT', entry_side(is_short)) + trade.orders.append(o) + o = Order.parse_from_ccxt_object(mock_order_usdt_1_exit(is_short), + 'LTC/USDT', exit_side(is_short)) trade.orders.append(o) return trade -def mock_order_usdt_2(): +def mock_order_usdt_2(is_short: bool): return { - 'id': '1235', + 'id': f'1235_{direc(is_short)}', 'symbol': 'ETC/USDT', 'status': 'closed', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', 'price': 2.0, 'amount': 100.0, @@ -55,12 +92,12 @@ def mock_order_usdt_2(): } -def mock_order_usdt_2_sell(): +def mock_order_usdt_2_exit(is_short: bool): return { - 'id': '12366', + 'id': f'12366_{direc(is_short)}', 'symbol': 'ETC/USDT', 'status': 'closed', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'limit', 'price': 2.05, 'amount': 100.0, @@ -69,7 +106,7 @@ def mock_order_usdt_2_sell(): } -def mock_trade_usdt_2(fee): +def mock_trade_usdt_2(fee, is_short: bool): """ Closed trade... """ @@ -82,30 +119,33 @@ def mock_trade_usdt_2(fee): fee_close=fee.return_value, open_rate=2.0, close_rate=2.05, - close_profit=5.0, + close_profit=0.05, close_profit_abs=3.9875, exchange='binance', is_open=False, - open_order_id='dry_run_sell_12345', + open_order_id=f'12366_{direc(is_short)}', strategy='StrategyTestV2', timeframe=5, - exit_reason='sell_signal', + enter_tag='TEST1', + exit_reason='exit_signal', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_2(), 'ETC/USDT', 'buy') + o = Order.parse_from_ccxt_object(mock_order_usdt_2(is_short), 'ETC/USDT', entry_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_usdt_2_sell(), 'ETC/USDT', 'sell') + o = Order.parse_from_ccxt_object( + mock_order_usdt_2_exit(is_short), 'ETC/USDT', exit_side(is_short)) trade.orders.append(o) return trade -def mock_order_usdt_3(): +def mock_order_usdt_3(is_short: bool): return { - 'id': '41231a12a', + 'id': f'41231a12a_{direc(is_short)}', 'symbol': 'XRP/USDT', 'status': 'closed', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', 'price': 1.0, 'amount': 30.0, @@ -114,12 +154,12 @@ def mock_order_usdt_3(): } -def mock_order_usdt_3_sell(): +def mock_order_usdt_3_exit(is_short: bool): return { - 'id': '41231a666a', + 'id': f'41231a666a_{direc(is_short)}', 'symbol': 'XRP/USDT', 'status': 'closed', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'stop_loss_limit', 'price': 1.1, 'average': 1.1, @@ -129,7 +169,7 @@ def mock_order_usdt_3_sell(): } -def mock_trade_usdt_3(fee): +def mock_trade_usdt_3(fee, is_short: bool): """ Closed trade """ @@ -142,29 +182,32 @@ def mock_trade_usdt_3(fee): fee_close=fee.return_value, open_rate=1.0, close_rate=1.1, - close_profit=10.0, + close_profit=0.1, close_profit_abs=9.8425, exchange='binance', is_open=False, strategy='StrategyTestV2', timeframe=5, + enter_tag='TEST3', exit_reason='roi', open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), close_date=datetime.now(tz=timezone.utc), + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_3(), 'XRP/USDT', 'buy') + o = Order.parse_from_ccxt_object(mock_order_usdt_3(is_short), 'XRP/USDT', entry_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_usdt_3_sell(), 'XRP/USDT', 'sell') + o = Order.parse_from_ccxt_object(mock_order_usdt_3_exit(is_short), + 'XRP/USDT', exit_side(is_short)) trade.orders.append(o) return trade -def mock_order_usdt_4(): +def mock_order_usdt_4(is_short: bool): return { - 'id': 'prod_buy_12345', + 'id': f'prod_buy_12345_{direc(is_short)}', 'symbol': 'ETC/USDT', 'status': 'open', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', 'price': 2.0, 'amount': 10.0, @@ -173,7 +216,7 @@ def mock_order_usdt_4(): } -def mock_trade_usdt_4(fee): +def mock_trade_usdt_4(fee, is_short: bool): """ Simulate prod entry """ @@ -188,21 +231,22 @@ def mock_trade_usdt_4(fee): is_open=True, open_rate=2.0, exchange='binance', - open_order_id='prod_buy_12345', + open_order_id=f'prod_buy_12345_{direc(is_short)}', strategy='StrategyTestV2', timeframe=5, + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_4(), 'ETC/USDT', 'buy') + o = Order.parse_from_ccxt_object(mock_order_usdt_4(is_short), 'ETC/USDT', entry_side(is_short)) trade.orders.append(o) return trade -def mock_order_usdt_5(): +def mock_order_usdt_5(is_short: bool): return { - 'id': 'prod_buy_3455', + 'id': f'prod_buy_3455_{direc(is_short)}', 'symbol': 'XRP/USDT', 'status': 'closed', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', 'price': 2.0, 'amount': 10.0, @@ -211,12 +255,12 @@ def mock_order_usdt_5(): } -def mock_order_usdt_5_stoploss(): +def mock_order_usdt_5_stoploss(is_short: bool): return { - 'id': 'prod_stoploss_3455', + 'id': f'prod_stoploss_3455_{direc(is_short)}', 'symbol': 'XRP/USDT', 'status': 'open', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'stop_loss_limit', 'price': 2.0, 'amount': 10.0, @@ -225,7 +269,7 @@ def mock_order_usdt_5_stoploss(): } -def mock_trade_usdt_5(fee): +def mock_trade_usdt_5(fee, is_short: bool): """ Simulate prod entry with stoploss """ @@ -241,22 +285,23 @@ def mock_trade_usdt_5(fee): open_rate=2.0, exchange='binance', strategy='SampleStrategy', - stoploss_order_id='prod_stoploss_3455', + stoploss_order_id=f'prod_stoploss_3455_{direc(is_short)}', timeframe=5, + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_5(), 'XRP/USDT', 'buy') + o = Order.parse_from_ccxt_object(mock_order_usdt_5(is_short), 'XRP/USDT', entry_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_usdt_5_stoploss(), 'XRP/USDT', 'stoploss') + o = Order.parse_from_ccxt_object(mock_order_usdt_5_stoploss(is_short), 'XRP/USDT', 'stoploss') trade.orders.append(o) return trade -def mock_order_usdt_6(): +def mock_order_usdt_6(is_short: bool): return { - 'id': 'prod_buy_6', + 'id': f'prod_entry_6_{direc(is_short)}', 'symbol': 'LTC/USDT', 'status': 'closed', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', 'price': 10.0, 'amount': 2.0, @@ -265,12 +310,12 @@ def mock_order_usdt_6(): } -def mock_order_usdt_6_sell(): +def mock_order_usdt_6_exit(is_short: bool): return { - 'id': 'prod_sell_6', + 'id': f'prod_exit_6_{direc(is_short)}', 'symbol': 'LTC/USDT', 'status': 'open', - 'side': 'sell', + 'side': exit_side(is_short), 'type': 'limit', 'price': 12.0, 'amount': 2.0, @@ -279,7 +324,7 @@ def mock_order_usdt_6_sell(): } -def mock_trade_usdt_6(fee): +def mock_trade_usdt_6(fee, is_short: bool): """ Simulate prod entry with open sell order """ @@ -295,69 +340,49 @@ def mock_trade_usdt_6(fee): open_rate=10.0, exchange='binance', strategy='SampleStrategy', - open_order_id="prod_sell_6", + open_order_id=f'prod_exit_6_{direc(is_short)}', timeframe=5, + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_6(), 'LTC/USDT', 'buy') + o = Order.parse_from_ccxt_object(mock_order_usdt_6(is_short), 'LTC/USDT', entry_side(is_short)) trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_usdt_6_sell(), 'LTC/USDT', 'sell') + o = Order.parse_from_ccxt_object(mock_order_usdt_6_exit(is_short), + 'LTC/USDT', exit_side(is_short)) trade.orders.append(o) return trade -def mock_order_usdt_7(): +def mock_order_usdt_7(is_short: bool): return { - 'id': 'prod_buy_7', - 'symbol': 'LTC/USDT', + 'id': f'1234_{direc(is_short)}', + 'symbol': 'ADA/USDT', 'status': 'closed', - 'side': 'buy', + 'side': entry_side(is_short), 'type': 'limit', - 'price': 10.0, - 'amount': 2.0, - 'filled': 2.0, + 'price': 2.0, + 'amount': 10.0, + 'filled': 10.0, 'remaining': 0.0, } -def mock_order_usdt_7_sell(): - return { - 'id': 'prod_sell_7', - 'symbol': 'LTC/USDT', - 'status': 'closed', - 'side': 'sell', - 'type': 'limit', - 'price': 8.0, - 'amount': 2.0, - 'filled': 2.0, - 'remaining': 0.0, - } - - -def mock_trade_usdt_7(fee): - """ - Simulate prod entry with open sell order - """ +def mock_trade_usdt_7(fee, is_short: bool): trade = Trade( - pair='LTC/USDT', + pair='ADA/USDT', stake_amount=20.0, - amount=2.0, - amount_requested=2.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), - close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=5), + amount=10.0, + amount_requested=10.0, fee_open=fee.return_value, fee_close=fee.return_value, - is_open=False, - open_rate=10.0, - close_rate=8.0, - close_profit=-0.2, - close_profit_abs=-4.0, + is_open=True, + open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17), + open_rate=2.0, exchange='binance', - strategy='SampleStrategy', - open_order_id="prod_sell_6", + open_order_id=f'1234_{direc(is_short)}', + strategy='StrategyTestV2', timeframe=5, + is_short=is_short, ) - o = Order.parse_from_ccxt_object(mock_order_usdt_7(), 'LTC/USDT', 'buy') - trade.orders.append(o) - o = Order.parse_from_ccxt_object(mock_order_usdt_7_sell(), 'LTC/USDT', 'sell') + o = Order.parse_from_ccxt_object(mock_order_usdt_7(is_short), 'ADA/USDT', entry_side(is_short)) trade.orders.append(o) return trade diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 4157bd899..977140ebb 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -85,7 +85,7 @@ def test_load_backtest_data_new_format(testdatadir): filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) - assert set(bt_data.columns) == set(BT_DATA_COLUMNS + ['close_timestamp', 'open_timestamp']) + assert set(bt_data.columns) == set(BT_DATA_COLUMNS) assert len(bt_data) == 179 # Test loading from string (must yield same result) @@ -110,7 +110,7 @@ def test_load_backtest_data_multi(testdatadir): bt_data = load_backtest_data(filename, strategy=strategy) assert isinstance(bt_data, DataFrame) assert set(bt_data.columns) == set( - BT_DATA_COLUMNS + ['close_timestamp', 'open_timestamp']) + BT_DATA_COLUMNS) assert len(bt_data) == 179 # Test loading from string (must yield same result) diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py new file mode 100755 index 000000000..09fbe9957 --- /dev/null +++ b/tests/data/test_entryexitanalysis.py @@ -0,0 +1,191 @@ +import logging +from unittest.mock import MagicMock, PropertyMock + +import pandas as pd +import pytest + +from freqtrade.commands.analyze_commands import start_analysis_entries_exits +from freqtrade.commands.optimize_commands import start_backtesting +from freqtrade.enums import ExitType +from freqtrade.optimize.backtesting import Backtesting +from tests.conftest import get_args, patch_exchange, patched_configuration_load_config_file + + +@pytest.fixture(autouse=True) +def entryexitanalysis_cleanup() -> None: + yield None + + Backtesting.cleanup() + + +def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, tmpdir, capsys): + caplog.set_level(logging.INFO) + + default_conf.update({ + "use_exit_signal": True, + "exit_profit_only": False, + "exit_profit_offset": 0.0, + "ignore_roi_if_entry_signal": False, + }) + patch_exchange(mocker) + result1 = pd.DataFrame({'pair': ['ETH/BTC', 'LTC/BTC', 'ETH/BTC', 'LTC/BTC'], + 'profit_ratio': [0.025, 0.05, -0.1, -0.05], + 'profit_abs': [0.5, 2.0, -4.0, -2.0], + 'open_date': pd.to_datetime(['2018-01-29 18:40:00', + '2018-01-30 03:30:00', + '2018-01-30 08:10:00', + '2018-01-31 13:30:00', ], utc=True + ), + 'close_date': pd.to_datetime(['2018-01-29 20:45:00', + '2018-01-30 05:35:00', + '2018-01-30 09:10:00', + '2018-01-31 15:00:00', ], utc=True), + 'trade_duration': [235, 40, 60, 90], + 'is_open': [False, False, False, False], + 'stake_amount': [0.01, 0.01, 0.01, 0.01], + 'open_rate': [0.104445, 0.10302485, 0.10302485, 0.10302485], + 'close_rate': [0.104969, 0.103541, 0.102041, 0.102541], + "is_short": [False, False, False, False], + 'enter_tag': ["enter_tag_long_a", + "enter_tag_long_b", + "enter_tag_long_a", + "enter_tag_long_b"], + 'exit_reason': [ExitType.ROI, + ExitType.EXIT_SIGNAL, + ExitType.STOP_LOSS, + ExitType.TRAILING_STOP_LOSS] + }) + + backtestmock = MagicMock(side_effect=[ + { + 'results': result1, + 'config': default_conf, + 'locks': [], + 'rejected_signals': 20, + 'timedout_entry_orders': 0, + 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, + 'final_balance': 1000, + } + ]) + mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', + PropertyMock(return_value=['ETH/BTC', 'LTC/BTC', 'DASH/BTC'])) + mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) + + patched_configuration_load_config_file(mocker, default_conf) + + args = [ + 'backtesting', + '--config', 'config.json', + '--datadir', str(testdatadir), + '--user-data-dir', str(tmpdir), + '--timeframe', '5m', + '--timerange', '1515560100-1517287800', + '--export', 'signals', + '--cache', 'none', + ] + args = get_args(args) + start_backtesting(args) + + captured = capsys.readouterr() + assert 'BACKTESTING REPORT' in captured.out + assert 'EXIT REASON STATS' in captured.out + assert 'LEFT OPEN TRADES REPORT' in captured.out + + base_args = [ + 'backtesting-analysis', + '--config', 'config.json', + '--datadir', str(testdatadir), + '--user-data-dir', str(tmpdir), + ] + + # test group 0 and indicator list + args = get_args(base_args + + ['--analysis-groups', "0", + '--indicator-list', "close", "rsi", "profit_abs"] + ) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert 'LTC/BTC' in captured.out + assert 'ETH/BTC' in captured.out + assert 'enter_tag_long_a' in captured.out + assert 'enter_tag_long_b' in captured.out + assert 'exit_signal' in captured.out + assert 'roi' in captured.out + assert 'stop_loss' in captured.out + assert 'trailing_stop_loss' in captured.out + assert '0.5' in captured.out + assert '-4' in captured.out + assert '-2' in captured.out + assert '-3.5' in captured.out + assert '50' in captured.out + assert '0' in captured.out + assert '0.01616' in captured.out + assert '34.049' in captured.out + assert '0.104104' in captured.out + assert '47.0996' in captured.out + + # test group 1 + args = get_args(base_args + ['--analysis-groups', "1"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert 'enter_tag_long_a' in captured.out + assert 'enter_tag_long_b' in captured.out + assert 'total_profit_pct' in captured.out + assert '-3.5' in captured.out + assert '-1.75' in captured.out + assert '-7.5' in captured.out + assert '-3.75' in captured.out + assert '0' in captured.out + + # test group 2 + args = get_args(base_args + ['--analysis-groups', "2"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert 'enter_tag_long_a' in captured.out + assert 'enter_tag_long_b' in captured.out + assert 'exit_signal' in captured.out + assert 'roi' in captured.out + assert 'stop_loss' in captured.out + assert 'trailing_stop_loss' in captured.out + assert 'total_profit_pct' in captured.out + assert '-10' in captured.out + assert '-5' in captured.out + assert '2.5' in captured.out + + # test group 3 + args = get_args(base_args + ['--analysis-groups', "3"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert 'LTC/BTC' in captured.out + assert 'ETH/BTC' in captured.out + assert 'enter_tag_long_a' in captured.out + assert 'enter_tag_long_b' in captured.out + assert 'total_profit_pct' in captured.out + assert '-7.5' in captured.out + assert '-3.75' in captured.out + assert '-1.75' in captured.out + assert '0' in captured.out + assert '2' in captured.out + + # test group 4 + args = get_args(base_args + ['--analysis-groups', "4"]) + start_analysis_entries_exits(args) + captured = capsys.readouterr() + assert 'LTC/BTC' in captured.out + assert 'ETH/BTC' in captured.out + assert 'enter_tag_long_a' in captured.out + assert 'enter_tag_long_b' in captured.out + assert 'exit_signal' in captured.out + assert 'roi' in captured.out + assert 'stop_loss' in captured.out + assert 'trailing_stop_loss' in captured.out + assert 'total_profit_pct' in captured.out + assert '-10' in captured.out + assert '-5' in captured.out + assert '-4' in captured.out + assert '0.5' in captured.out + assert '1' in captured.out + assert '2.5' in captured.out diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 82d4a841c..9709e7ad0 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -158,21 +158,22 @@ def test_testdata_path(testdatadir) -> None: assert str(Path('tests') / 'testdata') in str(testdatadir) -@pytest.mark.parametrize("pair,expected_result,candle_type", [ - ("ETH/BTC", 'freqtrade/hello/world/ETH_BTC-5m.json', ""), - ("Fabric Token/ETH", 'freqtrade/hello/world/Fabric_Token_ETH-5m.json', ""), - ("ETHH20", 'freqtrade/hello/world/ETHH20-5m.json', ""), - (".XBTBON2H", 'freqtrade/hello/world/_XBTBON2H-5m.json', ""), - ("ETHUSD.d", 'freqtrade/hello/world/ETHUSD_d-5m.json', ""), - ("ACC_OLD/BTC", 'freqtrade/hello/world/ACC_OLD_BTC-5m.json', ""), - ("ETH/BTC", 'freqtrade/hello/world/futures/ETH_BTC-5m-mark.json', "mark"), - ("ACC_OLD/BTC", 'freqtrade/hello/world/futures/ACC_OLD_BTC-5m-index.json', "index"), +@pytest.mark.parametrize("pair,timeframe,expected_result,candle_type", [ + ("ETH/BTC", "5m", "freqtrade/hello/world/ETH_BTC-5m.json", ""), + ("ETH/USDT", "1M", "freqtrade/hello/world/ETH_USDT-1Mo.json", ""), + ("Fabric Token/ETH", "5m", "freqtrade/hello/world/Fabric_Token_ETH-5m.json", ""), + ("ETHH20", "5m", "freqtrade/hello/world/ETHH20-5m.json", ""), + (".XBTBON2H", "5m", "freqtrade/hello/world/_XBTBON2H-5m.json", ""), + ("ETHUSD.d", "5m", "freqtrade/hello/world/ETHUSD_d-5m.json", ""), + ("ACC_OLD/BTC", "5m", "freqtrade/hello/world/ACC_OLD_BTC-5m.json", ""), + ("ETH/BTC", "5m", "freqtrade/hello/world/futures/ETH_BTC-5m-mark.json", "mark"), + ("ACC_OLD/BTC", "5m", "freqtrade/hello/world/futures/ACC_OLD_BTC-5m-index.json", "index"), ]) -def test_json_pair_data_filename(pair, expected_result, candle_type): +def test_json_pair_data_filename(pair, timeframe, expected_result, candle_type): fn = JsonDataHandler._pair_data_filename( Path('freqtrade/hello/world'), pair, - '5m', + timeframe, CandleType.from_string(candle_type) ) assert isinstance(fn, Path) @@ -180,7 +181,7 @@ def test_json_pair_data_filename(pair, expected_result, candle_type): fn = JsonGzDataHandler._pair_data_filename( Path('freqtrade/hello/world'), pair, - '5m', + timeframe, candle_type=CandleType.from_string(candle_type) ) assert isinstance(fn, Path) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 5c8d7d3b0..45f8a3817 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -154,6 +154,7 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side): order = { 'type': 'stop_loss_limit', 'price': 1500, + 'stopPrice': 1500, 'info': {'stopPrice': 1500}, } assert exchange.stoploss_adjust(sl1, order, side=side) @@ -490,11 +491,11 @@ def test_fill_leverage_tiers_binance_dryrun(default_conf, mocker, leverage_tiers default_conf['margin_mode'] = MarginMode.ISOLATED exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") exchange.fill_leverage_tiers() - - leverage_tiers = leverage_tiers - + assert len(exchange._leverage_tiers.keys()) > 100 for key, value in leverage_tiers.items(): - assert exchange._leverage_tiers[key] == value + v = exchange._leverage_tiers[key] + assert isinstance(v, list) + assert len(v) == len(value) def test__set_leverage_binance(mocker, default_conf): diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index 2a148c388..74106f28b 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -13,6 +13,7 @@ import pytest from freqtrade.enums import CandleType from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date +from freqtrade.exchange.exchange import timeframe_to_msecs from freqtrade.resolvers.exchange_resolver import ExchangeResolver from tests.conftest import get_default_conf_usdt @@ -135,6 +136,7 @@ def exchange_futures(request, exchange_conf, class_mocker): class_mocker.patch( 'freqtrade.exchange.binance.Binance.fill_leverage_tiers') class_mocker.patch('freqtrade.exchange.exchange.Exchange.fetch_trading_fees') + class_mocker.patch('freqtrade.exchange.okx.Okx.additional_exchange_init') exchange = ExchangeResolver.load_exchange(request.param, exchange_conf, validate=True) yield exchange, request.param @@ -151,6 +153,25 @@ class TestCCXTExchange(): assert isinstance(markets[pair], dict) assert exchange.market_is_spot(markets[pair]) + def test_has_validations(self, exchange): + + exchange, exchangename = exchange + + exchange.validate_ordertypes({ + 'entry': 'limit', + 'exit': 'limit', + 'stoploss': 'limit', + }) + + if exchangename == 'gateio': + # gateio doesn't have market orders on spot + return + exchange.validate_ordertypes({ + 'entry': 'market', + 'exit': 'market', + 'stoploss': 'market', + }) + def test_load_markets_futures(self, exchange_futures): exchange, exchangename = exchange_futures if not exchange: @@ -197,8 +218,13 @@ class TestCCXTExchange(): l2 = exchange.fetch_l2_order_book(pair) assert 'asks' in l2 assert 'bids' in l2 + assert len(l2['asks']) >= 1 + assert len(l2['bids']) >= 1 l2_limit_range = exchange._ft_has['l2_limit_range'] l2_limit_range_required = exchange._ft_has['l2_limit_range_required'] + if exchangename == 'gateio': + # TODO: Gateio is unstable here at the moment, ignoring the limit partially. + return for val in [1, 2, 5, 25, 100]: l2 = exchange.fetch_l2_order_book(pair, val) if not l2_limit_range or val in l2_limit_range: @@ -218,7 +244,7 @@ class TestCCXTExchange(): assert len(l2['asks']) == next_limit assert len(l2['asks']) == next_limit - def test_fetch_ohlcv(self, exchange): + def test_ccxt_fetch_ohlcv(self, exchange): exchange, exchangename = exchange pair = EXCHANGES[exchangename]['pair'] timeframe = EXCHANGES[exchangename]['timeframe'] @@ -230,11 +256,44 @@ class TestCCXTExchange(): assert len(ohlcv[pair_tf]) == len(exchange.klines(pair_tf)) # assert len(exchange.klines(pair_tf)) > 200 # Assume 90% uptime ... - assert len(exchange.klines(pair_tf)) > exchange.ohlcv_candle_limit(timeframe) * 0.90 + assert len(exchange.klines(pair_tf)) > exchange.ohlcv_candle_limit( + timeframe, CandleType.SPOT) * 0.90 # Check if last-timeframe is within the last 2 intervals now = datetime.now(timezone.utc) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2)) assert exchange.klines(pair_tf).iloc[-1]['date'] >= timeframe_to_prev_date(timeframe, now) + def test_ccxt__async_get_candle_history(self, exchange): + exchange, exchangename = exchange + # For some weired reason, this test returns random lengths for bittrex. + if not exchange._ft_has['ohlcv_has_history'] or exchangename == 'bittrex': + return + pair = EXCHANGES[exchangename]['pair'] + timeframe = EXCHANGES[exchangename]['timeframe'] + candle_type = CandleType.SPOT + timeframe_ms = timeframe_to_msecs(timeframe) + now = timeframe_to_prev_date( + timeframe, datetime.now(timezone.utc)) + for offset in (360, 120, 30, 10, 5, 2): + since = now - timedelta(days=offset) + since_ms = int(since.timestamp() * 1000) + + res = exchange.loop.run_until_complete(exchange._async_get_candle_history( + pair=pair, + timeframe=timeframe, + since_ms=since_ms, + candle_type=candle_type + ) + ) + assert res + assert res[0] == pair + assert res[1] == timeframe + assert res[2] == candle_type + candles = res[3] + candle_count = exchange.ohlcv_candle_limit(timeframe, candle_type, since_ms) * 0.9 + candle_count1 = (now.timestamp() * 1000 - since_ms) // timeframe_ms + assert len(candles) >= min(candle_count, candle_count1) + assert candles[0][0] == since_ms or (since_ms + timeframe_ms) + def test_ccxt_fetch_funding_rate_history(self, exchange_futures): exchange, exchangename = exchange_futures if not exchange: diff --git a/tests/exchange/test_ccxt_precise.py b/tests/exchange/test_ccxt_precise.py new file mode 100644 index 000000000..026adb4c1 --- /dev/null +++ b/tests/exchange/test_ccxt_precise.py @@ -0,0 +1,75 @@ +from ccxt import Precise + + +ws = Precise('-1.123e-6') +ws = Precise('-1.123e-6') +xs = Precise('0.00000002') +ys = Precise('69696900000') +zs = Precise('0') + + +def test_precise(): + assert ys * xs == '1393.938' + assert xs * ys == '1393.938' + + assert ys + xs == '69696900000.00000002' + assert xs + ys == '69696900000.00000002' + assert xs - ys == '-69696899999.99999998' + assert ys - xs == '69696899999.99999998' + assert xs / ys == '0' + assert ys / xs == '3484845000000000000' + + assert ws * xs == '-0.00000000000002246' + assert xs * ws == '-0.00000000000002246' + + assert ws + xs == '-0.000001103' + assert xs + ws == '-0.000001103' + + assert xs - ws == '0.000001143' + assert ws - xs == '-0.000001143' + + assert xs / ws == '-0.017809439002671415' + assert ws / xs == '-56.15' + + assert zs * ws == '0' + assert zs * xs == '0' + assert zs * ys == '0' + assert ws * zs == '0' + assert xs * zs == '0' + assert ys * zs == '0' + + assert zs + ws == '-0.000001123' + assert zs + xs == '0.00000002' + assert zs + ys == '69696900000' + assert ws + zs == '-0.000001123' + assert xs + zs == '0.00000002' + assert ys + zs == '69696900000' + + assert abs(Precise('-500.1')) == '500.1' + assert abs(Precise('213')) == '213' + + assert abs(Precise('-500.1')) == '500.1' + assert -Precise('213') == '-213' + + assert Precise('10.1') % Precise('0.5') == '0.1' + assert Precise('5550') % Precise('120') == '30' + + assert Precise('-0.0') == Precise('0') + assert Precise('5.534000') == Precise('5.5340') + + assert min(Precise('-3.1415'), Precise('-2')) == '-3.1415' + + assert max(Precise('3.1415'), Precise('-2')) == '3.1415' + + assert Precise('2') > Precise('1.2345') + assert not Precise('-3.1415') > Precise('-2') + assert not Precise('3.1415') > Precise('3.1415') + assert Precise.string_gt('3.14150000000000000000001', '3.1415') + + assert Precise('3.1415') >= Precise('3.1415') + assert Precise('3.14150000000000000000001') >= Precise('3.1415') + + assert not Precise('3.1415') < Precise('3.1415') + + assert Precise('3.1415') <= Precise('3.1415') + assert Precise('3.1415') <= Precise('3.14150000000000000000001') diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 86928a2f6..0c4df8f5a 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -17,9 +17,9 @@ from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOr from freqtrade.exchange import Binance, Bittrex, Exchange, Kraken from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, API_RETRY_COUNT, calculate_backoff, remove_credentials) -from freqtrade.exchange.exchange import (market_is_active, timeframe_to_minutes, timeframe_to_msecs, - timeframe_to_next_date, timeframe_to_prev_date, - timeframe_to_seconds) +from freqtrade.exchange.exchange import (date_minus_candles, market_is_active, timeframe_to_minutes, + timeframe_to_msecs, timeframe_to_next_date, + timeframe_to_prev_date, timeframe_to_seconds) from freqtrade.resolvers.exchange_resolver import ExchangeResolver from tests.conftest import get_mock_coro, get_patched_exchange, log_has, log_has_re, num_log_has_re @@ -99,6 +99,8 @@ def test_remove_credentials(default_conf, caplog) -> None: def test_init_ccxt_kwargs(default_conf, mocker, caplog): mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') + aei_mock = mocker.patch('freqtrade.exchange.Exchange.additional_exchange_init') + caplog.set_level(logging.INFO) conf = copy.deepcopy(default_conf) conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True, 'asyncio_loop': True} @@ -108,6 +110,7 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog): caplog) assert ex._api_async.aiohttp_trust_env assert not ex._api.aiohttp_trust_env + assert aei_mock.call_count == 1 # Reset logging and config caplog.clear() @@ -302,6 +305,7 @@ def test_amount_to_precision( (234.53, 4, 0.5, 235.0), (0.891534, 4, 0.0001, 0.8916), (64968.89, 4, 0.01, 64968.89), + (0.000000003483, 4, 1e-12, 0.000000003483), ]) def test_price_to_precision(default_conf, mocker, price, precision_mode, precision, expected): @@ -936,6 +940,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): def test_validate_timeframes_not_in_config(default_conf, mocker): + # TODO: this test does not assert ... del default_conf["timeframe"] api_mock = MagicMock() id_mock = PropertyMock(return_value='test_exchange') @@ -951,6 +956,7 @@ def test_validate_timeframes_not_in_config(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.validate_pairs') mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') mocker.patch('freqtrade.exchange.Exchange.validate_pricing') + mocker.patch('freqtrade.exchange.Exchange.validate_required_startup_candles') Exchange(default_conf) @@ -1081,6 +1087,13 @@ def test_validate_required_startup_candles(default_conf, mocker, caplog): with pytest.raises(OperationalException, match=r'This strategy requires 6000.*'): Exchange(default_conf) + # Emulate kraken mode + ex._ft_has['ohlcv_has_history'] = False + with pytest.raises(OperationalException, + match=r'This strategy requires 2500.*, ' + r'which is more than the amount.*'): + ex.validate_required_startup_candles(2500, '5m') + def test_exchange_has(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf) @@ -1122,7 +1135,7 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag assert order["symbol"] == "ETH/BTC" assert order["amount"] == 1 assert order["leverage"] == leverage - assert order["cost"] == 1 * 200 / leverage + assert order["cost"] == 1 * 200 @pytest.mark.parametrize('side,is_short,order_reason', [ @@ -1913,7 +1926,7 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_ exchange._async_get_candle_history = Mock(wraps=mock_candle_hist) # one_call calculation * 1.8 should do 2 calls - since = 5 * 60 * exchange.ohlcv_candle_limit('5m') * 1.8 + since = 5 * 60 * exchange.ohlcv_candle_limit('5m', CandleType.SPOT) * 1.8 ret = exchange.get_historic_ohlcv( pair, "5m", @@ -1979,7 +1992,7 @@ def test_get_historic_ohlcv_as_df(default_conf, mocker, exchange_name, candle_ty exchange._async_get_candle_history = Mock(wraps=mock_candle_hist) # one_call calculation * 1.8 should do 2 calls - since = 5 * 60 * exchange.ohlcv_candle_limit('5m') * 1.8 + since = 5 * 60 * exchange.ohlcv_candle_limit('5m', CandleType.SPOT) * 1.8 ret = exchange.get_historic_ohlcv_as_df( pair, "5m", @@ -2033,7 +2046,7 @@ async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_ ) # Required candles candles = (end_ts - start_ts) / 300_000 - exp = candles // exchange.ohlcv_candle_limit('5m') + 1 + exp = candles // exchange.ohlcv_candle_limit('5m', CandleType.SPOT) + 1 # Depending on the exchange, this should be called between 1 and 6 times. assert exchange._api_async.fetch_ohlcv.call_count == exp @@ -2183,6 +2196,8 @@ async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_ @pytest.mark.asyncio async def test__async_kucoin_get_candle_history(default_conf, mocker, caplog): + from freqtrade.exchange.common import _reset_logging_mixin + _reset_logging_mixin() caplog.set_level(logging.INFO) api_mock = MagicMock() api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.DDoSProtection( @@ -2836,6 +2851,7 @@ def test_get_historic_trades_notsupported(default_conf, mocker, caplog, exchange until=trades_history[-1][0]) +@pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_cancel_order_dry_run(default_conf, mocker, exchange_name): default_conf['dry_run'] = True @@ -3001,6 +3017,7 @@ def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name): exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123) +@pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_fetch_order(default_conf, mocker, exchange_name, caplog): default_conf['dry_run'] = True @@ -3053,6 +3070,7 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog): order_id='_', pair='TKN/BTC') +@pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_fetch_stoploss_order(default_conf, mocker, exchange_name): # Don't test FTX here - that needs a separate test @@ -3380,7 +3398,7 @@ def test_ohlcv_candle_limit(default_conf, mocker, exchange_name): expected = exchange._ft_has['ohlcv_candle_limit_per_timeframe'][timeframe] # This should only run for bittrex assert exchange_name == 'bittrex' - assert exchange.ohlcv_candle_limit(timeframe) == expected + assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT) == expected def test_timeframe_to_minutes(): @@ -3462,6 +3480,17 @@ def test_timeframe_to_next_date(): assert timeframe_to_next_date("5m", date) == date + timedelta(minutes=5) +def test_date_minus_candles(): + + date = datetime(2019, 8, 12, 13, 25, 0, tzinfo=timezone.utc) + + assert date_minus_candles("5m", 3, date) == date - timedelta(minutes=15) + assert date_minus_candles("5m", 5, date) == date - timedelta(minutes=25) + assert date_minus_candles("1m", 6, date) == date - timedelta(minutes=6) + assert date_minus_candles("1h", 3, date) == date - timedelta(hours=3, minutes=25) + assert date_minus_candles("1h", 3) == timeframe_to_prev_date('1h') - timedelta(hours=3) + + @pytest.mark.parametrize( "market_symbol,base,quote,exchange,spot,margin,futures,trademode,add_dict,expected_result", [ @@ -3556,7 +3585,7 @@ def test_order_has_fee(order, expected) -> None: def test_extract_cost_curr_rate(mocker, default_conf, order, expected) -> None: mocker.patch('freqtrade.exchange.Exchange.calculate_fee_rate', MagicMock(return_value=0.01)) ex = get_patched_exchange(mocker, default_conf) - assert ex.extract_cost_curr_rate(order) == expected + assert ex.extract_cost_curr_rate(order['fee'], order['symbol'], cost=20, amount=1) == expected @pytest.mark.parametrize("order,unknown_fee_rate,expected", [ @@ -3594,6 +3623,9 @@ def test_extract_cost_curr_rate(mocker, default_conf, order, expected) -> None: 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, 1, 4.0), ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5, 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, 2, 8.0), + # Missing currency + ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, + 'fee': {'currency': None, 'cost': 0.005}}, None, None), ]) def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_rate) -> None: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'last': 0.081}) @@ -3602,7 +3634,8 @@ def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_r ex = get_patched_exchange(mocker, default_conf) - assert ex.calculate_fee_rate(order) == expected + assert ex.calculate_fee_rate(order['fee'], order['symbol'], + cost=order['cost'], amount=order['amount']) == expected @pytest.mark.parametrize('retrycount,max_retries,expected', [ @@ -3831,6 +3864,7 @@ def test_validate_trading_mode_and_margin_mode( ("bibox", "spot", {"has": {"fetchCurrencies": False}}), ("bibox", "margin", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "margin"}}), ("bibox", "futures", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "swap"}}), + ("bybit", "spot", {"options": {"defaultType": "spot"}}), ("bybit", "futures", {"options": {"defaultType": "linear"}}), ("ftx", "futures", {"options": {"defaultType": "swap"}}), ("gateio", "futures", {"options": {"defaultType": "swap"}}), @@ -3929,6 +3963,70 @@ def test_calculate_funding_fees( ) == kraken_fee +@pytest.mark.parametrize( + 'mark_price,funding_rate,futures_funding_rate', [ + (1000, 0.001, None), + (1000, 0.001, 0.01), + (1000, 0.001, 0.0), + (1000, 0.001, -0.01), + ]) +def test_combine_funding_and_mark( + default_conf, + mocker, + funding_rate, + mark_price, + futures_funding_rate, +): + exchange = get_patched_exchange(mocker, default_conf) + prior2_date = timeframe_to_prev_date('1h', datetime.now(timezone.utc) - timedelta(hours=2)) + prior_date = timeframe_to_prev_date('1h', datetime.now(timezone.utc) - timedelta(hours=1)) + trade_date = timeframe_to_prev_date('1h', datetime.now(timezone.utc)) + funding_rates = DataFrame([ + {'date': prior2_date, 'open': funding_rate}, + {'date': prior_date, 'open': funding_rate}, + {'date': trade_date, 'open': funding_rate}, + ]) + mark_rates = DataFrame([ + {'date': prior2_date, 'open': mark_price}, + {'date': prior_date, 'open': mark_price}, + {'date': trade_date, 'open': mark_price}, + ]) + + df = exchange.combine_funding_and_mark(funding_rates, mark_rates, futures_funding_rate) + assert 'open_mark' in df.columns + assert 'open_fund' in df.columns + assert len(df) == 3 + + funding_rates = DataFrame([ + {'date': trade_date, 'open': funding_rate}, + ]) + mark_rates = DataFrame([ + {'date': prior2_date, 'open': mark_price}, + {'date': prior_date, 'open': mark_price}, + {'date': trade_date, 'open': mark_price}, + ]) + df = exchange.combine_funding_and_mark(funding_rates, mark_rates, futures_funding_rate) + + if futures_funding_rate is not None: + assert len(df) == 3 + assert df.iloc[0]['open_fund'] == futures_funding_rate + assert df.iloc[1]['open_fund'] == futures_funding_rate + assert df.iloc[2]['open_fund'] == funding_rate + else: + assert len(df) == 1 + + # Empty funding rates + funding_rates = DataFrame([], columns=['date', 'open']) + df = exchange.combine_funding_and_mark(funding_rates, mark_rates, futures_funding_rate) + if futures_funding_rate is not None: + assert len(df) == 3 + assert df.iloc[0]['open_fund'] == futures_funding_rate + assert df.iloc[1]['open_fund'] == futures_funding_rate + assert df.iloc[2]['open_fund'] == futures_funding_rate + else: + assert len(df) == 0 + + def test_get_or_calculate_liquidation_price(mocker, default_conf): api_mock = MagicMock() @@ -4799,8 +4897,10 @@ def test__get_params(mocker, default_conf, exchange_name): if exchange_name == 'okx': params2['tdMode'] = 'isolated' + params2['posSide'] = 'net' assert exchange._get_params( + side="buy", ordertype='market', reduceOnly=False, time_in_force='gtc', @@ -4808,6 +4908,7 @@ def test__get_params(mocker, default_conf, exchange_name): ) == params1 assert exchange._get_params( + side="buy", ordertype='market', reduceOnly=False, time_in_force='ioc', @@ -4815,6 +4916,7 @@ def test__get_params(mocker, default_conf, exchange_name): ) == params1 assert exchange._get_params( + side="buy", ordertype='limit', reduceOnly=False, time_in_force='gtc', @@ -4827,6 +4929,7 @@ def test__get_params(mocker, default_conf, exchange_name): exchange._params = {'test': True} assert exchange._get_params( + side="buy", ordertype='limit', reduceOnly=True, time_in_force='ioc', diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index 0f16d4433..5a83b964a 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -174,6 +174,7 @@ def test_stoploss_adjust_ftx(mocker, default_conf, sl1, sl2, sl3, side): assert not exchange.stoploss_adjust(sl3, order, side=side) +@pytest.mark.usefixtures("init_persistence") def test_fetch_stoploss_order_ftx(default_conf, mocker, limit_sell_order, limit_buy_order): default_conf['dry_run'] = True order = MagicMock() diff --git a/tests/exchange/test_gateio.py b/tests/exchange/test_gateio.py index ad30a7d86..dabdbba65 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -33,7 +33,14 @@ def test_validate_order_types_gateio(default_conf, mocker): match=r'Exchange .* does not support market orders.'): ExchangeResolver.load_exchange('gateio', default_conf, True) + # market-orders supported on futures markets. + default_conf['trading_mode'] = 'futures' + default_conf['margin_mode'] = 'isolated' + ex = ExchangeResolver.load_exchange('gateio', default_conf, True) + assert ex + +@pytest.mark.usefixtures("init_persistence") def test_fetch_stoploss_order_gateio(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, id='gateio') @@ -46,6 +53,25 @@ def test_fetch_stoploss_order_gateio(default_conf, mocker): assert fetch_order_mock.call_args_list[0][1]['pair'] == 'ETH/BTC' assert fetch_order_mock.call_args_list[0][1]['params'] == {'stop': True} + default_conf['trading_mode'] = 'futures' + default_conf['margin_mode'] = 'isolated' + + exchange = get_patched_exchange(mocker, default_conf, id='gateio') + + exchange.fetch_order = MagicMock(return_value={ + 'status': 'closed', + 'id': '1234', + 'stopPrice': 5.62, + 'info': { + 'trade_id': '222555' + } + }) + + exchange.fetch_stoploss_order('1234', 'ETH/BTC') + assert exchange.fetch_order.call_count == 2 + assert exchange.fetch_order.call_args_list[0][1]['order_id'] == '1234' + assert exchange.fetch_order.call_args_list[1][1]['order_id'] == '222555' + def test_cancel_stoploss_order_gateio(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, id='gateio') diff --git a/tests/exchange/test_kucoin.py b/tests/exchange/test_kucoin.py index 8af1e83a3..ebaf5ae81 100644 --- a/tests/exchange/test_kucoin.py +++ b/tests/exchange/test_kucoin.py @@ -123,5 +123,5 @@ def test_stoploss_adjust_kucoin(mocker, default_conf): assert exchange.stoploss_adjust(1501, order, 'sell') assert not exchange.stoploss_adjust(1499, order, 'sell') # Test with invalid order case - order['info']['stop'] = None - assert not exchange.stoploss_adjust(1501, order, 'sell') + order['stopPrice'] = None + assert exchange.stoploss_adjust(1501, order, 'sell') diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 37c1ea974..91c4a3368 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -1,7 +1,40 @@ +from datetime import datetime, timedelta, timezone from unittest.mock import MagicMock, PropertyMock +import pytest + from freqtrade.enums import MarginMode, TradingMode -from tests.conftest import get_patched_exchange +from freqtrade.enums.candletype import CandleType +from freqtrade.exchange.exchange import timeframe_to_minutes +from tests.conftest import get_mock_coro, get_patched_exchange +from tests.exchange.test_exchange import ccxt_exceptionhandlers + + +def test_okx_ohlcv_candle_limit(default_conf, mocker): + exchange = get_patched_exchange(mocker, default_conf, id='okx') + timeframes = ('1m', '5m', '1h') + start_time = int(datetime(2021, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) + + for timeframe in timeframes: + assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 300 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 300 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.MARK) == 100 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 100 + + assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 100 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 100 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 100 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 100 + one_call = int((datetime.now(timezone.utc) - timedelta( + minutes=290 * timeframe_to_minutes(timeframe))).timestamp() * 1000) + + assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT, one_call) == 300 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES, one_call) == 300 + + one_call = int((datetime.now(timezone.utc) - timedelta( + minutes=320 * timeframe_to_minutes(timeframe))).timestamp() * 1000) + assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT, one_call) == 100 + assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUTURES, one_call) == 100 def test_get_maintenance_ratio_and_amt_okx( @@ -170,13 +203,77 @@ def test_get_max_pair_stake_amount_okx(default_conf, mocker, leverage_tiers): assert exchange.get_max_pair_stake_amount('TTT/USDT', 1.0) == float('inf') # Not in tiers +@pytest.mark.parametrize('mode,side,reduceonly,result', [ + ('net', 'buy', False, 'net'), + ('net', 'sell', True, 'net'), + ('net', 'sell', False, 'net'), + ('net', 'buy', True, 'net'), + ('longshort', 'buy', False, 'long'), + ('longshort', 'sell', True, 'long'), + ('longshort', 'sell', False, 'short'), + ('longshort', 'buy', True, 'short'), +]) +def test__get_posSide(default_conf, mocker, mode, side, reduceonly, result): + + exchange = get_patched_exchange(mocker, default_conf, id="okx") + exchange.net_only = mode == 'net' + assert exchange._get_posSide(side, reduceonly) == result + + +def test_additional_exchange_init_okx(default_conf, mocker): + api_mock = MagicMock() + api_mock.fetch_accounts = MagicMock(return_value=[ + {'id': '2555', + 'type': '2', + 'currency': None, + 'info': {'acctLv': '2', + 'autoLoan': False, + 'ctIsoMode': 'automatic', + 'greeksType': 'PA', + 'level': 'Lv1', + 'levelTmp': '', + 'mgnIsoMode': 'automatic', + 'posMode': 'long_short_mode', + 'uid': '2555'}}]) + default_conf['dry_run'] = False + exchange = get_patched_exchange(mocker, default_conf, id="okx", api_mock=api_mock) + assert api_mock.fetch_accounts.call_count == 0 + exchange.trading_mode = TradingMode.FUTURES + # Default to netOnly + assert exchange.net_only + exchange.additional_exchange_init() + assert api_mock.fetch_accounts.call_count == 1 + assert not exchange.net_only + + api_mock.fetch_accounts = MagicMock(return_value=[ + {'id': '2555', + 'type': '2', + 'currency': None, + 'info': {'acctLv': '2', + 'autoLoan': False, + 'ctIsoMode': 'automatic', + 'greeksType': 'PA', + 'level': 'Lv1', + 'levelTmp': '', + 'mgnIsoMode': 'automatic', + 'posMode': 'net_mode', + 'uid': '2555'}}]) + exchange.additional_exchange_init() + assert api_mock.fetch_accounts.call_count == 1 + assert exchange.net_only + default_conf['trading_mode'] = 'futures' + default_conf['margin_mode'] = 'isolated' + ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'okx', + "additional_exchange_init", "fetch_accounts") + + def test_load_leverage_tiers_okx(default_conf, mocker, markets): api_mock = MagicMock() type(api_mock).has = PropertyMock(return_value={ 'fetchLeverageTiers': False, 'fetchMarketLeverageTiers': True, }) - api_mock.fetch_market_leverage_tiers = MagicMock(side_effect=[ + api_mock.fetch_market_leverage_tiers = get_mock_coro(side_effect=[ [ { 'tier': 1, diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index fc4125a42..a3dd59004 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -40,6 +40,8 @@ class BTContainer(NamedTuple): custom_entry_price: Optional[float] = None custom_exit_price: Optional[float] = None leverage: float = 1.0 + timeout: Optional[int] = None + adjust_entry_price: Optional[float] = None def _get_frame_time_from_offset(offset): diff --git a/tests/optimize/conftest.py b/tests/optimize/conftest.py index 8a9e0cbf0..9eb3a88cc 100644 --- a/tests/optimize/conftest.py +++ b/tests/optimize/conftest.py @@ -18,11 +18,11 @@ def hyperopt_conf(default_conf): 'runmode': RunMode.HYPEROPT, 'strategy': 'HyperoptableStrategy', 'hyperopt_loss': 'ShortTradeDurHyperOptLoss', - 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), - 'epochs': 1, - 'timerange': None, - 'spaces': ['default'], - 'hyperopt_jobs': 1, + 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), + 'epochs': 1, + 'timerange': None, + 'spaces': ['default'], + 'hyperopt_jobs': 1, 'hyperopt_min_trades': 1, }) return hyperconf diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index ea13de4c8..a18196507 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -7,6 +7,7 @@ import pytest from freqtrade.data.history import get_timerange from freqtrade.enums import ExitType from freqtrade.optimize.backtesting import Backtesting +from freqtrade.persistence.trade_model import LocalTrade from tests.conftest import patch_exchange from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, _get_frame_time_from_offset, tests_timeframe) @@ -522,7 +523,7 @@ tc32 = BTContainer(data=[ trailing_stop_positive=0.03, trades=[ BTrade(exit_reason=ExitType.TRAILING_STOP_LOSS, open_tick=1, close_tick=3, is_short=True) - ] +] ) # Test 33: trailing_stop should be triggered by low of next candle, without adjusting stoploss using @@ -662,7 +663,7 @@ tc41 = BTContainer(data=[ custom_entry_price=4000, trades=[ BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=1, is_short=True) - ] +] ) # Test 42: Custom-entry-price around candle low @@ -754,6 +755,91 @@ tc47 = BTContainer(data=[ trades=[] ) +# Test 48: Custom-entry-price below all candles - readjust order +tc48 = BTContainer(data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # timeout + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 1], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + stop_loss=-0.2, roi={"0": 0.10}, profit_perc=-0.087, + use_exit_signal=True, timeout=1000, + custom_entry_price=4200, adjust_entry_price=5200, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=False)] +) + + +# Test 49: Custom-entry-price short above all candles - readjust order +tc49 = BTContainer(data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # timeout + [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 1], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], + stop_loss=-0.2, roi={"0": 0.10}, profit_perc=0.05, + use_exit_signal=True, timeout=1000, + custom_entry_price=5300, adjust_entry_price=5000, + trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)] +) + +# Test 50: Custom-entry-price below all candles - readjust order cancels order +tc50 = BTContainer(data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], # Enter long - place order + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Order readjust - cancel order + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + stop_loss=-0.01, roi={"0": 0.10}, profit_perc=0.0, + use_exit_signal=True, timeout=1000, + custom_entry_price=4200, adjust_entry_price=None, + trades=[] +) + +# Test 51: Custom-entry-price below all candles - readjust order leaves order in place and timeout. +tc51 = BTContainer(data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], # Enter long - place order + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # Order readjust - replace order + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust - maintain order + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], # Timeout + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + stop_loss=-0.01, roi={"0": 0.10}, profit_perc=0.0, + use_exit_signal=True, timeout=60, + custom_entry_price=4200, adjust_entry_price=4100, + trades=[] +) + +# Test 52: Custom-entry-price below all candles - readjust order - stoploss +tc52 = BTContainer(data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0], + [1, 5000, 5500, 4951, 5000, 6172, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4500, 5100, 6172, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 0], # stoploss hit? + [4, 4750, 4950, 4350, 4750, 6172, 0, 0]], + stop_loss=-0.03, roi={"0": 0.10}, profit_perc=-0.03, + use_exit_signal=True, timeout=1000, + custom_entry_price=4200, adjust_entry_price=5200, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=False)] +) + + +# Test 53: Custom-entry-price short above all candles - readjust order - stoploss +tc53 = BTContainer(data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0], + [1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # enter trade (signal on last candle) + [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Order readjust + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 1], # stoploss hit? + [4, 4750, 4950, 4350, 4750, 6172, 0, 0, 0, 0]], + stop_loss=-0.03, roi={"0": 0.10}, profit_perc=-0.03, + use_exit_signal=True, timeout=1000, + custom_entry_price=5300, adjust_entry_price=5000, + trades=[BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=1, close_tick=2, is_short=True)] +) TESTS = [ tc0, @@ -804,6 +890,12 @@ TESTS = [ tc45, tc46, tc47, + tc48, + tc49, + tc50, + tc51, + tc52, + tc53, ] @@ -817,6 +909,11 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer) default_conf["timeframe"] = tests_timeframe default_conf["trailing_stop"] = data.trailing_stop default_conf["trailing_only_offset_is_reached"] = data.trailing_only_offset_is_reached + if data.timeout: + default_conf['unfilledtimeout'].update({ + 'entry': data.timeout, + 'exit': data.timeout, + }) # Only add this to configuration If it's necessary if data.trailing_stop_positive is not None: default_conf["trailing_stop_positive"] = data.trailing_stop_positive @@ -840,6 +937,8 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer) backtesting.strategy.custom_entry_price = MagicMock(return_value=data.custom_entry_price) if data.custom_exit_price: backtesting.strategy.custom_exit_price = MagicMock(return_value=data.custom_exit_price) + backtesting.strategy.adjust_entry_price = MagicMock(return_value=data.adjust_entry_price) + backtesting.strategy.use_custom_stoploss = data.use_custom_stoploss backtesting.strategy.leverage = lambda **kwargs: data.leverage caplog.set_level(logging.DEBUG) @@ -866,3 +965,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer) assert res.open_date == _get_frame_time_from_offset(trade.open_tick) assert res.close_date == _get_frame_time_from_offset(trade.close_tick) assert res.is_short == trade.is_short + assert len(LocalTrade.trades) == len(data.trades) + assert len(LocalTrade.trades_open) == 0 + backtesting.cleanup() + del backtesting diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index c87a0ef73..6912184aa 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -795,10 +795,27 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: 'is_open': [False, False], 'enter_tag': [None, None], "is_short": [False, False], + 'open_timestamp': [1517251200000, 1517283000000], + 'close_timestamp': [1517265300000, 1517285400000], + 'orders': [ + [ + {'amount': 0.00957442, 'safe_price': 0.104445, 'ft_order_side': 'buy', + 'order_filled_timestamp': 1517251200000, 'ft_is_entry': True}, + {'amount': 0.00957442, 'safe_price': 0.10496853383458644, 'ft_order_side': 'sell', + 'order_filled_timestamp': 1517265300000, 'ft_is_entry': False} + ], [ + {'amount': 0.0097064, 'safe_price': 0.10302485, 'ft_order_side': 'buy', + 'order_filled_timestamp': 1517283000000, 'ft_is_entry': True}, + {'amount': 0.0097064, 'safe_price': 0.10354126528822055, 'ft_order_side': 'sell', + 'order_filled_timestamp': 1517285400000, 'ft_is_entry': False} + ] + ] }) pd.testing.assert_frame_equal(results, expected) + assert 'orders' in results.columns data_pair = processed[pair] for _, t in results.iterrows(): + assert len(t['orders']) == 2 ln = data_pair.loc[data_pair["date"] == t["open_date"]] # Check open trade rate alignes to open rate assert ln is not None @@ -1168,6 +1185,9 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', @@ -1280,6 +1300,9 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }, { @@ -1289,6 +1312,9 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } ]) @@ -1431,6 +1457,9 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker, 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }, { @@ -1440,6 +1469,9 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker, 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } ]) @@ -1534,6 +1566,9 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }, { @@ -1543,6 +1578,9 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } ]) @@ -1606,6 +1644,9 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', diff --git a/tests/optimize/test_backtesting_adjust_position.py b/tests/optimize/test_backtesting_adjust_position.py index 5babfb548..fca9c01b2 100644 --- a/tests/optimize/test_backtesting_adjust_position.py +++ b/tests/optimize/test_backtesting_adjust_position.py @@ -22,7 +22,7 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> default_conf.update({ "stake_amount": 100.0, "dry_run_wallet": 1000.0, - "strategy": "StrategyTestV2" + "strategy": "StrategyTestV3" }) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) @@ -70,9 +70,14 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> 'is_open': [False, False], 'enter_tag': [None, None], 'is_short': [False, False], + 'open_timestamp': [1517251200000, 1517283000000], + 'close_timestamp': [1517265300000, 1517285400000], }) - pd.testing.assert_frame_equal(results, expected) + pd.testing.assert_frame_equal(results.drop(columns=['orders']), expected) data_pair = processed[pair] + assert len(results.iloc[0]['orders']) == 6 + assert len(results.iloc[1]['orders']) == 2 + for _, t in results.iterrows(): ln = data_pair.loc[data_pair["date"] == t["open_date"]] # Check open trade rate alignes to open rate diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 75944390e..0f615b7a3 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -1,7 +1,7 @@ # pragma pylint: disable=missing-docstring,W0212,C0103 from datetime import datetime, timedelta from pathlib import Path -from unittest.mock import ANY, MagicMock +from unittest.mock import ANY, MagicMock, PropertyMock import pandas as pd import pytest @@ -17,9 +17,9 @@ from freqtrade.optimize.hyperopt_auto import HyperOptAuto from freqtrade.optimize.hyperopt_tools import HyperoptTools from freqtrade.optimize.optimize_reports import generate_strategy_stats from freqtrade.optimize.space import SKDecimal -from freqtrade.strategy.hyper import IntParameter -from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re, patch_exchange, - patched_configuration_load_config_file) +from freqtrade.strategy import IntParameter +from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, get_markets, log_has, log_has_re, + patch_exchange, patched_configuration_load_config_file) def generate_result_metrics(): @@ -368,6 +368,9 @@ def test_hyperopt_format_results(hyperopt): 'rejected_signals': 2, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'backtest_start_time': 1619718665, 'backtest_end_time': 1619718665, } @@ -438,6 +441,9 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } @@ -503,7 +509,6 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: hyperopt.min_date = Arrow(2017, 12, 10) hyperopt.max_date = Arrow(2017, 12, 13) hyperopt.init_spaces() - hyperopt.dimensions = hyperopt.dimensions generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values())) assert generate_optimizer_value == response_expected @@ -850,12 +855,13 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: 'strategy': 'HyperoptableStrategy', 'user_data_dir': Path(tmpdir), 'hyperopt_random_state': 42, - 'spaces': ['all'] + 'spaces': ['all'], }) hyperopt = Hyperopt(hyperopt_conf) hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0) assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter) + assert hyperopt.backtesting.strategy.bot_loop_started is True assert hyperopt.backtesting.strategy.buy_rsi.in_space is True assert hyperopt.backtesting.strategy.buy_rsi.value == 35 @@ -877,6 +883,45 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: hyperopt.get_optimizer([], 2) +def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmpdir, fee) -> None: + mocker.patch('freqtrade.exchange.Exchange.validate_config', MagicMock()) + mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) + mocker.patch('freqtrade.exchange.Exchange._load_markets') + mocker.patch('freqtrade.exchange.Exchange.markets', + PropertyMock(return_value=get_markets())) + (Path(tmpdir) / 'hyperopt_results').mkdir(parents=True) + # No hyperopt needed + hyperopt_conf.update({ + 'strategy': 'HyperoptableStrategy', + 'user_data_dir': Path(tmpdir), + 'hyperopt_random_state': 42, + 'spaces': ['all'], + # Enforce parallelity + 'epochs': 2, + 'hyperopt_jobs': 2, + 'fee': fee.return_value, + }) + hyperopt = Hyperopt(hyperopt_conf) + hyperopt.backtesting.exchange.get_max_leverage = lambda *x, **xx: 1.0 + hyperopt.backtesting.exchange.get_min_pair_stake_amount = lambda *x, **xx: 1.0 + hyperopt.backtesting.exchange.get_max_pair_stake_amount = lambda *x, **xx: 100.0 + + assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) + assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter) + assert hyperopt.backtesting.strategy.bot_loop_started is True + + assert hyperopt.backtesting.strategy.buy_rsi.in_space is True + assert hyperopt.backtesting.strategy.buy_rsi.value == 35 + assert hyperopt.backtesting.strategy.sell_rsi.value == 74 + assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30 + buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range + assert isinstance(buy_rsi_range, range) + # Range from 0 - 50 (inclusive) + assert len(list(buy_rsi_range)) == 51 + + hyperopt.start() + + def test_SKDecimal(): space = SKDecimal(1, 2, decimals=2) assert 1.5 in space diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index ff8d420b3..562e12820 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -87,6 +87,9 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'backtest_start_time': Arrow.utcnow().int_timestamp, 'backtest_end_time': Arrow.utcnow().int_timestamp, 'run_id': '123', @@ -139,6 +142,9 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): 'rejected_signals': 20, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, + 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'backtest_start_time': Arrow.utcnow().int_timestamp, 'backtest_end_time': Arrow.utcnow().int_timestamp, 'run_id': '124', @@ -165,7 +171,7 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): _backup_file(filename_last, copy_file=True) assert not filename.is_file() - store_backtest_stats(filename, stats) + store_backtest_stats(filename, stats, '2022_01_01_15_05_13') # get real Filename (it's btresult-.json) last_fn = get_latest_backtest_filename(filename_last.parent) @@ -188,7 +194,7 @@ def test_store_backtest_stats(testdatadir, mocker): dump_mock = mocker.patch('freqtrade.optimize.optimize_reports.file_dump_json') - store_backtest_stats(testdatadir, {'metadata': {}}) + store_backtest_stats(testdatadir, {'metadata': {}}, '2022_01_01_15_05_13') assert dump_mock.call_count == 3 assert isinstance(dump_mock.call_args_list[0][0][0], Path) @@ -196,7 +202,7 @@ def test_store_backtest_stats(testdatadir, mocker): dump_mock.reset_mock() filename = testdatadir / 'testresult.json' - store_backtest_stats(filename, {'metadata': {}}) + store_backtest_stats(filename, {'metadata': {}}, '2022_01_01_15_05_13') assert dump_mock.call_count == 3 assert isinstance(dump_mock.call_args_list[0][0][0], Path) # result will be testdatadir / testresult-.json @@ -210,7 +216,7 @@ def test_store_backtest_candles(testdatadir, mocker): candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}} # mock directory exporting - store_backtest_signal_candles(testdatadir, candle_dict) + store_backtest_signal_candles(testdatadir, candle_dict, '2022_01_01_15_05_13') assert dump_mock.call_count == 1 assert isinstance(dump_mock.call_args_list[0][0][0], Path) @@ -219,7 +225,7 @@ def test_store_backtest_candles(testdatadir, mocker): dump_mock.reset_mock() # mock file exporting filename = Path(testdatadir / 'testresult') - store_backtest_signal_candles(filename, candle_dict) + store_backtest_signal_candles(filename, candle_dict, '2022_01_01_15_05_13') assert dump_mock.call_count == 1 assert isinstance(dump_mock.call_args_list[0][0][0], Path) # result will be testdatadir / testresult-_signals.pkl @@ -232,7 +238,7 @@ def test_write_read_backtest_candles(tmpdir): candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}} # test directory exporting - stored_file = store_backtest_signal_candles(Path(tmpdir), candle_dict) + stored_file = store_backtest_signal_candles(Path(tmpdir), candle_dict, '2022_01_01_15_05_13') scp = open(stored_file, "rb") pickled_signal_candles = joblib.load(scp) scp.close() @@ -246,7 +252,7 @@ def test_write_read_backtest_candles(tmpdir): # test file exporting filename = Path(tmpdir / 'testresult') - stored_file = store_backtest_signal_candles(filename, candle_dict) + stored_file = store_backtest_signal_candles(filename, candle_dict, '2022_01_01_15_05_13') scp = open(stored_file, "rb") pickled_signal_candles = joblib.load(scp) scp.close() diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index d80f23c8a..c56f405e2 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -470,12 +470,16 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf): "BTC", ['ETH/BTC', 'TKN/BTC']), # VolumePairList with no offset = unchanged pairlist ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, - {"method": "OffsetFilter", "offset": 0}], + {"method": "OffsetFilter", "offset": 0, "number_assets": 0}], "USDT", ['ETH/USDT', 'NANO/USDT', 'ADAHALF/USDT', 'ADADOUBLE/USDT']), # VolumePairList with offset = 2 ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, {"method": "OffsetFilter", "offset": 2}], "USDT", ['ADAHALF/USDT', 'ADADOUBLE/USDT']), + # VolumePairList with offset and limit + ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, + {"method": "OffsetFilter", "offset": 1, "number_assets": 2}], + "USDT", ['NANO/USDT', 'ADAHALF/USDT']), # VolumePairList with higher offset, than total pairlist ([{"method": "VolumePairList", "number_assets": 20, "sort_key": "quoteVolume"}, {"method": "OffsetFilter", "offset": 100}], @@ -758,8 +762,8 @@ def test_PerformanceFilter_keep_mid_order(mocker, default_conf_usdt, fee, caplog with time_machine.travel("2021-09-01 05:00:00 +00:00") as t: create_mock_trades_usdt(fee) pm.refresh_pairlist() - assert pm.whitelist == ['XRP/USDT', 'ETC/USDT', 'ETH/USDT', - 'NEO/USDT', 'TKN/USDT', 'ADA/USDT', 'LTC/USDT'] + assert pm.whitelist == ['XRP/USDT', 'ETC/USDT', 'ETH/USDT', 'LTC/USDT', + 'NEO/USDT', 'TKN/USDT', 'ADA/USDT', ] # assert log_has_re(r'Removing pair .* since .* is below .*', caplog) # Move to "outside" of lookback window, so original sorting is restored. @@ -1152,6 +1156,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo "0.01 and above 0.99 over the last days.'}]", None ), + ({"method": "OffsetFilter", "offset": 5, "number_assets": 10}, + "[{'OffsetFilter': 'OffsetFilter - Taking 10 Pairs, starting from 5.'}]", + None + ), ]) def test_pricefilter_desc(mocker, whitelist_conf, markets, pairlistconfig, desc_expected, exception_expected): diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index b2dc99610..172e1f077 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -250,14 +250,16 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog): assert not PairLocks.is_global_lock() +@pytest.mark.parametrize('only_per_side', [False, True]) @pytest.mark.usefixtures("init_persistence") -def test_LowProfitPairs(mocker, default_conf, fee, caplog): +def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side): default_conf['protections'] = [{ "method": "LowProfitPairs", "lookback_period": 400, "stop_duration": 60, "trade_limit": 2, "required_profit": 0.0, + "only_per_side": only_per_side, }] freqtrade = get_patched_freqtradebot(mocker, default_conf) message = r"Trading stopped due to .*" @@ -292,10 +294,11 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog): # Add positive trade Trade.query.session.add(generate_mock_trade( 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, - min_ago_open=20, min_ago_close=10, profit_rate=1.15, + min_ago_open=20, min_ago_close=10, profit_rate=1.15, is_short=True )) - assert not freqtrade.protections.stop_per_pair('XRP/BTC') - assert not PairLocks.is_pair_locked('XRP/BTC') + assert freqtrade.protections.stop_per_pair('XRP/BTC') != only_per_side + assert not PairLocks.is_pair_locked('XRP/BTC', side='*') + assert PairLocks.is_pair_locked('XRP/BTC', side='long') == only_per_side Trade.query.session.add(generate_mock_trade( 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, @@ -303,9 +306,10 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog): )) # Locks due to 2nd trade - assert not freqtrade.protections.global_stop() - assert freqtrade.protections.stop_per_pair('XRP/BTC') - assert PairLocks.is_pair_locked('XRP/BTC') + assert freqtrade.protections.global_stop() != only_per_side + assert freqtrade.protections.stop_per_pair('XRP/BTC') != only_per_side + assert PairLocks.is_pair_locked('XRP/BTC', side='long') + assert PairLocks.is_pair_locked('XRP/BTC', side='*') != only_per_side assert not PairLocks.is_global_lock() diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index f4a2f6099..d20646e60 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -11,11 +11,11 @@ from freqtrade.edge import PairInfo from freqtrade.enums import SignalDirection, State, TradingMode from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError from freqtrade.persistence import Trade -from freqtrade.persistence.models import Order from freqtrade.persistence.pairlock_middleware import PairLocks from freqtrade.rpc import RPC, RPCException from freqtrade.rpc.fiat_convert import CryptoToFiatConverter -from tests.conftest import create_mock_trades, get_patched_freqtradebot, patch_get_signal +from tests.conftest import (create_mock_trades, create_mock_trades_usdt, get_patched_freqtradebot, + patch_get_signal) # Functions for recurrent object patching @@ -233,9 +233,20 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_status_table(default_conf['stake_currency'], 'USD') - + mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=False) freqtradebot.enter_positions() + result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') + assert "Since" in headers + assert "Pair" in headers + assert 'instantly' == result[0][2] + assert 'ETH/BTC' in result[0][1] + assert '0.00' == result[0][3] + assert isnan(fiat_profit_sum) + + mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=True) + freqtradebot.process() + result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert "Since" in headers assert "Pair" in headers @@ -243,8 +254,8 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert 'ETH/BTC' in result[0][1] assert '-0.41%' == result[0][3] assert isnan(fiat_profit_sum) - # Test with fiatconvert + # Test with fiatconvert rpc._fiat_converter = CryptoToFiatConverter() result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert "Since" in headers @@ -273,8 +284,8 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert isnan(fiat_profit_sum) -def test_rpc_daily_profit(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, markets, mocker) -> None: +def test__rpc_timeunit_profit(default_conf_usdt, ticker, fee, + limit_buy_order, limit_sell_order, markets, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -283,45 +294,35 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, markets=PropertyMock(return_value=markets) ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot) - stake_currency = default_conf['stake_currency'] - fiat_display_currency = default_conf['fiat_display_currency'] + freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) + create_mock_trades_usdt(fee) + + stake_currency = default_conf_usdt['stake_currency'] + fiat_display_currency = default_conf_usdt['fiat_display_currency'] rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate buy & sell - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - trade.close_date = datetime.utcnow() - trade.is_open = False # Try valid data - update.message.text = '/daily 2' - days = rpc._rpc_daily_profit(7, stake_currency, fiat_display_currency) + days = rpc._rpc_timeunit_profit(7, stake_currency, fiat_display_currency) assert len(days['data']) == 7 - assert days['stake_currency'] == default_conf['stake_currency'] - assert days['fiat_display_currency'] == default_conf['fiat_display_currency'] + assert days['stake_currency'] == default_conf_usdt['stake_currency'] + assert days['fiat_display_currency'] == default_conf_usdt['fiat_display_currency'] for day in days['data']: - # [datetime.date(2018, 1, 11), '0.00000000 BTC', '0.000 USD'] - assert (day['abs_profit'] == 0.0 or - day['abs_profit'] == 0.00006217) - - assert (day['fiat_value'] == 0.0 or - day['fiat_value'] == 0.76748865) + # {'date': datetime.date(2022, 6, 11), 'abs_profit': 13.8299999, + # 'starting_balance': 1055.37, 'rel_profit': 0.0131044, + # 'fiat_value': 0.0, 'trade_count': 2} + assert day['abs_profit'] in (0.0, pytest.approx(13.8299999), pytest.approx(-4.0)) + assert day['rel_profit'] in (0.0, pytest.approx(0.01310441), pytest.approx(-0.00377583)) + assert day['trade_count'] in (0, 1, 2) + assert day['starting_balance'] in (pytest.approx(1059.37), pytest.approx(1055.37)) + assert day['fiat_value'] in (0.0, ) # ensure first day is current date assert str(days['data'][0]['date']) == str(datetime.utcnow().date()) # Try invalid data with pytest.raises(RPCException, match=r'.*must be an integer greater than 0*'): - rpc._rpc_daily_profit(0, stake_currency, fiat_display_currency) + rpc._rpc_timeunit_profit(0, stake_currency, fiat_display_currency) @pytest.mark.parametrize('is_short', [True, False]) @@ -405,13 +406,8 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short): assert stoploss_mock.call_count == 0 -def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.rpc.fiat_convert.CoinGeckoAPI', - get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}), - ) - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) +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( 'freqtrade.exchange.Exchange', @@ -419,10 +415,9 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, get_fee=fee, ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot) - stake_currency = default_conf['stake_currency'] - fiat_display_currency = default_conf['fiat_display_currency'] + freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) + stake_currency = default_conf_usdt['stake_currency'] + fiat_display_currency = default_conf_usdt['fiat_display_currency'] rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() @@ -435,75 +430,40 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, assert res['latest_trade_timestamp'] == 0 # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'sell') - trade.update_trade(oobj) - - # Update the ticker with a market going up - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up - ) - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - trade.close_date = datetime.utcnow() - trade.is_open = False - - freqtradebot.enter_positions() - trade = Trade.query.first() - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Update the ticker with a market going up - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up - ) - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - trade.close_date = datetime.utcnow() - trade.is_open = False + create_mock_trades_usdt(fee) stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) - assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05) - assert prec_satoshi(stats['profit_closed_percent_mean'], 6.2) - assert prec_satoshi(stats['profit_closed_fiat'], 0.93255) - assert prec_satoshi(stats['profit_all_coin'], 5.802e-05) - assert prec_satoshi(stats['profit_all_percent_mean'], 2.89) - assert prec_satoshi(stats['profit_all_fiat'], 0.8703) - assert stats['trade_count'] == 2 - assert stats['first_trade_date'] == 'just now' - assert stats['latest_trade_date'] == 'just now' - assert stats['avg_duration'] in ('0:00:00', '0:00:01', '0:00:02') - assert stats['best_pair'] == 'ETH/BTC' - assert prec_satoshi(stats['best_rate'], 6.2) + assert pytest.approx(stats['profit_closed_coin']) == 9.83 + assert pytest.approx(stats['profit_closed_percent_mean']) == -1.67 + assert pytest.approx(stats['profit_closed_fiat']) == 10.813 + assert pytest.approx(stats['profit_all_coin']) == -77.45964918 + assert pytest.approx(stats['profit_all_percent_mean']) == -57.86 + assert pytest.approx(stats['profit_all_fiat']) == -85.205614098 + assert stats['trade_count'] == 7 + assert stats['first_trade_date'] == '2 days ago' + assert stats['latest_trade_date'] == '17 minutes ago' + assert stats['avg_duration'] in ('0:17:40') + assert stats['best_pair'] == 'XRP/USDT' + assert stats['best_rate'] == 10.0 # Test non-available pair mocker.patch('freqtrade.exchange.Exchange.get_rate', - MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) + MagicMock(side_effect=ExchangeError("Pair 'XRP/USDT' not available"))) stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) - assert stats['trade_count'] == 2 - assert stats['first_trade_date'] == 'just now' - assert stats['latest_trade_date'] == 'just now' - assert stats['avg_duration'] in ('0:00:00', '0:00:01', '0:00:02') - assert stats['best_pair'] == 'ETH/BTC' - assert prec_satoshi(stats['best_rate'], 6.2) + assert stats['trade_count'] == 7 + assert stats['first_trade_date'] == '2 days ago' + assert stats['latest_trade_date'] == '17 minutes ago' + assert stats['avg_duration'] in ('0:17:40') + assert stats['best_pair'] == 'XRP/USDT' + assert stats['best_rate'] == 10.0 assert isnan(stats['profit_all_coin']) # Test that rpc_trade_statistics can handle trades that lacks # trade.open_rate (it is set to None) -def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, - ticker_sell_up, limit_buy_order, limit_sell_order): - mocker.patch.multiple( - 'freqtrade.rpc.fiat_convert.CoinGeckoAPI', - get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}), - ) +def test_rpc_trade_statistics_closed(mocker, default_conf_usdt, ticker, fee): mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', - return_value=15000.0) + return_value=1.1) mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -511,46 +471,32 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, get_fee=fee, ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(freqtradebot) - stake_currency = default_conf['stake_currency'] - fiat_display_currency = default_conf['fiat_display_currency'] + stake_currency = default_conf_usdt['stake_currency'] + fiat_display_currency = default_conf_usdt['fiat_display_currency'] rpc = RPC(freqtradebot) # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - # Update the ticker with a market going up - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_up, - get_fee=fee - ) - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - trade.close_date = datetime.utcnow() - trade.is_open = False + create_mock_trades_usdt(fee) for trade in Trade.query.order_by(Trade.id).all(): trade.open_rate = None stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) - assert prec_satoshi(stats['profit_closed_coin'], 0) - assert prec_satoshi(stats['profit_closed_percent_mean'], 0) - assert prec_satoshi(stats['profit_closed_fiat'], 0) - assert prec_satoshi(stats['profit_all_coin'], 0) - assert prec_satoshi(stats['profit_all_percent_mean'], 0) - assert prec_satoshi(stats['profit_all_fiat'], 0) - assert stats['trade_count'] == 1 - assert stats['first_trade_date'] == 'just now' - assert stats['latest_trade_date'] == 'just now' + assert stats['profit_closed_coin'] == 0 + assert stats['profit_closed_percent_mean'] == 0 + assert stats['profit_closed_fiat'] == 0 + assert stats['profit_all_coin'] == 0 + assert stats['profit_all_percent_mean'] == 0 + assert stats['profit_all_fiat'] == 0 + assert stats['trade_count'] == 7 + assert stats['first_trade_date'] == '2 days ago' + assert stats['latest_trade_date'] == '17 minutes ago' assert stats['avg_duration'] == '0:00:00' - assert stats['best_pair'] == 'ETH/BTC' - assert prec_satoshi(stats['best_rate'], 6.2) + assert stats['best_pair'] == 'XRP/USDT' + assert stats['best_rate'] == 10.0 def test_rpc_balance_handle_error(default_conf, mocker): @@ -902,8 +848,7 @@ def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None: assert cancel_order_mock.call_count == 3 -def test_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: +def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -912,34 +857,21 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, get_fee=fee, ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade + create_mock_trades_usdt(fee) - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False res = rpc._rpc_performance() - assert len(res) == 1 - assert res[0]['pair'] == 'ETH/BTC' + assert len(res) == 3 + assert res[0]['pair'] == 'XRP/USDT' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) + assert res[0]['profit_pct'] == 10.0 -def test_enter_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: +def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None: + mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -953,34 +885,22 @@ def test_enter_tag_performance_handle(default_conf, ticker, limit_buy_order, fee rpc = RPC(freqtradebot) # Create some test data + create_mock_trades_usdt(fee) freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False res = rpc._rpc_enter_tag_performance(None) - assert len(res) == 1 - assert res[0]['enter_tag'] == 'Other' + assert len(res) == 3 + assert res[0]['enter_tag'] == 'TEST3' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) + assert res[0]['profit_pct'] == 10.0 - trade.enter_tag = "TEST_TAG" res = rpc._rpc_enter_tag_performance(None) - assert len(res) == 1 - assert res[0]['enter_tag'] == 'TEST_TAG' + assert len(res) == 3 + assert res[0]['enter_tag'] == 'TEST3' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) + assert res[0]['profit_pct'] == 10.0 def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): @@ -1012,8 +932,7 @@ def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): assert prec_satoshi(res[0]['profit_pct'], 0.5) -def test_exit_reason_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: +def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -1022,39 +941,22 @@ def test_exit_reason_performance_handle(default_conf, ticker, limit_buy_order, f get_fee=fee, ) - freqtradebot = get_patched_freqtradebot(mocker, default_conf) + freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade + create_mock_trades_usdt(fee) - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False res = rpc._rpc_exit_reason_performance(None) - assert len(res) == 1 - assert res[0]['exit_reason'] == 'Other' + assert len(res) == 3 + assert res[0]['exit_reason'] == 'roi' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) + assert res[0]['profit_pct'] == 10.0 - trade.exit_reason = "TEST1" - res = rpc._rpc_exit_reason_performance(None) - - assert len(res) == 1 - assert res[0]['exit_reason'] == 'TEST1' - assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) + assert res[1]['exit_reason'] == 'exit_signal' + assert res[2]['exit_reason'] == 'Other' def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee): @@ -1086,8 +988,7 @@ def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee): assert prec_satoshi(res[0]['profit_pct'], 0.5) -def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: +def test_mix_tag_performance_handle(default_conf, ticker, fee, mocker) -> None: mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock()) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -1101,35 +1002,14 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee, rpc = RPC(freqtradebot) # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade + create_mock_trades_usdt(fee) - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False res = rpc._rpc_mix_tag_performance(None) - assert len(res) == 1 - assert res[0]['mix_tag'] == 'Other Other' + assert len(res) == 3 + assert res[0]['mix_tag'] == 'TEST3 roi' assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) - - trade.enter_tag = "TESTBUY" - trade.exit_reason = "TESTSELL" - res = rpc._rpc_mix_tag_performance(None) - - assert len(res) == 1 - assert res[0]['mix_tag'] == 'TESTBUY TESTSELL' - assert res[0]['count'] == 1 - assert prec_satoshi(res[0]['profit_pct'], 6.2) + assert res[0]['profit_pct'] == 10.0 def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee): diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index ac2f1c3ec..57c08f48e 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -578,9 +578,10 @@ def test_api_trades(botclient, mocker, fee, markets, is_short): ) rc = client_get(client, f"{BASE_URI}/trades") assert_response(rc) - assert len(rc.json()) == 3 + assert len(rc.json()) == 4 assert rc.json()['trades_count'] == 0 assert rc.json()['total_trades'] == 0 + assert rc.json()['offset'] == 0 create_mock_trades(fee, is_short=is_short) Trade.query.session.flush() @@ -724,7 +725,9 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): 'profit_closed_fiat': -83.19455985, 'profit_closed_ratio_mean': -0.0075, 'profit_closed_percent_mean': -0.75, 'profit_closed_ratio_sum': -0.015, 'profit_closed_percent_sum': -1.5, 'profit_closed_ratio': -6.739057628404269e-06, - 'profit_closed_percent': -0.0, 'winning_trades': 0, 'losing_trades': 2} + 'profit_closed_percent': -0.0, 'winning_trades': 0, 'losing_trades': 2, + 'profit_factor': 0.0, 'trading_volume': 91.074, + } ), ( False, @@ -737,7 +740,9 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): 'profit_closed_fiat': 9.124559849999999, 'profit_closed_ratio_mean': 0.0075, 'profit_closed_percent_mean': 0.75, 'profit_closed_ratio_sum': 0.015, 'profit_closed_percent_sum': 1.5, 'profit_closed_ratio': 7.391275897987988e-07, - 'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0} + 'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0, + 'profit_factor': None, 'trading_volume': 91.074, + } ), ( None, @@ -750,7 +755,9 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): 'profit_closed_fiat': -67.02260985, 'profit_closed_ratio_mean': 0.0025, 'profit_closed_percent_mean': 0.25, 'profit_closed_ratio_sum': 0.005, 'profit_closed_percent_sum': 0.5, 'profit_closed_ratio': -5.429078808526421e-06, - 'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1} + 'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1, + 'profit_factor': 0.02775724835771106, 'trading_volume': 91.074, + } ) ]) def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected): @@ -803,6 +810,10 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected) 'closed_trade_count': 2, 'winning_trades': expected['winning_trades'], 'losing_trades': expected['losing_trades'], + 'profit_factor': expected['profit_factor'], + 'max_drawdown': ANY, + 'max_drawdown_abs': ANY, + 'trading_volume': expected['trading_volume'], } @@ -852,8 +863,8 @@ def test_api_performance(botclient, fee): close_rate=0.265441, ) - trade.close_profit = trade.calc_profit_ratio() - trade.close_profit_abs = trade.calc_profit() + trade.close_profit = trade.calc_profit_ratio(trade.close_rate) + trade.close_profit_abs = trade.calc_profit(trade.close_rate) Trade.query.session.add(trade) trade = Trade( @@ -868,8 +879,8 @@ def test_api_performance(botclient, fee): fee_open=fee.return_value, close_rate=0.391 ) - trade.close_profit = trade.calc_profit_ratio() - trade.close_profit_abs = trade.calc_profit() + trade.close_profit = trade.calc_profit_ratio(trade.close_rate) + trade.close_profit_abs = trade.calc_profit(trade.close_rate) Trade.query.session.add(trade) Trade.commit() @@ -972,6 +983,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short, 'exchange': 'binance', 'leverage': 1.0, 'interest_rate': 0.0, + 'liquidation_price': None, 'funding_fees': None, 'trading_mode': ANY, 'orders': [ANY], @@ -1175,6 +1187,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): 'exchange': 'binance', 'leverage': None, 'interest_rate': None, + 'liquidation_price': None, 'funding_fees': None, 'trading_mode': 'spot', 'orders': [], @@ -1382,12 +1395,15 @@ def test_api_strategies(botclient): rc = client_get(client, f"{BASE_URI}/strategies") assert_response(rc) + assert rc.json() == {'strategies': [ 'HyperoptableStrategy', + 'HyperoptableStrategyV2', 'InformativeDecoratorTest', 'StrategyTestV2', 'StrategyTestV3', - 'StrategyTestV3Futures', + 'StrategyTestV3Analysis', + 'StrategyTestV3Futures' ]} diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 2bc4fc5c3..91ee92fd7 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -12,6 +12,7 @@ from unittest.mock import ANY, MagicMock import arrow import pytest +from pandas import DataFrame from telegram import Chat, Message, ReplyKeyboardMarkup, Update from telegram.error import BadRequest, NetworkError, TelegramError @@ -27,8 +28,9 @@ from freqtrade.persistence.models import Order from freqtrade.rpc import RPC from freqtrade.rpc.rpc import RPCException from freqtrade.rpc.telegram import Telegram, authorized_only -from tests.conftest import (CURRENT_TEST_STRATEGY, create_mock_trades, get_patched_freqtradebot, - log_has, log_has_re, patch_exchange, patch_get_signal, patch_whitelist) +from tests.conftest import (CURRENT_TEST_STRATEGY, create_mock_trades, create_mock_trades_usdt, + get_patched_freqtradebot, log_has, log_has_re, patch_exchange, + patch_get_signal, patch_whitelist) class DummyCls(Telegram): @@ -404,12 +406,10 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: assert msg_mock.call_count == 1 -def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: - default_conf['max_open_trades'] = 1 +def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None: mocker.patch( 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', - return_value=15000.0 + return_value=1.1 ) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -417,25 +417,12 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) + # Move date to within day + time_machine.move_to('2022-06-11 08:00:00+00:00') # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobjs) - - trade.close_date = datetime.utcnow() - trade.is_open = False + create_mock_trades_usdt(fee) # Try valid data # /daily 2 @@ -446,10 +433,11 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, assert "Daily Profit over the last 2 days:" in msg_mock.call_args_list[0][0][0] assert 'Day ' in msg_mock.call_args_list[0][0][0] assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] + assert ' 13.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 15.21 USD' in msg_mock.call_args_list[0][0][0] + assert '(2)' in msg_mock.call_args_list[0][0][0] + assert '(2) 13.83 USDT 15.21 USD 1.31%' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -458,32 +446,23 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, assert msg_mock.call_count == 1 assert "Daily Profit over the last 7 days:" in msg_mock.call_args_list[0][0][0] assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] + assert str((datetime.utcnow() - timedelta(days=5)).date()) in msg_mock.call_args_list[0][0][0] + assert ' 13.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 15.21 USD' in msg_mock.call_args_list[0][0][0] + assert '(2)' in msg_mock.call_args_list[0][0][0] + assert '(1)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() - freqtradebot.config['max_open_trades'] = 2 - # Add two other trades - n = freqtradebot.enter_positions() - assert n == 2 - - trades = Trade.query.all() - for trade in trades: - trade.update_trade(oobj) - trade.update_trade(oobjs) - trade.close_date = datetime.utcnow() - trade.is_open = False # /daily 1 context = MagicMock() context.args = ["1"] telegram._daily(update=update, context=context) - assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] + assert ' 13.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 15.21 USD' in msg_mock.call_args_list[0][0][0] + assert '(2)' in msg_mock.call_args_list[0][0][0] def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: @@ -512,15 +491,14 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: context = MagicMock() context.args = ["today"] telegram._daily(update=update, context=context) - assert str('Daily Profit over the last 7 days:') in msg_mock.call_args_list[0][0][0] + assert 'Daily Profit over the last 7 days:' in msg_mock.call_args_list[0][0][0] -def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: - default_conf['max_open_trades'] = 1 +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=15000.0 + return_value=1.1 ) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -528,25 +506,10 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobjs) - - trade.close_date = datetime.utcnow() - trade.is_open = False + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) + # Move to saturday - so all trades are within that week + time_machine.move_to('2022-06-11') + create_mock_trades_usdt(fee) # Try valid data # /weekly 2 @@ -560,10 +523,10 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee, today = datetime.utcnow().date() first_iso_day_of_current_week = today - timedelta(days=today.weekday()) assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] + assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -573,44 +536,10 @@ def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee, assert "Weekly Profit over the last 8 weeks (starting from Monday):" \ in msg_mock.call_args_list[0][0][0] assert 'Weekly' in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - freqtradebot.config['max_open_trades'] = 2 - # Add two other trades - n = freqtradebot.enter_positions() - assert n == 2 - - trades = Trade.query.all() - for trade in trades: - trade.update_trade(oobj) - trade.update_trade(oobjs) - trade.close_date = datetime.utcnow() - trade.is_open = False - - # /weekly 1 - # By default, the 8 previous weeks are shown - # So the previous modified trade should be excluded from the stats - context = MagicMock() - context.args = ["1"] - telegram._weekly(update=update, context=context) - assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] - - -def test_weekly_wrong_input(default_conf, update, ticker, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) + assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Try invalid data msg_mock.reset_mock() @@ -629,16 +558,17 @@ def test_weekly_wrong_input(default_conf, update, ticker, mocker) -> None: context = MagicMock() context.args = ["this week"] telegram._weekly(update=update, context=context) - assert str('Weekly Profit over the last 8 weeks (starting from Monday):') \ + assert ( + 'Weekly Profit over the last 8 weeks (starting from Monday):' in msg_mock.call_args_list[0][0][0] + ) -def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: - default_conf['max_open_trades'] = 1 +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=15000.0 + return_value=1.1 ) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -646,25 +576,10 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobjs) - - trade.close_date = datetime.utcnow() - trade.is_open = False + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) + # Move to day within the month so all mock trades fall into this week. + time_machine.move_to('2022-06-11') + create_mock_trades_usdt(fee) # Try valid data # /monthly 2 @@ -677,10 +592,10 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee, today = datetime.utcnow().date() current_month = f"{today.year}-{today.month:02} " assert current_month in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] + assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() @@ -691,24 +606,13 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee, assert 'Monthly Profit over the last 6 months:' in msg_mock.call_args_list[0][0][0] assert 'Month ' in msg_mock.call_args_list[0][0][0] assert current_month in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] + assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] + assert '(0)' in msg_mock.call_args_list[0][0][0] # Reset msg_mock msg_mock.reset_mock() - freqtradebot.config['max_open_trades'] = 2 - # Add two other trades - n = freqtradebot.enter_positions() - assert n == 2 - - trades = Trade.query.all() - for trade in trades: - trade.update_trade(oobj) - trade.update_trade(oobjs) - trade.close_date = datetime.utcnow() - trade.is_open = False # /monthly 12 context = MagicMock() @@ -716,24 +620,14 @@ def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee, telegram._monthly(update=update, context=context) assert msg_mock.call_count == 1 assert 'Monthly Profit over the last 12 months:' in msg_mock.call_args_list[0][0][0] - assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] + assert ' 9.83 USDT' in msg_mock.call_args_list[0][0][0] + assert ' 10.81 USD' in msg_mock.call_args_list[0][0][0] + assert '(3)' in msg_mock.call_args_list[0][0][0] # The one-digit months should contain a zero, Eg: September 2021 = "2021-09" # Since we loaded the last 12 months, any month should appear assert str('-09') in msg_mock.call_args_list[0][0][0] - -def test_monthly_wrong_input(default_conf, update, ticker, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - # Try invalid data msg_mock.reset_mock() freqtradebot.state = State.RUNNING @@ -754,16 +648,16 @@ def test_monthly_wrong_input(default_conf, update, ticker, mocker) -> None: assert str('Monthly Profit over the last 6 months:') in msg_mock.call_args_list[0][0][0] -def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) +def test_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( 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, + fetch_ticker=ticker_usdt, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) patch_get_signal(freqtradebot) telegram._profit(update=update, context=MagicMock()) @@ -775,10 +669,6 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, freqtradebot.enter_positions() trade = Trade.query.first() - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - context = MagicMock() # Test with invalid 2nd argument (should silently pass) context.args = ["aaa"] @@ -786,15 +676,16 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, assert msg_mock.call_count == 1 assert 'No closed trade' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] - mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=0.01) - assert ('∙ `-0.000005 BTC (-0.50%) (-0.0 \N{GREEK CAPITAL LETTER SIGMA}%)`' + mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=1000) + assert ('∙ `0.298 USDT (0.50%) (0.03 \N{GREEK CAPITAL LETTER SIGMA}%)`' in msg_mock.call_args_list[-1][0][0]) msg_mock.reset_mock() # Update the ticker with a market going up mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', ticker_sell_up) # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') + oobj = Order.parse_from_ccxt_object( + limit_sell_order_usdt, limit_sell_order_usdt['symbol'], 'sell') trade.update_trade(oobj) trade.close_date = datetime.now(timezone.utc) @@ -805,20 +696,22 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, telegram._profit(update=update, context=context) assert msg_mock.call_count == 1 assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`' + assert ('∙ `5.685 USDT (9.45%) (0.57 \N{GREEK CAPITAL LETTER SIGMA}%)`' in msg_mock.call_args_list[-1][0][0]) - assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] + assert '∙ `6.253 USD`' in msg_mock.call_args_list[-1][0][0] assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`' + assert ('∙ `5.685 USDT (9.45%) (0.57 \N{GREEK CAPITAL LETTER SIGMA}%)`' in msg_mock.call_args_list[-1][0][0]) - assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] + assert '∙ `6.253 USD`' in msg_mock.call_args_list[-1][0][0] - assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0] + assert '*Best Performing:* `ETH/USDT: 9.45%`' in msg_mock.call_args_list[-1][0][0] + assert '*Max Drawdown:*' in msg_mock.call_args_list[-1][0][0] + assert '*Profit factor:*' in msg_mock.call_args_list[-1][0][0] + assert '*Trading volume:* `60 USDT`' in msg_mock.call_args_list[-1][0][0] @pytest.mark.parametrize('is_short', [True, False]) -def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker, is_short) -> None: +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( 'freqtrade.exchange.Exchange', @@ -1350,71 +1243,43 @@ def test_force_enter_no_pair(default_conf, update, mocker) -> None: assert fbuy_mock.call_count == 1 -def test_telegram_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: +def test_telegram_performance_handle(default_conf_usdt, update, ticker, fee, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade + create_mock_trades_usdt(fee) - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False telegram._performance(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert 'Performance' in msg_mock.call_args_list[0][0][0] - assert 'ETH/BTC\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] + assert 'XRP/USDT\t9.842 USDT (10.00%) (1)' in msg_mock.call_args_list[0][0][0] def test_telegram_entry_tag_performance_handle( - default_conf, update, ticker, fee, limit_buy_order, limit_sell_order, mocker) -> None: + default_conf_usdt, update, ticker, fee, mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) patch_get_signal(freqtradebot) - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade + create_mock_trades_usdt(fee) - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - trade.enter_tag = "TESTBUY" - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False context = MagicMock() telegram._enter_tag_performance(update=update, context=context) assert msg_mock.call_count == 1 assert 'Entry Tag Performance' in msg_mock.call_args_list[0][0][0] - assert 'TESTBUY\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] + assert 'TEST1\t3.987 USDT (5.00%) (1)' in msg_mock.call_args_list[0][0][0] - context.args = [trade.pair] + context.args = ['XRP/USDT'] telegram._enter_tag_performance(update=update, context=context) assert msg_mock.call_count == 2 @@ -1427,37 +1292,24 @@ def test_telegram_entry_tag_performance_handle( assert "Error" in msg_mock.call_args_list[0][0][0] -def test_telegram_exit_reason_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: +def test_telegram_exit_reason_performance_handle(default_conf_usdt, update, ticker, fee, + mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) patch_get_signal(freqtradebot) - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - trade.exit_reason = 'TESTSELL' - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) + create_mock_trades_usdt(fee) - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False context = MagicMock() telegram._exit_reason_performance(update=update, context=context) assert msg_mock.call_count == 1 assert 'Exit Reason Performance' in msg_mock.call_args_list[0][0][0] - assert 'TESTSELL\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] - context.args = [trade.pair] + assert 'roi\t9.842 USDT (10.00%) (1)' in msg_mock.call_args_list[0][0][0] + context.args = ['XRP/USDT'] telegram._exit_reason_performance(update=update, context=context) assert msg_mock.call_count == 2 @@ -1471,43 +1323,27 @@ def test_telegram_exit_reason_performance_handle(default_conf, update, ticker, f assert "Error" in msg_mock.call_args_list[0][0][0] -def test_telegram_mix_tag_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: +def test_telegram_mix_tag_performance_handle(default_conf_usdt, update, ticker, fee, + mocker) -> None: mocker.patch.multiple( 'freqtrade.exchange.Exchange', fetch_ticker=ticker, get_fee=fee, ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) patch_get_signal(freqtradebot) # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - trade.enter_tag = "TESTBUY" - trade.exit_reason = "TESTSELL" - - # Simulate fulfilled LIMIT_BUY order for trade - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False + create_mock_trades_usdt(fee) context = MagicMock() telegram._mix_tag_performance(update=update, context=context) assert msg_mock.call_count == 1 assert 'Mix Tag Performance' in msg_mock.call_args_list[0][0][0] - assert ('TESTBUY TESTSELL\t0.00006217 BTC (6.20%) (1)' + assert ('TEST3 roi\t9.842 USDT (10.00%) (1)' in msg_mock.call_args_list[0][0][0]) - context.args = [trade.pair] + context.args = ['XRP/USDT'] telegram._mix_tag_performance(update=update, context=context) assert msg_mock.call_count == 2 @@ -1820,8 +1656,17 @@ def test_show_config_handle(default_conf, update, mocker) -> None: (RPCMessageType.ENTRY, 'Long', 'long_signal_01', 1.0), (RPCMessageType.ENTRY, 'Long', 'long_signal_01', 5.0), (RPCMessageType.ENTRY, 'Short', 'short_signal_01', 2.0)]) -def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type, - enter, enter_signal, leverage) -> None: +def test_send_msg_enter_notification(default_conf, mocker, caplog, message_type, + enter, enter_signal, leverage) -> None: + default_conf['telegram']['notification_settings']['show_candle'] = 'ohlc' + df = DataFrame({ + 'open': [1.1], + 'high': [2.2], + 'low': [1.0], + 'close': [1.5], + }) + mocker.patch('freqtrade.data.dataprovider.DataProvider.get_analyzed_dataframe', + return_value=(df, 1)) msg = { 'type': message_type, @@ -1839,6 +1684,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type, 'fiat_currency': 'USD', 'current_rate': 1.099e-05, 'amount': 1333.3333333333335, + 'analyzed_candle': {'open': 1.1, 'high': 2.2, 'low': 1.0, 'close': 1.5}, 'open_date': arrow.utcnow().shift(hours=-1) } telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) @@ -1847,7 +1693,8 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type, leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else '' assert msg_mock.call_args[0][0] == ( - f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n' + f'\N{LARGE BLUE CIRCLE} *Binance (dry):* {enter} ETH/BTC (#1)\n' + '*Candle OHLC*: `1.1, 2.2, 1.0, 1.5`\n' f'*Enter Tag:* `{enter_signal}`\n' '*Amount:* `1333.33333333`\n' f'{leverage_text}' @@ -1875,7 +1722,8 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type, @pytest.mark.parametrize('message_type,enter_signal', [ (RPCMessageType.ENTRY_CANCEL, 'long_signal_01'), (RPCMessageType.ENTRY_CANCEL, 'short_signal_01')]) -def test_send_msg_buy_cancel_notification(default_conf, mocker, message_type, enter_signal) -> None: +def test_send_msg_enter_cancel_notification( + default_conf, mocker, message_type, enter_signal) -> None: telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) @@ -1887,7 +1735,7 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker, message_type, en 'pair': 'ETH/BTC', 'reason': CANCEL_REASON['TIMEOUT'] }) - assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance:* ' + assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance (dry):* ' 'Cancelling enter Order for ETH/BTC (#1). ' 'Reason: cancelled due to timeout.') @@ -1949,7 +1797,7 @@ def test_send_msg_entry_fill_notification(default_conf, mocker, message_type, en }) leverage_text = f'*Leverage:* `{leverage}`\n' if leverage != 1.0 else '' assert msg_mock.call_args[0][0] == ( - f'\N{CHECK MARK} *Binance:* {entered}ed ETH/BTC (#1)\n' + f'\N{CHECK MARK} *Binance (dry):* {entered}ed ETH/BTC (#1)\n' f'*Enter Tag:* `{enter_signal}`\n' '*Amount:* `1333.33333333`\n' f"{leverage_text}" @@ -1987,7 +1835,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: 'close_date': arrow.utcnow(), }) assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' + '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' '*Enter Tag:* `buy_signal1`\n' '*Exit Reason:* `stop_loss`\n' @@ -2021,7 +1869,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None: 'close_date': arrow.utcnow(), }) assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' + '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' '*Unrealized Profit:* `-57.41%`\n' '*Enter Tag:* `buy_signal1`\n' '*Exit Reason:* `stop_loss`\n' @@ -2050,10 +1898,12 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None: 'reason': 'Cancelled on exchange' }) assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).' + '\N{WARNING SIGN} *Binance (dry):* Cancelling exit Order for KEY/ETH (#1).' ' Reason: Cancelled on exchange.') msg_mock.reset_mock() + # Test with live mode (no dry appendix) + telegram._config['dry_run'] = False telegram.send_msg({ 'type': RPCMessageType.EXIT_CANCEL, 'trade_id': 1, @@ -2102,7 +1952,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker, direction, leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else '' assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n' + '\N{WARNING SIGN} *Binance (dry):* Exited KEY/ETH (#1)\n' '*Profit:* `-57.41%`\n' f'*Enter Tag:* `{enter_signal}`\n' '*Exit Reason:* `stop_loss`\n' @@ -2158,6 +2008,7 @@ def test_send_msg_unknown_type(default_conf, mocker) -> None: def test_send_msg_buy_notification_no_fiat( default_conf, mocker, message_type, enter, enter_signal, leverage) -> None: del default_conf['fiat_display_currency'] + default_conf['dry_run'] = False telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) telegram.send_msg({ @@ -2227,7 +2078,7 @@ def test_send_msg_sell_notification_no_fiat( leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else '' assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n' + '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' '*Unrealized Profit:* `-57.41%`\n' f'*Enter Tag:* `{enter_signal}`\n' '*Exit Reason:* `stop_loss`\n' diff --git a/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py index db357f80f..4d65b4966 100644 --- a/tests/rpc/test_rpc_webhook.py +++ b/tests/rpc/test_rpc_webhook.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring, C0103, protected-access +from datetime import datetime, timedelta from unittest.mock import MagicMock import pytest @@ -7,6 +8,7 @@ from requests import RequestException from freqtrade.enums import ExitType, RPCMessageType from freqtrade.rpc import RPC +from freqtrade.rpc.discord import Discord from freqtrade.rpc.webhook import Webhook from tests.conftest import get_patched_freqtradebot, log_has @@ -406,3 +408,42 @@ def test__send_msg_with_raw_format(default_conf, mocker, caplog): webhook._send_msg(msg) assert post.call_args[1] == {'data': msg['data'], 'headers': {'Content-Type': 'text/plain'}} + + +def test_send_msg_discord(default_conf, mocker): + + default_conf["discord"] = { + 'enabled': True, + 'webhook_url': "https://webhookurl..." + } + msg_mock = MagicMock() + mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock) + discord = Discord(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf) + + msg = { + 'type': RPCMessageType.EXIT_FILL, + 'trade_id': 1, + 'exchange': 'Binance', + 'pair': 'ETH/BTC', + 'direction': 'Long', + 'gain': "profit", + 'close_rate': 0.005, + 'amount': 0.8, + 'order_type': 'limit', + 'open_date': datetime.now() - timedelta(days=1), + 'close_date': datetime.now(), + 'open_rate': 0.004, + 'current_rate': 0.005, + 'profit_amount': 0.001, + 'profit_ratio': 0.20, + 'stake_currency': 'BTC', + 'enter_tag': 'enter_tagggg', + 'exit_reason': ExitType.STOP_LOSS.value, + } + discord.send_msg(msg=msg) + + assert msg_mock.call_count == 1 + assert 'embeds' in msg_mock.call_args_list[0][0][0] + assert 'title' in msg_mock.call_args_list[0][0][0]['embeds'][0] + assert 'color' in msg_mock.call_args_list[0][0][0]['embeds'][0] + assert 'fields' in msg_mock.call_args_list[0][0][0]['embeds'][0] diff --git a/tests/strategy/strats/hyperoptable_strategy.py b/tests/strategy/strats/hyperoptable_strategy.py index f4dcf1a05..9850a5675 100644 --- a/tests/strategy/strats/hyperoptable_strategy.py +++ b/tests/strategy/strats/hyperoptable_strategy.py @@ -1,13 +1,13 @@ # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement from pandas import DataFrame -from strategy_test_v2 import StrategyTestV2 +from strategy_test_v3 import StrategyTestV3 import freqtrade.vendor.qtpylib.indicators as qtpylib from freqtrade.strategy import BooleanParameter, DecimalParameter, IntParameter, RealParameter -class HyperoptableStrategy(StrategyTestV2): +class HyperoptableStrategy(StrategyTestV3): """ Default Strategy provided by freqtrade bot. Please do not modify this strategy, it's intended for internal use only. @@ -27,7 +27,6 @@ class HyperoptableStrategy(StrategyTestV2): 'sell_minusdi': 0.4 } - buy_rsi = IntParameter([0, 50], default=30, space='buy') buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy') sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell', @@ -45,6 +44,17 @@ class HyperoptableStrategy(StrategyTestV2): }) return prot + bot_loop_started = False + + def bot_loop_start(self): + self.bot_loop_started = True + + def bot_start(self, **kwargs) -> None: + """ + Parameters can also be defined here ... + """ + self.buy_rsi = IntParameter([0, 50], default=30, space='buy') + def informative_pairs(self): """ Define additional, informative pair/interval combinations to be cached from the exchange. diff --git a/tests/strategy/strats/hyperoptable_strategy_v2.py b/tests/strategy/strats/hyperoptable_strategy_v2.py new file mode 100644 index 000000000..94a15b456 --- /dev/null +++ b/tests/strategy/strats/hyperoptable_strategy_v2.py @@ -0,0 +1,54 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +from strategy_test_v2 import StrategyTestV2 + +from freqtrade.strategy import BooleanParameter, DecimalParameter, IntParameter, RealParameter + + +class HyperoptableStrategyV2(StrategyTestV2): + """ + Default Strategy provided by freqtrade bot. + Please do not modify this strategy, it's intended for internal use only. + Please look at the SampleStrategy in the user_data/strategy directory + or strategy repository https://github.com/freqtrade/freqtrade-strategies + for samples and inspiration. + """ + + buy_params = { + 'buy_rsi': 35, + # Intentionally not specified, so "default" is tested + # 'buy_plusdi': 0.4 + } + + sell_params = { + 'sell_rsi': 74, + 'sell_minusdi': 0.4 + } + + buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy') + sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') + sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell', + load=False) + protection_enabled = BooleanParameter(default=True) + protection_cooldown_lookback = IntParameter([0, 50], default=30) + + @property + def protections(self): + prot = [] + if self.protection_enabled.value: + prot.append({ + "method": "CooldownPeriod", + "stop_duration_candles": self.protection_cooldown_lookback.value + }) + return prot + + bot_loop_started = False + + def bot_loop_start(self): + self.bot_loop_started = True + + def bot_start(self, **kwargs) -> None: + """ + Parameters can also be defined here ... + """ + self.buy_rsi = IntParameter([0, 50], default=30, space='buy') diff --git a/tests/strategy/strats/strategy_test_v2.py b/tests/strategy/strats/strategy_test_v2.py index 85ff856e1..9e1c47575 100644 --- a/tests/strategy/strats/strategy_test_v2.py +++ b/tests/strategy/strats/strategy_test_v2.py @@ -1,12 +1,9 @@ # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement -from datetime import datetime - import talib.abstract as ta from pandas import DataFrame import freqtrade.vendor.qtpylib.indicators as qtpylib -from freqtrade.persistence import Trade from freqtrade.strategy import IStrategy @@ -149,12 +146,3 @@ class StrategyTestV2(IStrategy): ), 'sell'] = 1 return dataframe - - def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, min_stake: float, max_stake: float, **kwargs): - - if current_profit < -0.0075: - orders = trade.select_filled_orders('buy') - return round(orders[0].cost, 0) - - return None diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index df83d3663..2c7ccbdf2 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement from datetime import datetime +from typing import Optional import talib.abstract as ta from pandas import DataFrame @@ -177,15 +178,16 @@ class StrategyTestV3(IStrategy): return dataframe def leverage(self, pair: str, current_time: datetime, current_rate: float, - proposed_leverage: float, max_leverage: float, side: str, - **kwargs) -> float: + proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], + side: str, **kwargs) -> float: # Return 3.0 in all cases. # Bot-logic must make sure it's an allowed leverage and eventually adjust accordingly. return 3.0 def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, - current_profit: float, min_stake: float, max_stake: float, **kwargs): + current_profit: float, + min_stake: Optional[float], max_stake: float, **kwargs): if current_profit < -0.0075: orders = trade.select_filled_orders(trade.entry_side) diff --git a/tests/strategy/strats/strategy_test_v3_analysis.py b/tests/strategy/strats/strategy_test_v3_analysis.py new file mode 100644 index 000000000..290fef156 --- /dev/null +++ b/tests/strategy/strats/strategy_test_v3_analysis.py @@ -0,0 +1,175 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +import talib.abstract as ta +from pandas import DataFrame + +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy import (BooleanParameter, DecimalParameter, IntParameter, IStrategy, + RealParameter) + + +class StrategyTestV3Analysis(IStrategy): + """ + Strategy used by tests freqtrade bot. + Please do not modify this strategy, it's intended for internal use only. + Please look at the SampleStrategy in the user_data/strategy directory + or strategy repository https://github.com/freqtrade/freqtrade-strategies + for samples and inspiration. + """ + INTERFACE_VERSION = 3 + + # Minimal ROI designed for the strategy + minimal_roi = { + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 + } + + # Optimal stoploss designed for the strategy + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # Optional order type mapping + order_types = { + 'entry': 'limit', + 'exit': 'limit', + 'stoploss': 'limit', + 'stoploss_on_exchange': False + } + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 20 + + # Optional time in force for orders + order_time_in_force = { + 'entry': 'gtc', + 'exit': 'gtc', + } + + buy_params = { + 'buy_rsi': 35, + # Intentionally not specified, so "default" is tested + # 'buy_plusdi': 0.4 + } + + sell_params = { + 'sell_rsi': 74, + 'sell_minusdi': 0.4 + } + + buy_rsi = IntParameter([0, 50], default=30, space='buy') + buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy') + sell_rsi = IntParameter(low=50, high=100, default=70, space='sell') + sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell', + load=False) + protection_enabled = BooleanParameter(default=True) + protection_cooldown_lookback = IntParameter([0, 50], default=30) + + # TODO: Can this work with protection tests? (replace HyperoptableStrategy implicitly ... ) + # @property + # def protections(self): + # prot = [] + # if self.protection_enabled.value: + # prot.append({ + # "method": "CooldownPeriod", + # "stop_duration_candles": self.protection_cooldown_lookback.value + # }) + # return prot + + bot_started = False + + def bot_start(self): + self.bot_started = True + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Momentum Indicator + # ------------------------------------ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # Minus Directional Indicator / Movement + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # Plus Directional Indicator / Movement + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Stoch fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # Bollinger bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_middleband'] = bollinger['mid'] + dataframe['bb_upperband'] = bollinger['upper'] + + # EMA - Exponential Moving Average + dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi'] < self.buy_rsi.value) & + (dataframe['fastd'] < 35) & + (dataframe['adx'] > 30) & + (dataframe['plus_di'] > self.buy_plusdi.value) + ) | + ( + (dataframe['adx'] > 65) & + (dataframe['plus_di'] > self.buy_plusdi.value) + ), + ['enter_long', 'enter_tag']] = 1, 'enter_tag_long' + + dataframe.loc[ + ( + qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value) + ), + ['enter_short', 'enter_tag']] = 1, 'enter_tag_short' + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + ( + (qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) | + (qtpylib.crossed_above(dataframe['fastd'], 70)) + ) & + (dataframe['adx'] > 10) & + (dataframe['minus_di'] > 0) + ) | + ( + (dataframe['adx'] > 70) & + (dataframe['minus_di'] > self.sell_minusdi.value) + ), + ['exit_long', 'exit_tag']] = 1, 'exit_tag_long' + + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value) + ), + ['exit_long', 'exit_tag']] = 1, 'exit_tag_short' + + return dataframe diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 6e57a3182..f6996a7a2 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -16,10 +16,12 @@ from freqtrade.exceptions import OperationalException, StrategyError from freqtrade.optimize.space import SKDecimal from freqtrade.persistence import PairLocks, Trade from freqtrade.resolvers import StrategyResolver -from freqtrade.strategy.hyper import (BaseParameter, BooleanParameter, CategoricalParameter, - DecimalParameter, IntParameter, RealParameter) +from freqtrade.strategy.hyper import detect_parameters +from freqtrade.strategy.parameters import (BaseParameter, BooleanParameter, CategoricalParameter, + DecimalParameter, IntParameter, RealParameter) from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper -from tests.conftest import CURRENT_TEST_STRATEGY, TRADE_SIDES, log_has, log_has_re +from tests.conftest import (CURRENT_TEST_STRATEGY, TRADE_SIDES, create_mock_trades, log_has, + log_has_re) from .strats.strategy_test_v3 import StrategyTestV3 @@ -495,37 +497,113 @@ def test_custom_exit(default_conf, fee, caplog) -> None: enter=False, exit_=False, low=None, high=None) - assert res.exit_flag is False - assert res.exit_type == ExitType.NONE + assert res == [] strategy.custom_exit = MagicMock(return_value=True) res = strategy.should_exit(trade, 1, now, enter=False, exit_=False, low=None, high=None) - assert res.exit_flag is True - assert res.exit_type == ExitType.CUSTOM_EXIT - assert res.exit_reason == 'custom_exit' + assert res[0].exit_flag is True + assert res[0].exit_type == ExitType.CUSTOM_EXIT + assert res[0].exit_reason == 'custom_exit' strategy.custom_exit = MagicMock(return_value='hello world') res = strategy.should_exit(trade, 1, now, enter=False, exit_=False, low=None, high=None) - assert res.exit_type == ExitType.CUSTOM_EXIT - assert res.exit_flag is True - assert res.exit_reason == 'hello world' + assert res[0].exit_type == ExitType.CUSTOM_EXIT + assert res[0].exit_flag is True + assert res[0].exit_reason == 'hello world' caplog.clear() strategy.custom_exit = MagicMock(return_value='h' * 100) res = strategy.should_exit(trade, 1, now, enter=False, exit_=False, low=None, high=None) - assert res.exit_type == ExitType.CUSTOM_EXIT - assert res.exit_flag is True - assert res.exit_reason == 'h' * 64 + assert res[0].exit_type == ExitType.CUSTOM_EXIT + assert res[0].exit_flag is True + assert res[0].exit_reason == 'h' * 64 assert log_has_re('Custom exit reason returned from custom_exit is too long.*', caplog) +def test_should_sell(default_conf, fee) -> None: + + strategy = StrategyResolver.load_strategy(default_conf) + trade = Trade( + pair='ETH/BTC', + stake_amount=0.01, + amount=1, + open_date=arrow.utcnow().shift(hours=-1).datetime, + fee_open=fee.return_value, + fee_close=fee.return_value, + exchange='binance', + open_rate=1, + ) + now = arrow.utcnow().datetime + res = strategy.should_exit(trade, 1, now, + enter=False, exit_=False, + low=None, high=None) + + assert res == [] + strategy.min_roi_reached = MagicMock(return_value=True) + + res = strategy.should_exit(trade, 1, now, + enter=False, exit_=False, + low=None, high=None) + assert len(res) == 1 + assert res == [ExitCheckTuple(exit_type=ExitType.ROI)] + + strategy.min_roi_reached = MagicMock(return_value=True) + strategy.stop_loss_reached = MagicMock( + return_value=ExitCheckTuple(exit_type=ExitType.STOP_LOSS)) + + res = strategy.should_exit(trade, 1, now, + enter=False, exit_=False, + low=None, high=None) + assert len(res) == 2 + assert res == [ + ExitCheckTuple(exit_type=ExitType.STOP_LOSS), + ExitCheckTuple(exit_type=ExitType.ROI), + ] + + strategy.custom_exit = MagicMock(return_value='hello world') + # custom-exit and exit-signal is first + res = strategy.should_exit(trade, 1, now, + enter=False, exit_=False, + low=None, high=None) + assert len(res) == 3 + assert res == [ + ExitCheckTuple(exit_type=ExitType.CUSTOM_EXIT, exit_reason='hello world'), + ExitCheckTuple(exit_type=ExitType.STOP_LOSS), + ExitCheckTuple(exit_type=ExitType.ROI), + ] + + strategy.stop_loss_reached = MagicMock( + return_value=ExitCheckTuple(exit_type=ExitType.TRAILING_STOP_LOSS)) + # Regular exit signal + res = strategy.should_exit(trade, 1, now, + enter=False, exit_=True, + low=None, high=None) + assert len(res) == 3 + assert res == [ + ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL), + ExitCheckTuple(exit_type=ExitType.ROI), + ExitCheckTuple(exit_type=ExitType.TRAILING_STOP_LOSS), + ] + + # Regular exit signal, no ROI + strategy.min_roi_reached = MagicMock(return_value=False) + res = strategy.should_exit(trade, 1, now, + enter=False, exit_=True, + low=None, high=None) + assert len(res) == 2 + assert res == [ + ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL), + ExitCheckTuple(exit_type=ExitType.TRAILING_STOP_LOSS), + ] + + @pytest.mark.parametrize('side', TRADE_SIDES) def test_leverage_callback(default_conf, side) -> None: default_conf['strategy'] = 'StrategyTestV2' @@ -538,6 +616,7 @@ def test_leverage_callback(default_conf, side) -> None: proposed_leverage=1.0, max_leverage=5.0, side=side, + entry_tag=None, ) == 1 default_conf['strategy'] = CURRENT_TEST_STRATEGY @@ -549,6 +628,7 @@ def test_leverage_callback(default_conf, side) -> None: proposed_leverage=1.0, max_leverage=5.0, side=side, + entry_tag='entry_tag_test', ) == 3 @@ -733,6 +813,28 @@ def test_strategy_safe_wrapper(value): assert ret == value +@pytest.mark.usefixtures("init_persistence") +def test_strategy_safe_wrapper_trade_copy(fee): + create_mock_trades(fee) + + def working_method(trade): + assert len(trade.orders) > 0 + assert trade.orders + trade.orders = [] + assert len(trade.orders) == 0 + return trade + + trade = Trade.get_open_trades()[0] + # Don't assert anything before strategy_wrapper. + # This ensures that relationship loading works correctly. + ret = strategy_safe_wrapper(working_method, message='DeadBeef')(trade=trade) + assert isinstance(ret, Trade) + assert id(trade) != id(ret) + # Did not modify the original order + assert len(trade.orders) > 0 + assert len(ret.orders) == 0 + + def test_hyperopt_parameters(): from skopt.space import Categorical, Integer, Real with pytest.raises(OperationalException, match=r"Name is determined.*"): @@ -814,10 +916,10 @@ def test_hyperopt_parameters(): def test_auto_hyperopt_interface(default_conf): - default_conf.update({'strategy': 'HyperoptableStrategy'}) + default_conf.update({'strategy': 'HyperoptableStrategyV2'}) PairLocks.timeframe = default_conf['timeframe'] strategy = StrategyResolver.load_strategy(default_conf) - + strategy.ft_bot_start() with pytest.raises(OperationalException): next(strategy.enumerate_parameters('deadBeef')) @@ -832,15 +934,18 @@ def test_auto_hyperopt_interface(default_conf): assert strategy.sell_minusdi.value == 0.5 all_params = strategy.detect_all_parameters() assert isinstance(all_params, dict) - assert len(all_params['buy']) == 2 + # Only one buy param at class level + assert len(all_params['buy']) == 1 + # Running detect params at instance level reveals both parameters. + assert len(list(detect_parameters(strategy, 'buy'))) == 2 assert len(all_params['sell']) == 2 # Number of Hyperoptable parameters - assert all_params['count'] == 6 + assert all_params['count'] == 5 strategy.__class__.sell_rsi = IntParameter([0, 10], default=5, space='buy') with pytest.raises(OperationalException, match=r"Inconclusive parameter.*"): - [x for x in strategy.detect_parameters('sell')] + [x for x in detect_parameters(strategy, 'sell')] def test_auto_hyperopt_interface_loadparams(default_conf, mocker, caplog): diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index 3ed1eb0ce..bdfcf3211 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -34,7 +34,7 @@ def test_search_all_strategies_no_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=False) assert isinstance(strategies, list) - assert len(strategies) == 5 + assert len(strategies) == 7 assert isinstance(strategies[0], dict) @@ -42,10 +42,10 @@ def test_search_all_strategies_with_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=True) assert isinstance(strategies, list) - assert len(strategies) == 6 + assert len(strategies) == 8 # with enum_failed=True search_all_objects() shall find 2 good strategies # and 1 which fails to load - assert len([x for x in strategies if x['class'] is not None]) == 5 + assert len([x for x in strategies if x['class'] is not None]) == 7 assert len([x for x in strategies if x['class'] is None]) == 1 @@ -224,12 +224,12 @@ def test_strategy_override_process_only_new_candles(caplog, default_conf): default_conf.update({ 'strategy': CURRENT_TEST_STRATEGY, - 'process_only_new_candles': True + 'process_only_new_candles': False }) strategy = StrategyResolver.load_strategy(default_conf) - assert strategy.process_only_new_candles - assert log_has("Override strategy 'process_only_new_candles' with value in config file: True.", + assert not strategy.process_only_new_candles + assert log_has("Override strategy 'process_only_new_candles' with value in config file: False.", caplog) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index bb9c0c391..4963e2b0a 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -210,13 +210,14 @@ def test_edge_overrides_stoploss(limit_order, fee, caplog, mocker, # # mocking the ticker: price is falling ... enter_price = limit_order['buy']['price'] + ticker_val = { + 'bid': enter_price, + 'ask': enter_price, + 'last': enter_price, + } mocker.patch.multiple( 'freqtrade.exchange.Exchange', - fetch_ticker=MagicMock(return_value={ - 'bid': enter_price * buy_price_mult, - 'ask': enter_price * buy_price_mult, - 'last': enter_price * buy_price_mult, - }), + fetch_ticker=MagicMock(return_value=ticker_val), get_fee=fee, ) ############################################# @@ -229,9 +230,12 @@ def test_edge_overrides_stoploss(limit_order, fee, caplog, mocker, freqtrade.enter_positions() trade = Trade.query.first() caplog.clear() - oobj = Order.parse_from_ccxt_object(limit_order['buy'], 'ADA/USDT', 'buy') - trade.update_trade(oobj) ############################################# + ticker_val.update({ + 'bid': enter_price * buy_price_mult, + 'ask': enter_price * buy_price_mult, + 'last': enter_price * buy_price_mult, + }) # stoploss shoud be hit assert freqtrade.handle_trade(trade) is not ignore_strat_sl @@ -1775,9 +1779,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, 'type': 'stop_loss_limit', 'price': 3, 'average': 2, - 'info': { - 'stopPrice': '2.178' - } + 'stopPrice': '2.178' }) mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging) @@ -2149,7 +2151,7 @@ def test_handle_trade( assert trade.close_rate == 2.0 if is_short else 2.2 assert trade.close_profit == close_profit - assert trade.calc_profit() == 5.685 + assert trade.calc_profit(trade.close_rate) == 5.685 assert trade.close_date is not None assert trade.exit_reason == 'sell_signal1' @@ -2362,7 +2364,7 @@ def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog): @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_entry_usercustom( +def test_manage_open_orders_entry_usercustom( default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, limit_sell_order_old, fee, mocker, is_short ) -> None: @@ -2394,12 +2396,12 @@ def test_check_handle_timedout_entry_usercustom( Trade.query.session.add(open_trade) # Ensure default is to return empty (so not mocked yet) - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 # Return false - trade remains open freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False) - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() nb_trades = len(trades) @@ -2407,7 +2409,7 @@ def test_check_handle_timedout_entry_usercustom( assert freqtrade.strategy.check_entry_timeout.call_count == 1 freqtrade.strategy.check_entry_timeout = MagicMock(side_effect=KeyError) - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() nb_trades = len(trades) @@ -2416,7 +2418,7 @@ def test_check_handle_timedout_entry_usercustom( freqtrade.strategy.check_entry_timeout = MagicMock(return_value=True) # Trade should be closed since the function returns true - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_wr_mock.call_count == 1 assert rpc_mock.call_count == 1 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() @@ -2426,7 +2428,7 @@ def test_check_handle_timedout_entry_usercustom( @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_entry( +def test_manage_open_orders_entry( default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, limit_sell_order_old, fee, mocker, is_short ) -> None: @@ -2450,8 +2452,9 @@ def test_check_handle_timedout_entry( Trade.query.session.add(open_trade) freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False) + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234) # check it does cancel buy orders over the time limit - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 1 assert rpc_mock.call_count == 1 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() @@ -2459,6 +2462,99 @@ def test_check_handle_timedout_entry( assert nb_trades == 0 # Custom user buy-timeout is never called assert freqtrade.strategy.check_entry_timeout.call_count == 0 + # Entry adjustment is never called + assert freqtrade.strategy.adjust_entry_price.call_count == 0 + + +@pytest.mark.parametrize("is_short", [False, True]) +def test_adjust_entry_cancel( + default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, + limit_sell_order_old, fee, mocker, caplog, is_short +) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + old_order = limit_sell_order_old if is_short else limit_buy_order_old + old_order['id'] = open_trade.open_order_id + limit_buy_cancel = deepcopy(old_order) + limit_buy_cancel['status'] = 'canceled' + cancel_order_mock = MagicMock(return_value=limit_buy_cancel) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker_usdt, + fetch_order=MagicMock(return_value=old_order), + cancel_order_with_result=cancel_order_mock, + get_fee=fee + ) + + open_trade.is_short = is_short + Trade.query.session.add(open_trade) + + # Timeout to not interfere + freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False) + + # check that order is cancelled + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=None) + freqtrade.manage_open_orders() + trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() + assert len(trades) == 0 + assert len(Order.query.all()) == 0 + assert log_has_re( + f"{'Sell' if is_short else 'Buy'} order user requested order cancel*", caplog) + assert log_has_re( + f"{'Sell' if is_short else 'Buy'} order fully cancelled.*", caplog) + + # Entry adjustment is called + assert freqtrade.strategy.adjust_entry_price.call_count == 1 + + +@pytest.mark.parametrize("is_short", [False, True]) +def test_adjust_entry_maintain_replace( + default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade, + limit_sell_order_old, fee, mocker, caplog, is_short +) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + old_order = limit_sell_order_old if is_short else limit_buy_order_old + old_order['id'] = open_trade.open_order_id + limit_buy_cancel = deepcopy(old_order) + limit_buy_cancel['status'] = 'canceled' + cancel_order_mock = MagicMock(return_value=limit_buy_cancel) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker_usdt, + fetch_order=MagicMock(return_value=old_order), + cancel_order_with_result=cancel_order_mock, + get_fee=fee + ) + + open_trade.is_short = is_short + Trade.query.session.add(open_trade) + + # Timeout to not interfere + freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False) + + # Check that order is maintained + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=old_order['price']) + freqtrade.manage_open_orders() + trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() + assert len(trades) == 1 + assert len(Order.get_open_orders()) == 1 + # Entry adjustment is called + assert freqtrade.strategy.adjust_entry_price.call_count == 1 + + # Check that order is replaced + freqtrade.get_valid_enter_price_and_stake = MagicMock(return_value={100, 10, 1}) + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234) + freqtrade.manage_open_orders() + trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() + assert len(trades) == 1 + nb_all_orders = len(Order.query.all()) + assert nb_all_orders == 2 + # New order seems to be in closed status? + # nb_open_orders = len(Order.get_open_orders()) + # assert nb_open_orders == 1 + assert log_has_re( + f"{'Sell' if is_short else 'Buy'} order cancelled to be replaced*", caplog) + # Entry adjustment is called + assert freqtrade.strategy.adjust_entry_price.call_count == 1 @pytest.mark.parametrize("is_short", [False, True]) @@ -2480,22 +2576,22 @@ def test_check_handle_cancelled_buy( get_fee=fee ) freqtrade = FreqtradeBot(default_conf_usdt) + open_trade.orders = [] open_trade.is_short = is_short Trade.query.session.add(open_trade) # check it does cancel buy orders over the time limit - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 assert rpc_mock.call_count == 1 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() - nb_trades = len(trades) - assert nb_trades == 0 + assert len(trades) == 0 assert log_has_re( f"{'Sell' if is_short else 'Buy'} order cancelled on exchange for Trade.*", caplog) @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_buy_exception( +def test_manage_open_orders_buy_exception( default_conf_usdt, ticker_usdt, open_trade, is_short, fee, mocker ) -> None: rpc_mock = patch_RPCManager(mocker) @@ -2515,7 +2611,7 @@ def test_check_handle_timedout_buy_exception( Trade.query.session.add(open_trade) # check it does cancel buy orders over the time limit - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 assert rpc_mock.call_count == 0 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() @@ -2524,7 +2620,7 @@ def test_check_handle_timedout_buy_exception( @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_exit_usercustom( +def test_manage_open_orders_exit_usercustom( default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker, is_short, open_trade_usdt, caplog ) -> None: @@ -2553,13 +2649,13 @@ def test_check_handle_timedout_exit_usercustom( Trade.query.session.add(open_trade_usdt) # Ensure default is false - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False) freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False) # Return false - No impact - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 assert rpc_mock.call_count == 0 assert open_trade_usdt.is_open is False @@ -2569,7 +2665,7 @@ def test_check_handle_timedout_exit_usercustom( freqtrade.strategy.check_exit_timeout = MagicMock(side_effect=KeyError) freqtrade.strategy.check_entry_timeout = MagicMock(side_effect=KeyError) # Return Error - No impact - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 assert rpc_mock.call_count == 0 assert open_trade_usdt.is_open is False @@ -2579,7 +2675,7 @@ def test_check_handle_timedout_exit_usercustom( # Return True - sells! freqtrade.strategy.check_exit_timeout = MagicMock(return_value=True) freqtrade.strategy.check_entry_timeout = MagicMock(return_value=True) - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 1 assert rpc_mock.call_count == 1 assert open_trade_usdt.is_open is True @@ -2592,7 +2688,7 @@ def test_check_handle_timedout_exit_usercustom( mocker.patch('freqtrade.persistence.Trade.get_exit_order_count', return_value=1) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit', side_effect=DependencyException) - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert log_has_re('Unable to emergency sell .*', caplog) et_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit') @@ -2602,16 +2698,16 @@ def test_check_handle_timedout_exit_usercustom( # If cancelling fails - no emergency sell! with patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit', return_value=False): - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert et_mock.call_count == 0 - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert log_has_re('Emergency exiting trade.*', caplog) assert et_mock.call_count == 1 @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_exit( +def test_manage_open_orders_exit( default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker, is_short, open_trade_usdt ) -> None: rpc_mock = patch_RPCManager(mocker) @@ -2638,7 +2734,7 @@ def test_check_handle_timedout_exit( freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False) freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False) # check it does cancel sell orders over the time limit - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 1 assert rpc_mock.call_count == 1 assert open_trade_usdt.is_open is True @@ -2674,7 +2770,7 @@ def test_check_handle_cancelled_exit( Trade.query.session.add(open_trade_usdt) # check it does cancel sell orders over the time limit - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 0 assert rpc_mock.call_count == 1 assert open_trade_usdt.is_open is True @@ -2684,7 +2780,7 @@ def test_check_handle_cancelled_exit( @pytest.mark.parametrize("is_short", [False, True]) @pytest.mark.parametrize("leverage", [1, 3, 5, 10]) -def test_check_handle_timedout_partial( +def test_manage_open_orders_partial( default_conf_usdt, ticker_usdt, limit_buy_order_old_partial, is_short, leverage, open_trade, mocker ) -> None: @@ -2710,7 +2806,7 @@ def test_check_handle_timedout_partial( # check it does cancel buy orders over the time limit # note this is for a partially-complete buy order - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert cancel_order_mock.call_count == 1 assert rpc_mock.call_count == 2 trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all() @@ -2721,7 +2817,7 @@ def test_check_handle_timedout_partial( @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_partial_fee( +def test_manage_open_orders_partial_fee( default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short, limit_buy_order_old_partial, trades_for_order, limit_buy_order_old_partial_canceled, mocker @@ -2753,7 +2849,7 @@ def test_check_handle_timedout_partial_fee( Trade.query.session.add(open_trade) # cancelling a half-filled order should update the amount to the bought amount # and apply fees if necessary. - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert log_has_re(r"Applying fee on amount for Trade.*", caplog) @@ -2770,7 +2866,7 @@ def test_check_handle_timedout_partial_fee( @pytest.mark.parametrize("is_short", [False, True]) -def test_check_handle_timedout_partial_except( +def test_manage_open_orders_partial_except( default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short, limit_buy_order_old_partial, trades_for_order, limit_buy_order_old_partial_canceled, mocker @@ -2801,7 +2897,7 @@ def test_check_handle_timedout_partial_except( Trade.query.session.add(open_trade) # cancelling a half-filled order should update the amount to the bought amount # and apply fees if necessary. - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert log_has_re(r"Could not update trade amount: .*", caplog) @@ -2817,8 +2913,8 @@ def test_check_handle_timedout_partial_except( assert trades[0].fee_open == fee() -def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_trade_usdt, mocker, - caplog) -> None: +def test_manage_open_orders_exception(default_conf_usdt, ticker_usdt, open_trade_usdt, mocker, + caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) cancel_order_mock = MagicMock() @@ -2839,7 +2935,7 @@ def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_tr Trade.query.session.add(open_trade_usdt) caplog.clear() - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ADA/USDT, amount=30.00000000, " r"is_short=False, leverage=1.0, " r"open_rate=2.00000000, open_since=" @@ -2863,6 +2959,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_ freqtrade = FreqtradeBot(default_conf_usdt) freqtrade._notify_enter_cancel = MagicMock() + # TODO: Convert to real trade trade = MagicMock() trade.pair = 'LTC/USDT' trade.open_rate = 200 @@ -2870,6 +2967,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_ trade.entry_side = "buy" l_order['filled'] = 0.0 l_order['status'] = 'open' + trade.nr_of_successful_entries = 0 reason = CANCEL_REASON['TIMEOUT'] assert freqtrade.handle_cancel_enter(trade, l_order, reason) assert cancel_order_mock.call_count == 1 @@ -2912,7 +3010,9 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_sho freqtrade = FreqtradeBot(default_conf_usdt) reason = CANCEL_REASON['TIMEOUT'] + # TODO: Convert to real trade trade = MagicMock() + trade.nr_of_successful_entries = 0 trade.pair = 'LTC/ETH' trade.entry_side = "sell" if is_short else "buy" assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason) @@ -2945,12 +3045,14 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_order freqtrade = FreqtradeBot(default_conf_usdt) freqtrade._notify_enter_cancel = MagicMock() - + # TODO: Convert to real trade trade = MagicMock() trade.pair = 'LTC/USDT' trade.entry_side = "buy" trade.open_rate = 200 trade.entry_side = "buy" + trade.open_order_id = "open_order_noop" + trade.nr_of_successful_entries = 0 l_order['filled'] = 0.0 l_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] @@ -3396,7 +3498,7 @@ def test_execute_trade_exit_with_stoploss_on_exchange( assert trade trades = [trade] - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() freqtrade.exit_positions(trades) # Increase the price and sell it @@ -3448,7 +3550,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit( # Create some test data freqtrade.enter_positions() - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() trade = Trade.query.first() trades = [trade] assert trade.stoploss_order_id is None @@ -3488,7 +3590,7 @@ def test_may_execute_trade_exit_after_stoploss_on_exchange_hit( assert rpc_mock.call_count == 3 assert rpc_mock.call_args_list[0][0][0]['type'] == RPCMessageType.ENTRY assert rpc_mock.call_args_list[1][0][0]['type'] == RPCMessageType.ENTRY_FILL - assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.EXIT + assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.EXIT_FILL @pytest.mark.parametrize( @@ -3673,6 +3775,7 @@ def test_exit_profit_only( trade = Trade.query.first() assert trade.is_short == is_short oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside) + trade.update_order(limit_order[eside]) trade.update_trade(oobj) freqtrade.wallets.update() if profit_only: @@ -3848,9 +3951,9 @@ def test_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limit_order_ # Test if entry-signal is absent (should sell due to roi = true) if is_short: - patch_get_signal(freqtrade, enter_long=False, exit_short=False) + patch_get_signal(freqtrade, enter_long=False, exit_short=False, exit_tag='something') else: - patch_get_signal(freqtrade, enter_long=False, exit_long=False) + patch_get_signal(freqtrade, enter_long=False, exit_long=False, exit_tag='something') assert freqtrade.handle_trade(trade) is True assert trade.exit_reason == ExitType.ROI.value @@ -3961,6 +4064,7 @@ def test_trailing_stop_loss_positive( trade = Trade.query.first() assert trade.is_short == is_short oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside) + trade.update_order(limit_order[eside]) trade.update_trade(oobj) caplog.set_level(logging.DEBUG) # stop-loss not reached @@ -4693,9 +4797,6 @@ def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog, is_s freqtrade.config['dry_run'] = False freqtrade.startup_update_open_orders() - assert log_has_re(r"Error updating Order .*", caplog) - caplog.clear() - assert len(Order.get_open_orders()) == 3 matching_buy_order = mock_order_4(is_short=is_short) matching_buy_order.update({ @@ -4706,6 +4807,20 @@ def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog, is_s # Only stoploss and sell orders are kept open assert len(Order.get_open_orders()) == 2 + caplog.clear() + mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=ExchangeError) + freqtrade.startup_update_open_orders() + assert log_has_re(r"Error updating Order .*", caplog) + + mocker.patch('freqtrade.exchange.Exchange.fetch_order', side_effect=InvalidOrderException) + hto_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_timedout_order') + # Orders which are no longer found after X days should be assumed as canceled. + freqtrade.startup_update_open_orders() + assert log_has_re(r"Order is older than \d days.*", caplog) + assert hto_mock.call_count == 2 + assert hto_mock.call_args_list[0][0][0]['status'] == 'canceled' + assert hto_mock.call_args_list[1][0][0]['status'] == 'canceled' + @pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize("is_short", [False, True]) @@ -5214,7 +5329,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: assert trade.stake_amount == 110 assert not trade.fee_updated('buy') - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() trade = Trade.query.first() assert trade @@ -5320,7 +5435,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: MagicMock(return_value=closed_dca_order_1)) mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order', MagicMock(return_value=closed_dca_order_1)) - freqtrade.check_handle_timedout() + freqtrade.manage_open_orders() # Assert trade is as expected (averaged dca) trade = Trade.query.first() diff --git a/tests/test_integration.py b/tests/test_integration.py index 8f56c1fea..83f54becb 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -52,8 +52,8 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee, side_effect=[stoploss_order_closed, stoploss_order_open, stoploss_order_open]) # Sell 3rd trade (not called for the first trade) should_sell_mock = MagicMock(side_effect=[ - ExitCheckTuple(exit_type=ExitType.NONE), - ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL)] + [], + [ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL)]] ) cancel_order_mock = MagicMock() mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss) @@ -160,11 +160,11 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, mocker, balance_rati _notify_exit=MagicMock(), ) should_sell_mock = MagicMock(side_effect=[ - ExitCheckTuple(exit_type=ExitType.NONE), - ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL), - ExitCheckTuple(exit_type=ExitType.NONE), - ExitCheckTuple(exit_type=ExitType.NONE), - ExitCheckTuple(exit_type=ExitType.NONE)] + [], + [ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL)], + [], + [], + []] ) mocker.patch("freqtrade.strategy.interface.IStrategy.should_exit", should_sell_mock) @@ -351,3 +351,107 @@ def test_dca_short(default_conf_usdt, ticker_usdt, fee, mocker) -> None: assert trade.nr_of_successful_entries == 2 assert trade.nr_of_successful_exits == 1 + + +def test_dca_order_adjust(default_conf_usdt, ticker_usdt, fee, mocker) -> None: + default_conf_usdt['position_adjustment_enable'] = True + + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker_usdt, + get_fee=fee, + amount_to_precision=lambda s, x, y: y, + price_to_precision=lambda s, x, y: y, + ) + mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=False) + + patch_get_signal(freqtrade) + freqtrade.strategy.custom_entry_price = lambda **kwargs: ticker_usdt['ask'] * 0.96 + + freqtrade.enter_positions() + + assert len(Trade.get_trades().all()) == 1 + trade: Trade = Trade.get_trades().first() + assert len(trade.orders) == 1 + assert trade.open_order_id is not None + assert pytest.approx(trade.stake_amount) == 60 + assert trade.open_rate == 1.96 + assert trade.stop_loss_pct is None + assert trade.stop_loss == 0.0 + assert trade.initial_stop_loss == 0.0 + assert trade.initial_stop_loss_pct is None + # No adjustment + freqtrade.process() + trade = Trade.get_trades().first() + assert len(trade.orders) == 1 + assert trade.open_order_id is not None + assert pytest.approx(trade.stake_amount) == 60 + + # Cancel order and place new one + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1.99) + freqtrade.process() + trade = Trade.get_trades().first() + assert len(trade.orders) == 2 + assert trade.open_order_id is not None + # Open rate is not adjusted yet + assert trade.open_rate == 1.96 + assert trade.stop_loss_pct is None + assert trade.stop_loss == 0.0 + assert trade.initial_stop_loss == 0.0 + assert trade.initial_stop_loss_pct is None + + # Fill order + mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=True) + freqtrade.process() + trade = Trade.get_trades().first() + assert len(trade.orders) == 2 + assert trade.open_order_id is None + # Open rate is not adjusted yet + assert trade.open_rate == 1.99 + assert trade.stop_loss_pct == -0.1 + assert trade.stop_loss == 1.99 * 0.9 + assert trade.initial_stop_loss == 1.99 * 0.9 + assert trade.initial_stop_loss_pct == -0.1 + + # 2nd order - not filling + freqtrade.strategy.adjust_trade_position = MagicMock(return_value=120) + mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=False) + + freqtrade.process() + trade = Trade.get_trades().first() + assert len(trade.orders) == 3 + assert trade.open_order_id is not None + assert trade.open_rate == 1.99 + assert trade.orders[-1].price == 1.96 + assert trade.orders[-1].cost == 120 + + # Replace new order with diff. order at a lower price + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1.95) + + freqtrade.process() + trade = Trade.get_trades().first() + assert len(trade.orders) == 4 + assert trade.open_order_id is not None + assert trade.open_rate == 1.99 + assert trade.orders[-1].price == 1.95 + assert pytest.approx(trade.orders[-1].cost) == 120 + + # Fill DCA order + freqtrade.strategy.adjust_trade_position = MagicMock(return_value=None) + mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=True) + freqtrade.strategy.adjust_entry_price = MagicMock(side_effect=ValueError) + + freqtrade.process() + trade = Trade.get_trades().first() + assert len(trade.orders) == 4 + assert trade.open_order_id is None + assert pytest.approx(trade.open_rate) == 1.963153456 + assert trade.orders[-1].price == 1.95 + assert pytest.approx(trade.orders[-1].cost) == 120 + assert trade.orders[-1].status == 'closed' + + assert pytest.approx(trade.amount) == 91.689215 + # Check the 2 filled orders equal the above amount + assert pytest.approx(trade.orders[1].amount) == 30.150753768 + assert pytest.approx(trade.orders[-1].amount) == 61.538461232 diff --git a/tests/test_persistence.py b/tests/test_persistence.py index b66c12086..a09711048 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -13,7 +13,7 @@ from sqlalchemy import create_engine, text from freqtrade import constants from freqtrade.enums import TradingMode from freqtrade.exceptions import DependencyException, OperationalException -from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db +from freqtrade.persistence import LocalTrade, Order, Trade, init_db from freqtrade.persistence.migrations import get_last_sequence_ids, set_sequence_ids from freqtrade.persistence.models import PairLock from tests.conftest import create_mock_trades, create_mock_trades_with_leverage, log_has, log_has_re @@ -24,7 +24,7 @@ spot, margin, futures = TradingMode.SPOT, TradingMode.MARGIN, TradingMode.FUTURE def test_init_create_session(default_conf): # Check if init create a session - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) assert hasattr(Trade, '_session') assert 'scoped_session' in type(Trade._session).__name__ @@ -36,7 +36,7 @@ def test_init_custom_db_url(default_conf, tmpdir): default_conf.update({'db_url': f'sqlite:///{filename}'}) - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) assert Path(filename).is_file() r = Trade._session.execute(text("PRAGMA journal_mode")) assert r.first() == ('wal',) @@ -45,10 +45,10 @@ def test_init_custom_db_url(default_conf, tmpdir): def test_init_invalid_db_url(): # Update path to a value other than default, but still in-memory with pytest.raises(OperationalException, match=r'.*no valid database URL*'): - init_db('unknown:///some.url', True) + init_db('unknown:///some.url') with pytest.raises(OperationalException, match=r'Bad db-url.*For in-memory database, pl.*'): - init_db('sqlite:///', True) + init_db('sqlite:///') def test_init_prod_db(default_conf, mocker): @@ -57,7 +57,7 @@ def test_init_prod_db(default_conf, mocker): create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock()) - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) assert create_engine_mock.call_count == 1 assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tradesv3.sqlite' @@ -70,7 +70,7 @@ def test_init_dryrun_db(default_conf, tmpdir): 'db_url': f'sqlite:///{filename}' }) - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) assert Path(filename).is_file() @@ -606,9 +606,9 @@ def test_calc_open_close_trade_price( trade.close_rate = 2.2 trade.recalc_open_trade_value() assert isclose(trade._calc_open_trade_value(), open_value) - assert isclose(trade.calc_close_trade_value(), close_value) - assert isclose(trade.calc_profit(), round(profit, 8)) - assert pytest.approx(trade.calc_profit_ratio()) == profit_ratio + assert isclose(trade.calc_close_trade_value(trade.close_rate), close_value) + assert isclose(trade.calc_profit(trade.close_rate), round(profit, 8)) + assert pytest.approx(trade.calc_profit_ratio(trade.close_rate)) == profit_ratio @pytest.mark.usefixtures("init_persistence") @@ -660,7 +660,7 @@ def test_calc_close_trade_price_exception(limit_buy_order_usdt, fee): trade.open_order_id = 'something' oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy') trade.update_trade(oobj) - assert trade.calc_close_trade_value() == 0.0 + assert trade.calc_close_trade_value(trade.close_rate) == 0.0 @pytest.mark.usefixtures("init_persistence") @@ -813,7 +813,7 @@ def test_calc_close_trade_price( funding_fees=funding_fees ) trade.open_order_id = 'close_trade' - assert round(trade.calc_close_trade_value(rate=close_rate, fee=fee_rate), 8) == result + assert round(trade.calc_close_trade_value(rate=close_rate), 8) == result @pytest.mark.parametrize( @@ -884,6 +884,17 @@ def test_calc_close_trade_price( ('binance', False, 3, 2.2, 0.0025, 4.684999, 0.23366583, futures, -1), ('binance', True, 1, 2.2, 0.0025, -7.315, -0.12222222, futures, -1), ('binance', True, 3, 2.2, 0.0025, -7.315, -0.36666666, futures, -1), + + # FUTURES, funding_fee=0 + ('binance', False, 1, 2.1, 0.0025, 2.6925, 0.04476309, futures, 0), + ('binance', False, 3, 2.1, 0.0025, 2.6925, 0.13428928, futures, 0), + ('binance', True, 1, 2.1, 0.0025, -3.3074999, -0.05526316, futures, 0), + ('binance', True, 3, 2.1, 0.0025, -3.3074999, -0.16578947, futures, 0), + + ('binance', False, 1, 1.9, 0.0025, -3.2925, -0.05473815, futures, 0), + ('binance', False, 3, 1.9, 0.0025, -3.2925, -0.16421446, futures, 0), + ('binance', True, 1, 1.9, 0.0025, 2.7075, 0.0452381, futures, 0), + ('binance', True, 3, 1.9, 0.0025, 2.7075, 0.13571429, futures, 0), ]) @pytest.mark.usefixtures("init_persistence") def test_calc_profit( @@ -1129,56 +1140,6 @@ def test_calc_profit( assert pytest.approx(trade.calc_profit_ratio(rate=close_rate)) == round(profit_ratio, 8) -@pytest.mark.usefixtures("init_persistence") -def test_clean_dry_run_db(default_conf, fee): - - # Simulate dry_run entries - trade = Trade( - pair='ADA/USDT', - stake_amount=0.001, - amount=123.0, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.123, - exchange='binance', - open_order_id='dry_run_buy_12345' - ) - Trade.query.session.add(trade) - - trade = Trade( - pair='ETC/BTC', - stake_amount=0.001, - amount=123.0, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.123, - exchange='binance', - open_order_id='dry_run_sell_12345' - ) - Trade.query.session.add(trade) - - # Simulate prod entry - trade = Trade( - pair='ETC/BTC', - stake_amount=0.001, - amount=123.0, - fee_open=fee.return_value, - fee_close=fee.return_value, - open_rate=0.123, - exchange='binance', - open_order_id='prod_buy_12345' - ) - Trade.query.session.add(trade) - - # We have 3 entries: 2 dry_run, 1 prod - assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 3 - - clean_dry_run_db() - - # We have now only the prod - assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 1 - - def test_migrate_new(mocker, default_conf, fee, caplog): """ Test Database migration (starting with new pairformat) @@ -1239,7 +1200,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): 0.00258580, {stake}, {amount}, '2019-11-28 12:44:24.000000', 0.0, 0.0, 0.0, '5m', - 'buy_order', 'stop_order_id222') + 'buy_order', 'dry_stop_order_id222') """.format(fee=fee.return_value, stake=default_conf.get("stake_amount"), amount=amount @@ -1265,7 +1226,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): 'buy', 'ETC/BTC', 0, - 'buy_order', + 'dry_buy_order', 'closed', 'ETC/BTC', 'limit', @@ -1277,12 +1238,44 @@ def test_migrate_new(mocker, default_conf, fee, caplog): {amount * 0.00258580} ), ( + 1, + 'buy', + 'ETC/BTC', + 1, + 'dry_buy_order22', + 'canceled', + 'ETC/BTC', + 'limit', + 'buy', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ), + ( 1, 'stoploss', 'ETC/BTC', + 1, + 'dry_stop_order_id11X', + 'canceled', + 'ETC/BTC', + 'limit', + 'sell', + 0.00258580, + {amount}, + {amount}, 0, - 'stop_order_id222', - 'closed', + {amount * 0.00258580} + ), + ( + 1, + 'stoploss', + 'ETC/BTC', + 1, + 'dry_stop_order_id222', + 'open', 'ETC/BTC', 'limit', 'sell', @@ -1310,7 +1303,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): connection.execute(text("create table trades_bak1 as select * from trades")) # Run init to test migration - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) assert len(Trade.query.filter(Trade.id == 1).all()) == 1 trade = Trade.query.filter(Trade.id == 1).first() @@ -1331,7 +1324,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert trade.exit_reason is None assert trade.strategy is None assert trade.timeframe == '5m' - assert trade.stoploss_order_id == 'stop_order_id222' + assert trade.stoploss_order_id == 'dry_stop_order_id222' assert trade.stoploss_last_update is None assert log_has("trying trades_bak1", caplog) assert log_has("trying trades_bak2", caplog) @@ -1341,12 +1334,21 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert trade.close_profit_abs is None orders = trade.orders - assert len(orders) == 2 - assert orders[0].order_id == 'buy_order' + assert len(orders) == 4 + assert orders[0].order_id == 'dry_buy_order' assert orders[0].ft_order_side == 'buy' - assert orders[1].order_id == 'stop_order_id222' - assert orders[1].ft_order_side == 'stoploss' + assert orders[-1].order_id == 'dry_stop_order_id222' + assert orders[-1].ft_order_side == 'stoploss' + assert orders[-1].ft_is_open is True + + assert orders[1].order_id == 'dry_buy_order22' + assert orders[1].ft_order_side == 'buy' + assert orders[1].ft_is_open is False + + assert orders[2].order_id == 'dry_stop_order_id11X' + assert orders[2].ft_order_side == 'stoploss' + assert orders[2].ft_is_open is False def test_migrate_too_old(mocker, default_conf, fee, caplog): @@ -1393,7 +1395,7 @@ def test_migrate_too_old(mocker, default_conf, fee, caplog): # Run init to test migration with pytest.raises(OperationalException, match=r'Your database seems to be very old'): - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) def test_migrate_get_last_sequence_ids(): @@ -1416,14 +1418,14 @@ def test_migrate_set_sequence_ids(): engine = MagicMock() engine.begin = MagicMock() engine.name = 'postgresql' - set_sequence_ids(engine, 22, 55) + set_sequence_ids(engine, 22, 55, 5) assert engine.begin.call_count == 1 engine.reset_mock() engine.begin.reset_mock() engine.name = 'somethingelse' - set_sequence_ids(engine, 22, 55) + set_sequence_ids(engine, 22, 55, 6) assert engine.begin.call_count == 0 @@ -1467,7 +1469,7 @@ def test_migrate_pairlocks(mocker, default_conf, fee, caplog): connection.execute(text(create_index2)) connection.execute(text(create_index3)) - init_db(default_conf['db_url'], default_conf['dry_run']) + init_db(default_conf['db_url']) assert len(PairLock.query.all()) == 2 assert len(PairLock.query.filter(PairLock.pair == '*').all()) == 1 @@ -2114,6 +2116,24 @@ def test_get_trades_proxy(fee, use_db, is_short): Trade.use_db = True +@pytest.mark.usefixtures("init_persistence") +@pytest.mark.parametrize('is_short', [True, False]) +def test_get_trades__query(fee, is_short): + query = Trade.get_trades([]) + # without orders there should be no join issued. + query1 = Trade.get_trades([], include_orders=False) + + assert "JOIN orders" in str(query) + assert "JOIN orders" not in str(query1) + + create_mock_trades(fee, is_short) + query = Trade.get_trades([]) + query1 = Trade.get_trades([], include_orders=False) + + assert "JOIN orders" in str(query) + assert "JOIN orders" not in str(query1) + + def test_get_trades_backtest(): Trade.use_db = False with pytest.raises(NotImplementedError, match=r"`Trade.get_trades\(\)` not .*"): @@ -2308,6 +2328,7 @@ def test_Trade_object_idem(): 'get_exit_reason_performance', 'get_enter_tag_performance', 'get_mix_tag_performance', + 'get_trading_volume', ) @@ -2721,3 +2742,23 @@ def test_select_filled_orders(fee): orders = trades[4].select_filled_orders('sell') assert orders is not None assert len(orders) == 0 + + +@pytest.mark.usefixtures("init_persistence") +def test_order_to_ccxt(limit_buy_order_open): + + order = Order.parse_from_ccxt_object(limit_buy_order_open, 'mocked', 'buy') + order.query.session.add(order) + Order.query.session.commit() + + order_resp = Order.order_by_id(limit_buy_order_open['id']) + assert order_resp + + raw_order = order_resp.to_ccxt_object() + del raw_order['fee'] + del raw_order['datetime'] + del raw_order['info'] + assert raw_order['stopPrice'] is None + del raw_order['stopPrice'] + del limit_buy_order_open['datetime'] + assert raw_order == limit_buy_order_open