diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ff57b270..d2e420e8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: 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: @@ -70,7 +70,7 @@ jobs: if: matrix.python-version == '3.9' - 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 +78,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: | @@ -157,29 +159,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: | @@ -273,7 +261,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.9 + python-version: "3.10" - name: pre-commit dependencies run: | @@ -292,7 +280,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.9 + python-version: "3.10" - name: Documentation build run: | @@ -327,7 +315,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 @@ -358,7 +346,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.8 + python-version: "3.9" - name: Extract branch name shell: bash @@ -419,7 +407,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 2170b704a..d59010154 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,10 +14,10 @@ repos: exclude: build_helpers additional_dependencies: - types-cachetools==5.0.1 - - types-filelock==3.2.5 - - types-requests==2.27.20 - - types-tabulate==0.8.7 - - types-python-dateutil==2.8.12 + - types-filelock==3.2.6 + - types-requests==2.27.27 + - types-tabulate==0.8.9 + - types-python-dateutil==2.8.16 # stages: [push] - repo: https://github.com/pycqa/isort diff --git a/Dockerfile b/Dockerfile index 8f5b85698..5f7b52265 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9.9-slim-bullseye as base +FROM python:3.10.4-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/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/docs/backtesting.md b/docs/backtesting.md index a0a304400..76718d206 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -287,45 +287,54 @@ A backtesting result will look like that: | ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 | | LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 | | TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 | -================ SUMMARY METRICS =============== -| Metric | Value | -|------------------------+---------------------| -| Backtesting from | 2019-01-01 00:00:00 | -| Backtesting to | 2019-05-01 00:00:00 | -| Max open trades | 3 | -| | | -| Total/Daily Avg Trades | 429 / 3.575 | -| Starting balance | 0.01000000 BTC | -| Final balance | 0.01762792 BTC | -| Absolute profit | 0.00762792 BTC | -| Total profit % | 76.2% | -| CAGR % | 460.87% | -| Trades per day | 3.575 | -| Avg. stake amount | 0.001 BTC | -| Total trade volume | 0.429 BTC | -| | | -| Best Pair | LSK/BTC 26.26% | -| Worst Pair | ZEC/BTC -10.18% | -| Best Trade | LSK/BTC 4.25% | -| Worst Trade | ZEC/BTC -10.25% | -| Best day | 0.00076 BTC | -| Worst day | -0.00036 BTC | -| Days win/draw/lose | 12 / 82 / 25 | -| Avg. Duration Winners | 4:23:00 | -| Avg. Duration Loser | 6:55:00 | -| Rejected Entry signals | 3089 | -| Entry/Exit Timeouts | 0 / 0 | -| | | -| Min balance | 0.00945123 BTC | -| Max balance | 0.01846651 BTC | -| Drawdown (Account) | 13.33% | -| Drawdown | 0.0015 BTC | -| Drawdown high | 0.0013 BTC | -| Drawdown low | -0.0002 BTC | -| Drawdown Start | 2019-02-15 14:10:00 | -| Drawdown End | 2019-04-11 18:15:00 | -| Market change | -5.88% | -=============================================== +================== SUMMARY METRICS ================== +| Metric | Value | +|-----------------------------+---------------------| +| Backtesting from | 2019-01-01 00:00:00 | +| Backtesting to | 2019-05-01 00:00:00 | +| Max open trades | 3 | +| | | +| Total/Daily Avg Trades | 429 / 3.575 | +| Starting balance | 0.01000000 BTC | +| Final balance | 0.01762792 BTC | +| Absolute profit | 0.00762792 BTC | +| Total profit % | 76.2% | +| CAGR % | 460.87% | +| Avg. stake amount | 0.001 BTC | +| Total trade volume | 0.429 BTC | +| | | +| Long / Short | 352 / 77 | +| Total profit Long % | 1250.58% | +| Total profit Short % | -15.02% | +| Absolute profit Long | 0.00838792 BTC | +| Absolute profit Short | -0.00076 BTC | +| | | +| Best Pair | LSK/BTC 26.26% | +| Worst Pair | ZEC/BTC -10.18% | +| Best Trade | LSK/BTC 4.25% | +| Worst Trade | ZEC/BTC -10.25% | +| Best day | 0.00076 BTC | +| Worst day | -0.00036 BTC | +| Days win/draw/lose | 12 / 82 / 25 | +| Avg. Duration Winners | 4:23:00 | +| 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 | +| Max % of account underwater | 25.19% | +| Absolute Drawdown (Account) | 13.33% | +| Drawdown | 0.0015 BTC | +| Drawdown high | 0.0013 BTC | +| Drawdown low | -0.0002 BTC | +| Drawdown Start | 2019-02-15 14:10:00 | +| Drawdown End | 2019-04-11 18:15:00 | +| Market change | -5.88% | +===================================================== ``` ### Backtesting report table @@ -377,50 +386,54 @@ The last element of the backtest report is the summary metrics table. It contains some useful key metrics about performance of your strategy on backtesting data. ``` -================ SUMMARY METRICS =============== -| Metric | Value | -|------------------------+---------------------| -| Backtesting from | 2019-01-01 00:00:00 | -| Backtesting to | 2019-05-01 00:00:00 | -| Max open trades | 3 | -| | | -| Total/Daily Avg Trades | 429 / 3.575 | -| Starting balance | 0.01000000 BTC | -| Final balance | 0.01762792 BTC | -| Absolute profit | 0.00762792 BTC | -| Total profit % | 76.2% | -| CAGR % | 460.87% | -| Avg. stake amount | 0.001 BTC | -| Total trade volume | 0.429 BTC | -| | | -| Long / Short | 352 / 77 | -| Total profit Long % | 1250.58% | -| Total profit Short % | -15.02% | -| Absolute profit Long | 0.00838792 BTC | -| Absolute profit Short | -0.00076 BTC | -| | | -| Best Pair | LSK/BTC 26.26% | -| Worst Pair | ZEC/BTC -10.18% | -| Best Trade | LSK/BTC 4.25% | -| Worst Trade | ZEC/BTC -10.25% | -| Best day | 0.00076 BTC | -| Worst day | -0.00036 BTC | -| Days win/draw/lose | 12 / 82 / 25 | -| Avg. Duration Winners | 4:23:00 | -| Avg. Duration Loser | 6:55:00 | -| Rejected Entry signals | 3089 | -| Entry/Exit Timeouts | 0 / 0 | -| | | -| Min balance | 0.00945123 BTC | -| Max balance | 0.01846651 BTC | -| Drawdown (Account) | 13.33% | -| Drawdown | 0.0015 BTC | -| Drawdown high | 0.0013 BTC | -| Drawdown low | -0.0002 BTC | -| Drawdown Start | 2019-02-15 14:10:00 | -| Drawdown End | 2019-04-11 18:15:00 | -| Market change | -5.88% | -================================================ +================== SUMMARY METRICS ================== +| Metric | Value | +|-----------------------------+---------------------| +| Backtesting from | 2019-01-01 00:00:00 | +| Backtesting to | 2019-05-01 00:00:00 | +| Max open trades | 3 | +| | | +| Total/Daily Avg Trades | 429 / 3.575 | +| Starting balance | 0.01000000 BTC | +| Final balance | 0.01762792 BTC | +| Absolute profit | 0.00762792 BTC | +| Total profit % | 76.2% | +| CAGR % | 460.87% | +| Avg. stake amount | 0.001 BTC | +| Total trade volume | 0.429 BTC | +| | | +| Long / Short | 352 / 77 | +| Total profit Long % | 1250.58% | +| Total profit Short % | -15.02% | +| Absolute profit Long | 0.00838792 BTC | +| Absolute profit Short | -0.00076 BTC | +| | | +| Best Pair | LSK/BTC 26.26% | +| Worst Pair | ZEC/BTC -10.18% | +| Best Trade | LSK/BTC 4.25% | +| Worst Trade | ZEC/BTC -10.25% | +| Best day | 0.00076 BTC | +| Worst day | -0.00036 BTC | +| Days win/draw/lose | 12 / 82 / 25 | +| Avg. Duration Winners | 4:23:00 | +| 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 | +| Max % of account underwater | 25.19% | +| Absolute Drawdown (Account) | 13.33% | +| Drawdown | 0.0015 BTC | +| Drawdown high | 0.0013 BTC | +| Drawdown low | -0.0002 BTC | +| Drawdown Start | 2019-02-15 14:10:00 | +| Drawdown End | 2019-04-11 18:15:00 | +| Market change | -5.88% | +===================================================== ``` @@ -440,8 +453,13 @@ 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. -- `Drawdown (Account)`: Maximum Account Drawdown experienced. Calculated as $(Absolute Drawdown) / (DrawdownHigh + startingBalance)$. +- `Max % of account underwater`: Maximum percentage your account has decreased from the top since the simulation started. +Calculated as the maximum of `(Max Balance - Current Balance) / (Max Balance)`. +- `Absolute Drawdown (Account)`: Maximum Account Drawdown experienced. Calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`. - `Drawdown`: Maximum, absolute drawdown experienced. Difference between Drawdown High and Subsequent Low point. - `Drawdown high` / `Drawdown low`: Profit at the beginning and end of the largest drawdown period. A negative low value means initial capital lost. - `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command). @@ -457,7 +475,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 @@ -473,7 +491,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 @@ -512,8 +530,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..1acbca565 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -34,6 +34,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. @@ -58,6 +59,7 @@ This loop will be repeated again and again until the bot is stopped. * 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/data-download.md b/docs/data-download.md index 9bfc1e685..681fb717d 100644 --- a/docs/data-download.md +++ b/docs/data-download.md @@ -30,6 +30,7 @@ usage: freqtrade download-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--data-format-ohlcv {json,jsongz,hdf5}] [--data-format-trades {json,jsongz,hdf5}] [--trading-mode {spot,margin,futures}] + [--prepend] optional arguments: -h, --help show this help message and exit @@ -62,6 +63,7 @@ optional arguments: `jsongz`). --trading-mode {spot,margin,futures} Select Trading mode + --prepend Allow data prepending. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -157,10 +159,21 @@ freqtrade download-data --exchange binance --pairs .*/USDT - To change the exchange used to download the historical data from, please use a different configuration file (you'll probably need to adjust rate limits etc.) - To use `pairs.json` from some other directory, use `--pairs-file some_other_dir/pairs.json`. - To download historical candle (OHLCV) data for only 10 days, use `--days 10` (defaults to 30 days). -- To download historical candle (OHLCV) data from a fixed starting point, use `--timerange 20200101-` - which will download all data from January 1st, 2020. Eventually set end dates are ignored. +- To download historical candle (OHLCV) data from a fixed starting point, use `--timerange 20200101-` - which will download all data from January 1st, 2020. - Use `--timeframes` to specify what timeframe download the historical candle (OHLCV) data for. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute data. - To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options. +#### Download additional data before the current timerange + +Assuming you downloaded all data from 2022 (`--timerange 20220101-`) - but you'd now like to also backtest with earlier data. +You can do so by using the `--prepend` flag, combined with `--timerange` - specifying an end-date. + +``` bash +freqtrade download-data --exchange binance --pairs ETH/USDT XRP/USDT BTC/USDT --prepend --timerange 20210101-20220101 +``` + +!!! Note + Freqtrade will ignore the end-date in this mode if data is available, updating the end-date to the existing data start point. ### Data format diff --git a/docs/developer.md b/docs/developer.md index 1cc16294b..ce7fb37e1 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -200,11 +200,12 @@ For that reason, they must implement the following methods: * `global_stop()` * `stop_per_pair()`. -`global_stop()` and `stop_per_pair()` must return a ProtectionReturn tuple, which consists of: +`global_stop()` and `stop_per_pair()` must return a ProtectionReturn object, which consists of: * lock pair - boolean * lock until - datetime - until when should the pair be locked (will be rounded up to the next new candle) * reason - string, used for logging and storage in the database +* lock_side - long, short or '*'. The `until` portion should be calculated using the provided `calculate_lock_end()` method. @@ -313,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(lev_tiers, 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 3f613a208..030d73f4b 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -116,7 +116,9 @@ optional arguments: ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss, SharpeHyperOptLossDaily, SortinoHyperOptLoss, SortinoHyperOptLossDaily, - CalmarHyperOptLoss, MaxDrawDownHyperOptLoss, ProfitDrawDownHyperOptLoss + CalmarHyperOptLoss, MaxDrawDownHyperOptLoss, + MaxDrawDownRelativeHyperOptLoss, + ProfitDrawDownHyperOptLoss --disable-param-export Disable automatic hyperopt parameter export. --ignore-missing-spaces, --ignore-unparameterized-spaces @@ -563,7 +565,8 @@ Currently, the following loss functions are builtin: * `SharpeHyperOptLossDaily` - optimizes Sharpe Ratio calculated on **daily** trade returns relative to standard deviation. * `SortinoHyperOptLoss` - optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation. * `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation. -* `MaxDrawDownHyperOptLoss` - Optimizes Maximum drawdown. +* `MaxDrawDownHyperOptLoss` - Optimizes Maximum absolute drawdown. +* `MaxDrawDownRelativeHyperOptLoss` - Optimizes both maximum absolute drawdown while also adjusting for maximum relative drawdown. * `CalmarHyperOptLoss` - Optimizes Calmar Ratio calculated on trade returns relative to max drawdown. * `ProfitDrawDownHyperOptLoss` - Optimizes by max Profit & min Drawdown objective. `DRAWDOWN_MULT` variable within the hyperoptloss file can be adjusted to be stricter or more flexible on drawdown purposes. diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 5b5e285fe..0f55c1b79 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -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 0757d2f6d..d67924cfe 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -48,6 +48,8 @@ If `trade_limit` or more trades resulted in stoploss, trading will stop for `sto This applies across all pairs, unless `only_per_pair` is set to true, which will then only look at one pair at a time. +Similarly, this protection will by default look at all trades (long and short). 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 stoplosses. + The below example stops trading for all pairs for 4 candles after the last trade if the bot hit stoploss 4 times within the last 24 candles. ``` python @@ -59,7 +61,8 @@ def protections(self): "lookback_period_candles": 24, "trade_limit": 4, "stop_duration_candles": 4, - "only_per_pair": False + "only_per_pair": False, + "only_per_side": False } ] ``` @@ -93,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 @@ -104,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..2ee6f8444 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -101,6 +101,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 97be17243..3fa35d80d 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,5 +1,5 @@ mkdocs==1.3.0 -mkdocs-material==8.2.10 +mkdocs-material==8.2.15 mdx_truly_sane_lists==1.2 pymdown-extensions==9.4 -jinja2==3.1.1 +jinja2==3.1.2 diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 5ff499b01..06e7152aa 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" @@ -562,6 +563,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 @@ -604,6 +613,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. @@ -655,7 +667,7 @@ class DigDeeperStrategy(IStrategy): # 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, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: # We need to leave most of the funds for possible further DCA orders @@ -663,7 +675,7 @@ class DigDeeperStrategy(IStrategy): 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 +725,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). 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/utils.md b/docs/utils.md index 5ef5646c3..9b799e5fc 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" diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index e7f2f9d6f..29506905b 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2022.4.2' +__version__ = '2022.5' if 'dev' in __version__: try: diff --git a/freqtrade/commands/__init__.py b/freqtrade/commands/__init__.py index 129836000..0e637c487 100644 --- a/freqtrade/commands/__init__.py +++ b/freqtrade/commands/__init__.py @@ -10,6 +10,7 @@ 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/arguments.py b/freqtrade/commands/arguments.py index 62b79da2e..815e28175 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -72,7 +72,8 @@ ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs", "trading_mode"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "include_inactive", "timerange", "download_trades", "exchange", "timeframes", - "erase", "dataformat_ohlcv", "dataformat_trades", "trading_mode"] + "erase", "dataformat_ohlcv", "dataformat_trades", "trading_mode", + "prepend_data"] ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_url", "trade_source", "export", "exportfilename", @@ -81,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"] @@ -180,7 +183,7 @@ class Arguments: self._build_args(optionlist=['version'], parser=self.parser) from freqtrade.commands import (start_backtesting, start_backtesting_show, - start_convert_data, start_convert_trades, + 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, @@ -373,6 +376,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 df8966e85..aac9f5713 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.', @@ -443,6 +448,11 @@ AVAILABLE_CLI_OPTIONS = { default=['1m', '5m'], nargs='+', ), + "prepend_data": Arg( + '--prepend', + help='Allow data prepending.', + action='store_true', + ), "erase": Arg( '--erase', help='Clean all existing data for the selected exchange/pairs/timeframes.', diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index e41512ccc..61a99782e 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -79,12 +79,19 @@ 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, new_pairs_days=config['new_pairs_days'], erase=bool(config.get('erase')), data_format=config['dataformat_ohlcv'], trading_mode=config.get('trading_mode', 'spot'), + prepend=config.get('prepend_data', False) ) except KeyboardInterrupt: 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/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 dde56c220..3f563b6cd 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -147,6 +147,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.') @@ -393,6 +396,8 @@ class Configuration: self._args_to_config(config, argname='trade_source', logstring='Using trades from: {}') + self._args_to_config(config, argname='prepend_data', + logstring='Prepend detected. Allowing data prepending.') self._args_to_config(config, argname='erase', logstring='Erase detected. Deleting existing data.') @@ -485,7 +490,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']: @@ -496,5 +502,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 0ceabe917..9fbd70e42 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -28,7 +28,8 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily', 'CalmarHyperOptLoss', - 'MaxDrawDownHyperOptLoss', 'ProfitDrawDownHyperOptLoss'] + 'MaxDrawDownHyperOptLoss', 'MaxDrawDownRelativeHyperOptLoss', + 'ProfitDrawDownHyperOptLoss'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'OffsetFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', @@ -301,12 +302,12 @@ 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', @@ -482,6 +483,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 @@ -493,3 +496,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..fef432576 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -353,7 +353,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/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 8560fd29e..bead59814 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.") @@ -139,8 +144,9 @@ def _load_cached_data_for_updating( timeframe: str, timerange: Optional[TimeRange], data_handler: IDataHandler, - candle_type: CandleType -) -> Tuple[DataFrame, Optional[int]]: + candle_type: CandleType, + prepend: bool = False, +) -> Tuple[DataFrame, Optional[int], Optional[int]]: """ Load cached data to download more data. If timerange is passed in, checks whether data from an before the stored data will be @@ -150,9 +156,12 @@ def _load_cached_data_for_updating( Note: Only used by download_pair_history(). """ start = None + end = None if timerange: if timerange.starttype == 'date': start = datetime.fromtimestamp(timerange.startts, tz=timezone.utc) + if timerange.stoptype == 'date': + end = datetime.fromtimestamp(timerange.stopts, tz=timezone.utc) # Intentionally don't pass timerange in - since we need to load the full dataset. data = data_handler.ohlcv_load(pair, timeframe=timeframe, @@ -160,14 +169,17 @@ def _load_cached_data_for_updating( drop_incomplete=True, warn_no_data=False, candle_type=candle_type) if not data.empty: - if start and start < data.iloc[0]['date']: + if not prepend and start and start < data.iloc[0]['date']: # Earlier data than existing data requested, redownload all data = DataFrame(columns=DEFAULT_DATAFRAME_COLUMNS) else: - start = data.iloc[-1]['date'] - + if prepend: + end = data.iloc[0]['date'] + else: + start = data.iloc[-1]['date'] start_ms = int(start.timestamp() * 1000) if start else None - return data, start_ms + end_ms = int(end.timestamp() * 1000) if end else None + return data, start_ms, end_ms def _download_pair_history(pair: str, *, @@ -180,6 +192,7 @@ def _download_pair_history(pair: str, *, timerange: Optional[TimeRange] = None, candle_type: CandleType, erase: bool = False, + prepend: bool = False, ) -> bool: """ Download latest candles from the exchange for the pair and timeframe passed in parameters @@ -187,8 +200,6 @@ def _download_pair_history(pair: str, *, exists in a cache. If timerange starts earlier than the data in the cache, the full data will be redownloaded - Based on @Rybolov work: https://github.com/rybolov/freqtrade-data - :param pair: pair to download :param timeframe: Timeframe (e.g "5m") :param timerange: range of time to download @@ -203,14 +214,17 @@ def _download_pair_history(pair: str, *, if data_handler.ohlcv_purge(pair, timeframe, candle_type=candle_type): logger.info(f'Deleting existing data for pair {pair}, {timeframe}, {candle_type}.') - logger.info( - f'Download history data for pair: "{pair}" ({process}), timeframe: {timeframe}, ' - f'candle type: {candle_type} and store in {datadir}.' - ) + data, since_ms, until_ms = _load_cached_data_for_updating( + pair, timeframe, timerange, + data_handler=data_handler, + candle_type=candle_type, + prepend=prepend) - data, since_ms = _load_cached_data_for_updating(pair, timeframe, timerange, - data_handler=data_handler, - candle_type=candle_type) + logger.info(f'({process}) - Download history data for "{pair}", {timeframe}, ' + f'{candle_type} and store in {datadir}.' + f'From {format_ms_time(since_ms) if since_ms else "start"} to ' + f'{format_ms_time(until_ms) if until_ms else "now"}' + ) logger.debug("Current Start: %s", f"{data.iloc[0]['date']:%Y-%m-%d %H:%M:%S}" if not data.empty else 'None') @@ -225,6 +239,7 @@ def _download_pair_history(pair: str, *, days=-new_pairs_days).int_timestamp * 1000, is_new_pair=data.empty, candle_type=candle_type, + until_ms=until_ms if until_ms else None ) # TODO: Maybe move parsing to exchange class (?) new_dataframe = ohlcv_to_dataframe(new_data, timeframe, pair, @@ -257,6 +272,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes timerange: Optional[TimeRange] = None, new_pairs_days: int = 30, erase: bool = False, data_format: str = None, + prepend: bool = False, ) -> List[str]: """ Refresh stored ohlcv data for backtesting and hyperopt operations. @@ -266,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) @@ -280,7 +297,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes timerange=timerange, data_handler=data_handler, timeframe=str(timeframe), new_pairs_days=new_pairs_days, candle_type=candle_type, - erase=erase) + erase=erase, prepend=prepend) if trading_mode == 'futures': # Predefined candletype (and timeframe) depending on exchange # Downloads what is necessary to backtest based on futures data. @@ -294,7 +311,7 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes timerange=timerange, data_handler=data_handler, timeframe=str(tf_mark), new_pairs_days=new_pairs_days, candle_type=funding_candle_type, - erase=erase) + erase=erase, prepend=prepend) return pairs_not_available @@ -312,8 +329,9 @@ def _download_trades_history(exchange: Exchange, try: until = None - if (timerange and timerange.starttype == 'date'): - since = timerange.startts * 1000 + if timerange: + if timerange.starttype == 'date': + since = timerange.startts * 1000 if timerange.stoptype == 'date': until = timerange.stopts * 1000 else: diff --git a/freqtrade/data/history/idatahandler.py b/freqtrade/data/history/idatahandler.py index 4a5eb6bc2..07dc7c763 100644 --- a/freqtrade/data/history/idatahandler.py +++ b/freqtrade/data/history/idatahandler.py @@ -5,7 +5,7 @@ It's subclasses handle and storing data from disk. """ import logging import re -from abc import ABC, abstractclassmethod, abstractmethod +from abc import ABC, abstractmethod from copy import deepcopy from datetime import datetime, timezone from pathlib import Path @@ -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 @@ -38,7 +38,8 @@ class IDataHandler(ABC): """ raise NotImplementedError() - @abstractclassmethod + @classmethod + @abstractmethod def ohlcv_get_available_data( cls, datadir: Path, trading_mode: TradingMode) -> ListPairsWithTimeframes: """ @@ -48,7 +49,8 @@ class IDataHandler(ABC): :return: List of Tuples of (pair, timeframe) """ - @abstractclassmethod + @classmethod + @abstractmethod def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]: """ Returns a list of all pairs with ohlcv data available in this datadir @@ -118,7 +120,8 @@ class IDataHandler(ABC): :param candle_type: Any of the enum CandleType (must match trading mode!) """ - @abstractclassmethod + @classmethod + @abstractmethod def trades_get_pairs(cls, datadir: Path) -> List[str]: """ Returns a list of all pairs for which trade data is available in this @@ -190,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}" @@ -207,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/data/metrics.py b/freqtrade/data/metrics.py index 44d5ce6ec..c11a2df88 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -72,18 +72,28 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str, return df -def _calc_drawdown_series(profit_results: pd.DataFrame, *, date_col: str, value_col: str - ) -> pd.DataFrame: +def _calc_drawdown_series(profit_results: pd.DataFrame, *, date_col: str, value_col: str, + starting_balance: float) -> pd.DataFrame: max_drawdown_df = pd.DataFrame() max_drawdown_df['cumulative'] = profit_results[value_col].cumsum() max_drawdown_df['high_value'] = max_drawdown_df['cumulative'].cummax() max_drawdown_df['drawdown'] = max_drawdown_df['cumulative'] - max_drawdown_df['high_value'] max_drawdown_df['date'] = profit_results.loc[:, date_col] + if starting_balance: + cumulative_balance = starting_balance + max_drawdown_df['cumulative'] + max_balance = starting_balance + max_drawdown_df['high_value'] + max_drawdown_df['drawdown_relative'] = ((max_balance - cumulative_balance) / max_balance) + else: + # NOTE: This is not completely accurate, + # but might good enough if starting_balance is not available + max_drawdown_df['drawdown_relative'] = ( + (max_drawdown_df['high_value'] - max_drawdown_df['cumulative']) + / max_drawdown_df['high_value']) return max_drawdown_df def calculate_underwater(trades: pd.DataFrame, *, date_col: str = 'close_date', - value_col: str = 'profit_ratio' + value_col: str = 'profit_ratio', starting_balance: float = 0.0 ): """ Calculate max drawdown and the corresponding close dates @@ -97,13 +107,18 @@ def calculate_underwater(trades: pd.DataFrame, *, date_col: str = 'close_date', if len(trades) == 0: raise ValueError("Trade dataframe empty.") profit_results = trades.sort_values(date_col).reset_index(drop=True) - max_drawdown_df = _calc_drawdown_series(profit_results, date_col=date_col, value_col=value_col) + max_drawdown_df = _calc_drawdown_series( + profit_results, + date_col=date_col, + value_col=value_col, + starting_balance=starting_balance) return max_drawdown_df def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date', - value_col: str = 'profit_abs', starting_balance: float = 0 + value_col: str = 'profit_abs', starting_balance: float = 0, + relative: bool = False ) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]: """ Calculate max drawdown and the corresponding close dates @@ -119,9 +134,15 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date' if len(trades) == 0: raise ValueError("Trade dataframe empty.") profit_results = trades.sort_values(date_col).reset_index(drop=True) - max_drawdown_df = _calc_drawdown_series(profit_results, date_col=date_col, value_col=value_col) + max_drawdown_df = _calc_drawdown_series( + profit_results, + date_col=date_col, + value_col=value_col, + starting_balance=starting_balance + ) - idxmin = max_drawdown_df['drawdown'].idxmin() + idxmin = max_drawdown_df['drawdown_relative'].idxmax() if relative \ + else max_drawdown_df['drawdown'].idxmin() if idxmin == 0: raise ValueError("No losing trade, therefore no drawdown.") high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]['high_value'].idxmax(), date_col] @@ -129,12 +150,10 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date' high_val = max_drawdown_df.loc[max_drawdown_df.iloc[:idxmin] ['high_value'].idxmax(), 'cumulative'] low_val = max_drawdown_df.loc[idxmin, 'cumulative'] - max_drawdown_rel = 0.0 - if high_val + starting_balance != 0: - max_drawdown_rel = (high_val - low_val) / (high_val + starting_balance) + max_drawdown_rel = max_drawdown_df.loc[idxmin, 'drawdown_relative'] return ( - abs(min(max_drawdown_df['drawdown'])), + abs(max_drawdown_df.loc[idxmin, 'drawdown']), high_date, low_date, high_val, 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 8c442cd26..1b6496a64 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -57,7 +57,7 @@ class Binance(Exchange): (side == "buy" and stop_loss < float(order['info']['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,6 +95,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: Optional[int] = None ) -> Tuple[str, str, str, List]: """ Overwrite to introduce "fast new pair" functionality by detecting the pair's listing date @@ -115,7 +116,8 @@ class Binance(Exchange): since_ms=since_ms, is_new_pair=is_new_pair, raise_=raise_, - candle_type=candle_type + candle_type=candle_type, + until_ms=until_ms, ) def funding_fee_cutoff(self, open_date: datetime): diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index ddffe1250..126b3b62f 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -1,91 +1,195 @@ { "RAY/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": 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": "386950.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.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" } @@ -93,106 +197,113 @@ ], "SUSHI/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": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "2000000", + "notionalCap": "50000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -200,91 +311,97 @@ ], "CVC/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": 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": "386950.0" } @@ -292,91 +409,97 @@ ], "BTS/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": 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": "386950.0" } @@ -384,91 +507,97 @@ ], "HOT/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": 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": "386950.0" } @@ -476,91 +605,97 @@ ], "ZRX/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": 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": "386950.0" } @@ -568,91 +703,97 @@ ], "QTUM/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": 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": "386950.0" } @@ -660,91 +801,97 @@ ], "IOTA/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": 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": "386950.0" } @@ -752,151 +899,161 @@ ], "BTC/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.004, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "50000", - "minNotional": "0", + "notionalCap": "50000", + "notionalFloor": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, + "tier": 2.0, + "currency": "BUSD", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.005, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.005", "cum": "50.0" } }, { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 3.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.01", "cum": "1300.0" } }, { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 7500000, + "tier": 4.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "maxNotional": "7500000", - "minNotional": "1000000", + "notionalCap": "7500000", + "notionalFloor": "1000000", "maintMarginRatio": "0.025", "cum": "16300.0" } }, { - "tier": 5, - "minNotional": 7500000, - "maxNotional": 40000000, + "tier": 5.0, + "currency": "BUSD", + "minNotional": 7500000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 6, + "maxLeverage": 6.0, "info": { "bracket": "5", "initialLeverage": "6", - "maxNotional": "40000000", - "minNotional": "7500000", + "notionalCap": "40000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.05", "cum": "203800.0" } }, { - "tier": 6, - "minNotional": 40000000, - "maxNotional": 100000000, + "tier": 6.0, + "currency": "BUSD", + "minNotional": 40000000.0, + "maxNotional": 100000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "maxNotional": "100000000", - "minNotional": "40000000", + "notionalCap": "100000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.1", "cum": "2203800.0" } }, { - "tier": 7, - "minNotional": 100000000, - "maxNotional": 200000000, + "tier": 7.0, + "currency": "BUSD", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "maxNotional": "200000000", - "minNotional": "100000000", + "notionalCap": "200000000", + "notionalFloor": "100000000", "maintMarginRatio": "0.125", "cum": "4703800.0" } }, { - "tier": 8, - "minNotional": 200000000, - "maxNotional": 400000000, + "tier": 8.0, + "currency": "BUSD", + "minNotional": 200000000.0, + "maxNotional": 400000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "8", "initialLeverage": "3", - "maxNotional": "400000000", - "minNotional": "200000000", + "notionalCap": "400000000", + "notionalFloor": "200000000", "maintMarginRatio": "0.15", "cum": "9703800.0" } }, { - "tier": 9, - "minNotional": 400000000, - "maxNotional": 600000000, + "tier": 9.0, + "currency": "BUSD", + "minNotional": 400000000.0, + "maxNotional": 600000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "maxNotional": "600000000", - "minNotional": "400000000", + "notionalCap": "600000000", + "notionalFloor": "400000000", "maintMarginRatio": "0.25", "cum": "4.97038E7" } }, { - "tier": 10, - "minNotional": 600000000, - "maxNotional": 1000000000, + "tier": 10.0, + "currency": "BUSD", + "minNotional": 600000000.0, + "maxNotional": 1000000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "maxNotional": "1000000000", - "minNotional": "600000000", + "notionalCap": "1000000000", + "notionalFloor": "600000000", "maintMarginRatio": "0.5", "cum": "1.997038E8" } @@ -904,91 +1061,97 @@ ], "WAVES/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": 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": "386950.0" } @@ -996,136 +1159,145 @@ ], "ADA/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" } @@ -1133,183 +1305,97 @@ ], "LIT/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "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", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -1317,136 +1403,145 @@ ], "XTZ/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" } @@ -1454,136 +1549,145 @@ ], "BNB/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" } @@ -1591,183 +1695,293 @@ ], "AKRO/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" } } ], - "HNT/USDT": [ + "DAR/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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -1775,136 +1989,145 @@ ], "ETC/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" } @@ -1912,136 +2135,145 @@ ], "XMR/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" } @@ -2049,91 +2281,97 @@ ], "YFI/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": 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": "386950.0" } @@ -2141,365 +2379,259 @@ ], "FTT/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "30000000", + "notionalFloor": "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, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.005, - "maxLeverage": 100, + "maxLeverage": 100.0, "info": { "bracket": "1", "initialLeverage": "100", - "maxNotional": "10000", - "minNotional": "0", + "notionalCap": "10000", + "notionalFloor": "0", "maintMarginRatio": "0.005", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 10000, - "maxNotional": 100000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.0065, - "maxLeverage": 75, + "maxLeverage": 75.0, "info": { "bracket": "2", "initialLeverage": "75", - "maxNotional": "100000", - "minNotional": "10000", + "notionalCap": "100000", + "notionalFloor": "10000", "maintMarginRatio": "0.0065", "cum": "15.0" } }, { - "tier": 3, - "minNotional": 100000, - "maxNotional": 500000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "3", "initialLeverage": "50", - "maxNotional": "500000", - "minNotional": "100000", + "notionalCap": "500000", + "notionalFloor": "100000", "maintMarginRatio": "0.01", "cum": "365.0" } }, { - "tier": 4, - "minNotional": 500000, - "maxNotional": 1500000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "maxNotional": "1500000", - "minNotional": "500000", + "notionalCap": "1500000", + "notionalFloor": "500000", "maintMarginRatio": "0.02", "cum": "5365.0" } }, { - "tier": 5, - "minNotional": 1500000, - "maxNotional": 4000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "maxNotional": "4000000", - "minNotional": "1500000", + "notionalCap": "4000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.05", "cum": "50365.0" } }, { - "tier": 6, - "minNotional": 4000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "maxNotional": "10000000", - "minNotional": "4000000", + "notionalCap": "10000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", "cum": "250365.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.125", "cum": "500365.0" } }, { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 40000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "8", "initialLeverage": "3", - "maxNotional": "40000000", - "minNotional": "20000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.15", "cum": "1000365.0" } }, { - "tier": 9, - "minNotional": 40000000, - "maxNotional": 150000000, + "tier": 9.0, + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 150000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "maxNotional": "150000000", - "minNotional": "40000000", + "notionalCap": "150000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.25", "cum": "5000365.0" } }, { - "tier": 10, - "minNotional": 150000000, - "maxNotional": 500000000, + "tier": 10.0, + "currency": "USDT", + "minNotional": 150000000.0, + "maxNotional": 500000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "maxNotional": "500000000", - "minNotional": "150000000", + "notionalCap": "500000000", + "notionalFloor": "150000000", "maintMarginRatio": "0.5", "cum": "4.2500365E7" } @@ -2507,106 +2639,113 @@ ], "ALICE/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -2614,91 +2753,195 @@ ], "ALPHA/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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -2706,91 +2949,97 @@ ], "SFP/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": 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": "386950.0" } @@ -2798,91 +3047,97 @@ ], "REEF/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": 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": "386950.0" } @@ -2890,91 +3145,97 @@ ], "BAT/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": 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": "386950.0" } @@ -2982,106 +3243,113 @@ ], "DOGE/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "250000", - "minNotional": "50000", + "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": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "10000000", + "notionalCap": "50000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", "cum": "3232000.0" } @@ -3089,136 +3357,145 @@ ], "TRX/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" } @@ -3226,275 +3503,195 @@ ], "RLC/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": 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": "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, + "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": 9223372036854776000, + "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": "9223372036854775807", - "minNotional": "1000000", + "notionalCap": "9223372036854775807", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3502,91 +3699,97 @@ ], "STORJ/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": 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": "386950.0" } @@ -3594,275 +3797,195 @@ ], "SNX/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": 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": "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": 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": "386950.0" } @@ -3870,457 +3993,505 @@ ], "AUDIO/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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], "XLM/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" } } ], - "BTCBUSD_210129": [ + "APE/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.004, - "maxLeverage": 20, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "1", "initialLeverage": "20", - "maxNotional": "5000", - "minNotional": "0", - "maintMarginRatio": "0.004", + "notionalCap": "25000", + "notionalFloor": "0", + "maintMarginRatio": "0.025", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 5000, - "maxNotional": 25000, - "maintenanceMarginRate": 0.005, - "maxLeverage": 15, + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "2", - "initialLeverage": "15", - "maxNotional": "25000", - "minNotional": "5000", - "maintMarginRatio": "0.005", - "cum": "5.0" + "initialLeverage": "10", + "notionalCap": "100000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "625.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, - "maintenanceMarginRate": 0.01, - "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.01", - "cum": "130.0" + "initialLeverage": "5", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1", + "cum": "5625.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 500000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 7, + "tier": 4.0, + "currency": "BUSD", + "minNotional": 250000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 2.0, "info": { "bracket": "4", - "initialLeverage": "7", - "maxNotional": "500000", - "minNotional": "100000", - "maintMarginRatio": "0.025", - "cum": "1630.0" + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "250000", + "maintMarginRatio": "0.125", + "cum": "11875.0" } }, { - "tier": 5, - "minNotional": 500000, - "maxNotional": 2000000, - "maintenanceMarginRate": 0.05, - "maxLeverage": 6, + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, "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" + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" } } ], "IOTX/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": 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": "386950.0" } @@ -4328,91 +4499,97 @@ ], "NEO/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": 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": "386950.0" } @@ -4420,91 +4597,97 @@ ], "UNFI/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": 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": "386950.0" } @@ -4512,106 +4695,113 @@ ], "SAND/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -4619,91 +4809,97 @@ ], "DASH/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": 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": "386950.0" } @@ -4711,91 +4907,97 @@ ], "KAVA/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": 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": "386950.0" } @@ -4803,183 +5005,309 @@ ], "RUNE/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": 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": "386950.0" } } ], - "CTK/USDT": [ + "APE/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", + "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" + } + } + ], + "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, - "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": 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": "386950.0" } @@ -4987,136 +5315,145 @@ ], "LINK/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" } @@ -5124,106 +5461,227 @@ ], "CELR/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "654500.0" + } + } + ], + "BNX/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": 50000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "50000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -5231,91 +5689,97 @@ ], "RSR/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": 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": "386950.0" } @@ -5323,91 +5787,97 @@ ], "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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "30000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -5415,91 +5885,97 @@ ], "DGB/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": 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": "386950.0" } @@ -5507,91 +5983,97 @@ ], "SKL/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": 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": "386950.0" } @@ -5599,91 +6081,97 @@ ], "REN/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": 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": "386950.0" } @@ -5691,91 +6179,195 @@ ], "LPT/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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5783,91 +6375,97 @@ ], "TOMO/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": 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": "386950.0" } @@ -5875,91 +6473,97 @@ ], "MTL/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": 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": "386950.0" } @@ -5967,136 +6571,145 @@ ], "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" } @@ -6104,91 +6717,97 @@ ], "DODO/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": 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": "386950.0" } @@ -6196,91 +6815,97 @@ ], "EGLD/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": 50000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "1000000", + "notionalCap": "50000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -6288,91 +6913,97 @@ ], "KSM/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": 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": "386950.0" } @@ -6380,275 +7011,195 @@ ], "BNB/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "30000000", + "notionalFloor": "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, + "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": 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": "386950.0" } @@ -6656,198 +7207,309 @@ ], "VET/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } } ], - "TRB/USDT": [ + "IMX/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": 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": "386950.0" + } + } + ], + "TRB/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" } @@ -6855,198 +7517,309 @@ ], "MANA/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } } ], - "COTI/USDT": [ + "FLOW/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": 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": "386950.0" + } + } + ], + "COTI/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" } @@ -7054,581 +7827,571 @@ ], "CHR/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": 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": "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, + "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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], "GRT/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": 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": "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, + "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": 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": "386950.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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7636,91 +8399,97 @@ ], "MASK/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": 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": "386950.0" } @@ -7728,335 +8497,243 @@ ], "EOS/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" } } ], - "ETHUSDT_211231": [ + "OGN/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 375000, - "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": "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", + "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": 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": "386950.0" } @@ -8064,183 +8741,309 @@ ], "SC/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": 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": "386950.0" } } ], + "ETHUSDT_220624": [ + { + "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" + } + } + ], "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": 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": "386950.0" } @@ -8248,489 +9051,195 @@ ], "STMX/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": 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": "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, + "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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "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", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8738,91 +9247,97 @@ ], "KNC/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": 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": "386950.0" } @@ -8830,91 +9345,97 @@ ], "SRM/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": 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": "386950.0" } @@ -8922,106 +9443,113 @@ ], "ENJ/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -9029,91 +9557,97 @@ ], "C98/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": 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": "386950.0" } @@ -9121,91 +9655,97 @@ ], "ZEN/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": 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": "386950.0" } @@ -9213,106 +9753,113 @@ ], "ATOM/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -9320,106 +9867,113 @@ ], "NEAR/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -9427,91 +9981,97 @@ ], "SOL/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "30000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -9519,91 +10079,97 @@ ], "ENS/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": 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": "386950.0" } @@ -9611,136 +10177,145 @@ ], "BCH/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" } @@ -9748,91 +10323,97 @@ ], "ATA/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": 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": "386950.0" } @@ -9840,91 +10421,97 @@ ], "IOST/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": 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": "386950.0" } @@ -9932,91 +10519,97 @@ ], "HBAR/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": 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": "386950.0" } @@ -10024,106 +10617,113 @@ ], "ZEC/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -10131,106 +10731,113 @@ ], "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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -10238,91 +10845,97 @@ ], "TLM/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": 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": "386950.0" } @@ -10330,183 +10943,97 @@ ], "ANT/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "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", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -10514,151 +11041,161 @@ ], "ETH/BUSD": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 25000, + "tier": 1.0, + "currency": "BUSD", + "minNotional": 0.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.004, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "1", "initialLeverage": "50", - "maxNotional": "25000", - "minNotional": "0", + "notionalCap": "25000", + "notionalFloor": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 25000, - "maxNotional": 100000, + "tier": 2.0, + "currency": "BUSD", + "minNotional": 25000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.005, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "100000", - "minNotional": "25000", + "notionalCap": "100000", + "notionalFloor": "25000", "maintMarginRatio": "0.005", "cum": "25.0" } }, { - "tier": 3, - "minNotional": 100000, - "maxNotional": 500000, + "tier": 3.0, + "currency": "BUSD", + "minNotional": 100000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "maxNotional": "500000", - "minNotional": "100000", + "notionalCap": "500000", + "notionalFloor": "100000", "maintMarginRatio": "0.01", "cum": "525.0" } }, { - "tier": 4, - "minNotional": 500000, - "maxNotional": 1500000, + "tier": 4.0, + "currency": "BUSD", + "minNotional": 500000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "maxNotional": "1500000", - "minNotional": "500000", + "notionalCap": "1500000", + "notionalFloor": "500000", "maintMarginRatio": "0.025", "cum": "8025.0" } }, { - "tier": 5, - "minNotional": 1500000, - "maxNotional": 4000000, + "tier": 5.0, + "currency": "BUSD", + "minNotional": 1500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 6, + "maxLeverage": 6.0, "info": { "bracket": "5", "initialLeverage": "6", - "maxNotional": "4000000", - "minNotional": "1500000", + "notionalCap": "4000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.05", "cum": "45525.0" } }, { - "tier": 6, - "minNotional": 4000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "BUSD", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "maxNotional": "10000000", - "minNotional": "4000000", + "notionalCap": "10000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", "cum": "245525.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 7.0, + "currency": "BUSD", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.125", "cum": "495525.0" } }, { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 40000000, + "tier": 8.0, + "currency": "BUSD", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "8", "initialLeverage": "3", - "maxNotional": "40000000", - "minNotional": "20000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.15", "cum": "995525.0" } }, { - "tier": 9, - "minNotional": 40000000, - "maxNotional": 150000000, + "tier": 9.0, + "currency": "BUSD", + "minNotional": 40000000.0, + "maxNotional": 150000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "maxNotional": "150000000", - "minNotional": "40000000", + "notionalCap": "150000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.25", "cum": "4995525.0" } }, { - "tier": 10, - "minNotional": 150000000, - "maxNotional": 500000000, + "tier": 10.0, + "currency": "BUSD", + "minNotional": 150000000.0, + "maxNotional": 500000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "maxNotional": "500000000", - "minNotional": "150000000", + "notionalCap": "500000000", + "notionalFloor": "150000000", "maintMarginRatio": "0.5", "cum": "4.2495525E7" } @@ -10666,106 +11203,113 @@ ], "GALA/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -10773,121 +11317,129 @@ ], "AAVE/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "500.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": "8000.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": "58000.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": "108000.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -10895,91 +11447,97 @@ ], "GTC/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": 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": "386950.0" } @@ -10987,106 +11545,113 @@ ], "ALGO/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -11094,305 +11659,211 @@ ], "ICP/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": 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": "386950.0" } } ], - "BTCUSDT_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" - } - } - ], "LRC/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -11400,305 +11871,211 @@ ], "AVAX/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": 250000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "150000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { - "tier": 3, - "minNotional": 150000, - "maxNotional": 250000, + "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": "250000", - "minNotional": "150000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.05", - "cum": "4500.0" + "cum": "7000.0" } }, { - "tier": 4, - "minNotional": 250000, - "maxNotional": 500000, + "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": "500000", - "minNotional": "250000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "17000.0" + "cum": "57000.0" } }, { - "tier": 5, - "minNotional": 500000, - "maxNotional": 750000, + "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": "750000", - "minNotional": "500000", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "29500.0" + "cum": "107000.0" } }, { - "tier": 6, - "minNotional": 750000, - "maxNotional": 1000000, + "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": "1000000", - "minNotional": "750000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "123250.0" + "cum": "732000.0" } }, { - "tier": 7, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "50000000", + "notionalFloor": "10000000", "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" + "cum": "3232000.0" } } ], "ARPA/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": 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": "386950.0" } @@ -11706,91 +12083,97 @@ ], "CELO/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": 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": "386950.0" } @@ -11798,91 +12181,97 @@ ], "ROSE/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": 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": "386950.0" } @@ -11890,106 +12279,113 @@ ], "MATIC/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": 750000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "maxNotional": "750000", - "minNotional": "500000", + "notionalCap": "750000", + "notionalFloor": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { - "tier": 6, - "minNotional": 750000, - "maxNotional": 1000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "750000", + "notionalCap": "1000000", + "notionalFloor": "750000", "maintMarginRatio": "0.25", "cum": "123250.0" } }, { - "tier": 7, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "373250.0" } @@ -11997,91 +12393,97 @@ ], "1INCH/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": 100000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 100000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "100000000", - "minNotional": "1000000", + "notionalCap": "100000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } @@ -12089,91 +12491,97 @@ ], "MKR/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": 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": "386950.0" } @@ -12181,91 +12589,97 @@ ], "PEOPLE/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": 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": "386950.0" } @@ -12273,121 +12687,129 @@ ], "THETA/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "500.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": "8000.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": "58000.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": "108000.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -12395,335 +12817,227 @@ ], "UNI/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "500.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": "8000.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": "58000.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": "108000.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", + "notionalCap": "50000000", + "notionalFloor": "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, + "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": 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": "386950.0" } @@ -12731,91 +13045,97 @@ ], "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": 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": "386950.0" } @@ -12823,91 +13143,97 @@ ], "RVN/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": 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": "386950.0" } @@ -12915,121 +13241,129 @@ ], "FIL/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "500.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": "8000.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": "58000.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": "108000.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 20000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "maxNotional": "20000000", - "minNotional": "10000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { - "tier": 8, - "minNotional": 20000000, - "maxNotional": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "20000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -13037,91 +13371,97 @@ ], "NKN/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": 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": "386950.0" } @@ -13129,91 +13469,97 @@ ], "KLAY/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": 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": "386950.0" } @@ -13221,91 +13567,97 @@ ], "DEFI/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": 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": "386950.0" } @@ -13313,91 +13665,97 @@ ], "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": 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": "386950.0" } @@ -13405,91 +13763,97 @@ ], "BTCDOM/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": 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": "386950.0" } @@ -13497,106 +13861,113 @@ ], "SOL/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "250000", - "minNotional": "50000", + "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": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "10000000", + "notionalCap": "50000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", "cum": "3232000.0" } @@ -13604,243 +13975,259 @@ ], "BTC/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.004, - "maxLeverage": 125, + "maxLeverage": 125.0, "info": { "bracket": "1", "initialLeverage": "125", - "maxNotional": "50000", - "minNotional": "0", + "notionalCap": "50000", + "notionalFloor": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { - "tier": 2, - "minNotional": 50000, - "maxNotional": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.005, - "maxLeverage": 100, + "maxLeverage": 100.0, "info": { "bracket": "2", "initialLeverage": "100", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.005", "cum": "50.0" } }, { - "tier": 3, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 3.0, + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50, + "maxLeverage": 50.0, "info": { "bracket": "3", "initialLeverage": "50", - "maxNotional": "1000000", - "minNotional": "250000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.01", "cum": "1300.0" } }, { - "tier": 4, - "minNotional": 1000000, - "maxNotional": 7500000, + "tier": 4.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "maxNotional": "7500000", - "minNotional": "1000000", + "notionalCap": "10000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.025", "cum": "16300.0" } }, { - "tier": 5, - "minNotional": 7500000, - "maxNotional": 40000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10, + "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "maxNotional": "40000000", - "minNotional": "7500000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.05", - "cum": "203800.0" + "cum": "266300.0" } }, { - "tier": 6, - "minNotional": 40000000, - "maxNotional": 100000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5, + "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "maxNotional": "100000000", - "minNotional": "40000000", + "notionalCap": "50000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.1", - "cum": "2203800.0" + "cum": "1266300.0" } }, { - "tier": 7, - "minNotional": 100000000, - "maxNotional": 200000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 50000000.0, + "maxNotional": 100000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4, + "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "maxNotional": "200000000", - "minNotional": "100000000", + "notionalCap": "100000000", + "notionalFloor": "50000000", "maintMarginRatio": "0.125", - "cum": "4703800.0" + "cum": "2516300.0" } }, { - "tier": 8, - "minNotional": 200000000, - "maxNotional": 400000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, "maintenanceMarginRate": 0.15, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "8", "initialLeverage": "3", - "maxNotional": "400000000", - "minNotional": "200000000", + "notionalCap": "200000000", + "notionalFloor": "100000000", "maintMarginRatio": "0.15", - "cum": "9703800.0" + "cum": "5016300.0" } }, { - "tier": 9, - "minNotional": 400000000, - "maxNotional": 600000000, + "tier": 9.0, + "currency": "USDT", + "minNotional": 200000000.0, + "maxNotional": 300000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "maxNotional": "600000000", - "minNotional": "400000000", + "notionalCap": "300000000", + "notionalFloor": "200000000", "maintMarginRatio": "0.25", - "cum": "4.97038E7" + "cum": "2.50163E7" } }, { - "tier": 10, - "minNotional": 600000000, - "maxNotional": 1000000000, + "tier": 10.0, + "currency": "USDT", + "minNotional": 300000000.0, + "maxNotional": 9.223372036854776e+18, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "maxNotional": "1000000000", - "minNotional": "600000000", + "notionalCap": "9223372036854775807", + "notionalFloor": "300000000", "maintMarginRatio": "0.5", - "cum": "1.997038E8" + "cum": "1.000163E8" } } ], "OMG/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.024, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "notionalCap": "5000", + "notionalFloor": "0", "maintMarginRatio": "0.024", "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": "5.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": "630.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": "5630.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": "11880.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "10000000", - "minNotional": "1000000", + "notionalCap": "10000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386880.0" } @@ -13848,91 +14235,97 @@ ], "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": 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": "386950.0" } @@ -13940,810 +14333,521 @@ ], "BLZ/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": 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": "386950.0" } } ], - "BTCUSDT_211231": [ + "GMT/USDT": [ { - "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, + "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": 750000, + "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": "750000", - "minNotional": "500000", + "notionalCap": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { - "tier": 6, - "minNotional": 750000, - "maxNotional": 1000000, + "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": "1000000", - "minNotional": "750000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.25", - "cum": "123250.0" + "cum": "154500.0" } }, { - "tier": 7, - "minNotional": 1000000, - "maxNotional": 30000000, + "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": "30000000", - "minNotional": "1000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", - "cum": "373250.0" + "cum": "654500.0" } } ], - "YFII/USDT": [ + "FTM/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": 250000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20, + "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "maxNotional": "25000", - "minNotional": "5000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", - "cum": "75.0" + "cum": "750.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "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": "100000", - "minNotional": "25000", + "notionalCap": "1000000", + "notionalFloor": "250000", "maintMarginRatio": "0.05", - "cum": "700.0" + "cum": "7000.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "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": "250000", - "minNotional": "100000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "5700.0" + "cum": "57000.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "11950.0" + "cum": "107000.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "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" + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "732000.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "50000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", - "cum": "386950.0" + "cum": "3232000.0" } } ], "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": 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": "386950.0" } } ], - "BTCBUSD_210226": [ - { - "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" - } - } - ], "XRP/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "30000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -14751,91 +14855,97 @@ ], "DOGE/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": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "5000000", + "notionalCap": "30000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -14843,228 +14953,341 @@ ], "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": [ + "FTT/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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -15072,106 +15295,113 @@ ], "CRV/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -15179,91 +15409,97 @@ ], "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": 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": 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": "386950.0" } @@ -15271,136 +15507,145 @@ ], "DOT/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": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "maxNotional": "50000000", - "minNotional": "10000000", + "notionalCap": "50000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { - "tier": 9, - "minNotional": 50000000, - "maxNotional": 100000000, + "tier": 9.0, + "currency": "USDT", + "minNotional": 50000000.0, + "maxNotional": 100000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "maxNotional": "100000000", - "minNotional": "50000000", + "notionalCap": "100000000", + "notionalFloor": "50000000", "maintMarginRatio": "0.5", "cum": "1.3733035E7" } @@ -15408,198 +15653,293 @@ ], "XEM/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": 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": "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": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "386875.0" + } + } + ], "ONE/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": 30000000, + "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": "30000000", - "minNotional": "2000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -15607,213 +15947,243 @@ ], "ZIL/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", + "notionalCap": "150000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", - "cum": "75.0" + "cum": "750.0" } }, { - "tier": 3, - "minNotional": 25000, - "maxNotional": 100000, + "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": "100000", - "minNotional": "25000", + "notionalCap": "250000", + "notionalFloor": "150000", "maintMarginRatio": "0.05", - "cum": "700.0" + "cum": "4500.0" } }, { - "tier": 4, - "minNotional": 100000, - "maxNotional": 250000, + "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": "250000", - "minNotional": "100000", + "notionalCap": "500000", + "notionalFloor": "250000", "maintMarginRatio": "0.1", - "cum": "5700.0" + "cum": "17000.0" } }, { - "tier": 5, - "minNotional": 250000, - "maxNotional": 1000000, + "tier": 5.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "2", - "maxNotional": "1000000", - "minNotional": "250000", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.125", - "cum": "11950.0" + "cum": "29500.0" } }, { - "tier": 6, - "minNotional": 1000000, - "maxNotional": 30000000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "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", - "maxNotional": "30000000", - "minNotional": "1000000", + "notionalCap": "30000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", - "cum": "386950.0" + "cum": "654500.0" } } ], "AXS/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": 250000, + "tier": 2.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "maxNotional": "250000", - "minNotional": "50000", + "notionalCap": "250000", + "notionalFloor": "50000", "maintMarginRatio": "0.02", "cum": "500.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": "8000.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": "58000.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": "108000.0" } }, { - "tier": 6, - "minNotional": 5000000, - "maxNotional": 10000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1665, - "maxLeverage": 3, + "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "maxNotional": "10000000", - "minNotional": "5000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { - "tier": 7, - "minNotional": 10000000, - "maxNotional": 15000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "maxNotional": "15000000", - "minNotional": "10000000", + "notionalCap": "15000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { - "tier": 8, - "minNotional": 15000000, - "maxNotional": 50000000, + "tier": 8.0, + "currency": "USDT", + "minNotional": 15000000.0, + "maxNotional": 50000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "maxNotional": "50000000", - "minNotional": "15000000", + "notionalCap": "50000000", + "notionalFloor": "15000000", "maintMarginRatio": "0.5", "cum": "4900500.0" } @@ -15821,106 +16191,113 @@ ], "DYDX/USDT": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 50000, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "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": 4000000, + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, - "maxLeverage": 2, + "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "maxNotional": "4000000", - "minNotional": "1000000", + "notionalCap": "4000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { - "tier": 7, - "minNotional": 4000000, - "maxNotional": 30000000, + "tier": 7.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, - "maxLeverage": 1, + "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "maxNotional": "30000000", - "minNotional": "4000000", + "notionalCap": "30000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", "cum": "1154500.0" } @@ -15928,91 +16305,97 @@ ], "OCEAN/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": 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": "386950.0" } @@ -16020,275 +16403,195 @@ ], "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": [ - { - "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": 9223372036854776000, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1, - "info": { - "bracket": "6", - "initialLeverage": "1", - "maxNotional": "9223372036854775807", - "minNotional": "1000000", - "maintMarginRatio": "0.5", - "cum": "386950.0" - } - } - ], "ANKR/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" } @@ -16296,186 +16599,312 @@ ], "DUSK/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": 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": "386950.0" } } ], - "CTSI/USDT": [ + "BTCUSDT_220624": [ { - "tier": 1, - "minNotional": 0, - "maxNotional": 5000, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25, + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 375000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", "initialLeverage": "25", - "maxNotional": "5000", - "minNotional": "0", + "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" + } + } + ], + "CTSI/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": 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": "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..841f45cd0 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 @@ -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 b300156b5..c1a9059a7 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 @@ -92,8 +92,8 @@ class Exchange: it does basic validation whether the specified exchange and pairs are valid. :return: None """ - self._api: ccxt.Exchange = None - self._api_async: ccxt_async.Exchange = None + self._api: ccxt.Exchange + self._api_async: ccxt_async.Exchange self._markets: Dict = {} self._trading_fees: Dict[str, Any] = {} self._leverage_tiers: Dict[str, List[Dict]] = {} @@ -198,6 +198,7 @@ class Exchange: if self.trading_mode != TradingMode.SPOT: self.fill_leverage_tiers() + self.additional_exchange_init() def __del__(self): """ @@ -290,27 +291,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]: @@ -606,19 +618,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 +703,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) @@ -931,19 +953,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, @@ -962,7 +991,7 @@ class Exchange: *, pair: str, ordertype: str, - side: str, + side: BuySell, amount: float, rate: float, leverage: float, @@ -973,7 +1002,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 @@ -1058,7 +1087,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 @@ -1135,7 +1164,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: @@ -1157,8 +1186,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: @@ -1183,7 +1212,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) @@ -1209,8 +1238,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): @@ -1322,7 +1351,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 @@ -1350,7 +1379,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 @@ -1434,6 +1463,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: """ @@ -1460,20 +1506,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() @@ -1648,7 +1681,8 @@ class Exchange: def get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, candle_type: CandleType, - is_new_pair: bool = False) -> List: + is_new_pair: bool = False, + until_ms: int = None) -> List: """ Get candle history using asyncio and returns the list of candles. Handles all async work for this. @@ -1656,13 +1690,14 @@ class Exchange: :param pair: Pair to download :param timeframe: Timeframe to get data for :param since_ms: Timestamp in milliseconds to get history from + :param until_ms: Timestamp in milliseconds to get history up to :param candle_type: '', mark, index, premiumIndex, or funding_rate :return: List with candle (OHLCV) data """ pair, _, _, data = self.loop.run_until_complete( self._async_get_historic_ohlcv(pair=pair, timeframe=timeframe, - since_ms=since_ms, is_new_pair=is_new_pair, - candle_type=candle_type)) + since_ms=since_ms, until_ms=until_ms, + is_new_pair=is_new_pair, candle_type=candle_type)) logger.info(f"Downloaded data for {pair} with length {len(data)}.") return data @@ -1683,6 +1718,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: Optional[int] = None ) -> Tuple[str, str, str, List]: """ Download historic ohlcv @@ -1690,7 +1726,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, @@ -1698,7 +1735,7 @@ class Exchange: ) input_coroutines = [self._async_get_candle_history( pair, timeframe, candle_type, since) for since in - range(since_ms, arrow.utcnow().int_timestamp * 1000, one_call)] + range(since_ms, until_ms or (arrow.utcnow().int_timestamp * 1000), one_call)] data: List = [] # Chunk requests into batches of 100 to avoid overwelming ccxt Throttling @@ -1726,7 +1763,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) @@ -1741,7 +1779,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 @@ -1844,7 +1882,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: @@ -2379,14 +2419,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, @@ -2661,9 +2722,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: @@ -2678,7 +2740,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: @@ -2688,6 +2750,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..4147e8290 100644 --- a/freqtrade/exchange/gateio.py +++ b/freqtrade/exchange/gateio.py @@ -71,14 +71,14 @@ class Gateio(Exchange): } return trades - def fetch_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: return self.fetch_order( order_id=order_id, pair=pair, params={'stop': True} ) - def cancel_stoploss_order(self, order_id: str, pair: str, params={}) -> Dict: + def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict: return self.cancel_order( order_id=order_id, pair=pair, 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/okx.py b/freqtrade/exchange/okx.py index fb7388ee1..012f51080 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,7 +22,7 @@ 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", } @@ -34,14 +37,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 +107,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 +120,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 c20c14861..a2a12a03a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -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,7 +67,7 @@ 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.get('db_url', None)) self.wallets = Wallets(self.config, self.exchange) @@ -122,7 +123,7 @@ 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() 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 @@ -298,7 +299,8 @@ 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 ExchangeError as e: @@ -401,7 +403,10 @@ class FreqtradeBot(LoggingMixin): logger.info("No currency pair in active pair whitelist, " "but checking to exit open trades.") return trades_created - if PairLocks.is_global_lock(): + if PairLocks.is_global_lock(side='*'): + # This only checks for total locks (both sides). + # per-side locks will be evaluated by `is_pair_locked` within create_trade, + # once the direction for the trade is clear. lock = PairLocks.get_pair_longest_lock('*') if lock: self.log_once(f"Global pairlock active until " @@ -435,16 +440,6 @@ class FreqtradeBot(LoggingMixin): analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(pair, self.strategy.timeframe) nowtime = analyzed_df.iloc[-1]['date'] if len(analyzed_df) > 0 else None - if self.strategy.is_pair_locked(pair, nowtime): - lock = PairLocks.get_pair_longest_lock(pair, nowtime) - if lock: - self.log_once(f"Pair {pair} is still locked until " - f"{lock.lock_end_time.strftime(constants.DATETIME_PRINT_FORMAT)} " - f"due to {lock.reason}.", - logger.info) - else: - self.log_once(f"Pair {pair} is still locked.", logger.info) - return False # get_free_open_trades is checked before create_trade is called # but it is still used here to prevent opening too many trades within one iteration @@ -460,6 +455,16 @@ class FreqtradeBot(LoggingMixin): ) if signal: + if self.strategy.is_pair_locked(pair, candle_date=nowtime, side=signal): + lock = PairLocks.get_pair_longest_lock(pair, nowtime, signal) + if lock: + self.log_once(f"Pair {pair} {lock.side} is locked until " + f"{lock.lock_end_time.strftime(constants.DATETIME_PRINT_FORMAT)} " + f"due to {lock.reason}.", + logger.info) + else: + self.log_once(f"Pair {pair} is currently locked.", logger.info) + return False stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge) bid_check_dom = self.config.get('entry_pricing', {}).get('check_depth_of_market', {}) @@ -532,7 +537,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 @@ -582,6 +588,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 @@ -591,12 +598,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 @@ -741,23 +749,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.') @@ -826,7 +837,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', @@ -856,7 +867,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', @@ -1008,7 +1019,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: @@ -1095,7 +1106,7 @@ class FreqtradeBot(LoggingMixin): """ Check and execute trade exit """ - should_exit: ExitCheckTuple = self.strategy.should_exit( + exits: List[ExitCheckTuple] = self.strategy.should_exit( trade, exit_rate, datetime.now(timezone.utc), @@ -1103,21 +1114,22 @@ class FreqtradeBot(LoggingMixin): 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 + for should_exit in exits: + if should_exit.exit_flag: + logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}' + f'{f" Tag: {exit_tag}" if exit_tag is not None else ""}') + exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag) + if exited: + return True return False - def check_handle_timedout(self) -> None: + def manage_open_orders(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: @@ -1128,33 +1140,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) + + full_cancel = False + cancel_reason = constants.CANCEL_REASON['REPLACE'] + if not adjusted_entry_price: + full_cancel = True if trade.nr_of_successful_entries == 0 else 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, + allow_full_cancel=full_cancel) + 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: """ @@ -1176,7 +1243,10 @@ 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, + allow_full_cancel: Optional[bool] = True + ) -> bool: """ Buy cancel - cancel order :return: True if order was fully cancelled @@ -1214,9 +1284,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 allow_full_cancel: + 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']}" @@ -1336,7 +1407,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, @@ -1345,6 +1416,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' @@ -1362,7 +1434,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) @@ -1379,10 +1451,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 requested abortion of {trade.pair} exit.") return False try: @@ -1409,7 +1481,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), @@ -1575,7 +1647,6 @@ class FreqtradeBot(LoggingMixin): # 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, @@ -1593,21 +1664,21 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open: if send_msg and not stoploss_order and not trade.open_order_id: self._notify_exit(trade, '', True) - self.handle_protections(trade.pair) - elif send_msg and not trade.open_order_id: + self.handle_protections(trade.pair, trade.trade_direction) + elif send_msg and not trade.open_order_id and not stoploss_order: # Enter fill self._notify_enter(trade, order, fill=True) return False - def handle_protections(self, pair: str) -> None: - prot_trig = self.protections.stop_per_pair(pair) + def handle_protections(self, pair: str, side: LongShort) -> None: + prot_trig = self.protections.stop_per_pair(pair, side=side) if prot_trig: msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } msg.update(prot_trig.to_json()) self.rpc.send_msg(msg) - prot_trig_glb = self.protections.global_stop() + prot_trig_glb = self.protections.global_stop(side=side) if prot_trig_glb: msg = {'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL, } msg.update(prot_trig_glb.to_json()) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index dd8d82b13..f1e9b7251 100755 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -187,7 +187,7 @@ 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() def _load_protections(self, strategy: IStrategy): if self.config.get('enable_protections', False): @@ -275,8 +275,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 +301,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 +500,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 +532,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 +617,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 +669,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]: @@ -713,19 +733,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 +833,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 @@ -861,35 +888,90 @@ class Backtesting: return 'short' return None - def run_protections(self, enable_protections, pair: str, current_time: datetime): + def run_protections( + self, enable_protections, pair: str, current_time: datetime, side: LongShort): if enable_protections: - self.protections.stop_per_pair(pair, current_time) - self.protections.global_stop(current_time) + 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, 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]: + if self.check_order_cancel(trade, order, current_time): + # delete trade due to order timeout + return True + elif 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) -> bool: + """ + Check if current analyzed order has to be canceled. + Returns True if the trade should be Deleted (initial order was canceled). + """ + 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)] + 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)] return False + 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)] + 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( self, data: Dict, pair: str, row_index: int, current_time: datetime) -> Optional[Tuple]: try: @@ -959,9 +1041,9 @@ 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) self.wallets.update() @@ -976,7 +1058,7 @@ class Backtesting: and self.trade_slot_available(max_open_trades, open_trade_count_start) and current_time != end_date and trade_dir is not None - and not PairLocks.is_pair_locked(pair, row[DATE_IDX]) + and not PairLocks.is_pair_locked(pair, row[DATE_IDX], trade_dir) ): trade = self._enter_trade(pair, row, trade_dir) if trade: @@ -992,7 +1074,7 @@ class Backtesting: # 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() @@ -1014,7 +1096,8 @@ class Backtesting: LocalTrade.close_bt_trade(trade) trades.append(trade) self.wallets.update() - self.run_protections(enable_protections, pair, current_time) + self.run_protections( + enable_protections, pair, current_time, trade.trade_direction) # Move time one configured time_interval ahead. self.progress.increment() @@ -1031,6 +1114,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']), } 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..d1697709b 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -27,8 +27,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 +61,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 +75,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 +87,8 @@ 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.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']) diff --git a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown_relative.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown_relative.py new file mode 100644 index 000000000..3182afb47 --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown_relative.py @@ -0,0 +1,47 @@ +""" +MaxDrawDownRelativeHyperOptLoss + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. +""" +from typing import Dict + +from pandas import DataFrame + +from freqtrade.data.metrics import calculate_underwater +from freqtrade.optimize.hyperopt import IHyperOptLoss + + +class MaxDrawDownRelativeHyperOptLoss(IHyperOptLoss): + + """ + Defines the loss function for hyperopt. + + This implementation optimizes for max draw down and profit + Less max drawdown more profit -> Lower return value + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, config: Dict, + *args, **kwargs) -> float: + + """ + Objective function. + + Uses profit ratio weighted max_drawdown when drawdown is available. + Otherwise directly optimizes profit ratio. + """ + total_profit = results['profit_abs'].sum() + try: + drawdown_df = calculate_underwater( + results, + value_col='profit_abs', + starting_balance=config['dry_run_wallet'] + ) + max_drawdown = abs(min(drawdown_df['drawdown'])) + relative_drawdown = max(drawdown_df['drawdown_relative']) + if max_drawdown == 0: + return -total_profit + return -total_profit / max_drawdown / relative_drawdown + except (Exception, ValueError): + return -total_profit diff --git a/freqtrade/optimize/hyperopt_loss_interface.py b/freqtrade/optimize/hyperopt_loss_interface.py index ac8239b75..8366dcc4f 100644 --- a/freqtrade/optimize/hyperopt_loss_interface.py +++ b/freqtrade/optimize/hyperopt_loss_interface.py @@ -19,11 +19,11 @@ class IHyperOptLoss(ABC): @staticmethod @abstractmethod - def hyperopt_loss_function(results: DataFrame, trade_count: int, + def hyperopt_loss_function(*, results: DataFrame, trade_count: int, min_date: datetime, max_date: datetime, config: Dict, processed: Dict[str, DataFrame], backtest_stats: Dict[str, Any], - *args, **kwargs) -> float: + **kwargs) -> float: """ Objective function, returns smaller number for better results """ diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 9c1a276a9..93336fa3f 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -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,9 +501,12 @@ 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) = 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, + 'max_relative_drawdown': max_relative_drawdown, 'max_drawdown_abs': drawdown_abs, 'drawdown_start': drawdown_start.strftime(DATETIME_PRINT_FORMAT), 'drawdown_start_ts': drawdown_start.timestamp() * 1000, @@ -521,6 +527,7 @@ def generate_strategy_stats(pairlist: List[str], strat_stats.update({ 'max_drawdown': 0.0, 'max_drawdown_account': 0.0, + 'max_relative_drawdown': 0.0, 'max_drawdown_abs': 0.0, 'max_drawdown_low': 0.0, 'max_drawdown_high': 0.0, @@ -729,6 +736,32 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ] if strat_results.get('trade_count_short', 0) > 0 else [] + drawdown_metrics = [] + if 'max_relative_drawdown' in strat_results: + # Compatibility to show old hyperopt results + drawdown_metrics.append( + ('Max % of account underwater', f"{strat_results['max_relative_drawdown']:.2%}") + ) + drawdown_metrics.extend([ + ('Absolute Drawdown (Account)', f"{strat_results['max_drawdown_account']:.2%}") + if 'max_drawdown_account' in strat_results else ( + 'Drawdown', f"{strat_results['max_drawdown']:.2%}"), + ('Absolute Drawdown', round_coin_value(strat_results['max_drawdown_abs'], + strat_results['stake_currency'])), + ('Drawdown high', round_coin_value(strat_results['max_drawdown_high'], + strat_results['stake_currency'])), + ('Drawdown low', round_coin_value(strat_results['max_drawdown_low'], + strat_results['stake_currency'])), + ('Drawdown Start', strat_results['drawdown_start']), + ('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. @@ -777,6 +810,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'], @@ -784,18 +818,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Max balance', round_coin_value(strat_results['csum_max'], strat_results['stake_currency'])), - # Compatibility to show old hyperopt results - ('Drawdown (Account)', f"{strat_results['max_drawdown_account']:.2%}") - if 'max_drawdown_account' in strat_results else ( - 'Drawdown', f"{strat_results['max_drawdown']:.2%}"), - ('Drawdown', round_coin_value(strat_results['max_drawdown_abs'], - strat_results['stake_currency'])), - ('Drawdown high', round_coin_value(strat_results['max_drawdown_high'], - strat_results['stake_currency'])), - ('Drawdown low', round_coin_value(strat_results['max_drawdown_low'], - strat_results['stake_currency'])), - ('Drawdown Start', strat_results['drawdown_start']), - ('Drawdown End', strat_results['drawdown_end']), + *drawdown_metrics, ('Market change', f"{strat_results['market_change']:.2%}"), ] 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 f020f990c..53e35d9da 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -9,7 +9,7 @@ from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) -def get_table_names_for_table(inspector, tabletype): +def get_table_names_for_table(inspector, tabletype) -> List[str]: return [t for t in inspector.get_table_names() if t.startswith(tabletype)] @@ -21,7 +21,7 @@ def get_column_def(columns: List, column: str, default: str) -> str: return default if not has_column(columns, column) else column -def get_backup_name(tabs, backup_prefix: str): +def get_backup_name(tabs: List[str], backup_prefix: str): table_back_name = backup_prefix for i, table_back_name in enumerate(tabs): table_back_name = f'{backup_prefix}{i}' @@ -46,7 +46,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 +54,19 @@ 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): + with engine.begin() as connection: + # drop indexes on backup table in new session + for index in inspector.get_indexes(table_bak_name): + if engine.name == 'mysql': + connection.execute(text(f"drop index {index['name']} on {table_bak_name}")) + else: + connection.execute(text(f"drop index {index['name']}")) def migrate_trades_and_orders_table( @@ -89,7 +102,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') @@ -116,13 +132,7 @@ def migrate_trades_and_orders_table( with engine.begin() as connection: connection.execute(text(f"alter table trades rename to {trade_back_name}")) - with engine.begin() as connection: - # drop indexes on backup table in new session - for index in inspector.get_indexes(trade_back_name): - if engine.name == 'mysql': - connection.execute(text(f"drop index {index['name']} on {trade_back_name}")) - else: - connection.execute(text(f"drop index {index['name']}")) + drop_index_on_table(engine, inspector, trade_back_name) order_id, trade_id = get_last_sequence_ids(engine, trade_back_name, order_back_name) @@ -205,6 +215,31 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List): """)) +def migrate_pairlocks_table( + decl_base, inspector, engine, + pairlock_back_name: str, cols: List): + + # Schema migration necessary + with engine.begin() as connection: + connection.execute(text(f"alter table pairlocks rename to {pairlock_back_name}")) + + drop_index_on_table(engine, inspector, pairlock_back_name) + + side = get_column_def(cols, 'side', "'*'") + + # let SQLAlchemy create the schema as required + decl_base.metadata.create_all(engine) + # Copy data back - following the correct schema + with engine.begin() as connection: + connection.execute(text(f"""insert into pairlocks + (id, pair, side, reason, lock_time, + lock_end_time, active) + select id, pair, {side} side, reason, lock_time, + lock_end_time, active + from {pairlock_back_name} + """)) + + def set_sqlite_to_wal(engine): if engine.name == 'sqlite' and str(engine.url) != 'sqlite://': # Set Mode to @@ -220,10 +255,13 @@ def check_migrate(engine, decl_base, previous_tables) -> None: cols_trades = inspector.get_columns('trades') cols_orders = inspector.get_columns('orders') + cols_pairlocks = inspector.get_columns('pairlocks') tabs = get_table_names_for_table(inspector, 'trades') table_back_name = get_backup_name(tabs, 'trades_bak') order_tabs = get_table_names_for_table(inspector, 'orders') order_table_bak_name = get_backup_name(order_tabs, 'orders_bak') + pairlock_tabs = get_table_names_for_table(inspector, 'pairlocks') + pairlock_table_bak_name = get_backup_name(pairlock_tabs, 'pairlocks_bak') # Check if migration necessary # Migrates both trades and orders table! @@ -236,6 +274,13 @@ def check_migrate(engine, decl_base, previous_tables) -> None: decl_base, inspector, engine, table_back_name, cols_trades, order_table_bak_name, cols_orders) + if not has_column(cols_pairlocks, 'side'): + logger.info(f"Running database migration for pairlocks - " + f"backup: {pairlock_table_bak_name}") + + migrate_pairlocks_table( + decl_base, inspector, engine, pairlock_table_bak_name, cols_pairlocks + ) if 'orders' not in previous_tables and 'trades' in previous_tables: raise OperationalException( "Your database seems to be very old. " diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 2cacc06e2..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) +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 -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,1390 +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) -> str: - 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) - 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}, 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) -> 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) - 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, - '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/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index afbd9781b..ec57e91fc 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -31,7 +31,7 @@ class PairLocks(): @staticmethod def lock_pair(pair: str, until: datetime, reason: str = None, *, - now: datetime = None) -> PairLock: + now: datetime = None, side: str = '*') -> PairLock: """ Create PairLock from now to "until". Uses database by default, unless PairLocks.use_db is set to False, @@ -40,12 +40,14 @@ class PairLocks(): :param until: End time of the lock. Will be rounded up to the next candle. :param reason: Reason string that will be shown as reason for the lock :param now: Current timestamp. Used to determine lock start time. + :param side: Side to lock pair, can be 'long', 'short' or '*' """ lock = PairLock( pair=pair, lock_time=now or datetime.now(timezone.utc), lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until), reason=reason, + side=side, active=True ) if PairLocks.use_db: @@ -56,7 +58,8 @@ class PairLocks(): return lock @staticmethod - def get_pair_locks(pair: Optional[str], now: Optional[datetime] = None) -> List[PairLock]: + def get_pair_locks( + pair: Optional[str], now: Optional[datetime] = None, side: str = '*') -> List[PairLock]: """ Get all currently active locks for this pair :param pair: Pair to check for. Returns all current locks if pair is empty @@ -67,26 +70,28 @@ class PairLocks(): now = datetime.now(timezone.utc) if PairLocks.use_db: - return PairLock.query_pair_locks(pair, now).all() + return PairLock.query_pair_locks(pair, now, side).all() else: locks = [lock for lock in PairLocks.locks if ( lock.lock_end_time >= now and lock.active is True and (pair is None or lock.pair == pair) + and (lock.side == '*' or lock.side == side) )] return locks @staticmethod - def get_pair_longest_lock(pair: str, now: Optional[datetime] = None) -> Optional[PairLock]: + def get_pair_longest_lock( + pair: str, now: Optional[datetime] = None, side: str = '*') -> Optional[PairLock]: """ Get the lock that expires the latest for the pair given. """ - locks = PairLocks.get_pair_locks(pair, now) + locks = PairLocks.get_pair_locks(pair, now, side=side) locks = sorted(locks, key=lambda l: l.lock_end_time, reverse=True) return locks[0] if locks else None @staticmethod - def unlock_pair(pair: str, now: Optional[datetime] = None) -> None: + def unlock_pair(pair: str, now: Optional[datetime] = None, side: str = '*') -> None: """ Release all locks for this pair. :param pair: Pair to unlock @@ -97,7 +102,7 @@ class PairLocks(): now = datetime.now(timezone.utc) logger.info(f"Releasing all locks for {pair}.") - locks = PairLocks.get_pair_locks(pair, now) + locks = PairLocks.get_pair_locks(pair, now, side=side) for lock in locks: lock.active = False if PairLocks.use_db: @@ -134,7 +139,7 @@ class PairLocks(): lock.active = False @staticmethod - def is_global_lock(now: Optional[datetime] = None) -> bool: + def is_global_lock(now: Optional[datetime] = None, side: str = '*') -> bool: """ :param now: Datetime object (generated via datetime.now(timezone.utc)). defaults to datetime.now(timezone.utc) @@ -142,10 +147,10 @@ class PairLocks(): if not now: now = datetime.now(timezone.utc) - return len(PairLocks.get_pair_locks('*', now)) > 0 + return len(PairLocks.get_pair_locks('*', now, side)) > 0 @staticmethod - def is_pair_locked(pair: str, now: Optional[datetime] = None) -> bool: + def is_pair_locked(pair: str, now: Optional[datetime] = None, side: str = '*') -> bool: """ :param pair: Pair to check for :param now: Datetime object (generated via datetime.now(timezone.utc)). @@ -154,7 +159,10 @@ class PairLocks(): if not now: now = datetime.now(timezone.utc) - return len(PairLocks.get_pair_locks(pair, now)) > 0 or PairLocks.is_global_lock(now) + return ( + len(PairLocks.get_pair_locks(pair, now, side)) > 0 + or PairLocks.is_global_lock(now, side) + ) @staticmethod def get_all_locks() -> List[PairLock]: diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py new file mode 100644 index 000000000..45a16bfbd --- /dev/null +++ b/freqtrade/persistence/trade_model.py @@ -0,0 +1,1374 @@ +""" +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, 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) + 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_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, + '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) -> 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, trade: 'LocalTrade'): + self.order_filled_date = close_date + self.filled = self.amount + 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) -> 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, 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() + 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 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: 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] + + @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 diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 773577d7b..a64281156 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -159,12 +159,15 @@ def add_profit(fig, row, data: pd.DataFrame, column: str, name: str) -> make_sub def add_max_drawdown(fig, row, trades: pd.DataFrame, df_comb: pd.DataFrame, - timeframe: str) -> make_subplots: + timeframe: str, starting_balance: float) -> make_subplots: """ Add scatter points indicating max drawdown """ try: - _, highdate, lowdate, _, _, max_drawdown = calculate_max_drawdown(trades) + _, highdate, lowdate, _, _, max_drawdown = calculate_max_drawdown( + trades, + starting_balance=starting_balance + ) drawdown = go.Scatter( x=[highdate, lowdate], @@ -189,22 +192,37 @@ def add_max_drawdown(fig, row, trades: pd.DataFrame, df_comb: pd.DataFrame, return fig -def add_underwater(fig, row, trades: pd.DataFrame) -> make_subplots: +def add_underwater(fig, row, trades: pd.DataFrame, starting_balance: float) -> make_subplots: """ - Add underwater plot + Add underwater plots """ try: - underwater = calculate_underwater(trades, value_col="profit_abs") + underwater = calculate_underwater( + trades, + value_col="profit_abs", + starting_balance=starting_balance + ) - underwater = go.Scatter( + underwater_plot = go.Scatter( x=underwater['date'], y=underwater['drawdown'], name="Underwater Plot", fill='tozeroy', fillcolor='#cc362b', - line={'color': '#cc362b'}, + line={'color': '#cc362b'} ) - fig.add_trace(underwater, row, 1) + + underwater_plot_relative = go.Scatter( + x=underwater['date'], + y=(-underwater['drawdown_relative']), + name="Underwater Plot (%)", + fill='tozeroy', + fillcolor='green', + line={'color': 'green'} + ) + + fig.add_trace(underwater_plot, row, 1) + fig.add_trace(underwater_plot_relative, row + 1, 1) except ValueError: logger.warning("No trades found - not plotting underwater plot") return fig @@ -507,7 +525,8 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame], - trades: pd.DataFrame, timeframe: str, stake_currency: str) -> go.Figure: + trades: pd.DataFrame, timeframe: str, stake_currency: str, + starting_balance: float) -> go.Figure: # Combine close-values for all pairs, rename columns to "pair" try: df_comb = combine_dataframes_with_mean(data, "close") @@ -531,8 +550,8 @@ def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame], name='Avg close price', ) - fig = make_subplots(rows=5, cols=1, shared_xaxes=True, - row_heights=[1, 1, 1, 0.5, 1], + fig = make_subplots(rows=6, cols=1, shared_xaxes=True, + row_heights=[1, 1, 1, 0.5, 0.75, 0.75], vertical_spacing=0.05, subplot_titles=[ "AVG Close Price", @@ -540,6 +559,7 @@ def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame], "Profit per pair", "Parallelism", "Underwater", + "Relative Drawdown", ]) fig['layout'].update(title="Freqtrade Profit plot") fig['layout']['yaxis1'].update(title='Price') @@ -547,14 +567,16 @@ def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame], fig['layout']['yaxis3'].update(title=f'Profit {stake_currency}') fig['layout']['yaxis4'].update(title='Trade count') fig['layout']['yaxis5'].update(title='Underwater Plot') + fig['layout']['yaxis6'].update(title='Underwater Plot Relative (%)', tickformat=',.2%') fig['layout']['xaxis']['rangeslider'].update(visible=False) fig.update_layout(modebar_add=["v1hovermode", "toggleSpikeLines"]) fig.add_trace(avgclose, 1, 1) fig = add_profit(fig, 2, df_comb, 'cum_profit', 'Profit') - fig = add_max_drawdown(fig, 2, trades, df_comb, timeframe) + fig = add_max_drawdown(fig, 2, trades, df_comb, timeframe, starting_balance) fig = add_parallelism(fig, 4, trades, timeframe) - fig = add_underwater(fig, 5, trades) + # Two rows consumed + fig = add_underwater(fig, 5, trades, starting_balance) for pair in pairs: profit_col = f'cum_profit_{pair}' @@ -611,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'] @@ -670,7 +693,8 @@ def plot_profit(config: Dict[str, Any]) -> None: # this could be useful to gauge the overall market trend fig = generate_profit_graph(plot_elements['pairs'], plot_elements['ohlcv'], trades, config['timeframe'], - config.get('stake_currency', '')) + config.get('stake_currency', ''), + config.get('available_capital', config['dry_run_wallet'])) store_plot_file(fig, filename='freqtrade-profit-plot.html', directory=config['user_data_dir'] / 'plot', auto_open=config.get('plot_auto_open', False)) diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index bb6f75012..418c0f14e 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -32,18 +32,19 @@ class AgeFilter(IPairList): self._min_days_listed = pairlistconfig.get('min_days_listed', 10) self._max_days_listed = pairlistconfig.get('max_days_listed', None) + 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/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..de016c3a6 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -33,12 +33,12 @@ class RangeStabilityFilter(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("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/protectionmanager.py b/freqtrade/plugins/protectionmanager.py index 2510d6fee..d33294fa7 100644 --- a/freqtrade/plugins/protectionmanager.py +++ b/freqtrade/plugins/protectionmanager.py @@ -5,6 +5,7 @@ import logging from datetime import datetime, timezone from typing import Dict, List, Optional +from freqtrade.constants import LongShort from freqtrade.persistence import PairLocks from freqtrade.persistence.models import PairLock from freqtrade.plugins.protections import IProtection @@ -44,28 +45,31 @@ class ProtectionManager(): """ return [{p.name: p.short_desc()} for p in self._protection_handlers] - def global_stop(self, now: Optional[datetime] = None) -> Optional[PairLock]: + def global_stop(self, now: Optional[datetime] = None, + side: LongShort = 'long') -> Optional[PairLock]: if not now: now = datetime.now(timezone.utc) result = None for protection_handler in self._protection_handlers: if protection_handler.has_global_stop: - lock, until, reason = protection_handler.global_stop(now) - - # Early stopping - first positive result blocks further trades - if lock and until: - if not PairLocks.is_global_lock(until): - result = PairLocks.lock_pair('*', until, reason, now=now) + lock = protection_handler.global_stop(date_now=now, side=side) + if lock and lock.until: + if not PairLocks.is_global_lock(lock.until, side=lock.lock_side): + result = PairLocks.lock_pair( + '*', lock.until, lock.reason, now=now, side=lock.lock_side) return result - def stop_per_pair(self, pair, now: Optional[datetime] = None) -> Optional[PairLock]: + def stop_per_pair(self, pair, now: Optional[datetime] = None, + side: LongShort = 'long') -> Optional[PairLock]: if not now: now = datetime.now(timezone.utc) result = None for protection_handler in self._protection_handlers: if protection_handler.has_local_stop: - lock, until, reason = protection_handler.stop_per_pair(pair, now) - if lock and until: - if not PairLocks.is_pair_locked(pair, until): - result = PairLocks.lock_pair(pair, until, reason, now=now) + lock = protection_handler.stop_per_pair( + pair=pair, date_now=now, side=side) + if lock and lock.until: + if not PairLocks.is_pair_locked(pair, lock.until, lock.lock_side): + result = PairLocks.lock_pair( + pair, lock.until, lock.reason, now=now, side=lock.lock_side) return result diff --git a/freqtrade/plugins/protections/cooldown_period.py b/freqtrade/plugins/protections/cooldown_period.py index a2d8eca34..426b8f1b6 100644 --- a/freqtrade/plugins/protections/cooldown_period.py +++ b/freqtrade/plugins/protections/cooldown_period.py @@ -1,7 +1,9 @@ import logging from datetime import datetime, timedelta +from typing import Optional +from freqtrade.constants import LongShort from freqtrade.persistence import Trade from freqtrade.plugins.protections import IProtection, ProtectionReturn @@ -26,7 +28,7 @@ class CooldownPeriod(IProtection): """ return (f"{self.name} - Cooldown period of {self.stop_duration_str}.") - def _cooldown_period(self, pair: str, date_now: datetime, ) -> ProtectionReturn: + def _cooldown_period(self, pair: str, date_now: datetime) -> Optional[ProtectionReturn]: """ Get last trade for this pair """ @@ -45,11 +47,15 @@ class CooldownPeriod(IProtection): self.log_once(f"Cooldown for {pair} for {self.stop_duration_str}.", logger.info) until = self.calculate_lock_end([trade], self._stop_duration) - return True, until, self._reason() + return ProtectionReturn( + lock=True, + until=until, + reason=self._reason(), + ) - return False, None, None + return None - def global_stop(self, date_now: datetime) -> ProtectionReturn: + def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period". @@ -57,9 +63,10 @@ class CooldownPeriod(IProtection): If true, all pairs will be locked with until """ # Not implemented for cooldown period. - return False, None, None + return None - def stop_per_pair(self, pair: str, date_now: datetime) -> ProtectionReturn: + def stop_per_pair( + self, pair: str, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for this pair This must evaluate to true for the whole period of the "cooldown period". diff --git a/freqtrade/plugins/protections/iprotection.py b/freqtrade/plugins/protections/iprotection.py index e0a89e334..890988226 100644 --- a/freqtrade/plugins/protections/iprotection.py +++ b/freqtrade/plugins/protections/iprotection.py @@ -1,9 +1,11 @@ import logging from abc import ABC, abstractmethod +from dataclasses import dataclass from datetime import datetime, timedelta, timezone -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional +from freqtrade.constants import LongShort from freqtrade.exchange import timeframe_to_minutes from freqtrade.misc import plural from freqtrade.mixins import LoggingMixin @@ -12,7 +14,13 @@ from freqtrade.persistence import LocalTrade logger = logging.getLogger(__name__) -ProtectionReturn = Tuple[bool, Optional[datetime], Optional[str]] + +@dataclass +class ProtectionReturn: + lock: bool + until: datetime + reason: Optional[str] + lock_side: str = '*' class IProtection(LoggingMixin, ABC): @@ -80,14 +88,15 @@ class IProtection(LoggingMixin, ABC): """ @abstractmethod - def global_stop(self, date_now: datetime) -> ProtectionReturn: + def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period". """ @abstractmethod - def stop_per_pair(self, pair: str, date_now: datetime) -> ProtectionReturn: + def stop_per_pair( + self, pair: str, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for this pair This must evaluate to true for the whole period of the "cooldown period". diff --git a/freqtrade/plugins/protections/low_profit_pairs.py b/freqtrade/plugins/protections/low_profit_pairs.py index 7822ce73c..099242b8d 100644 --- a/freqtrade/plugins/protections/low_profit_pairs.py +++ b/freqtrade/plugins/protections/low_profit_pairs.py @@ -1,8 +1,9 @@ import logging from datetime import datetime, timedelta -from typing import Any, Dict +from typing import Any, Dict, Optional +from freqtrade.constants import LongShort from freqtrade.persistence import Trade from freqtrade.plugins.protections import IProtection, ProtectionReturn @@ -20,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: """ @@ -35,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) -> ProtectionReturn: + def _low_profit( + self, date_now: datetime, pair: str, side: LongShort) -> Optional[ProtectionReturn]: """ Evaluate recent trades for pair """ @@ -51,33 +54,42 @@ class LowProfitPairs(IProtection): # trades = Trade.get_trades(filters).all() if len(trades) < self._trade_limit: # Not enough trades in the relevant period - return False, None, None + 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} " f"within {self._lookback_period} minutes.", logger.info) until = self.calculate_lock_end(trades, self._stop_duration) - return True, until, self._reason(profit) + return ProtectionReturn( + lock=True, + until=until, + reason=self._reason(profit), + lock_side=(side if self._only_per_side else '*') + ) - return False, None, None + return None - def global_stop(self, date_now: datetime) -> ProtectionReturn: + def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period". :return: Tuple of [bool, until, reason]. If true, all pairs will be locked with until """ - return False, None, None + return None - def stop_per_pair(self, pair: str, date_now: datetime) -> ProtectionReturn: + def stop_per_pair( + self, pair: str, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for this pair This must evaluate to true for the whole period of the "cooldown period". :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/max_drawdown_protection.py b/freqtrade/plugins/protections/max_drawdown_protection.py index 4111b7ff4..e0b016cb8 100644 --- a/freqtrade/plugins/protections/max_drawdown_protection.py +++ b/freqtrade/plugins/protections/max_drawdown_protection.py @@ -1,10 +1,11 @@ import logging from datetime import datetime, timedelta -from typing import Any, Dict +from typing import Any, Dict, Optional import pandas as pd +from freqtrade.constants import LongShort from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.persistence import Trade from freqtrade.plugins.protections import IProtection, ProtectionReturn @@ -39,7 +40,7 @@ class MaxDrawdown(IProtection): return (f'{drawdown} passed {self._max_allowed_drawdown} in {self.lookback_period_str}, ' f'locking for {self.stop_duration_str}.') - def _max_drawdown(self, date_now: datetime) -> ProtectionReturn: + def _max_drawdown(self, date_now: datetime) -> Optional[ProtectionReturn]: """ Evaluate recent trades for drawdown ... """ @@ -51,14 +52,14 @@ class MaxDrawdown(IProtection): if len(trades) < self._trade_limit: # Not enough trades in the relevant period - return False, None, None + return None # Drawdown is always positive try: # TODO: This should use absolute profit calculation, considering account balance. drawdown, _, _, _, _, _ = calculate_max_drawdown(trades_df, value_col='close_profit') except ValueError: - return False, None, None + return None if drawdown > self._max_allowed_drawdown: self.log_once( @@ -66,11 +67,15 @@ class MaxDrawdown(IProtection): f" within {self.lookback_period_str}.", logger.info) until = self.calculate_lock_end(trades, self._stop_duration) - return True, until, self._reason(drawdown) + return ProtectionReturn( + lock=True, + until=until, + reason=self._reason(drawdown), + ) - return False, None, None + return None - def global_stop(self, date_now: datetime) -> ProtectionReturn: + def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period". @@ -79,11 +84,12 @@ class MaxDrawdown(IProtection): """ return self._max_drawdown(date_now) - def stop_per_pair(self, pair: str, date_now: datetime) -> ProtectionReturn: + def stop_per_pair( + self, pair: str, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for this pair This must evaluate to true for the whole period of the "cooldown period". :return: Tuple of [bool, until, reason]. If true, this pair will be locked with until """ - return False, None, None + return None diff --git a/freqtrade/plugins/protections/stoploss_guard.py b/freqtrade/plugins/protections/stoploss_guard.py index 8d7fb2a0e..713a2da07 100644 --- a/freqtrade/plugins/protections/stoploss_guard.py +++ b/freqtrade/plugins/protections/stoploss_guard.py @@ -1,8 +1,9 @@ import logging from datetime import datetime, timedelta -from typing import Any, Dict +from typing import Any, Dict, Optional +from freqtrade.constants import LongShort from freqtrade.enums import ExitType from freqtrade.persistence import Trade from freqtrade.plugins.protections import IProtection, ProtectionReturn @@ -21,6 +22,7 @@ class StoplossGuard(IProtection): self._trade_limit = protection_config.get('trade_limit', 10) self._disable_global_stop = protection_config.get('only_per_pair', False) + self._only_per_side = protection_config.get('only_per_side', False) def short_desc(self) -> str: """ @@ -36,7 +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: str = None) -> ProtectionReturn: + def _stoploss_guard(self, date_now: datetime, pair: Optional[str], + side: LongShort) -> Optional[ProtectionReturn]: """ Evaluate recent trades """ @@ -48,15 +51,24 @@ class StoplossGuard(IProtection): ExitType.STOPLOSS_ON_EXCHANGE.value) and trade.close_profit and trade.close_profit < 0)] + if self._only_per_side: + # Long or short trades only + trades = [trade for trade in trades if trade.trade_direction == side] + if len(trades) < self._trade_limit: - return False, None, None + return None self.log_once(f"Trading stopped due to {self._trade_limit} " f"stoplosses within {self._lookback_period} minutes.", logger.info) until = self.calculate_lock_end(trades, self._stop_duration) - return True, until, self._reason() + return ProtectionReturn( + lock=True, + until=until, + reason=self._reason(), + lock_side=(side if self._only_per_side else '*') + ) - def global_stop(self, date_now: datetime) -> ProtectionReturn: + def global_stop(self, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for all pairs This must evaluate to true for the whole period of the "cooldown period". @@ -64,14 +76,15 @@ class StoplossGuard(IProtection): If true, all pairs will be locked with until """ if self._disable_global_stop: - return False, None, None - return self._stoploss_guard(date_now, None) + return None + return self._stoploss_guard(date_now, None, side) - def stop_per_pair(self, pair: str, date_now: datetime) -> ProtectionReturn: + def stop_per_pair( + self, pair: str, date_now: datetime, side: LongShort) -> Optional[ProtectionReturn]: """ Stops trading (position entering) for this pair This must evaluate to true for the whole period of the "cooldown period". :return: Tuple of [bool, until, reason]. If true, this pair will be locked with until """ - return self._stoploss_guard(date_now, pair) + return self._stoploss_guard(date_now, pair, side) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 41712632b..26b100408 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -172,6 +172,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 a9135cce2..f21334bc6 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -256,6 +256,7 @@ class TradeSchema(BaseModel): leverage: Optional[float] interest_rate: Optional[float] + liquidation_price: Optional[float] funding_fees: Optional[float] trading_mode: Optional[TradingMode] @@ -291,6 +292,7 @@ class LockModel(BaseModel): lock_time: str lock_timestamp: int pair: str + side: str reason: str diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 12adc34d1..a98e3f96d 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -177,16 +177,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 +238,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 +251,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 diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 1a9be4503..f26de8b5c 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -1410,14 +1410,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`" + "*/fe |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/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..5c09dd862 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -3,295 +3,19 @@ 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 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 typing import Any, Dict, Iterator, List, Tuple from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException +from freqtrade.misc import deep_merge_dicts, json_load +from freqtrade.optimize.hyperopt_tools import HyperoptTools +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 @@ -345,7 +69,7 @@ class HyperStrategyMixin: @classmethod def detect_all_parameters(cls) -> Dict: """ Detect all parameters and return them as a list""" - params: Dict = { + params: Dict[str, Any] = { 'buy': list(cls.detect_parameters('buy')), 'sell': list(cls.detect_parameters('sell')), 'protection': list(cls.detect_parameters('protection')), @@ -424,7 +148,7 @@ class HyperStrategyMixin: """ Returns list of Parameters that are not part of the current optimize job """ - params = { + params: Dict[str, Dict] = { 'buy': {}, 'sell': {}, 'protection': {}, diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index a23269770..c521943b1 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -15,10 +15,8 @@ from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, SignalTagType, SignalType, TradingMode) 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 +82,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 +144,13 @@ 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)() + @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ @@ -431,7 +436,7 @@ 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, + proposed_stake: float, min_stake: Optional[float], max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: """ Customize stake size for each new trade. @@ -449,8 +454,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,6 +477,34 @@ 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: @@ -545,7 +579,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ return self.__class__.__name__ - def lock_pair(self, pair: str, until: datetime, reason: str = None) -> None: + def lock_pair(self, pair: str, until: datetime, reason: str = None, side: str = '*') -> None: """ Locks pair until a given timestamp happens. Locked pairs are not analyzed, and are prevented from opening new trades. @@ -555,8 +589,9 @@ class IStrategy(ABC, HyperStrategyMixin): :param until: datetime in UTC until the pair should be blocked from opening new trades. Needs to be timezone aware `datetime.now(timezone.utc)` :param reason: Optional string explaining why the pair was locked. + :param side: Side to check, can be long, short or '*' """ - PairLocks.lock_pair(pair, until, reason) + PairLocks.lock_pair(pair, until, reason, side=side) def unlock_pair(self, pair: str) -> None: """ @@ -576,7 +611,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ PairLocks.unlock_reason(reason, datetime.now(timezone.utc)) - def is_pair_locked(self, pair: str, candle_date: datetime = None) -> bool: + def is_pair_locked(self, pair: str, *, candle_date: datetime = None, side: str = '*') -> bool: """ Checks if a pair is currently locked The 2nd, optional parameter ensures that locks are applied until the new candle arrives, @@ -584,15 +619,16 @@ class IStrategy(ABC, HyperStrategyMixin): of 2 seconds for an entry order to happen on an old signal. :param pair: "Pair to check" :param candle_date: Date of the last candle. Optional, defaults to current date + :param side: Side to check, can be long, short or '*' :returns: locking state of the pair in question. """ if not candle_date: # Simple call ... - return PairLocks.is_pair_locked(pair) + return PairLocks.is_pair_locked(pair, side=side) else: lock_time = timeframe_to_next_date(self.timeframe, candle_date) - return PairLocks.is_pair_locked(pair, lock_time) + return PairLocks.is_pair_locked(pair, lock_time, side=side) def analyze_ticker(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ @@ -850,16 +886,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) @@ -889,19 +925,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 @@ -910,24 +947,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, @@ -1042,7 +1084,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/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index ed40ef509..3854efd85 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: @@ -52,8 +80,8 @@ 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: + proposed_stake: float, min_stake: Optional[float], max_stake: float, + entry_tag: 'Optional[str]', side: str, **kwargs) -> float: """ Customize stake size for each new trade. @@ -118,7 +146,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. @@ -216,7 +244,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. diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index d93689a0e..0c2197917 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -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 c4fe366a5..e863238bd 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,9 +6,9 @@ coveralls==3.3.1 flake8==4.0.1 -flake8-tidy-imports==4.6.0 -mypy==0.942 -pre-commit==2.18.1 +flake8-tidy-imports==4.8.0 +mypy==0.950 +pre-commit==2.19.0 pytest==7.1.2 pytest-asyncio==0.18.3 pytest-cov==3.0.0 @@ -16,14 +16,14 @@ pytest-mock==3.7.0 pytest-random-order==1.0.4 isort==5.10.1 # For datetime mocking -time-machine==2.6.0 +time-machine==2.7.0 # 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.20 -types-tabulate==0.8.7 -types-python-dateutil==2.8.12 +types-filelock==3.2.6 +types-requests==2.27.27 +types-tabulate==0.8.9 +types-python-dateutil==2.8.16 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 32fc3f4b9..b8762214a 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.0 progressbar2==4.0.0 diff --git a/requirements-plot.txt b/requirements-plot.txt index d9faed301..e17efbc71 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.8.0 diff --git a/requirements.txt b/requirements.txt index ab8329979..ae1ff7a89 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,23 +1,23 @@ -numpy==1.22.3 +numpy==1.22.4 pandas==1.4.2 pandas-ta==0.3.14b -ccxt==1.80.61 +ccxt==1.83.62 # Pin cryptography for now due to rust build errors with piwheels -cryptography==36.0.2 +cryptography==37.0.2 aiohttp==3.8.1 -SQLAlchemy==1.4.35 +SQLAlchemy==1.4.36 python-telegram-bot==13.11 arrow==1.2.2 cachetools==4.2.2 requests==2.27.1 urllib3==1.26.9 -jsonschema==4.4.0 +jsonschema==4.5.1 TA-Lib==0.4.24 technical==1.3.0 tabulate==0.8.9 pycoingecko==2.2.0 -jinja2==3.1.1 +jinja2==3.1.2 tables==3.7.0 blosc==1.10.6 joblib==1.1.0 @@ -34,11 +34,11 @@ orjson==3.6.8 sdnotify==0.3.2 # API Server -fastapi==0.75.2 +fastapi==0.78.0 uvicorn==0.17.6 -pyjwt==2.3.0 +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 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 e0b010387..bb51c3a2f 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 @@ -155,7 +155,7 @@ function install_macos() { # Install bot Debian_ubuntu function install_debian() { sudo apt-get update - sudo apt-get install -y gcc build-essential autoconf libtool pkg-config make wget git $(echo lib${PYTHON}-dev ${PYTHON}-venv) + sudo apt-get install -y gcc build-essential autoconf libtool pkg-config make wget git curl $(echo lib${PYTHON}-dev ${PYTHON}-venv) install_talib } 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..02738b0e9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -384,7 +384,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 +1616,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, diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index f9f49e280..4157bd899 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -376,3 +376,38 @@ def test_calculate_max_drawdown2(): df = DataFrame(zip(values[:5], dates[:5]), columns=['profit', 'open_date']) with pytest.raises(ValueError, match='No losing trade, therefore no drawdown.'): calculate_max_drawdown(df, date_col='open_date', value_col='profit') + + +@pytest.mark.parametrize('profits,relative,highd,lowd,result,result_rel', [ + ([0.0, -500.0, 500.0, 10000.0, -1000.0], False, 3, 4, 1000.0, 0.090909), + ([0.0, -500.0, 500.0, 10000.0, -1000.0], True, 0, 1, 500.0, 0.5), + +]) +def test_calculate_max_drawdown_abs(profits, relative, highd, lowd, result, result_rel): + """ + Test case from issue https://github.com/freqtrade/freqtrade/issues/6655 + [1000, 500, 1000, 11000, 10000] # absolute results + [1000, 50%, 0%, 0%, ~9%] # Relative drawdowns + """ + init_date = Arrow(2020, 1, 1) + dates = [init_date.shift(days=i) for i in range(len(profits))] + df = DataFrame(zip(profits, dates), columns=['profit_abs', 'open_date']) + # sort by profit and reset index + df = df.sort_values('profit_abs').reset_index(drop=True) + df1 = df.copy() + drawdown, hdate, ldate, hval, lval, drawdown_rel = calculate_max_drawdown( + df, date_col='open_date', starting_balance=1000, relative=relative) + # Ensure df has not been altered. + assert df.equals(df1) + + assert isinstance(drawdown, float) + assert isinstance(drawdown_rel, float) + assert hdate == init_date.shift(days=highd) + assert ldate == init_date.shift(days=lowd) + + # High must be before low + assert hdate < ldate + # High value must be higher than low value + assert hval > lval + assert drawdown == result + assert pytest.approx(drawdown_rel) == result_rel diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 0585fa0d4..9709e7ad0 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -149,8 +149,8 @@ def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog, load_pair_history(datadir=tmpdir1, timeframe='1m', pair='MEME/BTC', candle_type=candle_type) assert file.is_file() assert log_has_re( - r'Download history data for pair: "MEME/BTC" \(0/1\), timeframe: 1m, ' - r'candle type: spot and store in .*', caplog + r'\(0/1\) - Download history data for "MEME/BTC", 1m, ' + r'spot and store in .*', caplog ) @@ -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) @@ -223,42 +224,65 @@ def test_load_cached_data_for_updating(mocker, testdatadir) -> None: # timeframe starts earlier than the cached data # should fully update data timerange = TimeRange('date', None, test_data[0][0] / 1000 - 1, 0) - data, start_ts = _load_cached_data_for_updating( + data, start_ts, end_ts = _load_cached_data_for_updating( 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT) assert data.empty assert start_ts == test_data[0][0] - 1000 + assert end_ts is None + + # timeframe starts earlier than the cached data - prepending + + timerange = TimeRange('date', None, test_data[0][0] / 1000 - 1, 0) + data, start_ts, end_ts = _load_cached_data_for_updating( + 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT, True) + assert_frame_equal(data, test_data_df.iloc[:-1]) + assert start_ts == test_data[0][0] - 1000 + assert end_ts == test_data[0][0] # timeframe starts in the center of the cached data # should return the cached data w/o the last item timerange = TimeRange('date', None, test_data[0][0] / 1000 + 1, 0) - data, start_ts = _load_cached_data_for_updating( + data, start_ts, end_ts = _load_cached_data_for_updating( 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT) assert_frame_equal(data, test_data_df.iloc[:-1]) assert test_data[-2][0] <= start_ts < test_data[-1][0] + assert end_ts is None # timeframe starts after the cached data # should return the cached data w/o the last item timerange = TimeRange('date', None, test_data[-1][0] / 1000 + 100, 0) - data, start_ts = _load_cached_data_for_updating( + data, start_ts, end_ts = _load_cached_data_for_updating( 'UNITTEST/BTC', '1m', timerange, data_handler, CandleType.SPOT) assert_frame_equal(data, test_data_df.iloc[:-1]) assert test_data[-2][0] <= start_ts < test_data[-1][0] + assert end_ts is None # no datafile exist # should return timestamp start time timerange = TimeRange('date', None, now_ts - 10000, 0) - data, start_ts = _load_cached_data_for_updating( + data, start_ts, end_ts = _load_cached_data_for_updating( 'NONEXIST/BTC', '1m', timerange, data_handler, CandleType.SPOT) assert data.empty assert start_ts == (now_ts - 10000) * 1000 + assert end_ts is None + + # no datafile exist + # should return timestamp start and end time time + timerange = TimeRange('date', 'date', now_ts - 1000000, now_ts - 100000) + data, start_ts, end_ts = _load_cached_data_for_updating( + 'NONEXIST/BTC', '1m', timerange, data_handler, CandleType.SPOT) + assert data.empty + assert start_ts == (now_ts - 1000000) * 1000 + assert end_ts == (now_ts - 100000) * 1000 # no datafile exist, no timeframe is set # should return an empty array and None - data, start_ts = _load_cached_data_for_updating( + data, start_ts, end_ts = _load_cached_data_for_updating( 'NONEXIST/BTC', '1m', None, data_handler, CandleType.SPOT) assert data.empty assert start_ts is None + assert end_ts is None @pytest.mark.parametrize('candle_type,subdir,file_tail', [ diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 5c8d7d3b0..324be9962 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -490,11 +490,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..e016873cb 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 @@ -218,7 +220,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 +232,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 9faf404cd..708a0e889 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) @@ -1872,7 +1885,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", @@ -1938,7 +1951,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", @@ -1983,6 +1996,20 @@ async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_ assert exchange._api_async.fetch_ohlcv.call_count > 200 assert res[0] == ohlcv[0] + exchange._api_async.fetch_ohlcv.reset_mock() + end_ts = 1_500_500_000_000 + start_ts = 1_500_000_000_000 + respair, restf, _, res = await exchange._async_get_historic_ohlcv( + pair, "5m", since_ms=start_ts, candle_type=candle_type, is_new_pair=False, + until_ms=end_ts + ) + # Required candles + candles = (end_ts - start_ts) / 300_000 + 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 + @pytest.mark.parametrize('candle_type', [CandleType.FUTURES, CandleType.MARK, CandleType.SPOT]) def test_refresh_latest_ohlcv(mocker, default_conf, caplog, candle_type) -> None: @@ -2128,6 +2155,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( @@ -2781,6 +2810,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 @@ -2946,6 +2976,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 @@ -2998,6 +3029,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 @@ -3325,7 +3357,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(): @@ -3407,6 +3439,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", [ @@ -3776,6 +3819,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"}}), @@ -3874,6 +3918,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() @@ -4744,8 +4852,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', @@ -4753,6 +4863,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', @@ -4760,6 +4871,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', @@ -4772,6 +4884,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..92f8186a6 100644 --- a/tests/exchange/test_gateio.py +++ b/tests/exchange/test_gateio.py @@ -34,6 +34,7 @@ def test_validate_order_types_gateio(default_conf, mocker): ExchangeResolver.load_exchange('gateio', default_conf, True) +@pytest.mark.usefixtures("init_persistence") def test_fetch_stoploss_order_gateio(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, id='gateio') diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 37c1ea974..19c09ad9e 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 freqtrade.enums.candletype import CandleType +from freqtrade.exchange.exchange import timeframe_to_minutes from tests.conftest import 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,6 +203,70 @@ 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={ 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/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index ea13de4c8..4b4c446e0 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -522,7 +522,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 +662,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 +754,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 +889,12 @@ TESTS = [ tc45, tc46, tc47, + tc48, + tc49, + tc50, + tc51, + tc52, + tc53, ] @@ -817,6 +908,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 +936,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 +964,5 @@ 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 + backtesting.cleanup() + del backtesting diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index c87a0ef73..f169e0a35 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1168,6 +1168,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 +1283,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 +1295,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 +1440,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 +1452,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 +1549,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 +1561,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 +1627,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..94505e3ce 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]) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 75944390e..8522894f7 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -17,7 +17,7 @@ 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 freqtrade.strategy import IntParameter from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) @@ -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, } diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index 4ec80ef49..be1c313f6 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -85,6 +85,7 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> "SharpeHyperOptLoss", "SharpeHyperOptLossDaily", "MaxDrawDownHyperOptLoss", + "MaxDrawDownRelativeHyperOptLoss", "CalmarHyperOptLoss", "ProfitDrawDownHyperOptLoss", diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index ff8d420b3..997c0436e 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', diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index d80f23c8a..c29e619b1 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}], @@ -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_pairlocks.py b/tests/plugins/test_pairlocks.py index f9e5583ed..0ba9bb746 100644 --- a/tests/plugins/test_pairlocks.py +++ b/tests/plugins/test_pairlocks.py @@ -21,8 +21,22 @@ def test_PairLocks(use_db): pair = 'ETH/BTC' assert not PairLocks.is_pair_locked(pair) PairLocks.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime) - # ETH/BTC locked for 4 minutes + # ETH/BTC locked for 4 minutes (on both sides) assert PairLocks.is_pair_locked(pair) + assert PairLocks.is_pair_locked(pair, side='long') + assert PairLocks.is_pair_locked(pair, side='short') + + pair = 'BNB/BTC' + PairLocks.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime, side='long') + assert not PairLocks.is_pair_locked(pair) + assert PairLocks.is_pair_locked(pair, side='long') + assert not PairLocks.is_pair_locked(pair, side='short') + + pair = 'BNB/USDT' + PairLocks.lock_pair(pair, arrow.utcnow().shift(minutes=4).datetime, side='short') + assert not PairLocks.is_pair_locked(pair) + assert not PairLocks.is_pair_locked(pair, side='long') + assert PairLocks.is_pair_locked(pair, side='short') # XRP/BTC should not be locked now pair = 'XRP/BTC' diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index 6b69f5481..172e1f077 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -11,9 +11,10 @@ from tests.conftest import get_patched_freqtradebot, log_has_re def generate_mock_trade(pair: str, fee: float, is_open: bool, - sell_reason: str = ExitType.EXIT_SIGNAL, + exit_reason: str = ExitType.EXIT_SIGNAL, min_ago_open: int = None, min_ago_close: int = None, - profit_rate: float = 0.9 + profit_rate: float = 0.9, + is_short: bool = False, ): open_rate = random.random() @@ -28,11 +29,12 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool, is_open=is_open, amount=0.01 / open_rate, exchange='binance', + is_short=is_short, ) trade.recalc_open_trade_value() if not is_open: - trade.close(open_rate * profit_rate) - trade.exit_reason = sell_reason + trade.close(open_rate * (2 - profit_rate if is_short else profit_rate)) + trade.exit_reason = exit_reason return trade @@ -45,9 +47,9 @@ def test_protectionmanager(mocker, default_conf): for handler in freqtrade.protections._protection_handlers: assert handler.name in constants.AVAILABLE_PROTECTIONS if not handler.has_global_stop: - assert handler.global_stop(datetime.utcnow()) == (False, None, None) + assert handler.global_stop(datetime.utcnow(), '*') is None if not handler.has_local_stop: - assert handler.stop_per_pair('XRP/BTC', datetime.utcnow()) == (False, None, None) + assert handler.stop_per_pair('XRP/BTC', datetime.utcnow(), '*') is None @pytest.mark.parametrize('timeframe,expected,protconf', [ @@ -68,7 +70,7 @@ def test_protectionmanager(mocker, default_conf): ('1h', [60, 540], [{"method": "StoplossGuard", "lookback_period_candles": 1, "stop_duration_candles": 9}]), ]) -def test_protections_init(mocker, default_conf, timeframe, expected, protconf): +def test_protections_init(default_conf, timeframe, expected, protconf): default_conf['timeframe'] = timeframe man = ProtectionManager(default_conf, protconf) assert len(man._protection_handlers) == len(protconf) @@ -76,8 +78,10 @@ def test_protections_init(mocker, default_conf, timeframe, expected, protconf): assert man._protection_handlers[0]._stop_duration == expected[1] +@pytest.mark.parametrize('is_short', [False, True]) @pytest.mark.usefixtures("init_persistence") -def test_stoploss_guard(mocker, default_conf, fee, caplog): +def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short): + # Active for both sides (long and short) default_conf['protections'] = [{ "method": "StoplossGuard", "lookback_period": 60, @@ -91,8 +95,8 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): caplog.clear() Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=200, min_ago_close=30, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=200, min_ago_close=30, is_short=is_short, )) assert not freqtrade.protections.global_stop() @@ -100,13 +104,13 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): caplog.clear() # This trade does not count, as it's closed too long ago Trade.query.session.add(generate_mock_trade( - 'BCH/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=250, min_ago_close=100, + 'BCH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=250, min_ago_close=100, is_short=is_short, )) Trade.query.session.add(generate_mock_trade( - 'ETH/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=240, min_ago_close=30, + 'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=240, min_ago_close=30, is_short=is_short, )) # 3 Trades closed - but the 2nd has been closed too long ago. assert not freqtrade.protections.global_stop() @@ -114,8 +118,8 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): caplog.clear() Trade.query.session.add(generate_mock_trade( - 'LTC/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=180, min_ago_close=30, + 'LTC/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=180, min_ago_close=30, is_short=is_short, )) assert freqtrade.protections.global_stop() @@ -130,15 +134,19 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): @pytest.mark.parametrize('only_per_pair', [False, True]) +@pytest.mark.parametrize('only_per_side', [False, True]) @pytest.mark.usefixtures("init_persistence") -def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair): +def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair, only_per_side): default_conf['protections'] = [{ "method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60, - "only_per_pair": only_per_pair + "only_per_pair": only_per_pair, + "only_per_side": only_per_side, }] + check_side = 'long' if only_per_side else '*' + is_short = False freqtrade = get_patched_freqtradebot(mocker, default_conf) message = r"Trading stopped due to .*" pair = 'XRP/BTC' @@ -148,8 +156,8 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair caplog.clear() Trade.query.session.add(generate_mock_trade( - pair, fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=200, min_ago_close=30, profit_rate=0.9, + pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=200, min_ago_close=30, profit_rate=0.9, is_short=is_short )) assert not freqtrade.protections.stop_per_pair(pair) @@ -158,13 +166,13 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair caplog.clear() # This trade does not count, as it's closed too long ago Trade.query.session.add(generate_mock_trade( - pair, fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=250, min_ago_close=100, profit_rate=0.9, + pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=250, min_ago_close=100, profit_rate=0.9, is_short=is_short )) # Trade does not count for per pair stop as it's the wrong pair. Trade.query.session.add(generate_mock_trade( - 'ETH/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=240, min_ago_close=30, profit_rate=0.9, + 'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=240, min_ago_close=30, profit_rate=0.9, is_short=is_short )) # 3 Trades closed - but the 2nd has been closed too long ago. assert not freqtrade.protections.stop_per_pair(pair) @@ -176,16 +184,34 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair caplog.clear() + # Trade does not count potentially, as it's in the wrong direction + Trade.query.session.add(generate_mock_trade( + pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=150, min_ago_close=25, profit_rate=0.9, is_short=not is_short + )) + freqtrade.protections.stop_per_pair(pair) + assert freqtrade.protections.global_stop() != only_per_pair + assert PairLocks.is_pair_locked(pair, side=check_side) != (only_per_side and only_per_pair) + assert PairLocks.is_global_lock(side=check_side) != only_per_pair + if only_per_side: + assert not PairLocks.is_pair_locked(pair, side='*') + assert not PairLocks.is_global_lock(side='*') + + caplog.clear() + # 2nd Trade that counts with correct pair Trade.query.session.add(generate_mock_trade( - pair, fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, - min_ago_open=180, min_ago_close=30, profit_rate=0.9, + pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, + min_ago_open=180, min_ago_close=30, profit_rate=0.9, is_short=is_short )) freqtrade.protections.stop_per_pair(pair) assert freqtrade.protections.global_stop() != only_per_pair - assert PairLocks.is_pair_locked(pair) - assert PairLocks.is_global_lock() != only_per_pair + assert PairLocks.is_pair_locked(pair, side=check_side) + assert PairLocks.is_global_lock(side=check_side) != only_per_pair + if only_per_side: + assert not PairLocks.is_pair_locked(pair, side='*') + assert not PairLocks.is_global_lock(side='*') @pytest.mark.usefixtures("init_persistence") @@ -203,7 +229,7 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog): caplog.clear() Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=200, min_ago_close=30, )) @@ -213,7 +239,7 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog): assert not PairLocks.is_global_lock() Trade.query.session.add(generate_mock_trade( - 'ETH/BTC', fee.return_value, False, sell_reason=ExitType.ROI.value, + 'ETH/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, min_ago_open=205, min_ago_close=35, )) @@ -224,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 .*" @@ -242,7 +270,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog): caplog.clear() Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=800, min_ago_close=450, profit_rate=0.9, )) @@ -253,7 +281,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog): assert not PairLocks.is_global_lock() Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=200, min_ago_close=120, profit_rate=0.9, )) @@ -265,21 +293,23 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog): # Add positive trade Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.ROI.value, - min_ago_open=20, min_ago_close=10, profit_rate=1.15, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, + 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, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=110, min_ago_close=20, profit_rate=0.8, )) # 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() @@ -300,15 +330,15 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): caplog.clear() Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=1000, min_ago_close=900, profit_rate=1.1, )) Trade.query.session.add(generate_mock_trade( - 'ETH/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=1000, min_ago_close=900, profit_rate=1.1, )) Trade.query.session.add(generate_mock_trade( - 'NEO/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'NEO/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=1000, min_ago_close=900, profit_rate=1.1, )) # No losing trade yet ... so max_drawdown will raise exception @@ -316,7 +346,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): assert not freqtrade.protections.stop_per_pair('XRP/BTC') Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=500, min_ago_close=400, profit_rate=0.9, )) # Not locked with one trade @@ -326,7 +356,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): assert not PairLocks.is_global_lock() Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.STOP_LOSS.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value, min_ago_open=1200, min_ago_close=1100, profit_rate=0.5, )) @@ -339,7 +369,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): # Winning trade ... (should not lock, does not change drawdown!) Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.ROI.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, min_ago_open=320, min_ago_close=410, profit_rate=1.5, )) assert not freqtrade.protections.global_stop() @@ -349,7 +379,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): # Add additional negative trade, causing a loss of > 15% Trade.query.session.add(generate_mock_trade( - 'XRP/BTC', fee.return_value, False, sell_reason=ExitType.ROI.value, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, min_ago_open=20, min_ago_close=10, profit_rate=0.8, )) assert not freqtrade.protections.stop_per_pair('XRP/BTC') diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index f4a2f6099..95645c8ba 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -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 diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index ac2f1c3ec..03ba895a1 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -972,6 +972,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 +1176,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': [], 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..340001ef2 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 @@ -185,7 +186,8 @@ class StrategyTestV3(IStrategy): 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/test_interface.py b/tests/strategy/test_interface.py index ea81fe968..12cbf5370 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -16,8 +16,8 @@ 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.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 @@ -495,37 +495,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' @@ -666,23 +742,23 @@ def test_is_pair_locked(default_conf): assert not strategy.is_pair_locked(pair) # latest candle is from 14:20, lock goes to 14:30 - assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-10)) - assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-50)) + assert strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=-10)) + assert strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=-50)) # latest candle is from 14:25 (lock should be lifted) # Since this is the "new candle" available at 14:30 - assert not strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-4)) + assert not strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=-4)) # Should not be locked after time expired - assert not strategy.is_pair_locked(pair, lock_time + timedelta(minutes=10)) + assert not strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=10)) # Change timeframe to 15m strategy.timeframe = '15m' # Candle from 14:14 - lock goes until 14:30 - assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-16)) - assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-15, seconds=-2)) + assert strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=-16)) + assert strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=-15, seconds=-2)) # Candle from 14:15 - lock goes until 14:30 - assert not strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-15)) + assert not strategy.is_pair_locked(pair, candle_date=lock_time + timedelta(minutes=-15)) def test_is_informative_pairs_callback(default_conf): diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index 3ed1eb0ce..919a4bd00 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -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 89fe88a2c..23ef4ffc2 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -21,6 +21,7 @@ from freqtrade.exceptions import (DependencyException, ExchangeError, Insufficie from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Order, PairLocks, Trade from freqtrade.persistence.models import PairLock +from freqtrade.plugins.protections.iprotection import ProtectionReturn from freqtrade.worker import Worker from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker, log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal, @@ -420,7 +421,7 @@ def test_enter_positions_global_pairlock(default_conf_usdt, ticker_usdt, limit_b assert not log_has_re(message, caplog) caplog.clear() - PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because') + PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because', side='*') n = freqtrade.enter_positions() assert n == 0 assert log_has_re(message, caplog) @@ -441,9 +442,9 @@ def test_handle_protections(mocker, default_conf_usdt, fee, is_short): freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) freqtrade.protections._protection_handlers[1].global_stop = MagicMock( - return_value=(True, arrow.utcnow().shift(hours=1).datetime, "asdf")) + return_value=ProtectionReturn(True, arrow.utcnow().shift(hours=1).datetime, "asdf")) create_mock_trades(fee, is_short) - freqtrade.handle_protections('ETC/BTC') + freqtrade.handle_protections('ETC/BTC', '*') send_msg_mock = freqtrade.rpc.send_msg assert send_msg_mock.call_count == 2 assert send_msg_mock.call_args_list[0][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER @@ -2361,7 +2362,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: @@ -2393,12 +2394,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) @@ -2406,7 +2407,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) @@ -2415,7 +2416,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() @@ -2425,7 +2426,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: @@ -2449,8 +2450,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() @@ -2458,6 +2460,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]) @@ -2483,18 +2578,17 @@ def test_check_handle_cancelled_buy( 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) @@ -2514,7 +2608,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() @@ -2523,7 +2617,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: @@ -2552,13 +2646,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 @@ -2568,7 +2662,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 @@ -2578,7 +2672,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 @@ -2591,7 +2685,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') @@ -2601,16 +2695,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) @@ -2637,7 +2731,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 @@ -2673,7 +2767,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 @@ -2683,7 +2777,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: @@ -2709,7 +2803,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() @@ -2720,7 +2814,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 @@ -2752,7 +2846,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) @@ -2769,7 +2863,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 @@ -2800,7 +2894,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) @@ -2816,8 +2910,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() @@ -2838,7 +2932,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=" @@ -2950,6 +3044,7 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_order trade.entry_side = "buy" trade.open_rate = 200 trade.entry_side = "buy" + trade.open_order_id = "open_order_noop" l_order['filled'] = 0.0 l_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] @@ -3395,7 +3490,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 @@ -3447,7 +3542,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 @@ -3487,7 +3582,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( @@ -3793,13 +3888,16 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee, exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS) ) trade.close(ticker_usdt_sell_down()['bid']) - assert freqtrade.strategy.is_pair_locked(trade.pair) + assert freqtrade.strategy.is_pair_locked(trade.pair, side='*') + # Boths sides are locked + assert freqtrade.strategy.is_pair_locked(trade.pair, side='long') + assert freqtrade.strategy.is_pair_locked(trade.pair, side='short') # reinit - should buy other pair. caplog.clear() freqtrade.enter_positions() - assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog) + assert log_has_re(fr"Pair {trade.pair} \* is locked.*", caplog) @pytest.mark.parametrize("is_short", [False, True]) @@ -4689,9 +4787,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({ @@ -4702,6 +4797,11 @@ 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=InvalidOrderException) + freqtrade.startup_update_open_orders() + assert log_has_re(r"Error updating Order .*", caplog) + @pytest.mark.usefixtures("init_persistence") @pytest.mark.parametrize("is_short", [False, True]) @@ -5210,7 +5310,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 @@ -5316,7 +5416,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 801e0e35f..be19a3f5f 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -13,8 +13,9 @@ 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 @@ -23,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__ @@ -35,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',) @@ -44,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): @@ -56,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' @@ -69,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() @@ -1128,56 +1129,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) @@ -1309,7 +1260,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() @@ -1392,7 +1343,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(): @@ -1415,18 +1366,67 @@ 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 +def test_migrate_pairlocks(mocker, default_conf, fee, caplog): + """ + Test Database migration (starting with new pairformat) + """ + caplog.set_level(logging.DEBUG) + # Always create all columns apart from the last! + create_table_old = """CREATE TABLE pairlocks ( + id INTEGER NOT NULL, + pair VARCHAR(25) NOT NULL, + reason VARCHAR(255), + lock_time DATETIME NOT NULL, + lock_end_time DATETIME NOT NULL, + active BOOLEAN NOT NULL, + PRIMARY KEY (id) + ) + """ + create_index1 = "CREATE INDEX ix_pairlocks_pair ON pairlocks (pair)" + create_index2 = "CREATE INDEX ix_pairlocks_lock_end_time ON pairlocks (lock_end_time)" + create_index3 = "CREATE INDEX ix_pairlocks_active ON pairlocks (active)" + insert_table_old = """INSERT INTO pairlocks ( + id, pair, reason, lock_time, lock_end_time, active) + VALUES (1, 'ETH/BTC', 'Auto lock', '2021-07-12 18:41:03', '2021-07-11 18:45:00', 1) + """ + insert_table_old2 = """INSERT INTO pairlocks ( + id, pair, reason, lock_time, lock_end_time, active) + VALUES (2, '*', 'Lock all', '2021-07-12 18:41:03', '2021-07-12 19:00:00', 1) + """ + engine = create_engine('sqlite://') + mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine) + # Create table using the old format + with engine.begin() as connection: + connection.execute(text(create_table_old)) + + connection.execute(text(insert_table_old)) + connection.execute(text(insert_table_old2)) + connection.execute(text(create_index1)) + connection.execute(text(create_index2)) + connection.execute(text(create_index3)) + + init_db(default_conf['db_url']) + + assert len(PairLock.query.all()) == 2 + assert len(PairLock.query.filter(PairLock.pair == '*').all()) == 1 + pairlocks = PairLock.query.filter(PairLock.pair == 'ETH/BTC').all() + assert len(pairlocks) == 1 + pairlocks[0].pair == 'ETH/BTC' + pairlocks[0].side == '*' + + def test_adjust_stop_loss(fee): trade = Trade( pair='ADA/USDT', @@ -2671,3 +2671,21 @@ 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'] + del limit_buy_order_open['datetime'] + assert raw_order == limit_buy_order_open diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 65df2d84c..9ee7a75c6 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -332,7 +332,13 @@ def test_generate_profit_graph(testdatadir): trades = trades[trades['pair'].isin(pairs)] - fig = generate_profit_graph(pairs, data, trades, timeframe="5m", stake_currency='BTC') + fig = generate_profit_graph( + pairs, + data, + trades, + timeframe="5m", + stake_currency='BTC', + starting_balance=0) assert isinstance(fig, go.Figure) assert fig.layout.title.text == "Freqtrade Profit plot" @@ -341,7 +347,7 @@ def test_generate_profit_graph(testdatadir): assert fig.layout.yaxis3.title.text == "Profit BTC" figure = fig.layout.figure - assert len(figure.data) == 7 + assert len(figure.data) == 8 avgclose = find_trace_in_fig_data(figure.data, "Avg close price") assert isinstance(avgclose, go.Scatter) @@ -356,6 +362,9 @@ def test_generate_profit_graph(testdatadir): underwater = find_trace_in_fig_data(figure.data, "Underwater Plot") assert isinstance(underwater, go.Scatter) + underwater_relative = find_trace_in_fig_data(figure.data, "Underwater Plot (%)") + assert isinstance(underwater_relative, go.Scatter) + for pair in pairs: profit_pair = find_trace_in_fig_data(figure.data, f"Profit {pair}") assert isinstance(profit_pair, go.Scatter) @@ -363,7 +372,7 @@ def test_generate_profit_graph(testdatadir): with pytest.raises(OperationalException, match=r"No trades found.*"): # Pair cannot be empty - so it's an empty dataframe. generate_profit_graph(pairs, data, trades.loc[trades['pair'].isnull()], timeframe="5m", - stake_currency='BTC') + stake_currency='BTC', starting_balance=0) def test_start_plot_dataframe(mocker):