diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index c32fb33c2..a18915462 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -24,4 +24,3 @@ Have you search for this feature before requesting it? It's highly likely that a ## Describe the enhancement *Explain the enhancement you would like* - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7c0655b20..90a10d4da 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,9 +1,9 @@ -Thank you for sending your pull request. But first, have you included + ## Summary -Explain in one sentence the goal of this PR + Solve the issue: #___ @@ -14,4 +14,4 @@ Solve the issue: #___ ## What's new? -*Explain in details what this PR solve or improve. You can include visuals.* + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8df7ab10..0ff57b270 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: - name: Mypy run: | - mypy freqtrade scripts + mypy freqtrade scripts tests - name: Discord notification uses: rjstone/discord-webhook-notify@v1 @@ -157,6 +157,12 @@ 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 @@ -229,7 +235,7 @@ jobs: - name: Tests run: | - pytest --random-order --cov=freqtrade --cov-config=.coveragerc + pytest --random-order - name: Backtesting run: | @@ -249,7 +255,7 @@ jobs: - name: Mypy run: | - mypy freqtrade scripts + mypy freqtrade scripts tests - name: Discord notification uses: rjstone/discord-webhook-notify@v1 @@ -259,6 +265,21 @@ jobs: details: Test Failed webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} + mypy_version_check: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: 3.9 + + - name: pre-commit dependencies + run: | + pip install pyaml + python build_helpers/pre_commit_update.py + docs_check: runs-on: ubuntu-20.04 steps: @@ -271,7 +292,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v3 with: - python-version: 3.8 + python-version: 3.9 - name: Documentation build run: | @@ -288,6 +309,9 @@ jobs: webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} cleanup-prior-runs: + permissions: + actions: write # for rokroskar/workflow-run-cleanup-action to obtain workflow name & cancel it + contents: read # for rokroskar/workflow-run-cleanup-action to obtain branch runs-on: ubuntu-20.04 steps: - name: Cleanup previous runs on this branch @@ -298,8 +322,12 @@ jobs: # Notify only once - when CI completes (and after deploy) in case it's successfull notify-complete: - needs: [ build_linux, build_macos, build_windows, docs_check ] + needs: [ build_linux, build_macos, build_windows, docs_check, mypy_version_check ] runs-on: ubuntu-20.04 + # Discord notification can't handle schedule events + if: (github.event_name != 'schedule') + permissions: + repository-projects: read steps: - name: Check user permission @@ -319,7 +347,7 @@ jobs: webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} deploy: - needs: [ build_linux, build_macos, build_windows, docs_check ] + needs: [ build_linux, build_macos, build_windows, docs_check, mypy_version_check ] runs-on: ubuntu-20.04 if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade' diff --git a/.github/workflows/docker_update_readme.yml b/.github/workflows/docker_update_readme.yml index 822533ee2..4587626f6 100644 --- a/.github/workflows/docker_update_readme.yml +++ b/.github/workflows/docker_update_readme.yml @@ -15,4 +15,3 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKERHUB_REPOSITORY: freqtradeorg/freqtrade - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 31af5b7c7..1185028b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,46 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pycqa/flake8 - rev: '4.0.1' + - repo: https://github.com/pycqa/flake8 + rev: "4.0.1" hooks: - - id: flake8 + - id: flake8 # stages: [push] -- repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.942' + - repo: https://github.com/pre-commit/mirrors-mypy + rev: "v0.942" hooks: - - id: mypy + - id: mypy + exclude: build_helpers + additional_dependencies: + - types-cachetools==5.0.1 + - types-filelock==3.2.5 + - types-requests==2.27.25 + - types-tabulate==0.8.8 + - types-python-dateutil==2.8.14 # stages: [push] -- repo: https://github.com/pycqa/isort - rev: '5.10.1' + - repo: https://github.com/pycqa/isort + rev: "5.10.1" hooks: - id: isort name: isort (python) # stages: [push] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + - id: end-of-file-fixer + exclude: | + (?x)^( + tests/.*| + .*\.svg + )$ + - id: mixed-line-ending + - id: debug-statements + - id: check-ast + - id: trailing-whitespace + exclude: | + (?x)^( + .*\.md + )$ diff --git a/.pylintrc b/.pylintrc index dce99c067..0932ecba4 100644 --- a/.pylintrc +++ b/.pylintrc @@ -7,4 +7,3 @@ ignore=vendor [TYPECHECK] ignored-modules=numpy,talib,talib.abstract - diff --git a/README.md b/README.md index 679dbcab0..cad39f9ac 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,14 @@ 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 + +- [X] [Binance](https://www.binance.com/) +- [X] [Gate.io](https://www.gate.io/ref/6266643) +- [X] [OKX](https://okx.com/). + +Please make sure to read the [exchange specific notes](docs/exchanges.md), as well as the [trading with leverage](docs/leverage.md) documentation before diving in. + ### Community tested Exchanges confirmed working by the community: diff --git a/build_helpers/pre_commit_update.py b/build_helpers/pre_commit_update.py new file mode 100644 index 000000000..8724d8ade --- /dev/null +++ b/build_helpers/pre_commit_update.py @@ -0,0 +1,42 @@ +# File used in CI to ensure pre-commit dependencies are kept uptodate. + +import sys +from pathlib import Path + +import yaml + + +pre_commit_file = Path('.pre-commit-config.yaml') +require_dev = Path('requirements-dev.txt') + +with require_dev.open('r') as rfile: + requirements = rfile.readlines() + +# Extract types only +type_reqs = [r.strip('\n') for r in requirements if r.startswith('types-')] + +with pre_commit_file.open('r') as file: + f = yaml.load(file, Loader=yaml.FullLoader) + + +mypy_repo = [repo for repo in f['repos'] if repo['repo'] + == 'https://github.com/pre-commit/mirrors-mypy'] + +hooks = mypy_repo[0]['hooks'][0]['additional_dependencies'] + +errors = [] +for hook in hooks: + if hook not in type_reqs: + errors.append(f"{hook} is missing in requirements-dev.txt.") + +for req in type_reqs: + if req not in hooks: + errors.append(f"{req} is missing in pre-config file.") + + +if errors: + for e in errors: + print(e) + sys.exit(1) + +sys.exit(0) diff --git a/config_examples/config_binance.example.json b/config_examples/config_binance.example.json index ad8862afa..35b9fcd20 100644 --- a/config_examples/config_binance.example.json +++ b/config_examples/config_binance.example.json @@ -90,7 +90,7 @@ }, "bot_name": "freqtrade", "initial_state": "running", - "force_enter_enable": false, + "force_entry_enable": false, "internals": { "process_throttle_secs": 5 } diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index 915db6c44..6382e1baf 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -182,6 +182,8 @@ "disable_dataframe_checks": false, "strategy": "SampleStrategy", "strategy_path": "user_data/strategies/", + "recursive_strategy_search": false, + "add_config_files": [], "dataformat_ohlcv": "json", "dataformat_trades": "jsongz" } diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md new file mode 100644 index 000000000..2a484da69 --- /dev/null +++ b/docs/advanced-backtesting.md @@ -0,0 +1,73 @@ +# Advanced Backtesting Analysis + +## Analyze the buy/entry and sell/exit tags + +It can be helpful to understand how a strategy behaves according to the buy/entry tags used to +mark up different buy conditions. You might want to see more complex statistics about each buy and +sell condition above those provided by the default backtesting output. You may also want to +determine indicator values on the signal candle that resulted in a trade opening. + +!!! Note + The following buy reason analysis is only available for backtesting, *not hyperopt*. + +We need to run backtesting with the `--export` option set to `signals` to enable the exporting of +signals **and** trades: + +``` bash +freqtrade backtesting -c --timeframe --strategy --timerange= --export=signals +``` + +This will tell freqtrade to output a pickled dictionary of strategy, pairs and corresponding +DataFrame of the candles that resulted in buy signals. Depending on how many buys your strategy +makes, this file may get quite large, so periodically check your `user_data/backtest_results` +folder to delete old exports. + +To analyze the buy tags, we need to use the `buy_reasons.py` script from +[froggleston's repo](https://github.com/froggleston/freqtrade-buyreasons). Follow the instructions +in their README to copy the script into your `freqtrade/scripts/` folder. + +Before running your next backtest, make sure you either delete your old backtest results or run +backtesting with the `--cache none` option to make sure no cached results are used. + +If all goes well, you should now see a `backtest-result-{timestamp}_signals.pkl` file in the +`user_data/backtest_results` folder. + +Now run the `buy_reasons.py` script, supplying a few options: + +``` bash +python3 scripts/buy_reasons.py -c -s -t -g0,1,2,3,4 +``` + +The `-g` option is used to specify the various tabular outputs, ranging from the simplest (0) +to the most detailed per pair, per buy and per sell tag (4). More options are available by +running with the `-h` option. + +### Tuning the buy tags and sell tags to display + +To show only certain buy and sell tags in the displayed output, use the following two options: + +``` +--enter_reason_list : Comma separated list of enter signals to analyse. Default: "all" +--exit_reason_list : Comma separated list of exit signals to analyse. Default: "stop_loss,trailing_stop_loss" +``` + +For example: + +```bash +python3 scripts/buy_reasons.py -c -s -t -g0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss" +``` + +### Outputting signal candle indicators + +The real power of the buy_reasons.py script comes from the ability to print out the indicator +values present on signal candles to allow fine-grained investigation and tuning of buy signal +indicators. To print out a column for a given set of indicators, use the `--indicator-list` +option: + +```bash +python3 scripts/buy_reasons.py -c -s -t -g0,1,2,3,4 --enter_reason_list "enter_tag_a,enter_tag_b" --exit_reason_list "roi,custom_exit_tag_a,stop_loss" --indicator_list "rsi,rsi_1h,bb_lowerband,ema_9,macd,macdsignal" +``` + +The indicators have to be present in your strategy's main DataFrame (either for your main +timeframe or for informative timeframes) otherwise they will simply be ignored in the script +output. diff --git a/docs/backtesting.md b/docs/backtesting.md index 5d836d01b..75225b654 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -20,7 +20,8 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--dry-run-wallet DRY_RUN_WALLET] [--timeframe-detail TIMEFRAME_DETAIL] [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] - [--export {none,trades}] [--export-filename PATH] + [--export {none,trades,signals}] + [--export-filename PATH] [--breakdown {day,week,month} [{day,week,month} ...]] [--cache {none,day,week,month}] @@ -63,18 +64,17 @@ optional arguments: `30m`, `1h`, `1d`). --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] Provide a space-separated list of strategies to - backtest. Please note that timeframe needs to be - set either in config or via command line. When using - this together with `--export trades`, the strategy- - name is injected into the filename (so `backtest- - data.json` becomes `backtest-data-SampleStrategy.json` - --export {none,trades} + backtest. Please note that timeframe needs to be set + either in config or via command line. When using this + together with `--export trades`, the strategy-name is + injected into the filename (so `backtest-data.json` + becomes `backtest-data-SampleStrategy.json` + --export {none,trades,signals} Export backtest results (default: trades). - --export-filename PATH - Save backtest results to the file with this filename. - Requires `--export` to be set as well. Example: - `--export-filename=user_data/backtest_results/backtest - _today.json` + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` --breakdown {day,week,month} [{day,week,month} ...] Show backtesting breakdown per [day, week, month]. --cache {none,day,week,month} @@ -287,44 +287,51 @@ 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% | -| 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 | +| | | +| 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 @@ -376,49 +383,51 @@ 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% | -| 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 | +| | | +| 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% | +===================================================== ``` @@ -439,7 +448,9 @@ It contains some useful key metrics about performance of your strategy on backte - `Rejected Entry signals`: Trade entry signals that could not be acted upon due to `max_open_trades` being reached. - `Entry/Exit Timeouts`: Entry/exit orders which did not fill (only applicable if custom pricing is used). - `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period. -- `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). diff --git a/docs/configuration.md b/docs/configuration.md index 49a59c070..80cd52c5b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -11,7 +11,7 @@ Per default, the bot loads the configuration from the `config.json` file, locate You can specify a different configuration file used by the bot with the `-c/--config` command-line option. -If you used the [Quick start](installation.md/#quick-start) method for installing +If you used the [Quick start](installation.md/#quick-start) method for installing the bot, the installation script should have already created the default configuration file (`config.json`) for you. If the default configuration file is not created we recommend to use `freqtrade new-config --config config.json` to generate a basic configuration file. @@ -53,14 +53,63 @@ FREQTRADE__EXCHANGE__SECRET= Multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream. +You can specify additional configuration files in `add_config_files`. Files specified in this parameter will be loaded and merged with the initial config file. The files are resolved relative to the initial configuration file. +This is similar to using multiple `--config` parameters, but simpler in usage as you don't have to specify all files for all commands. + !!! Tip "Use multiple configuration files to keep secrets secret" You can use a 2nd configuration file containing your secrets. That way you can share your "primary" configuration file, while still keeping your API keys for yourself. + ``` json title="user_data/config.json" + "add_config_files": [ + "config-private.json" + ] + ``` + + ``` bash + freqtrade trade --config user_data/config.json <...> + ``` + + The 2nd file should only specify what you intend to override. + If a key is in more than one of the configurations, then the "last specified configuration" wins (in the above example, `config-private.json`). + + For one-off commands, you can also use the below syntax by specifying multiple "--config" parameters. + ``` bash freqtrade trade --config user_data/config.json --config user_data/config-private.json <...> ``` - The 2nd file should only specify what you intend to override. - If a key is in more than one of the configurations, then the "last specified configuration" wins (in the above example, `config-private.json`). + + This is equivalent to the example above - but `config-private.json` is specified as cli argument. + +??? Note "config collision handling" + If the same configuration setting takes place in both `config.json` and `config-import.json`, then the parent configuration wins. + In the below case, `max_open_trades` would be 3 after the merging - as the reusable "import" configuration has this key overwritten. + + ``` json title="user_data/config.json" + { + "max_open_trades": 3, + "stake_currency": "USDT", + "add_config_files": [ + "config-import.json" + ] + } + ``` + + ``` json title="user_data/config-import.json" + { + "max_open_trades": 10, + "stake_amount": "unlimited", + } + ``` + + Resulting combined configuration: + + ``` json title="Result" + { + "max_open_trades": 10, + "stake_currency": "USDT", + "stake_amount": "unlimited" + } + ``` ## Configuration parameters @@ -124,6 +173,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `order_types` | Configure order-types depending on the action (`"entry"`, `"exit"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict | `order_time_in_force` | Configure time in force for entry and exit orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Dict | `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price.
*Defaults to `0.02` 2%).*
**Datatype:** Positive float +| `recursive_strategy_search` | Set to `true` to recursively search sub-directories inside `user_data/strategies` for a strategy.
**Datatype:** Boolean | `exchange.name` | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
**Datatype:** String | `exchange.sandbox` | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details.
**Datatype:** Boolean | `exchange.key` | API key to use for the exchange. Only required when you are in production mode.
**Keep it in secret, do not disclose publicly.**
**Datatype:** String @@ -175,6 +225,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `internals.sd_notify` | Enables use of the sd_notify protocol to tell systemd service manager about changes in the bot state and issue keep-alive pings. See [here](installation.md#7-optional-configure-freqtrade-as-a-systemd-service) for more details.
**Datatype:** Boolean | `logfile` | Specifies logfile name. Uses a rolling strategy for log file rotation for 10 files with the 1MB limit per file.
**Datatype:** String | `user_data_dir` | Directory containing user data.
*Defaults to `./user_data/`*.
**Datatype:** String +| `add_config_files` | Additional config files. These files will be loaded and merged with the current config file. The files are resolved relative to the initial file.
*Defaults to `[]`*.
**Datatype:** List of strings | `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data.
*Defaults to `json`*.
**Datatype:** String | `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 diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 9a79ee5ed..926ed3eae 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -122,5 +122,6 @@ Best avoid relative paths, since this starts at the storage location of the jupy * [Strategy debugging](strategy_analysis_example.md) - also available as Jupyter notebook (`user_data/notebooks/strategy_analysis_example.ipynb`) * [Plotting](plotting.md) +* [Tag Analysis](advanced-backtesting.md) Feel free to submit an issue or Pull Request enhancing this document if you would like to share ideas on how to best analyze the data. 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..185bfc92e 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. diff --git a/docs/exchanges.md b/docs/exchanges.md index b808096d2..18a7af5a1 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -64,7 +64,10 @@ Binance supports [time_in_force](configuration.md#understand-order_time_in_force For Binance, please add `"BNB/"` to your blacklist to avoid issues. Accounts having BNB accounts use this to pay for fees - if your first trade happens to be on `BNB`, further trades will consume this position and make the initial BNB trade unsellable as the expected amount is not there anymore. -### Binance Futures' order pricing +### Binance Futures + +Binance has specific (unfortunately complex) [Futures Trading Quantitative Rules](https://www.binance.com/en/support/faq/4f462ebe6ff445d4a170be7d9e897272) which need to be followed, and which prohibit a too low stake-amount (among others) for too many orders. +Violating these rules will result in a trading restriction. When trading on Binance Futures market, orderbook must be used because there is no price ticker data for futures. 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/protections.md b/docs/includes/protections.md index 0757d2f6d..bb4a7eb35 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 } ] ``` diff --git a/docs/index.md b/docs/index.md index 2aa80c240..e0a88a381 100644 --- a/docs/index.md +++ b/docs/index.md @@ -51,6 +51,14 @@ 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: + +- [X] [Binance](https://www.binance.com/) +- [X] [Gate.io](https://www.gate.io/ref/6266643) +- [X] [OKX](https://okx.com/). + +Please make sure to read the [exchange specific notes](exchanges.md), as well as the [trading with leverage](leverage.md) documentation before diving in. + ### Community tested Exchanges confirmed working by the community: diff --git a/docs/javascripts/config.js b/docs/javascripts/config.js index 95d619efc..80e81ba59 100644 --- a/docs/javascripts/config.js +++ b/docs/javascripts/config.js @@ -9,4 +9,4 @@ window.MathJax = { ignoreHtmlClass: ".*|", processHtmlClass: "arithmatex" } -}; \ No newline at end of file +}; diff --git a/docs/plotting.md b/docs/plotting.md index df988c578..6ae0c3f11 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -96,7 +96,7 @@ Strategy arguments: Example: ``` bash -freqtrade plot-dataframe -p BTC/ETH +freqtrade plot-dataframe -p BTC/ETH --strategy AwesomeStrategy ``` The `-p/--pairs` argument can be used to specify pairs you would like to plot. @@ -107,9 +107,6 @@ The `-p/--pairs` argument can be used to specify pairs you would like to plot. Specify custom indicators. Use `--indicators1` for the main plot and `--indicators2` for the subplot below (if values are in a different range than prices). -!!! Tip - You will almost certainly want to specify a custom strategy! This can be done by adding `-s Classname` / `--strategy ClassName` to the command. - ``` bash freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --indicators1 sma ema --indicators2 macd ``` diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 1f7db75c5..c5a4b64b3 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,5 +1,5 @@ mkdocs==1.3.0 -mkdocs-material==8.2.8 +mkdocs-material==8.2.12 mdx_truly_sane_lists==1.2 -pymdown-extensions==9.3 -jinja2==3.1.1 +pymdown-extensions==9.4 +jinja2==3.1.2 diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 678899cc6..5ff499b01 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -7,6 +7,7 @@ Depending on the callback used, they may be called when entering / exiting a tra Currently available callbacks: +* [`bot_start()`](#bot-start) * [`bot_loop_start()`](#bot-loop-start) * [`custom_stake_amount()`](#stake-size-management) * [`custom_exit()`](#custom-exit-signal) @@ -21,6 +22,29 @@ Currently available callbacks: !!! Tip "Callback calling sequence" You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic) +## Bot start + +A simple callback which is called once when the strategy is loaded. +This can be used to perform actions that must only be performed once and runs after dataprovider and wallet are set + +``` python +import requests + +class AwesomeStrategy(IStrategy): + + # ... populate_* methods + + def bot_start(self, **kwargs) -> None: + """ + Called only once after bot instantiation. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + """ + if self.config['runmode'].value in ('live', 'dry_run'): + # Assign this to the class by using self.* + # can then be used by populate_* methods + self.cust_remote_data = requests.get('https://some_remote_source.example.com') + +``` ## Bot loop start A simple callback which is called once at the start of every bot throttling iteration (roughly every 5 seconds, unless configured differently). @@ -88,10 +112,11 @@ Allows to define custom exit signals, indicating that specified position should For example you could implement a 1:2 risk-reward ROI with `custom_exit()`. -Using custom_exit() signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange. +Using `custom_exit()` signals in place of stoploss though *is not recommended*. It is a inferior method to using `custom_stoploss()` in this regard - which also allows you to keep the stoploss on exchange. !!! Note - Returning a (none-empty) `string` or `True` from this method is equal to setting exit signal on a candle at specified time. This method is not called when exit signal is set already, or if exit signals are disabled (`use_exit_signal=False` or `exit_profit_only=True` while profit is below `exit_profit_offset`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters. + Returning a (none-empty) `string` or `True` from this method is equal to setting exit signal on a candle at specified time. This method is not called when exit signal is set already, or if exit signals are disabled (`use_exit_signal=False`). `string` max length is 64 characters. Exceeding this limit will cause the message to be truncated to 64 characters. + `custom_exit()` will ignore `exit_profit_only`, and will always be called unless `use_exit_signal=False`, even if there is a new enter signal. An example of how we can use different indicators depending on the current profit and also exit trades that were open longer than one day: @@ -121,11 +146,11 @@ See [Dataframe access](strategy-advanced.md#dataframe-access) for more informati ## Custom stoploss -Called for open trade every throttling iteration (roughly every 5 seconds) until a trade is closed. +Called for open trade every iteration (roughly every 5 seconds) until a trade is closed. The usage of the custom stoploss method must be enabled by setting `use_custom_stoploss=True` on the strategy object. -The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss (before this method is called for the first time for a trade). +The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss (before this method is called for the first time for a trade), and is still mandatory. The method must return a stoploss value (float / number) as a percentage of the current price. E.g. If the `current_rate` is 200 USD, then returning `0.02` will set the stoploss price 2% lower, at 196 USD. @@ -364,30 +389,30 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods - def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, + def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, entry_tag: Optional[str], side: str, **kwargs) -> float: dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) new_entryprice = dataframe['bollinger_10_lowerband'].iat[-1] - + return new_entryprice def custom_exit_price(self, pair: str, trade: Trade, current_time: datetime, proposed_rate: float, - current_profit: float, **kwargs) -> float: + current_profit: float, exit_tag: Optional[str], **kwargs) -> float: dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) new_exitprice = dataframe['bollinger_10_upperband'].iat[-1] - + return new_exitprice ``` !!! Warning - Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_ratio` parameter. - **Example**: + Modifying entry and exit prices will only work for limit orders. Depending on the price chosen, this can result in a lot of unfilled orders. By default the maximum allowed distance between the current price and the custom price is 2%, this value can be changed in config with the `custom_price_max_distance_ratio` parameter. + **Example**: If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_ratio` is set to 2%, The retained valid custom entry price will be 98, which is 2% below the current (proposed) rate. !!! Warning "Backtesting" @@ -417,7 +442,7 @@ The function must return either `True` (cancel order) or `False` (keep order ali ``` python from datetime import datetime, timedelta -from freqtrade.persistence import Trade +from freqtrade.persistence import Trade, Order class AwesomeStrategy(IStrategy): @@ -429,7 +454,7 @@ class AwesomeStrategy(IStrategy): 'exit': 60 * 25 } - def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict, + def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order', current_time: datetime, **kwargs) -> bool: if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5): return True @@ -440,7 +465,7 @@ class AwesomeStrategy(IStrategy): return False - def check_exit_timeout(self, pair: str, trade: Trade, order: dict, + def check_exit_timeout(self, pair: str, trade: Trade, order: 'Order', current_time: datetime, **kwargs) -> bool: if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5): return True @@ -458,7 +483,7 @@ class AwesomeStrategy(IStrategy): ``` python from datetime import datetime -from freqtrade.persistence import Trade +from freqtrade.persistence import Trade, Order class AwesomeStrategy(IStrategy): @@ -470,22 +495,22 @@ class AwesomeStrategy(IStrategy): 'exit': 60 * 25 } - def check_entry_timeout(self, pair: str, trade: Trade, order: dict, + def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order', current_time: datetime, **kwargs) -> bool: ob = self.dp.orderbook(pair, 1) current_price = ob['bids'][0][0] # Cancel buy order if price is more than 2% above the order. - if current_price > order['price'] * 1.02: + if current_price > order.price * 1.02: return True return False - def check_exit_timeout(self, pair: str, trade: Trade, order: dict, + def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order', current_time: datetime, **kwargs) -> bool: ob = self.dp.orderbook(pair, 1) current_price = ob['asks'][0][0] # Cancel sell order if price is more than 2% below the order. - if current_price < order['price'] * 0.98: + if current_price < order.price * 0.98: return True return False ``` @@ -507,7 +532,7 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods 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. @@ -615,35 +640,35 @@ from freqtrade.persistence import Trade class DigDeeperStrategy(IStrategy): - + position_adjustment_enable = True - + # Attempts to handle large drops with DCA. High stoploss is required. stoploss = -0.30 - + # ... populate_* methods - + # Example specific variables max_entry_position_adjustment = 3 # This number is explained a bit further down max_dca_multiplier = 5.5 - + # This is called when placing the initial order (opening trade) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, entry_tag: Optional[str], side: str, **kwargs) -> float: - + # We need to leave most of the funds for possible further DCA orders # This also applies to fixed stakes return proposed_stake / self.max_dca_multiplier - + def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float, max_stake: float, **kwargs): """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. This means extra buy orders with additional fees. - + :param trade: trade object. :param current_time: datetime object, containing the current datetime :param current_rate: Current buy rate. @@ -653,7 +678,7 @@ class DigDeeperStrategy(IStrategy): :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: Stake amount to adjust your trade """ - + if current_profit > -0.05: return None diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index 2fa84a6df..ae0c6a6a3 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -93,7 +93,7 @@ from freqtrade.data.btanalysis import load_backtest_data, load_backtest_stats # if backtest_dir points to a directory, it'll automatically load the last backtest file. backtest_dir = config["user_data_dir"] / "backtest_results" -# backtest_dir can also point to a specific file +# backtest_dir can also point to a specific file # backtest_dir = config["user_data_dir"] / "backtest_results/backtest-result-2020-07-01_20-04-22.json" ``` diff --git a/docs/strategy_migration.md b/docs/strategy_migration.md index eb1729ba7..458e80d0e 100644 --- a/docs/strategy_migration.md +++ b/docs/strategy_migration.md @@ -9,7 +9,7 @@ You can use the quick summary as checklist. Please refer to the detailed section ## Quick summary / migration checklist -Note : `force_exit`, `force_enter`, `emergency_exit` are changed to `force_exit`, `force_enter`, `emergency_exit` respectively. +Note : `forcesell`, `forcebuy`, `emergencysell` are changed to `force_exit`, `force_enter`, `emergency_exit` respectively. * Strategy methods: * [`populate_buy_trend()` -> `populate_entry_trend()`](#populate_buy_trend) @@ -145,6 +145,9 @@ Please refer to the [Strategy documentation](strategy-customization.md#exit-sign ### `custom_sell` +`custom_sell` has been renamed to `custom_exit`. +It's now also being called for every iteration, independent of current profit and `exit_profit_only` settings. + ``` python hl_lines="2" class AwesomeStrategy(IStrategy): def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, @@ -180,11 +183,11 @@ class AwesomeStrategy(IStrategy): ``` python hl_lines="2 6" class AwesomeStrategy(IStrategy): - def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict, + def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order', current_time: datetime, **kwargs) -> bool: return False - def check_exit_timeout(self, pair: str, trade: 'Trade', order: dict, + def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order', current_time: datetime, **kwargs) -> bool: return False ``` diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index a5709059a..27f5f91b6 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -275,9 +275,12 @@ The relative profit of `1.2%` is the average profit per trade. The relative profit of `15.2 Σ%` is be based on the starting capital - so in this case, the starting capital was `0.00485701 * 1.152 = 0.00738 BTC`. Starting capital is either taken from the `available_capital` setting, or calculated by using current wallet size - profits. -### /forcesell +### /forceexit -> **BINANCE:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)` +> **BINANCE:** Exiting BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)` + +!!! Tip + You can get a list of all open trades by calling `/forceexit` without parameter, which will show a list of buttons to simply exit a trade. ### /forcelong [rate] | /forceshort [rate] diff --git a/docs/updating.md b/docs/updating.md index b23ce32dc..1839edc4c 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -2,6 +2,10 @@ To update your freqtrade installation, please use one of the below methods, corresponding to your installation method. +!!! Note "Tracking changes" + Breaking changes / changed behavior will be documented in the changelog that is posted alongside every release. + For the develop branch, please follow PR's to avoid being surprised by changes. + ## docker-compose !!! Note "Legacy installations using the `master` image" diff --git a/environment.yml b/environment.yml index f2f961894..19f3c7f5a 100644 --- a/environment.yml +++ b/environment.yml @@ -32,6 +32,7 @@ dependencies: - prompt-toolkit - schedule - python-dateutil + - joblib # ============================ @@ -54,7 +55,6 @@ dependencies: - scikit-learn - filelock - scikit-optimize - - joblib - progressbar2 # ============================ # 4/4 req plot diff --git a/freqtrade.service b/freqtrade.service index df220ed39..6f0c73ee4 100644 --- a/freqtrade.service +++ b/freqtrade.service @@ -11,4 +11,3 @@ Restart=on-failure [Install] WantedBy=default.target - diff --git a/freqtrade.service.watchdog b/freqtrade.service.watchdog index 66ea00d76..dcd32ae18 100644 --- a/freqtrade.service.watchdog +++ b/freqtrade.service.watchdog @@ -27,4 +27,3 @@ WatchdogSec=20 [Install] WantedBy=default.target - diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 7d4624bd1..ff1d16590 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -12,7 +12,7 @@ from freqtrade.constants import DEFAULT_CONFIG ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"] -ARGS_STRATEGY = ["strategy", "strategy_path"] +ARGS_STRATEGY = ["strategy", "strategy_path", "recursive_strategy_search"] ARGS_TRADE = ["db_url", "sd_notify", "dry_run", "dry_run_wallet", "fee"] @@ -37,7 +37,8 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path", ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] -ARGS_LIST_STRATEGIES = ["strategy_path", "print_one_column", "print_colorized"] +ARGS_LIST_STRATEGIES = ["strategy_path", "print_one_column", "print_colorized", + "recursive_strategy_search"] ARGS_LIST_HYPEROPTS = ["hyperopt_path", "print_one_column", "print_colorized"] @@ -71,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", diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 61890d650..58e208652 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -83,6 +83,11 @@ AVAILABLE_CLI_OPTIONS = { help='Reset sample files to their original state.', action='store_true', ), + "recursive_strategy_search": Arg( + '--recursive-strategy-search', + help='Recursively search for a strategy in the strategies folder.', + action='store_true', + ), # Main options "strategy": Arg( '-s', '--strategy', @@ -438,6 +443,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..a2e2a100a 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -85,6 +85,7 @@ def start_download_data(args: Dict[str, Any]) -> None: 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/list_commands.py b/freqtrade/commands/list_commands.py index c4bd0bf4d..2a5223917 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -41,7 +41,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: print(tabulate(exchanges, headers=['Exchange name', 'Valid', 'reason'])) -def _print_objs_tabular(objs: List, print_colorized: bool) -> None: +def _print_objs_tabular(objs: List, print_colorized: bool, base_dir: Path) -> None: if print_colorized: colorama_init(autoreset=True) red = Fore.RED @@ -55,7 +55,7 @@ def _print_objs_tabular(objs: List, print_colorized: bool) -> None: names = [s['name'] for s in objs] objs_to_print = [{ 'name': s['name'] if s['name'] else "--", - 'location': s['location'].name, + 'location': s['location'].relative_to(base_dir), 'status': (red + "LOAD FAILED" + reset if s['class'] is None else "OK" if names.count(s['name']) == 1 else yellow + "DUPLICATE NAME" + reset) @@ -77,7 +77,8 @@ def start_list_strategies(args: Dict[str, Any]) -> None: config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) - strategy_objs = StrategyResolver.search_all_objects(directory, not args['print_one_column']) + strategy_objs = StrategyResolver.search_all_objects( + directory, not args['print_one_column'], config.get('recursive_strategy_search', False)) # Sort alphabetically strategy_objs = sorted(strategy_objs, key=lambda x: x['name']) for obj in strategy_objs: @@ -89,7 +90,7 @@ def start_list_strategies(args: Dict[str, Any]) -> None: if args['print_one_column']: print('\n'.join([s['name'] for s in strategy_objs])) else: - _print_objs_tabular(strategy_objs, config.get('print_colorized', False)) + _print_objs_tabular(strategy_objs, config.get('print_colorized', False), directory) def start_list_timeframes(args: Dict[str, Any]) -> None: diff --git a/freqtrade/configuration/PeriodicCache.py b/freqtrade/configuration/PeriodicCache.py index 64fff668e..1a535440d 100644 --- a/freqtrade/configuration/PeriodicCache.py +++ b/freqtrade/configuration/PeriodicCache.py @@ -16,4 +16,4 @@ class PeriodicCache(TTLCache): return ts - offset # Init with smlight offset - super().__init__(maxsize=maxsize, ttl=ttl-1e-5, timer=local_timer, getsizeof=getsizeof) + super().__init__(maxsize=maxsize, ttl=ttl - 1e-5, timer=local_timer, getsizeof=getsizeof) diff --git a/freqtrade/configuration/config_setup.py b/freqtrade/configuration/config_setup.py index 02f2d4089..d49bf61f6 100644 --- a/freqtrade/configuration/config_setup.py +++ b/freqtrade/configuration/config_setup.py @@ -22,6 +22,6 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str # Ensure these modes are using Dry-run config['dry_run'] = True - validate_config_consistency(config) + validate_config_consistency(config, preliminary=True) return config diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 6e4a4b0ef..ee846e7e6 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -39,7 +39,7 @@ def _extend_validator(validator_class): FreqtradeValidator = _extend_validator(Draft4Validator) -def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]: +def validate_config_schema(conf: Dict[str, Any], preliminary: bool = False) -> Dict[str, Any]: """ Validate the configuration follow the Config Schema :param conf: Config in JSON format @@ -49,7 +49,10 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]: if conf.get('runmode', RunMode.OTHER) in (RunMode.DRY_RUN, RunMode.LIVE): conf_schema['required'] = constants.SCHEMA_TRADE_REQUIRED elif conf.get('runmode', RunMode.OTHER) in (RunMode.BACKTEST, RunMode.HYPEROPT): - conf_schema['required'] = constants.SCHEMA_BACKTEST_REQUIRED + if preliminary: + conf_schema['required'] = constants.SCHEMA_BACKTEST_REQUIRED + else: + conf_schema['required'] = constants.SCHEMA_BACKTEST_REQUIRED_FINAL else: conf_schema['required'] = constants.SCHEMA_MINIMAL_REQUIRED try: @@ -64,7 +67,7 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]: ) -def validate_config_consistency(conf: Dict[str, Any]) -> None: +def validate_config_consistency(conf: Dict[str, Any], preliminary: bool = False) -> None: """ Validate the configuration consistency. Should be ran after loading both configuration and strategy, @@ -85,7 +88,7 @@ def validate_config_consistency(conf: Dict[str, Any]) -> None: # validate configuration before returning logger.info('Validating configuration ...') - validate_config_schema(conf) + validate_config_schema(conf, preliminary=preliminary) def _validate_unlimited_amount(conf: Dict[str, Any]) -> None: diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 331901920..80df6fb3f 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -248,6 +248,12 @@ class Configuration: self._args_to_config(config, argname='strategy_list', logstring='Using strategy list of {} strategies', logfun=len) + self._args_to_config( + config, + argname='recursive_strategy_search', + logstring='Recursively searching for a strategy in the strategies folder.', + ) + self._args_to_config(config, argname='timeframe', logstring='Overriding timeframe with Command line argument') @@ -387,6 +393,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.') diff --git a/freqtrade/configuration/load_config.py b/freqtrade/configuration/load_config.py index 254ce3126..3fcbd1f2f 100644 --- a/freqtrade/configuration/load_config.py +++ b/freqtrade/configuration/load_config.py @@ -31,7 +31,7 @@ def log_config_error_range(path: str, errmsg: str) -> str: offset = int(offsetlist[0]) text = Path(path).read_text() # Fetch an offset of 80 characters around the error line - subtext = text[offset-min(80, offset):offset+80] + subtext = text[offset - min(80, offset):offset + 80] segments = subtext.split('\n') if len(segments) > 3: # Remove first and last lines, to avoid odd truncations @@ -75,19 +75,41 @@ def load_config_file(path: str) -> Dict[str, Any]: return config -def load_from_files(files: List[str]) -> Dict[str, Any]: - +def load_from_files(files: List[str], base_path: Path = None, level: int = 0) -> Dict[str, Any]: + """ + Recursively load configuration files if specified. + Sub-files are assumed to be relative to the initial config. + """ config: Dict[str, Any] = {} + if level > 5: + raise OperationalException("Config loop detected.") if not files: return deepcopy(MINIMAL_CONFIG) - + files_loaded = [] # We expect here a list of config filenames - for path in files: - logger.info(f'Using config: {path} ...') - # Merge config options, overwriting old values - config = deep_merge_dicts(load_config_file(path), config) + for filename in files: + logger.info(f'Using config: {filename} ...') + if filename == '-': + # Immediately load stdin and return + return load_config_file(filename) + file = Path(filename) + if base_path: + # Prepend basepath to allow for relative assignments + file = base_path / file - config['config_files'] = files + config_tmp = load_config_file(str(file)) + if 'add_config_files' in config_tmp: + config_sub = load_from_files( + config_tmp['add_config_files'], file.resolve().parent, level + 1) + files_loaded.extend(config_sub.get('config_files', [])) + config_tmp = deep_merge_dicts(config_tmp, config_sub) + + files_loaded.insert(0, str(file)) + + # Merge config options, overwriting prior values + config = deep_merge_dicts(config_tmp, config) + + config['config_files'] = files_loaded return config diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 8067c1f6a..53cae8a8e 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -14,7 +14,7 @@ PROCESS_THROTTLE_SECS = 5 # sec HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec TIMEOUT_UNITS = ['minutes', 'seconds'] -EXPORT_OPTIONS = ['none', 'trades'] +EXPORT_OPTIONS = ['none', 'trades', 'signals'] DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite:///tradesv3.dryrun.sqlite' UNLIMITED_STAKE_AMOUNT = 'unlimited' @@ -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', @@ -91,15 +92,14 @@ SUPPORTED_FIAT = [ ] MINIMAL_CONFIG = { - 'stake_currency': '', - 'dry_run': True, - 'exchange': { - 'name': '', - 'key': '', - 'secret': '', - 'pair_whitelist': [], - 'ccxt_async_config': { - 'enableRateLimit': True, + "stake_currency": "", + "dry_run": True, + "exchange": { + "name": "", + "key": "", + "secret": "", + "pair_whitelist": [], + "ccxt_async_config": { } } } @@ -463,6 +463,10 @@ SCHEMA_BACKTEST_REQUIRED = [ 'dataformat_ohlcv', 'dataformat_trades', ] +SCHEMA_BACKTEST_REQUIRED_FINAL = SCHEMA_BACKTEST_REQUIRED + [ + 'stoploss', + 'minimal_roi', +] SCHEMA_MINIMAL_REQUIRED = [ 'exchange', diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index c8654cfda..e29d9ebe4 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -5,14 +5,15 @@ import logging from copy import copy from datetime import datetime, timezone from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Union import numpy as np import pandas as pd from freqtrade.constants import LAST_BT_RESULT_FN from freqtrade.exceptions import OperationalException -from freqtrade.misc import get_backtest_metadata_filename, json_load +from freqtrade.misc import json_load +from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename from freqtrade.persistence import LocalTrade, Trade, init_db @@ -149,7 +150,14 @@ def load_backtest_stats(filename: Union[Path, str]) -> Dict[str, Any]: return data -def _load_and_merge_backtest_result(strategy_name: str, filename: Path, results: Dict[str, Any]): +def load_and_merge_backtest_result(strategy_name: str, filename: Path, results: Dict[str, Any]): + """ + Load one strategy from multi-strategy result + and merge it with results + :param strategy_name: Name of the strategy contained in the result + :param filename: Backtest-result-filename to load + :param results: dict to merge the result to. + """ bt_data = load_backtest_stats(filename) for k in ('metadata', 'strategy'): results[k][strategy_name] = bt_data[k][strategy_name] @@ -160,6 +168,30 @@ def _load_and_merge_backtest_result(strategy_name: str, filename: Path, results: break +def _get_backtest_files(dirname: Path) -> List[Path]: + return list(reversed(sorted(dirname.glob('backtest-result-*-[0-9][0-9].json')))) + + +def get_backtest_resultlist(dirname: Path): + """ + Get list of backtest results read from metadata files + """ + results = [] + for filename in _get_backtest_files(dirname): + metadata = load_backtest_metadata(filename) + if not metadata: + continue + for s, v in metadata.items(): + results.append({ + 'filename': filename.name, + 'strategy': s, + 'run_id': v['run_id'], + 'backtest_start_time': v['backtest_start_time'], + + }) + return results + + def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, str], min_backtest_date: datetime = None) -> Dict[str, Any]: """ @@ -179,7 +211,7 @@ def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, s } # Weird glob expression here avoids including .meta.json files. - for filename in reversed(sorted(dirname.glob('backtest-result-*-[0-9][0-9].json'))): + for filename in _get_backtest_files(dirname): metadata = load_backtest_metadata(filename) if not metadata: # Files are sorted from newest to oldest. When file without metadata is encountered it @@ -193,14 +225,7 @@ def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, s continue if min_backtest_date is not None: - try: - backtest_date = strategy_metadata['backtest_start_time'] - except KeyError: - # TODO: this can be removed starting from feb 2022 - # The metadata-file without start_time was only available in develop - # and was never included in an official release. - # Older metadata format without backtest time, too old to consider. - return results + backtest_date = strategy_metadata['backtest_start_time'] backtest_date = datetime.fromtimestamp(backtest_date, tz=timezone.utc) if backtest_date < min_backtest_date: # Do not use a cached result for this strategy as first result is too old. @@ -209,7 +234,7 @@ def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, s if strategy_metadata['run_id'] == run_id: del run_ids[strategy_name] - _load_and_merge_backtest_result(strategy_name, filename, results) + load_and_merge_backtest_result(strategy_name, filename, results) if len(run_ids) == 0: break @@ -375,157 +400,3 @@ def extract_trades_of_period(dataframe: pd.DataFrame, trades: pd.DataFrame, trades = trades.loc[(trades['open_date'] >= trades_start) & (trades['close_date'] <= trades_stop)] return trades - - -def calculate_market_change(data: Dict[str, pd.DataFrame], column: str = "close") -> float: - """ - Calculate market change based on "column". - Calculation is done by taking the first non-null and the last non-null element of each column - and calculating the pctchange as "(last - first) / first". - Then the results per pair are combined as mean. - - :param data: Dict of Dataframes, dict key should be pair. - :param column: Column in the original dataframes to use - :return: - """ - tmp_means = [] - for pair, df in data.items(): - start = df[column].dropna().iloc[0] - end = df[column].dropna().iloc[-1] - tmp_means.append((end - start) / start) - - return float(np.mean(tmp_means)) - - -def combine_dataframes_with_mean(data: Dict[str, pd.DataFrame], - column: str = "close") -> pd.DataFrame: - """ - Combine multiple dataframes "column" - :param data: Dict of Dataframes, dict key should be pair. - :param column: Column in the original dataframes to use - :return: DataFrame with the column renamed to the dict key, and a column - named mean, containing the mean of all pairs. - :raise: ValueError if no data is provided. - """ - df_comb = pd.concat([data[pair].set_index('date').rename( - {column: pair}, axis=1)[pair] for pair in data], axis=1) - - df_comb['mean'] = df_comb.mean(axis=1) - - return df_comb - - -def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str, - timeframe: str) -> pd.DataFrame: - """ - Adds a column `col_name` with the cumulative profit for the given trades array. - :param df: DataFrame with date index - :param trades: DataFrame containing trades (requires columns close_date and profit_abs) - :param col_name: Column name that will be assigned the results - :param timeframe: Timeframe used during the operations - :return: Returns df with one additional column, col_name, containing the cumulative profit. - :raise: ValueError if trade-dataframe was found empty. - """ - if len(trades) == 0: - raise ValueError("Trade dataframe empty.") - from freqtrade.exchange import timeframe_to_minutes - timeframe_minutes = timeframe_to_minutes(timeframe) - # Resample to timeframe to make sure trades match candles - _trades_sum = trades.resample(f'{timeframe_minutes}min', on='close_date' - )[['profit_abs']].sum() - df.loc[:, col_name] = _trades_sum['profit_abs'].cumsum() - # Set first value to 0 - df.loc[df.iloc[0].name, col_name] = 0 - # FFill to get continuous - df[col_name] = df[col_name].ffill() - return df - - -def _calc_drawdown_series(profit_results: pd.DataFrame, *, date_col: str, value_col: str - ) -> 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] - return max_drawdown_df - - -def calculate_underwater(trades: pd.DataFrame, *, date_col: str = 'close_date', - value_col: str = 'profit_ratio' - ): - """ - Calculate max drawdown and the corresponding close dates - :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) - :param date_col: Column in DataFrame to use for dates (defaults to 'close_date') - :param value_col: Column in DataFrame to use for values (defaults to 'profit_ratio') - :return: Tuple (float, highdate, lowdate, highvalue, lowvalue) with absolute max drawdown, - high and low time and high and low value. - :raise: ValueError if trade-dataframe was found empty. - """ - 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) - - 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 - ) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]: - """ - Calculate max drawdown and the corresponding close dates - :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) - :param date_col: Column in DataFrame to use for dates (defaults to 'close_date') - :param value_col: Column in DataFrame to use for values (defaults to 'profit_abs') - :param starting_balance: Portfolio starting balance - properly calculate relative drawdown. - :return: Tuple (float, highdate, lowdate, highvalue, lowvalue, relative_drawdown) - with absolute max drawdown, high and low time and high and low value, - and the relative account drawdown - :raise: ValueError if trade-dataframe was found empty. - """ - 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) - - idxmin = 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] - low_date = profit_results.loc[idxmin, date_col] - 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) - - return ( - abs(min(max_drawdown_df['drawdown'])), - high_date, - low_date, - high_val, - low_val, - max_drawdown_rel - ) - - -def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]: - """ - Calculate min/max cumsum of trades, to show if the wallet/stake amount ratio is sane - :param trades: DataFrame containing trades (requires columns close_date and profit_percent) - :param starting_balance: Add starting balance to results, to show the wallets high / low points - :return: Tuple (float, float) with cumsum of profit_abs - :raise: ValueError if trade-dataframe was found empty. - """ - if len(trades) == 0: - raise ValueError("Trade dataframe empty.") - - csum_df = pd.DataFrame() - csum_df['sum'] = trades['profit_abs'].cumsum() - csum_min = csum_df['sum'].min() + starting_balance - csum_max = csum_df['sum'].max() + starting_balance - - return csum_min, csum_max diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 515a345f1..eb36d2042 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -139,8 +139,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 +151,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 +164,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, *, @@ -179,6 +186,8 @@ def _download_pair_history(pair: str, *, data_handler: IDataHandler = None, 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 @@ -186,25 +195,31 @@ 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 :param candle_type: Any of the enum CandleType (must match trading mode!) + :param erase: Erase existing data :return: bool with success state """ data_handler = get_datahandler(datadir, data_handler=data_handler) try: - logger.info( - f'Download history data for pair: "{pair}" ({process}), timeframe: {timeframe}, ' - f'candle type: {candle_type} and store in {datadir}.' - ) + if erase: + if data_handler.ohlcv_purge(pair, timeframe, candle_type=candle_type): + logger.info(f'Deleting existing data for pair {pair}, {timeframe}, {candle_type}.') - data, since_ms = _load_cached_data_for_updating(pair, timeframe, timerange, - data_handler=data_handler, - candle_type=candle_type) + data, since_ms, until_ms = _load_cached_data_for_updating( + pair, timeframe, timerange, + data_handler=data_handler, + candle_type=candle_type, + prepend=prepend) + + 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') @@ -219,6 +234,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, @@ -251,6 +267,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. @@ -267,35 +284,28 @@ def refresh_backtest_ohlcv_data(exchange: Exchange, pairs: List[str], timeframes continue for timeframe in timeframes: - if erase: - if data_handler.ohlcv_purge(pair, timeframe, candle_type=candle_type): - logger.info(f'Deleting existing data for pair {pair}, interval {timeframe}.') - logger.info(f'Downloading pair {pair}, interval {timeframe}.') process = f'{idx}/{len(pairs)}' _download_pair_history(pair=pair, process=process, datadir=datadir, exchange=exchange, timerange=timerange, data_handler=data_handler, timeframe=str(timeframe), new_pairs_days=new_pairs_days, - candle_type=candle_type) + candle_type=candle_type, + 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. - timeframe = exchange._ft_has['mark_ohlcv_timeframe'] + tf_mark = exchange._ft_has['mark_ohlcv_timeframe'] fr_candle_type = CandleType.from_string(exchange._ft_has['mark_ohlcv_price']) # All exchanges need FundingRate for futures trading. # The timeframe is aligned to the mark-price timeframe. for funding_candle_type in (CandleType.FUNDING_RATE, fr_candle_type): - # TODO: this could be in most parts to the above. - if erase: - if data_handler.ohlcv_purge(pair, timeframe, candle_type=funding_candle_type): - logger.info( - f'Deleting existing data for pair {pair}, interval {timeframe}.') _download_pair_history(pair=pair, process=process, datadir=datadir, exchange=exchange, timerange=timerange, data_handler=data_handler, - timeframe=str(timeframe), new_pairs_days=new_pairs_days, - candle_type=funding_candle_type) + timeframe=str(tf_mark), new_pairs_days=new_pairs_days, + candle_type=funding_candle_type, + erase=erase, prepend=prepend) return pairs_not_available @@ -313,8 +323,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..2e6b070ca 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 @@ -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 diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py new file mode 100644 index 000000000..c11a2df88 --- /dev/null +++ b/freqtrade/data/metrics.py @@ -0,0 +1,192 @@ +import logging +from typing import Dict, Tuple + +import numpy as np +import pandas as pd + + +logger = logging.getLogger(__name__) + + +def calculate_market_change(data: Dict[str, pd.DataFrame], column: str = "close") -> float: + """ + Calculate market change based on "column". + Calculation is done by taking the first non-null and the last non-null element of each column + and calculating the pctchange as "(last - first) / first". + Then the results per pair are combined as mean. + + :param data: Dict of Dataframes, dict key should be pair. + :param column: Column in the original dataframes to use + :return: + """ + tmp_means = [] + for pair, df in data.items(): + start = df[column].dropna().iloc[0] + end = df[column].dropna().iloc[-1] + tmp_means.append((end - start) / start) + + return float(np.mean(tmp_means)) + + +def combine_dataframes_with_mean(data: Dict[str, pd.DataFrame], + column: str = "close") -> pd.DataFrame: + """ + Combine multiple dataframes "column" + :param data: Dict of Dataframes, dict key should be pair. + :param column: Column in the original dataframes to use + :return: DataFrame with the column renamed to the dict key, and a column + named mean, containing the mean of all pairs. + :raise: ValueError if no data is provided. + """ + df_comb = pd.concat([data[pair].set_index('date').rename( + {column: pair}, axis=1)[pair] for pair in data], axis=1) + + df_comb['mean'] = df_comb.mean(axis=1) + + return df_comb + + +def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str, + timeframe: str) -> pd.DataFrame: + """ + Adds a column `col_name` with the cumulative profit for the given trades array. + :param df: DataFrame with date index + :param trades: DataFrame containing trades (requires columns close_date and profit_abs) + :param col_name: Column name that will be assigned the results + :param timeframe: Timeframe used during the operations + :return: Returns df with one additional column, col_name, containing the cumulative profit. + :raise: ValueError if trade-dataframe was found empty. + """ + if len(trades) == 0: + raise ValueError("Trade dataframe empty.") + from freqtrade.exchange import timeframe_to_minutes + timeframe_minutes = timeframe_to_minutes(timeframe) + # Resample to timeframe to make sure trades match candles + _trades_sum = trades.resample(f'{timeframe_minutes}min', on='close_date' + )[['profit_abs']].sum() + df.loc[:, col_name] = _trades_sum['profit_abs'].cumsum() + # Set first value to 0 + df.loc[df.iloc[0].name, col_name] = 0 + # FFill to get continuous + df[col_name] = df[col_name].ffill() + return df + + +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', starting_balance: float = 0.0 + ): + """ + Calculate max drawdown and the corresponding close dates + :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) + :param date_col: Column in DataFrame to use for dates (defaults to 'close_date') + :param value_col: Column in DataFrame to use for values (defaults to 'profit_ratio') + :return: Tuple (float, highdate, lowdate, highvalue, lowvalue) with absolute max drawdown, + high and low time and high and low value. + :raise: ValueError if trade-dataframe was found empty. + """ + 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, + 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, + relative: bool = False + ) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]: + """ + Calculate max drawdown and the corresponding close dates + :param trades: DataFrame containing trades (requires columns close_date and profit_ratio) + :param date_col: Column in DataFrame to use for dates (defaults to 'close_date') + :param value_col: Column in DataFrame to use for values (defaults to 'profit_abs') + :param starting_balance: Portfolio starting balance - properly calculate relative drawdown. + :return: Tuple (float, highdate, lowdate, highvalue, lowvalue, relative_drawdown) + with absolute max drawdown, high and low time and high and low value, + and the relative account drawdown + :raise: ValueError if trade-dataframe was found empty. + """ + 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, + starting_balance=starting_balance + ) + + 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] + low_date = profit_results.loc[idxmin, date_col] + 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 = max_drawdown_df.loc[idxmin, 'drawdown_relative'] + + return ( + abs(max_drawdown_df.loc[idxmin, 'drawdown']), + high_date, + low_date, + high_val, + low_val, + max_drawdown_rel + ) + + +def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]: + """ + Calculate min/max cumsum of trades, to show if the wallet/stake amount ratio is sane + :param trades: DataFrame containing trades (requires columns close_date and profit_percent) + :param starting_balance: Add starting balance to results, to show the wallets high / low points + :return: Tuple (float, float) with cumsum of profit_abs + :raise: ValueError if trade-dataframe was found empty. + """ + if len(trades) == 0: + raise ValueError("Trade dataframe empty.") + + csum_df = pd.DataFrame() + csum_df['sum'] = trades['profit_abs'].cumsum() + csum_min = csum_df['sum'].min() + starting_balance + csum_max = csum_df['sum'].max() + starting_balance + + return csum_min, csum_max + + +def calculate_cagr(days_passed: int, starting_balance: float, final_balance: float) -> float: + """ + Calculate CAGR + :param days_passed: Days passed between start and ending balance + :param starting_balance: Starting balance + :param final_balance: Final balance to calculate CAGR against + :return: CAGR + """ + return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1 diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 8c442cd26..69ae5198a 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -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: 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 c0bb965d0..ddffe1250 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -2,90 +2,90 @@ "RAY/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -94,105 +94,105 @@ "SUSHI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 50000000, + "minNotional": 2000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "2000000", + "maxNotional": "50000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -201,90 +201,90 @@ "CVC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -293,90 +293,90 @@ "BTS/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -385,90 +385,90 @@ "HOT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -477,90 +477,90 @@ "ZRX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -569,90 +569,90 @@ "QTUM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -661,90 +661,90 @@ "IOTA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -753,150 +753,150 @@ "BTC/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.004, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.005, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.005", "cum": "50.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.01, "maxLeverage": 20, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.01", "cum": "1300.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 7500000, + "minNotional": 1000000, + "maxNotional": 7500000, "maintenanceMarginRate": 0.025, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "7500000", - "notionalFloor": "1000000", + "maxNotional": "7500000", + "minNotional": "1000000", "maintMarginRatio": "0.025", "cum": "16300.0" } }, { "tier": 5, - "notionalFloor": 7500000, - "notionalCap": 40000000, + "minNotional": 7500000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.05, "maxLeverage": 6, "info": { "bracket": "5", "initialLeverage": "6", - "notionalCap": "40000000", - "notionalFloor": "7500000", + "maxNotional": "40000000", + "minNotional": "7500000", "maintMarginRatio": "0.05", "cum": "203800.0" } }, { "tier": 6, - "notionalFloor": 40000000, - "notionalCap": 100000000, + "minNotional": 40000000, + "maxNotional": 100000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "100000000", - "notionalFloor": "40000000", + "maxNotional": "100000000", + "minNotional": "40000000", "maintMarginRatio": "0.1", "cum": "2203800.0" } }, { "tier": 7, - "notionalFloor": 100000000, - "notionalCap": 200000000, + "minNotional": 100000000, + "maxNotional": 200000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "200000000", - "notionalFloor": "100000000", + "maxNotional": "200000000", + "minNotional": "100000000", "maintMarginRatio": "0.125", "cum": "4703800.0" } }, { "tier": 8, - "notionalFloor": 200000000, - "notionalCap": 400000000, + "minNotional": 200000000, + "maxNotional": 400000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "8", "initialLeverage": "3", - "notionalCap": "400000000", - "notionalFloor": "200000000", + "maxNotional": "400000000", + "minNotional": "200000000", "maintMarginRatio": "0.15", "cum": "9703800.0" } }, { "tier": 9, - "notionalFloor": 400000000, - "notionalCap": 600000000, + "minNotional": 400000000, + "maxNotional": 600000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "600000000", - "notionalFloor": "400000000", + "maxNotional": "600000000", + "minNotional": "400000000", "maintMarginRatio": "0.25", "cum": "4.97038E7" } }, { "tier": 10, - "notionalFloor": 600000000, - "notionalCap": 1000000000, + "minNotional": 600000000, + "maxNotional": 1000000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "1000000000", - "notionalFloor": "600000000", + "maxNotional": "1000000000", + "minNotional": "600000000", "maintMarginRatio": "0.5", "cum": "1.997038E8" } @@ -905,90 +905,90 @@ "WAVES/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -997,135 +997,135 @@ "ADA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -1134,90 +1134,90 @@ "LIT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -1226,90 +1226,90 @@ "NU/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -1318,135 +1318,135 @@ "XTZ/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -1455,135 +1455,135 @@ "BNB/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -1592,90 +1592,90 @@ "AKRO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.012, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } @@ -1684,90 +1684,90 @@ "HNT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -1776,135 +1776,135 @@ "ETC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -1913,135 +1913,135 @@ "XMR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -2050,90 +2050,90 @@ "YFI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -2142,90 +2142,90 @@ "FTT/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -2234,120 +2234,120 @@ "BTCUSDT_210326": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 9223372036854776000, + "minNotional": 10000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "9223372036854775807", - "notionalFloor": "10000000", + "maxNotional": "9223372036854775807", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } @@ -2356,150 +2356,150 @@ "ETH/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.005, "maxLeverage": 100, "info": { "bracket": "1", "initialLeverage": "100", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.005", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 100000, + "minNotional": 10000, + "maxNotional": 100000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "2", "initialLeverage": "75", - "notionalCap": "100000", - "notionalFloor": "10000", + "maxNotional": "100000", + "minNotional": "10000", "maintMarginRatio": "0.0065", "cum": "15.0" } }, { "tier": 3, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "3", "initialLeverage": "50", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.01", "cum": "365.0" } }, { "tier": 4, - "notionalFloor": 500000, - "notionalCap": 1500000, + "minNotional": 500000, + "maxNotional": 1500000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "1500000", - "notionalFloor": "500000", + "maxNotional": "1500000", + "minNotional": "500000", "maintMarginRatio": "0.02", "cum": "5365.0" } }, { "tier": 5, - "notionalFloor": 1500000, - "notionalCap": 4000000, + "minNotional": 1500000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "4000000", - "notionalFloor": "1500000", + "maxNotional": "4000000", + "minNotional": "1500000", "maintMarginRatio": "0.05", "cum": "50365.0" } }, { "tier": 6, - "notionalFloor": 4000000, - "notionalCap": 10000000, + "minNotional": 4000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "4000000", + "maxNotional": "10000000", + "minNotional": "4000000", "maintMarginRatio": "0.1", "cum": "250365.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.125", "cum": "500365.0" } }, { "tier": 8, - "notionalFloor": 20000000, - "notionalCap": 40000000, + "minNotional": 20000000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "8", "initialLeverage": "3", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "maxNotional": "40000000", + "minNotional": "20000000", "maintMarginRatio": "0.15", "cum": "1000365.0" } }, { "tier": 9, - "notionalFloor": 40000000, - "notionalCap": 150000000, + "minNotional": 40000000, + "maxNotional": 150000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "150000000", - "notionalFloor": "40000000", + "maxNotional": "150000000", + "minNotional": "40000000", "maintMarginRatio": "0.25", "cum": "5000365.0" } }, { "tier": 10, - "notionalFloor": 150000000, - "notionalCap": 500000000, + "minNotional": 150000000, + "maxNotional": 500000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "500000000", - "notionalFloor": "150000000", + "maxNotional": "500000000", + "minNotional": "150000000", "maintMarginRatio": "0.5", "cum": "4.2500365E7" } @@ -2508,105 +2508,105 @@ "ALICE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -2615,90 +2615,90 @@ "ALPHA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -2707,90 +2707,90 @@ "SFP/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -2799,90 +2799,90 @@ "REEF/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -2891,90 +2891,90 @@ "BAT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -2983,105 +2983,105 @@ "DOGE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "7000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "57000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "107000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.25", "cum": "732000.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 50000000, + "minNotional": 10000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "10000000", + "maxNotional": "50000000", + "minNotional": "10000000", "maintMarginRatio": "0.5", "cum": "3232000.0" } @@ -3090,135 +3090,135 @@ "TRX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -3227,90 +3227,90 @@ "RLC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3319,90 +3319,90 @@ "DOTECOUSDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.012, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 9223372036854776000, + "minNotional": 1000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "9223372036854775807", - "notionalFloor": "1000000", + "maxNotional": "9223372036854775807", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } @@ -3411,90 +3411,90 @@ "BTCSTUSDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 9223372036854776000, + "minNotional": 1000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "9223372036854775807", - "notionalFloor": "1000000", + "maxNotional": "9223372036854775807", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3503,90 +3503,90 @@ "STORJ/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3595,90 +3595,90 @@ "SNX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3687,90 +3687,90 @@ "ETHUSDT_210625": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 250000, + "minNotional": 0, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "0", + "maxNotional": "250000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "7500.0" } }, { "tier": 3, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "57500.0" } }, { "tier": 4, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "107500.0" } }, { "tier": 5, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "232500.0" } }, { "tier": 6, - "notionalFloor": 10000000, - "notionalCap": 9223372036854776000, + "minNotional": 10000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "9223372036854775807", - "notionalFloor": "10000000", + "maxNotional": "9223372036854775807", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1232500.0" } @@ -3779,90 +3779,90 @@ "1000XEC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3871,90 +3871,90 @@ "AUDIO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -3963,135 +3963,135 @@ "XLM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -4100,135 +4100,135 @@ "BTCBUSD_210129": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.004, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.005, "maxLeverage": 15, "info": { "bracket": "2", "initialLeverage": "15", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.005", "cum": "5.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.01, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.01", "cum": "130.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.025, "maxLeverage": 7, "info": { "bracket": "4", "initialLeverage": "7", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.025", "cum": "1630.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 2000000, + "minNotional": 500000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.05, "maxLeverage": 6, "info": { "bracket": "5", "initialLeverage": "6", - "notionalCap": "2000000", - "notionalFloor": "500000", + "maxNotional": "2000000", + "minNotional": "500000", "maintMarginRatio": "0.05", "cum": "14130.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.1", "cum": "114130.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.125", "cum": "239130.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "8", "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.15", "cum": "489130.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 9223372036854776000, + "minNotional": 20000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "9223372036854775807", - "notionalFloor": "20000000", + "maxNotional": "9223372036854775807", + "minNotional": "20000000", "maintMarginRatio": "0.25", "cum": "2489130.0" } @@ -4237,90 +4237,90 @@ "IOTX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4329,90 +4329,90 @@ "NEO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4421,90 +4421,90 @@ "UNFI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4513,105 +4513,105 @@ "SAND/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -4620,90 +4620,90 @@ "DASH/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4712,90 +4712,90 @@ "KAVA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4804,90 +4804,90 @@ "RUNE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4896,90 +4896,90 @@ "CTK/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -4988,135 +4988,135 @@ "LINK/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -5125,105 +5125,105 @@ "CELR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -5232,90 +5232,90 @@ "RSR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5324,90 +5324,90 @@ "ADA/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -5416,90 +5416,90 @@ "DGB/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5508,90 +5508,90 @@ "SKL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5600,90 +5600,90 @@ "REN/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5692,90 +5692,90 @@ "LPT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5784,90 +5784,90 @@ "TOMO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5876,90 +5876,90 @@ "MTL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -5968,135 +5968,135 @@ "LTC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -6105,90 +6105,90 @@ "DODO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -6197,90 +6197,90 @@ "EGLD/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 50000000, + "minNotional": 1000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "1000000", + "maxNotional": "50000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -6289,90 +6289,90 @@ "KSM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -6381,90 +6381,90 @@ "BNB/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -6473,90 +6473,90 @@ "BTCUSDT_210625": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 250000, + "minNotional": 0, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "0", + "maxNotional": "250000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "7500.0" } }, { "tier": 3, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "57500.0" } }, { "tier": 4, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "107500.0" } }, { "tier": 5, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "232500.0" } }, { "tier": 6, - "notionalFloor": 10000000, - "notionalCap": 9223372036854776000, + "minNotional": 10000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "9223372036854775807", - "notionalFloor": "10000000", + "maxNotional": "9223372036854775807", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1232500.0" } @@ -6565,90 +6565,90 @@ "ONT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -6657,105 +6657,105 @@ "VET/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -6764,90 +6764,90 @@ "TRB/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -6856,105 +6856,105 @@ "MANA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -6963,90 +6963,90 @@ "COTI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7055,90 +7055,90 @@ "CHR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7147,105 +7147,105 @@ "ETHUSDT_210924": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 250000, + "minNotional": 0, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "0", + "maxNotional": "250000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "7500.0" } }, { "tier": 3, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "57500.0" } }, { "tier": 4, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "107500.0" } }, { "tier": 5, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "232500.0" } }, { "tier": 6, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1232500.0" } }, { "tier": 7, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6232500.0" } @@ -7254,90 +7254,90 @@ "BAKE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7346,90 +7346,90 @@ "GRT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7438,105 +7438,105 @@ "ETHUSDT_220325": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 375000, + "minNotional": 0, + "maxNotional": 375000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "375000", - "notionalFloor": "0", + "maxNotional": "375000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 375000, - "notionalCap": 2000000, + "minNotional": 375000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "375000", + "maxNotional": "2000000", + "minNotional": "375000", "maintMarginRatio": "0.05", "cum": "11250.0" } }, { "tier": 3, - "notionalFloor": 2000000, - "notionalCap": 4000000, + "minNotional": 2000000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "maxNotional": "4000000", + "minNotional": "2000000", "maintMarginRatio": "0.1", "cum": "111250.0" } }, { "tier": 4, - "notionalFloor": 4000000, - "notionalCap": 10000000, + "minNotional": 4000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "4000000", + "maxNotional": "10000000", + "minNotional": "4000000", "maintMarginRatio": "0.125", "cum": "211250.0" } }, { "tier": 5, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.15", "cum": "461250.0" } }, { "tier": 6, - "notionalFloor": 20000000, - "notionalCap": 40000000, + "minNotional": 20000000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "maxNotional": "40000000", + "minNotional": "20000000", "maintMarginRatio": "0.25", "cum": "2461250.0" } }, { "tier": 7, - "notionalFloor": 40000000, - "notionalCap": 400000000, + "minNotional": 40000000, + "maxNotional": 400000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "400000000", - "notionalFloor": "40000000", + "maxNotional": "400000000", + "minNotional": "40000000", "maintMarginRatio": "0.5", "cum": "1.246125E7" } @@ -7545,90 +7545,90 @@ "FLM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7637,90 +7637,90 @@ "MASK/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -7729,135 +7729,135 @@ "EOS/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -7866,105 +7866,105 @@ "ETHUSDT_211231": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 375000, + "minNotional": 0, + "maxNotional": 375000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "375000", - "notionalFloor": "0", + "maxNotional": "375000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 375000, - "notionalCap": 2000000, + "minNotional": 375000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "375000", + "maxNotional": "2000000", + "minNotional": "375000", "maintMarginRatio": "0.05", "cum": "11250.0" } }, { "tier": 3, - "notionalFloor": 2000000, - "notionalCap": 4000000, + "minNotional": 2000000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "maxNotional": "4000000", + "minNotional": "2000000", "maintMarginRatio": "0.1", "cum": "111250.0" } }, { "tier": 4, - "notionalFloor": 4000000, - "notionalCap": 10000000, + "minNotional": 4000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "4000000", + "maxNotional": "10000000", + "minNotional": "4000000", "maintMarginRatio": "0.125", "cum": "211250.0" } }, { "tier": 5, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.15", "cum": "461250.0" } }, { "tier": 6, - "notionalFloor": 20000000, - "notionalCap": 40000000, + "minNotional": 20000000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "maxNotional": "40000000", + "minNotional": "20000000", "maintMarginRatio": "0.25", "cum": "2461250.0" } }, { "tier": 7, - "notionalFloor": 40000000, - "notionalCap": 400000000, + "minNotional": 40000000, + "maxNotional": 400000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "400000000", - "notionalFloor": "40000000", + "maxNotional": "400000000", + "minNotional": "40000000", "maintMarginRatio": "0.5", "cum": "1.246125E7" } @@ -7973,90 +7973,90 @@ "OGN/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8065,90 +8065,90 @@ "SC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8157,90 +8157,90 @@ "BAL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8249,90 +8249,90 @@ "STMX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8341,90 +8341,90 @@ "BTTUSDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8433,120 +8433,120 @@ "LUNA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "500.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1665, "maxLeverage": 3, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 15000000, + "minNotional": 10000000, + "maxNotional": 15000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "10000000", + "maxNotional": "15000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { "tier": 8, - "notionalFloor": 15000000, - "notionalCap": 50000000, + "minNotional": 15000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "15000000", + "maxNotional": "50000000", + "minNotional": "15000000", "maintMarginRatio": "0.5", "cum": "4900500.0" } @@ -8555,90 +8555,90 @@ "DENT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8647,90 +8647,90 @@ "1000BTTC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8739,90 +8739,90 @@ "KNC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8831,90 +8831,90 @@ "SRM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -8923,105 +8923,105 @@ "ENJ/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -9030,90 +9030,90 @@ "C98/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -9122,90 +9122,90 @@ "ZEN/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -9214,105 +9214,105 @@ "ATOM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -9321,105 +9321,105 @@ "NEAR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -9428,90 +9428,90 @@ "SOL/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -9520,90 +9520,90 @@ "ENS/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -9612,135 +9612,135 @@ "BCH/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -9749,90 +9749,90 @@ "ATA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -9841,90 +9841,90 @@ "IOST/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -9933,90 +9933,90 @@ "HBAR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -10025,105 +10025,105 @@ "ZEC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -10132,105 +10132,105 @@ "1000SHIB/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -10239,90 +10239,90 @@ "TLM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -10331,90 +10331,90 @@ "ANT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -10423,90 +10423,90 @@ "BZRXUSDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -10515,150 +10515,150 @@ "ETH/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 25000, + "minNotional": 0, + "maxNotional": 25000, "maintenanceMarginRate": 0.004, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "25000", - "notionalFloor": "0", + "maxNotional": "25000", + "minNotional": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.005, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.005", "cum": "25.0" } }, { "tier": 3, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.01, "maxLeverage": 20, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.01", "cum": "525.0" } }, { "tier": 4, - "notionalFloor": 500000, - "notionalCap": 1500000, + "minNotional": 500000, + "maxNotional": 1500000, "maintenanceMarginRate": 0.025, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1500000", - "notionalFloor": "500000", + "maxNotional": "1500000", + "minNotional": "500000", "maintMarginRatio": "0.025", "cum": "8025.0" } }, { "tier": 5, - "notionalFloor": 1500000, - "notionalCap": 4000000, + "minNotional": 1500000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.05, "maxLeverage": 6, "info": { "bracket": "5", "initialLeverage": "6", - "notionalCap": "4000000", - "notionalFloor": "1500000", + "maxNotional": "4000000", + "minNotional": "1500000", "maintMarginRatio": "0.05", "cum": "45525.0" } }, { "tier": 6, - "notionalFloor": 4000000, - "notionalCap": 10000000, + "minNotional": 4000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "4000000", + "maxNotional": "10000000", + "minNotional": "4000000", "maintMarginRatio": "0.1", "cum": "245525.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.125", "cum": "495525.0" } }, { "tier": 8, - "notionalFloor": 20000000, - "notionalCap": 40000000, + "minNotional": 20000000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "8", "initialLeverage": "3", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "maxNotional": "40000000", + "minNotional": "20000000", "maintMarginRatio": "0.15", "cum": "995525.0" } }, { "tier": 9, - "notionalFloor": 40000000, - "notionalCap": 150000000, + "minNotional": 40000000, + "maxNotional": 150000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "150000000", - "notionalFloor": "40000000", + "maxNotional": "150000000", + "minNotional": "40000000", "maintMarginRatio": "0.25", "cum": "4995525.0" } }, { "tier": 10, - "notionalFloor": 150000000, - "notionalCap": 500000000, + "minNotional": 150000000, + "maxNotional": 500000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "500000000", - "notionalFloor": "150000000", + "maxNotional": "500000000", + "minNotional": "150000000", "maintMarginRatio": "0.5", "cum": "4.2495525E7" } @@ -10667,105 +10667,105 @@ "GALA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -10774,120 +10774,120 @@ "AAVE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "500.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1665, "maxLeverage": 3, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { "tier": 8, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -10896,90 +10896,90 @@ "GTC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -10988,105 +10988,105 @@ "ALGO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -11095,90 +11095,90 @@ "ICP/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -11187,105 +11187,105 @@ "BTCUSDT_210924": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 250000, + "minNotional": 0, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "0", + "maxNotional": "250000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "7500.0" } }, { "tier": 3, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "57500.0" } }, { "tier": 4, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "107500.0" } }, { "tier": 5, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "232500.0" } }, { "tier": 6, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1232500.0" } }, { "tier": 7, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6232500.0" } @@ -11294,105 +11294,105 @@ "LRC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -11401,105 +11401,105 @@ "AVAX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 750000, + "minNotional": 500000, + "maxNotional": 750000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "500000", + "maxNotional": "750000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 750000, - "notionalCap": 1000000, + "minNotional": 750000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "750000", + "maxNotional": "1000000", + "minNotional": "750000", "maintMarginRatio": "0.25", "cum": "123250.0" } }, { "tier": 7, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "373250.0" } @@ -11508,105 +11508,105 @@ "BTCUSDT_220325": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 375000, + "minNotional": 0, + "maxNotional": 375000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "375000", - "notionalFloor": "0", + "maxNotional": "375000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 375000, - "notionalCap": 2000000, + "minNotional": 375000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "375000", + "maxNotional": "2000000", + "minNotional": "375000", "maintMarginRatio": "0.05", "cum": "11250.0" } }, { "tier": 3, - "notionalFloor": 2000000, - "notionalCap": 4000000, + "minNotional": 2000000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "maxNotional": "4000000", + "minNotional": "2000000", "maintMarginRatio": "0.1", "cum": "111250.0" } }, { "tier": 4, - "notionalFloor": 4000000, - "notionalCap": 10000000, + "minNotional": 4000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "4000000", + "maxNotional": "10000000", + "minNotional": "4000000", "maintMarginRatio": "0.125", "cum": "211250.0" } }, { "tier": 5, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.15", "cum": "461250.0" } }, { "tier": 6, - "notionalFloor": 20000000, - "notionalCap": 40000000, + "minNotional": 20000000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "maxNotional": "40000000", + "minNotional": "20000000", "maintMarginRatio": "0.25", "cum": "2461250.0" } }, { "tier": 7, - "notionalFloor": 40000000, - "notionalCap": 400000000, + "minNotional": 40000000, + "maxNotional": 400000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "400000000", - "notionalFloor": "40000000", + "maxNotional": "400000000", + "minNotional": "40000000", "maintMarginRatio": "0.5", "cum": "1.246125E7" } @@ -11615,90 +11615,90 @@ "ARPA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -11707,90 +11707,90 @@ "CELO/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -11799,90 +11799,90 @@ "ROSE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -11891,105 +11891,105 @@ "MATIC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 750000, + "minNotional": 500000, + "maxNotional": 750000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "500000", + "maxNotional": "750000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 750000, - "notionalCap": 1000000, + "minNotional": 750000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "750000", + "maxNotional": "1000000", + "minNotional": "750000", "maintMarginRatio": "0.25", "cum": "123250.0" } }, { "tier": 7, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "373250.0" } @@ -11998,90 +11998,90 @@ "1INCH/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.012, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 100000000, + "minNotional": 1000000, + "maxNotional": 100000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "100000000", - "notionalFloor": "1000000", + "maxNotional": "100000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } @@ -12090,90 +12090,90 @@ "MKR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -12182,90 +12182,90 @@ "PEOPLE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -12274,120 +12274,120 @@ "THETA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "500.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1665, "maxLeverage": 3, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { "tier": 8, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -12396,120 +12396,120 @@ "UNI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "500.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1665, "maxLeverage": 3, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { "tier": 8, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -12518,120 +12518,120 @@ "ETHUSDT_210326": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 9223372036854776000, + "minNotional": 10000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "9223372036854775807", - "notionalFloor": "10000000", + "maxNotional": "9223372036854775807", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } @@ -12640,90 +12640,90 @@ "LINA/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -12732,90 +12732,90 @@ "AR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -12824,90 +12824,90 @@ "RVN/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -12916,120 +12916,120 @@ "FIL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "500.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1665, "maxLeverage": 3, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { "tier": 8, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6150500.0" } @@ -13038,90 +13038,90 @@ "NKN/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -13130,90 +13130,90 @@ "KLAY/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -13222,90 +13222,90 @@ "DEFI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -13314,90 +13314,90 @@ "COMP/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -13406,90 +13406,90 @@ "BTCDOM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -13498,105 +13498,105 @@ "SOL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "7000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "57000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "107000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.25", "cum": "732000.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 50000000, + "minNotional": 10000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "10000000", + "maxNotional": "50000000", + "minNotional": "10000000", "maintMarginRatio": "0.5", "cum": "3232000.0" } @@ -13605,150 +13605,150 @@ "BTC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.004, "maxLeverage": 125, "info": { "bracket": "1", "initialLeverage": "125", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.005, "maxLeverage": 100, "info": { "bracket": "2", "initialLeverage": "100", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.005", "cum": "50.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "3", "initialLeverage": "50", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.01", "cum": "1300.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 7500000, + "minNotional": 1000000, + "maxNotional": 7500000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "7500000", - "notionalFloor": "1000000", + "maxNotional": "7500000", + "minNotional": "1000000", "maintMarginRatio": "0.025", "cum": "16300.0" } }, { "tier": 5, - "notionalFloor": 7500000, - "notionalCap": 40000000, + "minNotional": 7500000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "40000000", - "notionalFloor": "7500000", + "maxNotional": "40000000", + "minNotional": "7500000", "maintMarginRatio": "0.05", "cum": "203800.0" } }, { "tier": 6, - "notionalFloor": 40000000, - "notionalCap": 100000000, + "minNotional": 40000000, + "maxNotional": 100000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "100000000", - "notionalFloor": "40000000", + "maxNotional": "100000000", + "minNotional": "40000000", "maintMarginRatio": "0.1", "cum": "2203800.0" } }, { "tier": 7, - "notionalFloor": 100000000, - "notionalCap": 200000000, + "minNotional": 100000000, + "maxNotional": 200000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "200000000", - "notionalFloor": "100000000", + "maxNotional": "200000000", + "minNotional": "100000000", "maintMarginRatio": "0.125", "cum": "4703800.0" } }, { "tier": 8, - "notionalFloor": 200000000, - "notionalCap": 400000000, + "minNotional": 200000000, + "maxNotional": 400000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "8", "initialLeverage": "3", - "notionalCap": "400000000", - "notionalFloor": "200000000", + "maxNotional": "400000000", + "minNotional": "200000000", "maintMarginRatio": "0.15", "cum": "9703800.0" } }, { "tier": 9, - "notionalFloor": 400000000, - "notionalCap": 600000000, + "minNotional": 400000000, + "maxNotional": 600000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "600000000", - "notionalFloor": "400000000", + "maxNotional": "600000000", + "minNotional": "400000000", "maintMarginRatio": "0.25", "cum": "4.97038E7" } }, { "tier": 10, - "notionalFloor": 600000000, - "notionalCap": 1000000000, + "minNotional": 600000000, + "maxNotional": 1000000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "1000000000", - "notionalFloor": "600000000", + "maxNotional": "1000000000", + "minNotional": "600000000", "maintMarginRatio": "0.5", "cum": "1.997038E8" } @@ -13757,90 +13757,90 @@ "OMG/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.024, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.024", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "5.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "630.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5630.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11880.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 10000000, + "minNotional": 1000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "1000000", + "maxNotional": "10000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386880.0" } @@ -13849,90 +13849,90 @@ "ICX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -13941,90 +13941,90 @@ "BLZ/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -14033,105 +14033,105 @@ "BTCUSDT_211231": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 375000, + "minNotional": 0, + "maxNotional": 375000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "375000", - "notionalFloor": "0", + "maxNotional": "375000", + "minNotional": "0", "maintMarginRatio": "0.02", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 375000, - "notionalCap": 2000000, + "minNotional": 375000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "375000", + "maxNotional": "2000000", + "minNotional": "375000", "maintMarginRatio": "0.05", "cum": "11250.0" } }, { "tier": 3, - "notionalFloor": 2000000, - "notionalCap": 4000000, + "minNotional": 2000000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "maxNotional": "4000000", + "minNotional": "2000000", "maintMarginRatio": "0.1", "cum": "111250.0" } }, { "tier": 4, - "notionalFloor": 4000000, - "notionalCap": 10000000, + "minNotional": 4000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "4000000", + "maxNotional": "10000000", + "minNotional": "4000000", "maintMarginRatio": "0.125", "cum": "211250.0" } }, { "tier": 5, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.15", "cum": "461250.0" } }, { "tier": 6, - "notionalFloor": 20000000, - "notionalCap": 40000000, + "minNotional": 20000000, + "maxNotional": 40000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "maxNotional": "40000000", + "minNotional": "20000000", "maintMarginRatio": "0.25", "cum": "2461250.0" } }, { "tier": 7, - "notionalFloor": 40000000, - "notionalCap": 400000000, + "minNotional": 40000000, + "maxNotional": 400000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "400000000", - "notionalFloor": "40000000", + "maxNotional": "400000000", + "minNotional": "40000000", "maintMarginRatio": "0.5", "cum": "1.246125E7" } @@ -14140,105 +14140,105 @@ "FTM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 750000, + "minNotional": 500000, + "maxNotional": 750000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "500000", + "maxNotional": "750000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 750000, - "notionalCap": 1000000, + "minNotional": 750000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "750000", + "maxNotional": "1000000", + "minNotional": "750000", "maintMarginRatio": "0.25", "cum": "123250.0" } }, { "tier": 7, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "373250.0" } @@ -14247,90 +14247,90 @@ "YFII/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -14339,90 +14339,90 @@ "KEEP/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -14431,90 +14431,90 @@ "BAND/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -14523,135 +14523,135 @@ "BTCBUSD_210226": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.004, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.004", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.005, "maxLeverage": 15, "info": { "bracket": "2", "initialLeverage": "15", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.005", "cum": "5.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.01, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.01", "cum": "130.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.025, "maxLeverage": 7, "info": { "bracket": "4", "initialLeverage": "7", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.025", "cum": "1630.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 2000000, + "minNotional": 500000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.05, "maxLeverage": 6, "info": { "bracket": "5", "initialLeverage": "6", - "notionalCap": "2000000", - "notionalFloor": "500000", + "maxNotional": "2000000", + "minNotional": "500000", "maintMarginRatio": "0.05", "cum": "14130.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.1", "cum": "114130.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.125", "cum": "239130.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "8", "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.15", "cum": "489130.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 9223372036854776000, + "minNotional": 20000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "9223372036854775807", - "notionalFloor": "20000000", + "maxNotional": "9223372036854775807", + "minNotional": "20000000", "maintMarginRatio": "0.25", "cum": "2489130.0" } @@ -14660,90 +14660,90 @@ "XRP/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -14752,90 +14752,90 @@ "DOGE/BUSD": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -14844,135 +14844,135 @@ "XRP/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 20000000, + "minNotional": 10000000, + "maxNotional": 20000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "maxNotional": "20000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 20000000, - "notionalCap": 50000000, + "minNotional": 20000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "20000000", + "maxNotional": "50000000", + "minNotional": "20000000", "maintMarginRatio": "0.5", "cum": "6233035.0" } @@ -14981,90 +14981,90 @@ "SXP/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -15073,105 +15073,105 @@ "CRV/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -15180,90 +15180,90 @@ "BEL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -15272,135 +15272,135 @@ "DOT/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 10000, + "minNotional": 0, + "maxNotional": 10000, "maintenanceMarginRate": 0.0065, "maxLeverage": 75, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", - "notionalFloor": "0", + "maxNotional": "10000", + "minNotional": "0", "maintMarginRatio": "0.0065", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 10000, - "notionalCap": 50000, + "minNotional": 10000, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "10000", + "maxNotional": "50000", + "minNotional": "10000", "maintMarginRatio": "0.01", "cum": "35.0" } }, { "tier": 3, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "535.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8035.0" } }, { "tier": 5, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58035.0" } }, { "tier": 6, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108035.0" } }, { "tier": 7, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.15", "cum": "233035.0" } }, { "tier": 8, - "notionalFloor": 10000000, - "notionalCap": 50000000, + "minNotional": 10000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "50000000", - "notionalFloor": "10000000", + "maxNotional": "50000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1233035.0" } }, { "tier": 9, - "notionalFloor": 50000000, - "notionalCap": 100000000, + "minNotional": 50000000, + "maxNotional": 100000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "100000000", - "notionalFloor": "50000000", + "maxNotional": "100000000", + "minNotional": "50000000", "maintMarginRatio": "0.5", "cum": "1.3733035E7" } @@ -15409,90 +15409,90 @@ "XEM/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -15501,105 +15501,105 @@ "ONE/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } @@ -15608,90 +15608,90 @@ "ZIL/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -15700,120 +15700,120 @@ "AXS/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 250000, + "minNotional": 50000, + "maxNotional": 250000, "maintenanceMarginRate": 0.02, "maxLeverage": 25, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "250000", - "notionalFloor": "50000", + "maxNotional": "250000", + "minNotional": "50000", "maintMarginRatio": "0.02", "cum": "500.0" } }, { "tier": 3, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.05", "cum": "8000.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.1", "cum": "58000.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.125", "cum": "108000.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 10000000, + "minNotional": 5000000, + "maxNotional": 10000000, "maintenanceMarginRate": 0.1665, "maxLeverage": 3, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "maxNotional": "10000000", + "minNotional": "5000000", "maintMarginRatio": "0.1665", "cum": "315500.0" } }, { "tier": 7, - "notionalFloor": 10000000, - "notionalCap": 15000000, + "minNotional": 10000000, + "maxNotional": 15000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "10000000", + "maxNotional": "15000000", + "minNotional": "10000000", "maintMarginRatio": "0.25", "cum": "1150500.0" } }, { "tier": 8, - "notionalFloor": 15000000, - "notionalCap": 50000000, + "minNotional": 15000000, + "maxNotional": 50000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "15000000", + "maxNotional": "50000000", + "minNotional": "15000000", "maintMarginRatio": "0.5", "cum": "4900500.0" } @@ -15822,105 +15822,105 @@ "DYDX/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 4000000, + "minNotional": 1000000, + "maxNotional": 4000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "4000000", - "notionalFloor": "1000000", + "maxNotional": "4000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 4000000, - "notionalCap": 30000000, + "minNotional": 4000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "4000000", + "maxNotional": "30000000", + "minNotional": "4000000", "maintMarginRatio": "0.5", "cum": "1154500.0" } @@ -15929,90 +15929,90 @@ "OCEAN/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -16021,90 +16021,90 @@ "CHZ/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.012, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } @@ -16113,90 +16113,90 @@ "LENDUSDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 9223372036854776000, + "minNotional": 1000000, + "maxNotional": 9223372036854776000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "9223372036854775807", - "notionalFloor": "1000000", + "maxNotional": "9223372036854775807", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -16205,90 +16205,90 @@ "ANKR/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.012, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.012", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "65.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "690.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5690.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11940.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386940.0" } @@ -16297,90 +16297,90 @@ "DUSK/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } @@ -16389,90 +16389,90 @@ "CTSI/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 5000, + "minNotional": 0, + "maxNotional": 5000, "maintenanceMarginRate": 0.01, "maxLeverage": 25, "info": { "bracket": "1", "initialLeverage": "25", - "notionalCap": "5000", - "notionalFloor": "0", + "maxNotional": "5000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 5000, - "notionalCap": 25000, + "minNotional": 5000, + "maxNotional": 25000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", + "maxNotional": "25000", + "minNotional": "5000", "maintMarginRatio": "0.025", "cum": "75.0" } }, { "tier": 3, - "notionalFloor": 25000, - "notionalCap": 100000, + "minNotional": 25000, + "maxNotional": 100000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "maxNotional": "100000", + "minNotional": "25000", "maintMarginRatio": "0.05", "cum": "700.0" } }, { "tier": 4, - "notionalFloor": 100000, - "notionalCap": 250000, + "minNotional": 100000, + "maxNotional": 250000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "maxNotional": "250000", + "minNotional": "100000", "maintMarginRatio": "0.1", "cum": "5700.0" } }, { "tier": 5, - "notionalFloor": 250000, - "notionalCap": 1000000, + "minNotional": 250000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", + "maxNotional": "1000000", + "minNotional": "250000", "maintMarginRatio": "0.125", "cum": "11950.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 30000000, + "minNotional": 1000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "1000000", + "maxNotional": "30000000", + "minNotional": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" } diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1a8cae62c..c7901fb01 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -9,6 +9,7 @@ import logging from copy import deepcopy from datetime import datetime, timedelta, timezone from math import ceil +from threading import Lock from typing import Any, Coroutine, Dict, List, Literal, Optional, Tuple, Union import arrow @@ -64,6 +65,7 @@ class Exchange: "ohlcv_params": {}, "ohlcv_candle_limit": 500, "ohlcv_partial_candle": True, + "ohlcv_require_since": False, # Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency "ohlcv_volume_currency": "base", # "base" or "quote" "tickers_have_quoteVolume": True, @@ -95,6 +97,9 @@ class Exchange: self._markets: Dict = {} self._trading_fees: Dict[str, Any] = {} self._leverage_tiers: Dict[str, List[Dict]] = {} + # Lock event loop. This is necessary to avoid race-conditions when using force* commands + # Due to funding fee fetching. + self._loop_lock = Lock() self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self._config: Dict = {} @@ -166,7 +171,7 @@ class Exchange: self._api_async = self._init_ccxt( exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config) - logger.info('Using Exchange "%s"', self.name) + logger.info(f'Using Exchange "{self.name}"') if validate: # Check if timeframe is available @@ -341,15 +346,11 @@ class Exchange: return sorted(set([x['quote'] for _, x in markets.items()])) def get_pair_quote_currency(self, pair: str) -> str: - """ - Return a pair's quote currency - """ + """ Return a pair's quote currency (base/quote:settlement) """ return self.markets.get(pair, {}).get('quote', '') def get_pair_base_currency(self, pair: str) -> str: - """ - Return a pair's base currency - """ + """ Return a pair's base currency (base/quote:settlement) """ return self.markets.get(pair, {}).get('base', '') def market_is_future(self, market: Dict[str, Any]) -> bool: @@ -372,6 +373,9 @@ class Exchange: return ( market.get('quote', None) is not None and market.get('base', None) is not None + and (self.precisionMode != TICK_SIZE + # Too low precision will falsify calculations + or market.get('precision', {}).get('price', None) > 1e-11) and ((self.trading_mode == TradingMode.SPOT and self.market_is_spot(market)) or (self.trading_mode == TradingMode.MARGIN and self.market_is_margin(market)) or (self.trading_mode == TradingMode.FUTURES and self.market_is_future(market))) @@ -555,7 +559,7 @@ class Exchange: # Therefore we also show that. raise OperationalException( f"The ccxt library does not provide the list of timeframes " - f"for the exchange \"{self.name}\" and this exchange " + f"for the exchange {self.name} and this exchange " f"is therefore not supported. ccxt fetchOHLCV: {self.exchange_has('fetchOHLCV')}") if timeframe and (timeframe not in self.timeframes): @@ -655,7 +659,7 @@ class Exchange: Re-implementation of ccxt internal methods - ensuring we can test the result is correct based on our definitions. """ - if self.markets[pair]['precision']['amount']: + if self.markets[pair]['precision']['amount'] is not None: amount = float(decimal_to_precision(amount, rounding_mode=TRUNCATE, precision=self.markets[pair]['precision']['amount'], counting_mode=self.precisionMode, @@ -785,7 +789,9 @@ class Exchange: rate: float, leverage: float, params: Dict = {}, stop_loss: bool = False) -> Dict[str, Any]: order_id = f'dry_run_{side}_{datetime.now().timestamp()}' - _amount = self.amount_to_precision(pair, amount) + # Rounding here must respect to contract sizes + _amount = self._contracts_to_amount( + pair, self.amount_to_precision(pair, self._amount_to_contracts(pair, amount))) dry_order: Dict[str, Any] = { 'id': order_id, 'symbol': pair, @@ -1671,7 +1677,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. @@ -1679,13 +1686,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 @@ -1706,6 +1714,7 @@ class Exchange: async def _async_get_historic_ohlcv(self, pair: str, timeframe: str, since_ms: int, candle_type: CandleType, is_new_pair: bool = False, raise_: bool = False, + until_ms: int = None ) -> Tuple[str, str, str, List]: """ Download historic ohlcv @@ -1721,7 +1730,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 @@ -1746,7 +1755,8 @@ class Exchange: def _build_coroutine(self, pair: str, timeframe: str, candle_type: CandleType, since_ms: Optional[int]) -> Coroutine: - if not since_ms and self.required_candle_call_count > 1: + 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) move_to = one_call * self.required_candle_call_count @@ -1806,7 +1816,8 @@ class Exchange: async def gather_stuff(): return await asyncio.gather(*input_coro, return_exceptions=True) - results = self.loop.run_until_complete(gather_stuff()) + with self._loop_lock: + results = self.loop.run_until_complete(gather_stuff()) for res in results: if isinstance(res, Exception): @@ -1865,17 +1876,18 @@ class Exchange: pair, timeframe, since_ms, s ) params = deepcopy(self._ft_has.get('ohlcv_params', {})) + candle_limit = self.ohlcv_candle_limit(timeframe) if candle_type != CandleType.SPOT: params.update({'price': candle_type}) if candle_type != CandleType.FUNDING_RATE: data = await self._api_async.fetch_ohlcv( pair, timeframe=timeframe, since=since_ms, - limit=self.ohlcv_candle_limit(timeframe), params=params) + limit=candle_limit, params=params) else: # Funding rate data = await self._api_async.fetch_funding_rate_history( pair, since=since_ms, - limit=self.ohlcv_candle_limit(timeframe)) + limit=candle_limit) # Convert funding rate to candle pattern data = [[x['timestamp'], x['fundingRate'], 0, 0, 0, 0] for x in data] # Some exchanges sort OHLCV in ASC order and others in DESC. @@ -2062,9 +2074,10 @@ class Exchange: if not self.exchange_has("fetchTrades"): raise OperationalException("This exchange does not support downloading Trades.") - return self.loop.run_until_complete( - self._async_get_trade_history(pair=pair, since=since, - until=until, from_id=from_id)) + with self._loop_lock: + return self.loop.run_until_complete( + self._async_get_trade_history(pair=pair, since=since, + until=until, from_id=from_id)) @retrier def _get_funding_fees_from_exchange(self, pair: str, since: Union[datetime, int]) -> float: @@ -2173,8 +2186,8 @@ class Exchange: def parse_leverage_tier(self, tier) -> Dict: info = tier.get('info', {}) return { - 'min': tier['notionalFloor'], - 'max': tier['notionalCap'], + 'min': tier['minNotional'], + 'max': tier['maxNotional'], 'mmr': tier['maintenanceMarginRate'], 'lev': tier['maxLeverage'], 'maintAmt': float(info['cum']) if 'cum' in info else None, @@ -2213,7 +2226,7 @@ class Exchange: lev = tier['lev'] if tier_index < len(pair_tiers) - 1: - next_tier = pair_tiers[tier_index+1] + next_tier = pair_tiers[tier_index + 1] next_floor = next_tier['min'] / next_tier['lev'] if next_floor > stake_amount: # Next tier min too high for stake amount return min((tier['max'] / stake_amount), lev) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index f20aab138..d2dcf84a6 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -20,6 +20,7 @@ class Ftx(Exchange): _ft_has: Dict = { "stoploss_on_exchange": True, "ohlcv_candle_limit": 1500, + "ohlcv_require_since": True, "ohlcv_volume_currency": "quote", "mark_ohlcv_price": "index", "mark_ohlcv_timeframe": "1h", diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 615b03c4c..a097703a8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -123,6 +123,8 @@ 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() + def notify_status(self, msg: str) -> None: """ Public method for users of this class (worker, etc.) to send notifications @@ -400,7 +402,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 " @@ -434,16 +439,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 @@ -459,7 +454,18 @@ 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', {}) if ((bid_check_dom.get('enabled', False)) and (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): @@ -606,7 +612,6 @@ class FreqtradeBot(LoggingMixin): Executes a limit buy for the given pair :param pair: pair for which we want to create a LIMIT_BUY :param stake_amount: amount of stake-currency for the pair - :param leverage: amount of leverage applied to this trade :return: True if a buy order is created, false if it fails. """ time_in_force = self.strategy.order_time_in_force['entry'] @@ -632,6 +637,7 @@ class FreqtradeBot(LoggingMixin): amount = (stake_amount / enter_limit_requested) * leverage order_type = ordertype or self.strategy.order_types['entry'] + if not pos_adjust and not strategy_safe_wrapper( self.strategy.confirm_trade_entry, default_retval=True)( pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, @@ -684,18 +690,9 @@ class FreqtradeBot(LoggingMixin): amount = safe_value_fallback(order, 'filled', 'amount') enter_limit_filled_price = safe_value_fallback(order, 'average', 'price') - # TODO: this might be unnecessary, as we're calling it in update_trade_state. - isolated_liq = self.exchange.get_liquidation_price( - leverage=leverage, - pair=pair, - amount=amount, - open_rate=enter_limit_filled_price, - is_short=is_short - ) - interest_rate = self.exchange.get_interest_rate() - # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker') + base_currency = self.exchange.get_pair_base_currency(pair) open_date = datetime.now(timezone.utc) funding_fees = self.exchange.get_funding_fees( pair=pair, amount=amount, is_short=is_short, open_date=open_date) @@ -703,6 +700,8 @@ class FreqtradeBot(LoggingMixin): if trade is None: trade = Trade( pair=pair, + base_currency=base_currency, + stake_currency=self.config['stake_currency'], stake_amount=stake_amount, amount=amount, is_open=True, @@ -719,8 +718,6 @@ class FreqtradeBot(LoggingMixin): timeframe=timeframe_to_minutes(self.config['timeframe']), leverage=leverage, is_short=is_short, - interest_rate=interest_rate, - liquidation_price=isolated_liq, trading_mode=self.trading_mode, funding_fees=funding_fees ) @@ -732,6 +729,7 @@ class FreqtradeBot(LoggingMixin): trade.open_order_id = order_id trade.orders.append(order_obj) + trade.recalc_trade_from_orders() Trade.query.session.add(trade) Trade.commit() @@ -747,8 +745,8 @@ class FreqtradeBot(LoggingMixin): else: logger.info(f"DCA order {order_status}, will wait for resolution: {trade}") - # Update fees if order is non-opened - if order_status in constants.NON_OPEN_EXCHANGE_STATES: + # Update fees if order is closed + if order_status == 'closed': self.update_trade_state(trade, order_id, order) return True @@ -1396,7 +1394,8 @@ class FreqtradeBot(LoggingMixin): default_retval=proposed_limit_rate)( pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc), - proposed_rate=proposed_limit_rate, current_profit=current_profit) + proposed_rate=proposed_limit_rate, current_profit=current_profit, + exit_tag=exit_check.exit_reason) limit = self.get_valid_price(custom_exit_price, proposed_limit_rate) @@ -1644,21 +1643,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, sub_trade=sub_trade, order=order_obj) - self.handle_protections(trade.pair) + self.handle_protections(trade.pair, trade.trade_direction) elif send_msg and not trade.open_order_id: # Enter fill self._notify_enter(trade, order_obj, fill=True, sub_trade=sub_trade) 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/leverage/interest.py b/freqtrade/leverage/interest.py index ff375b05e..367df5821 100644 --- a/freqtrade/leverage/interest.py +++ b/freqtrade/leverage/interest.py @@ -31,13 +31,13 @@ def interest( """ exchange_name = exchange_name.lower() if exchange_name == "binance": - return borrowed * rate * ceil(hours)/twenty_four + return borrowed * rate * ceil(hours) / twenty_four elif exchange_name == "kraken": # Rounded based on https://kraken-fees-calculator.github.io/ - return borrowed * rate * (one+ceil(hours/four)) + return borrowed * rate * (one + ceil(hours / four)) elif exchange_name == "ftx": # As Explained under #Interest rates section in # https://help.ftx.com/hc/en-us/articles/360053007671-Spot-Margin-Trading-Explainer - return borrowed * rate * ceil(hours)/twenty_four + return borrowed * rate * ceil(hours) / twenty_four else: raise OperationalException(f"Leverage not available on {exchange_name} with freqtrade") diff --git a/freqtrade/misc.py b/freqtrade/misc.py index acc7fc2e4..c3968e61c 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -2,13 +2,11 @@ Various tool function for Freqtrade and scripts """ import gzip -import hashlib import logging import re -from copy import deepcopy from datetime import datetime from pathlib import Path -from typing import Any, Iterator, List, Union +from typing import Any, Iterator, List from typing.io import IO from urllib.parse import urlparse @@ -86,6 +84,22 @@ def file_dump_json(filename: Path, data: Any, is_zip: bool = False, log: bool = logger.debug(f'done json to "{filename}"') +def file_dump_joblib(filename: Path, data: Any, log: bool = True) -> None: + """ + Dump object data into a file + :param filename: file to create + :param data: Object data to save + :return: + """ + import joblib + + if log: + logger.info(f'dumping joblib to "{filename}"') + with open(filename, 'wb') as fp: + joblib.dump(data, fp) + logger.debug(f'done joblib dump to "{filename}"') + + def json_load(datafile: IO) -> Any: """ load data with rapidjson @@ -126,7 +140,7 @@ def format_ms_time(date: int) -> str: convert MS date to readable format. : epoch-string in ms """ - return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S') + return datetime.fromtimestamp(date / 1000.0).strftime('%Y-%m-%dT%H:%M:%S') def deep_merge_dicts(source, destination, allow_null_overrides: bool = True): @@ -235,34 +249,3 @@ def parse_db_uri_for_logging(uri: str): return uri pwd = parsed_db_uri.netloc.split(':')[1].split('@')[0] return parsed_db_uri.geturl().replace(f':{pwd}@', ':*****@') - - -def get_strategy_run_id(strategy) -> str: - """ - Generate unique identification hash for a backtest run. Identical config and strategy file will - always return an identical hash. - :param strategy: strategy object. - :return: hex string id. - """ - digest = hashlib.sha1() - config = deepcopy(strategy.config) - - # Options that have no impact on results of individual backtest. - not_important_keys = ('strategy_list', 'original_config', 'telegram', 'api_server') - for k in not_important_keys: - if k in config: - del config[k] - - # Explicitly allow NaN values (e.g. max_open_trades). - # as it does not matter for getting the hash. - digest.update(rapidjson.dumps(config, default=str, - number_mode=rapidjson.NM_NAN).encode('utf-8')) - with open(strategy.__file__, 'rb') as fp: - digest.update(fp.read()) - return digest.hexdigest().lower() - - -def get_backtest_metadata_filename(filename: Union[Path, str]) -> Path: - """Return metadata filename for specified backtest results file.""" - filename = Path(filename) - return filename.parent / Path(f'{filename.stem}.meta{filename.suffix}') diff --git a/freqtrade/optimize/backtest_caching.py b/freqtrade/optimize/backtest_caching.py new file mode 100644 index 000000000..d9d270072 --- /dev/null +++ b/freqtrade/optimize/backtest_caching.py @@ -0,0 +1,40 @@ +import hashlib +from copy import deepcopy +from pathlib import Path +from typing import Union + +import rapidjson + + +def get_strategy_run_id(strategy) -> str: + """ + Generate unique identification hash for a backtest run. Identical config and strategy file will + always return an identical hash. + :param strategy: strategy object. + :return: hex string id. + """ + digest = hashlib.sha1() + config = deepcopy(strategy.config) + + # Options that have no impact on results of individual backtest. + not_important_keys = ('strategy_list', 'original_config', 'telegram', 'api_server') + for k in not_important_keys: + if k in config: + del config[k] + + # Explicitly allow NaN values (e.g. max_open_trades). + # as it does not matter for getting the hash. + digest.update(rapidjson.dumps(config, default=str, + number_mode=rapidjson.NM_NAN).encode('utf-8')) + # Include _ft_params_from_file - so changing parameter files cause cache eviction + digest.update(rapidjson.dumps( + strategy._ft_params_from_file, default=str, number_mode=rapidjson.NM_NAN).encode('utf-8')) + with open(strategy.__file__, 'rb') as fp: + digest.update(fp.read()) + return digest.hexdigest().lower() + + +def get_backtest_metadata_filename(filename: Union[Path, str]) -> Path: + """Return metadata filename for specified backtest results file.""" + filename = Path(filename) + return filename.parent / Path(f'{filename.stem}.meta{filename.suffix}') diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 57dffbdc8..7473b35ab 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -9,6 +9,7 @@ from copy import deepcopy from datetime import datetime, timedelta, timezone from typing import Any, Dict, List, Optional, Tuple +import pandas as pd from numpy import nan from pandas import DataFrame @@ -19,13 +20,15 @@ from freqtrade.data import history from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe from freqtrade.data.converter import trim_dataframe, trim_dataframes from freqtrade.data.dataprovider import DataProvider -from freqtrade.enums import BacktestState, CandleType, ExitCheckTuple, ExitType, TradingMode +from freqtrade.enums import (BacktestState, CandleType, ExitCheckTuple, ExitType, RunMode, + TradingMode) from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds -from freqtrade.misc import get_strategy_run_id from freqtrade.mixins import LoggingMixin +from freqtrade.optimize.backtest_caching import get_strategy_run_id from freqtrade.optimize.bt_progress import BTProgress from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results, + store_backtest_signal_candles, store_backtest_stats) from freqtrade.persistence import LocalTrade, Order, PairLocks, Trade from freqtrade.plugins.pairlistmanager import PairListManager @@ -51,6 +54,11 @@ ESHORT_IDX = 8 # Exit short ENTER_TAG_IDX = 9 EXIT_TAG_IDX = 10 +# Every change to this headers list must evaluate further usages of the resulting tuple +# and eventually change the constants for indexes at the top +HEADERS = ['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long', + 'enter_short', 'exit_short', 'enter_tag', 'exit_tag'] + class Backtesting: """ @@ -73,6 +81,8 @@ class Backtesting: self.run_ids: Dict[str, str] = {} self.strategylist: List[IStrategy] = [] self.all_results: Dict[str, Dict] = {} + self.processed_dfs: Dict[str, Dict] = {} + self._exchange_name = self.config['exchange']['name'] self.exchange = ExchangeResolver.load_exchange(self._exchange_name, self.config) self.dataprovider = DataProvider(self.config, self.exchange) @@ -174,9 +184,10 @@ class Backtesting: # Attach Wallets to Strategy baseclass strategy.wallets = self.wallets # Set stoploss_on_exchange to false for backtesting, - # since a "perfect" stoploss-sell is assumed anyway + # 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() def _load_protections(self, strategy: IStrategy): if self.config.get('enable_protections', False): @@ -259,10 +270,18 @@ class Backtesting: candle_type=CandleType.from_string(self.exchange._ft_has["mark_ohlcv_price"]) ) # Combine data to avoid combining the data per trade. + unavailable_pairs = [] for pair in self.pairlists.whitelist: + 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"]) + if unavailable_pairs: + raise OperationalException( + f"Pairs {', '.join(unavailable_pairs)} got no leverage tiers available. " + "It is therefore impossible to backtest with this pair at the moment.") else: self.futures_data = {} @@ -300,10 +319,7 @@ class Backtesting: :param processed: a processed dictionary with format {pair, data}, which gets cleared to optimize memory usage! """ - # Every change to this headers list must evaluate further usages of the resulting tuple - # and eventually change the constants for indexes at the top - headers = ['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long', - 'enter_short', 'exit_short', 'enter_tag', 'exit_tag'] + data: Dict = {} self.progress.init_step(BacktestState.CONVERT, len(processed)) @@ -315,7 +331,7 @@ class Backtesting: if not pair_data.empty: # Cleanup from prior runs - pair_data.drop(headers[5:] + ['buy', 'sell'], axis=1, errors='ignore') + pair_data.drop(HEADERS[5:] + ['buy', 'sell'], axis=1, errors='ignore') df_analyzed = self.strategy.advise_exit( self.strategy.advise_entry(pair_data, {'pair': pair}), @@ -328,13 +344,13 @@ class Backtesting: self.dataprovider._set_cached_df( pair, self.timeframe, df_analyzed, self.config['candle_type_def']) - # Create a copy of the dataframe before shifting, that way the buy signal/tag + # Create a copy of the dataframe before shifting, that way the entry signal/tag # remains on the correct candle for callbacks. df_analyzed = df_analyzed.copy() - # To avoid using data from future, we use buy/sell signals shifted + # To avoid using data from future, we use entry/exit signals shifted # from the previous candle - for col in headers[5:]: + for col in HEADERS[5:]: tag_col = col in ('enter_tag', 'exit_tag') if col in df_analyzed.columns: df_analyzed.loc[:, col] = df_analyzed.loc[:, col].replace( @@ -346,27 +362,27 @@ class Backtesting: # Convert from Pandas to list for performance reasons # (Looping Pandas is slow.) - data[pair] = df_analyzed[headers].values.tolist() if not df_analyzed.empty else [] + data[pair] = df_analyzed[HEADERS].values.tolist() if not df_analyzed.empty else [] return data - def _get_close_rate(self, row: Tuple, trade: LocalTrade, sell: ExitCheckTuple, + def _get_close_rate(self, row: Tuple, trade: LocalTrade, exit: ExitCheckTuple, trade_dur: int) -> float: """ Get close rate for backtesting result """ # Special handling if high or low hit STOP_LOSS or ROI - if sell.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS): - return self._get_close_rate_for_stoploss(row, trade, sell, trade_dur) - elif sell.exit_type == (ExitType.ROI): - return self._get_close_rate_for_roi(row, trade, sell, trade_dur) + if exit.exit_type in (ExitType.STOP_LOSS, ExitType.TRAILING_STOP_LOSS): + return self._get_close_rate_for_stoploss(row, trade, exit, trade_dur) + elif exit.exit_type == (ExitType.ROI): + return self._get_close_rate_for_roi(row, trade, exit, trade_dur) else: return row[OPEN_IDX] - def _get_close_rate_for_stoploss(self, row: Tuple, trade: LocalTrade, sell: ExitCheckTuple, + def _get_close_rate_for_stoploss(self, row: Tuple, trade: LocalTrade, exit: ExitCheckTuple, trade_dur: int) -> float: # our stoploss was already lower than candle high, # possibly due to a cancelled trade exit. - # sell at open price. + # exit at open price. is_short = trade.is_short or False leverage = trade.leverage or 1.0 side_1 = -1 if is_short else 1 @@ -380,7 +396,7 @@ class Backtesting: # Special case: trailing triggers within same candle as trade opened. Assume most # pessimistic price movement, which is moving just enough to arm stoploss and # immediately going down to stop price. - if sell.exit_type == ExitType.TRAILING_STOP_LOSS and trade_dur == 0: + if exit.exit_type == ExitType.TRAILING_STOP_LOSS and trade_dur == 0: if ( not self.strategy.use_custom_stoploss and self.strategy.trailing_stop and self.strategy.trailing_only_offset_is_reached @@ -399,7 +415,7 @@ class Backtesting: else: assert stop_rate < row[HIGH_IDX] - # Limit lower-end to candle low to avoid sells below the low. + # Limit lower-end to candle low to avoid exits below the low. # This still remains "worst case" - but "worst realistic case". if is_short: return min(row[HIGH_IDX], stop_rate) @@ -409,7 +425,7 @@ class Backtesting: # Set close_rate to stoploss return trade.stop_loss - def _get_close_rate_for_roi(self, row: Tuple, trade: LocalTrade, sell: ExitCheckTuple, + def _get_close_rate_for_roi(self, row: Tuple, trade: LocalTrade, exit: ExitCheckTuple, trade_dur: int) -> float: is_short = trade.is_short or False leverage = trade.leverage or 1.0 @@ -417,7 +433,7 @@ class Backtesting: roi_entry, roi = self.strategy.min_roi_reached_entry(trade_dur) if roi is not None and roi_entry is not None: if roi == -1 and roi_entry % self.timeframe_min == 0: - # When forceselling with ROI=-1, the roi time will always be equal to trade_dur. + # When force_exiting with ROI=-1, the roi time will always be equal to trade_dur. # If that entry is a multiple of the timeframe (so on candle open) # - we'll use open instead of close return row[OPEN_IDX] @@ -434,7 +450,7 @@ class Backtesting: and roi_entry % self.timeframe_min == 0 and is_new_roi): # new ROI entry came into effect. - # use Open rate if open_rate > calculated sell rate + # use Open rate if open_rate > calculated exit rate return row[OPEN_IDX] if (trade_dur == 0 and ( @@ -457,11 +473,11 @@ class Backtesting: # ROI on opening candles with custom pricing can only # trigger if the entry was at Open or lower wick. # details: https: // github.com/freqtrade/freqtrade/issues/6261 - # If open_rate is < open, only allow sells below the close on red candles. + # If open_rate is < open, only allow exits below the close on red candles. raise ValueError("Opening candle ROI on red candles.") # Use the maximum between close_rate and low as we - # cannot sell outside of a candle. + # cannot exit outside of a candle. # Applies when a new ROI setting comes in place and the whole candle is above that. return min(max(close_rate, row[LOW_IDX]), row[HIGH_IDX]) @@ -509,7 +525,7 @@ class Backtesting: """ Rate is within candle, therefore filled""" return row[LOW_IDX] <= rate <= row[HIGH_IDX] - def _get_sell_trade_entry_for_candle(self, trade: LocalTrade, + def _get_exit_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple) -> Optional[LocalTrade]: # Check if we need to adjust our current positions @@ -521,33 +537,33 @@ class Backtesting: if check_adjust_entry: trade = self._get_adjust_trade_entry_for_candle(trade, row) - sell_candle_time: datetime = row[DATE_IDX].to_pydatetime() + exit_candle_time: datetime = row[DATE_IDX].to_pydatetime() enter = row[SHORT_IDX] if trade.is_short else row[LONG_IDX] - exit_ = row[ESHORT_IDX] if trade.is_short else row[ELONG_IDX] - sell = self.strategy.should_exit( - trade, row[OPEN_IDX], sell_candle_time, # type: ignore - enter=enter, exit_=exit_, + 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 + enter=enter, exit_=exit_sig, low=row[LOW_IDX], high=row[HIGH_IDX] ) - if sell.exit_flag: - trade.close_date = sell_candle_time + if exit_.exit_flag: + trade.close_date = exit_candle_time trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60) try: - close_rate = self._get_close_rate(row, trade, sell, 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 close_rate current_profit = trade.calc_profit_ratio(close_rate) order_type = self.strategy.order_types['exit'] - if sell.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT): - # Custom exit pricing only for sell-signals + if exit_.exit_type in (ExitType.EXIT_SIGNAL, ExitType.CUSTOM_EXIT): + # Custom exit pricing only for exit-signals if order_type == 'limit': close_rate = strategy_safe_wrapper(self.strategy.custom_exit_price, default_retval=close_rate)( pair=trade.pair, trade=trade, - current_time=sell_candle_time, + current_time=exit_candle_time, proposed_rate=close_rate, current_profit=current_profit) # We can't place orders lower than current low. # freqtrade does not support this in live, and the order would fill immediately @@ -562,12 +578,12 @@ class Backtesting: pair=trade.pair, trade=trade, order_type='limit', amount=trade.amount, rate=close_rate, time_in_force=time_in_force, - sell_reason=sell.exit_reason, # deprecated - exit_reason=sell.exit_reason, - current_time=sell_candle_time): + sell_reason=exit_.exit_reason, # deprecated + exit_reason=exit_.exit_reason, + current_time=exit_candle_time): return None - trade.exit_reason = sell.exit_reason + 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 @@ -575,6 +591,7 @@ class Backtesting: 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] @@ -585,14 +602,14 @@ class Backtesting: def _exit_trade(self, trade: LocalTrade, sell_row: Tuple, close_rate: float, amount: float = None) -> Optional[LocalTrade]: self.order_id_counter += 1 - sell_candle_time = sell_row[DATE_IDX].to_pydatetime() + exit_candle_time = sell_row[DATE_IDX].to_pydatetime() order_type = self.strategy.order_types['exit'] amount = amount or trade.amount order = Order( id=self.order_id_counter, ft_trade_id=trade.id, - order_date=sell_candle_time, - order_update_date=sell_candle_time, + order_date=exit_candle_time, + order_update_date=exit_candle_time, ft_is_open=True, ft_pair=trade.pair, order_id=str(self.order_id_counter), @@ -611,8 +628,8 @@ class Backtesting: trade.orders.append(order) return trade - def _get_sell_trade_entry(self, trade: LocalTrade, row: Tuple) -> Optional[LocalTrade]: - sell_candle_time: datetime = row[DATE_IDX].to_pydatetime() + def _get_exit_trade_entry(self, trade: LocalTrade, row: Tuple) -> Optional[LocalTrade]: + exit_candle_time: datetime = row[DATE_IDX].to_pydatetime() if self.trading_mode == TradingMode.FUTURES: trade.funding_fees = self.exchange.calculate_funding_fees( @@ -620,37 +637,35 @@ class Backtesting: amount=trade.amount, is_short=trade.is_short, open_date=trade.open_date_utc, - close_date=sell_candle_time, + close_date=exit_candle_time, ) if self.timeframe_detail and trade.pair in self.detail_data: - sell_candle_end = sell_candle_time + timedelta(minutes=self.timeframe_min) + exit_candle_end = exit_candle_time + timedelta(minutes=self.timeframe_min) detail_data = self.detail_data[trade.pair] detail_data = detail_data.loc[ - (detail_data['date'] >= sell_candle_time) & - (detail_data['date'] < sell_candle_end) + (detail_data['date'] >= exit_candle_time) & + (detail_data['date'] < exit_candle_end) ].copy() if len(detail_data) == 0: # Fall back to "regular" data if no detail data was found for this candle - return self._get_sell_trade_entry_for_candle(trade, row) + return self._get_exit_trade_entry_for_candle(trade, row) detail_data.loc[:, 'enter_long'] = row[LONG_IDX] detail_data.loc[:, 'exit_long'] = row[ELONG_IDX] detail_data.loc[:, 'enter_short'] = row[SHORT_IDX] detail_data.loc[:, 'exit_short'] = row[ESHORT_IDX] detail_data.loc[:, 'enter_tag'] = row[ENTER_TAG_IDX] detail_data.loc[:, 'exit_tag'] = row[EXIT_TAG_IDX] - headers = ['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long', - 'enter_short', 'exit_short', 'enter_tag', 'exit_tag'] - for det_row in detail_data[headers].values.tolist(): - res = self._get_sell_trade_entry_for_candle(trade, det_row) + for det_row in detail_data[HEADERS].values.tolist(): + res = self._get_exit_trade_entry_for_candle(trade, det_row) if res: return res return None else: - return self._get_sell_trade_entry_for_candle(trade, row) + 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], @@ -665,7 +680,7 @@ class Backtesting: proposed_rate=propose_rate, entry_tag=entry_tag, side=direction, ) # default value is the open rate - # We can't place orders higher than current high (otherwise it'd be a stop limit buy) + # We can't place orders higher than current high (otherwise it'd be a stop limit entry) # which freqtrade does not support in live. if direction == "short": propose_rate = max(propose_rate, row[LOW_IDX]) @@ -746,6 +761,7 @@ class Backtesting: if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): self.order_id_counter += 1 + base_currency = self.exchange.get_pair_base_currency(pair) amount = round((stake_amount / propose_rate) * leverage, 8) is_short = (direction == 'short') # Necessary for Margin trading. Disabled until support is enabled. @@ -758,6 +774,8 @@ class Backtesting: id=self.trade_id_counter, open_order_id=self.order_id_counter, pair=pair, + base_currency=base_currency, + stake_currency=self.config['stake_currency'], open_rate=propose_rate, open_rate_requested=propose_rate, open_date=current_time, @@ -826,13 +844,13 @@ class Backtesting: if len(open_trades[pair]) > 0: for trade in open_trades[pair]: if trade.open_order_id and trade.nr_of_successful_entries == 0: - # Ignore trade if buy-order did not fill yet + # Ignore trade if entry-order did not fill yet continue - sell_row = data[pair][-1] + exit_row = data[pair][-1] - trade.close_date = sell_row[DATE_IDX].to_pydatetime() + trade.close_date = exit_row[DATE_IDX].to_pydatetime() trade.exit_reason = ExitType.FORCE_EXIT.value - trade.close(sell_row[OPEN_IDX], show_msg=False) + trade.close(exit_row[OPEN_IDX], show_msg=False) LocalTrade.close_bt_trade(trade) # Deepcopy object to have wallets update correctly trade1 = deepcopy(trade) @@ -862,10 +880,11 @@ 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: """ @@ -882,7 +901,7 @@ class Backtesting: # Remove trade due to entry timeout expiration. return True else: - # Close additional buy order + # Close additional entry order del trade.orders[trade.orders.index(order)] if order.side == trade.exit_side: self.timedout_exit_orders += 1 @@ -895,7 +914,7 @@ class Backtesting: self, data: Dict, pair: str, row_index: int, current_time: datetime) -> Optional[Tuple]: try: # Row is treated as "current incomplete candle". - # Buy / sell signals are shifted by 1 to compensate for this. + # entry / exit signals are shifted by 1 to compensate for this. row = data[pair][row_index] except IndexError: # missing Data for one pair at the end. @@ -960,14 +979,14 @@ class Backtesting: self.dataprovider._set_dataframe_max_index(row_index) for t in list(open_trades[pair]): - # 1. Cancel expired buy/sell orders. + # 1. Cancel expired entry/exit orders. if self.check_order_cancel(t, current_time): - # Close trade due to buy timeout expiration. + # Close trade due to entry timeout expiration. open_trade_count -= 1 open_trades[pair].remove(t) self.wallets.update() - # 2. Process buys. + # 2. Process entries. # without positionstacking, we can only have one open trade per pair. # max_open_trades must be respected # don't open on the last row @@ -977,13 +996,13 @@ 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: # TODO: hacky workaround to avoid opening > max_open_trades # This emulates previous behavior - not sure if this is correct - # Prevents buying if the trade-slot was freed in this candle + # Prevents entering if the trade-slot was freed in this candle open_trade_count_start += 1 open_trade_count += 1 # logger.debug(f"{pair} - Emulate creation of new trade: {trade}.") @@ -998,11 +1017,11 @@ class Backtesting: LocalTrade.add_bt_trade(trade) self.wallets.update() - # 4. Create sell orders (if any) + # 4. Create exit orders (if any) if not trade.open_order_id: - self._get_sell_trade_entry(trade, row) # Place sell order if necessary + self._get_exit_trade_entry(trade, row) # Place exit order if necessary - # 5. Process sell orders. + # 5. Process exit orders. order = trade.select_order(trade.exit_side, is_open=True) if order and self._get_order_filled(order.price, row): trade.open_order_id = None @@ -1015,13 +1034,14 @@ class Backtesting: trade.close_date = current_time trade.close(order.price, show_msg=False) - # logger.debug(f"{pair} - Backtesting sell {trade}") + # logger.debug(f"{pair} - Backtesting exit {trade}") open_trade_count -= 1 open_trades[pair].remove(trade) LocalTrade.close_bt_trade(trade) trades.append(trade) - self.run_protections(enable_protections, pair, current_time) self.wallets.update() + self.run_protections( + enable_protections, pair, current_time, trade.trade_direction) # Move time one configured time_interval ahead. self.progress.increment() @@ -1045,7 +1065,7 @@ class Backtesting: timerange: TimeRange): self.progress.init_step(BacktestState.ANALYZE, 0) - logger.info("Running backtesting for Strategy %s", strat.get_strategy_name()) + logger.info(f"Running backtesting for Strategy {strat.get_strategy_name()}") backtest_start_time = datetime.now(timezone.utc) self._set_strategy(strat) @@ -1071,7 +1091,7 @@ class Backtesting: "No data left after adjusting for startup candles.") # Use preprocessed_tmp for date generation (the trimmed dataframe). - # Backtesting will re-trim the dataframes after buy/sell signal generation. + # Backtesting will re-trim the dataframes after entry/exit signal generation. min_date, max_date = history.get_timerange(preprocessed_tmp) logger.info(f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} ' @@ -1093,8 +1113,31 @@ class Backtesting: }) self.all_results[self.strategy.get_strategy_name()] = results + if (self.config.get('export', 'none') == 'signals' and + self.dataprovider.runmode == RunMode.BACKTEST): + self._generate_trade_signal_candles(preprocessed_tmp, results) + return min_date, max_date + def _generate_trade_signal_candles(self, preprocessed_df, bt_results): + signal_candles_only = {} + for pair in preprocessed_df.keys(): + signal_candles_only_df = DataFrame() + + pairdf = preprocessed_df[pair] + resdf = bt_results['results'] + pairresults = resdf.loc[(resdf["pair"] == pair)] + + if pairdf.shape[0] > 0: + for t, v in pairresults.open_date.items(): + allinds = pairdf.loc[(pairdf['date'] < v)] + signal_inds = allinds.iloc[[-1]] + signal_candles_only_df = pd.concat([signal_candles_only_df, signal_inds]) + + signal_candles_only[pair] = signal_candles_only_df + + self.processed_dfs[self.strategy.get_strategy_name()] = signal_candles_only + def _get_min_cached_backtest_date(self): min_backtest_date = None backtest_cache_age = self.config.get('backtest_cache', constants.BACKTEST_CACHE_DEFAULT) @@ -1153,9 +1196,13 @@ class Backtesting: else: self.results = results - if self.config.get('export', 'none') == 'trades': + if self.config.get('export', 'none') in ('trades', 'signals'): store_backtest_stats(self.config['exportfilename'], self.results) + if (self.config.get('export', 'none') == 'signals' and + self.dataprovider.runmode == RunMode.BACKTEST): + store_backtest_signal_candles(self.config['exportfilename'], self.processed_dfs) + # Results may be mixed up now. Sort them so they follow --strategy-list order. if 'strategy_list' in self.config and len(self.results) > 0: self.results['strategy_comparison'] = sorted( diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index cc9bafb0b..30eabecd0 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -44,6 +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() 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 eb8f7ec94..1dafb483c 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -10,7 +10,7 @@ import warnings from datetime import datetime, timezone from math import ceil from pathlib import Path -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple import progressbar import rapidjson @@ -290,7 +290,7 @@ class Hyperopt: self.assign_params(params_dict, 'protection') if HyperoptTools.has_space(self.config, 'roi'): - self.backtesting.strategy.minimal_roi = ( # type: ignore + self.backtesting.strategy.minimal_roi = ( self.custom_hyperopt.generate_roi_table(params_dict)) if HyperoptTools.has_space(self.config, 'stoploss'): @@ -409,6 +409,51 @@ class Hyperopt: # Store non-trimmed data - will be trimmed after signal generation. dump(preprocessed, self.data_pickle_file) + def get_asked_points(self, n_points: int) -> Tuple[List[List[Any]], List[bool]]: + """ + Enforce points returned from `self.opt.ask` have not been already evaluated + + Steps: + 1. Try to get points using `self.opt.ask` first + 2. Discard the points that have already been evaluated + 3. Retry using `self.opt.ask` up to 3 times + 4. If still some points are missing in respect to `n_points`, random sample some points + 5. Repeat until at least `n_points` points in the `asked_non_tried` list + 6. Return a list with length truncated at `n_points` + """ + def unique_list(a_list): + new_list = [] + for item in a_list: + if item not in new_list: + new_list.append(item) + return new_list + i = 0 + asked_non_tried: List[List[Any]] = [] + is_random: List[bool] = [] + while i < 5 and len(asked_non_tried) < n_points: + if i < 3: + self.opt.cache_ = {} + asked = unique_list(self.opt.ask(n_points=n_points * 5)) + is_random = [False for _ in range(len(asked))] + else: + asked = unique_list(self.opt.space.rvs(n_samples=n_points * 5)) + is_random = [True for _ in range(len(asked))] + is_random += [rand for x, rand in zip(asked, is_random) + if x not in self.opt.Xi + and x not in asked_non_tried] + asked_non_tried += [x for x in asked + if x not in self.opt.Xi + and x not in asked_non_tried] + i += 1 + + if asked_non_tried: + return ( + asked_non_tried[:min(len(asked_non_tried), n_points)], + is_random[:min(len(asked_non_tried), n_points)] + ) + else: + return self.opt.ask(n_points=n_points), [False for _ in range(n_points)] + def start(self) -> None: self.random_state = self._set_random_state(self.config.get('hyperopt_random_state', None)) logger.info(f"Using optimizer random state: {self.random_state}") @@ -420,9 +465,10 @@ class Hyperopt: # We don't need exchange instance anymore while running hyperopt self.backtesting.exchange.close() - self.backtesting.exchange._api = None # type: ignore - self.backtesting.exchange._api_async = None # type: ignore + self.backtesting.exchange._api = None + self.backtesting.exchange._api_async = None self.backtesting.exchange.loop = None # type: ignore + self.backtesting.exchange._loop_lock = None # type: ignore # self.backtesting.exchange = None # type: ignore self.backtesting.pairlists = None # type: ignore @@ -473,7 +519,7 @@ class Hyperopt: n_rest = (i + 1) * jobs - self.total_epochs current_jobs = jobs - n_rest if n_rest > 0 else jobs - asked = self.opt.ask(n_points=current_jobs) + asked, is_random = self.get_asked_points(n_points=current_jobs) f_val = self.run_optimizer_parallel(parallel, asked, i) self.opt.tell(asked, [v['loss'] for v in f_val]) @@ -492,6 +538,7 @@ class Hyperopt: # evaluations can take different time. Here they are aligned in the # order they will be shown to the user. val['is_best'] = is_best + val['is_random'] = is_random[j] self.print_results(val) if is_best: diff --git a/freqtrade/optimize/hyperopt_loss_calmar.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_calmar.py similarity index 96% rename from freqtrade/optimize/hyperopt_loss_calmar.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_calmar.py index 846dae9ea..ea6c151e5 100644 --- a/freqtrade/optimize/hyperopt_loss_calmar.py +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_calmar.py @@ -10,7 +10,7 @@ from typing import Any, Dict from pandas import DataFrame -from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.optimize.hyperopt import IHyperOptLoss diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py similarity index 95% rename from freqtrade/optimize/hyperopt_loss_max_drawdown.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py index ce955d928..a8af704cd 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_max_drawdown.py @@ -8,7 +8,7 @@ from datetime import datetime from pandas import DataFrame -from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.optimize.hyperopt import IHyperOptLoss 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_onlyprofit.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_onlyprofit.py similarity index 100% rename from freqtrade/optimize/hyperopt_loss_onlyprofit.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_onlyprofit.py diff --git a/freqtrade/optimize/hyperopt_loss_profit_drawdown.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py similarity index 93% rename from freqtrade/optimize/hyperopt_loss_profit_drawdown.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py index 5bd12ff52..ed689edba 100644 --- a/freqtrade/optimize/hyperopt_loss_profit_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_profit_drawdown.py @@ -9,7 +9,7 @@ individual needs. """ from pandas import DataFrame -from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.data.metrics import calculate_max_drawdown from freqtrade.optimize.hyperopt import IHyperOptLoss diff --git a/freqtrade/optimize/hyperopt_loss_sharpe.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_sharpe.py similarity index 100% rename from freqtrade/optimize/hyperopt_loss_sharpe.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_sharpe.py diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_daily.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_sharpe_daily.py similarity index 100% rename from freqtrade/optimize/hyperopt_loss_sharpe_daily.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_sharpe_daily.py diff --git a/freqtrade/optimize/hyperopt_loss_short_trade_dur.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_short_trade_dur.py similarity index 100% rename from freqtrade/optimize/hyperopt_loss_short_trade_dur.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_short_trade_dur.py diff --git a/freqtrade/optimize/hyperopt_loss_sortino.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_sortino.py similarity index 100% rename from freqtrade/optimize/hyperopt_loss_sortino.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_sortino.py diff --git a/freqtrade/optimize/hyperopt_loss_sortino_daily.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_sortino_daily.py similarity index 100% rename from freqtrade/optimize/hyperopt_loss_sortino_daily.py rename to freqtrade/optimize/hyperopt_loss/hyperopt_loss_sortino_daily.py 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/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 8c84f772a..0421e6e38 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -41,7 +41,8 @@ class HyperoptTools(): """ from freqtrade.resolvers.strategy_resolver import StrategyResolver directory = Path(config.get('strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) - strategy_objs = StrategyResolver.search_all_objects(directory, False) + strategy_objs = StrategyResolver.search_all_objects( + directory, False, config.get('recursive_strategy_search', False)) strategies = [s for s in strategy_objs if s['name'] == strategy_name] if strategies: strategy = strategies[0] @@ -310,6 +311,8 @@ class HyperoptTools(): if not has_drawdown: # Ensure compatibility with older versions of hyperopt results trials['results_metrics.max_drawdown_account'] = None + if 'is_random' not in trials.columns: + trials['is_random'] = False # New mode, using backtest result for metrics trials['results_metrics.winsdrawslosses'] = trials.apply( @@ -322,12 +325,12 @@ class HyperoptTools(): 'results_metrics.profit_total', 'results_metrics.holding_avg', 'results_metrics.max_drawdown', 'results_metrics.max_drawdown_account', 'results_metrics.max_drawdown_abs', - 'loss', 'is_initial_point', 'is_best']] + 'loss', 'is_initial_point', 'is_random', 'is_best']] trials.columns = [ 'Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit', 'Total profit', 'Profit', 'Avg duration', 'max_drawdown', 'max_drawdown_account', - 'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best' + 'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_random', 'is_best' ] return trials @@ -349,9 +352,11 @@ class HyperoptTools(): trials = HyperoptTools.prepare_trials_columns(trials, has_account_drawdown) trials['is_profit'] = False - trials.loc[trials['is_initial_point'], 'Best'] = '* ' + trials.loc[trials['is_initial_point'] | trials['is_random'], 'Best'] = '* ' trials.loc[trials['is_best'], 'Best'] = 'Best' - trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best' + trials.loc[ + (trials['is_initial_point'] | trials['is_random']) & trials['is_best'], + 'Best'] = '* Best' trials.loc[trials['Total profit'] > 0, 'is_profit'] = True trials['Trades'] = trials['Trades'].astype(str) # perc_multi = 1 if legacy_mode else 100 @@ -390,8 +395,8 @@ class HyperoptTools(): lambda x: '{} {}'.format( round_coin_value(x['Total profit'], stake_currency, keep_trailing_zeros=True), f"({x['Profit']:,.2%})".rjust(10, ' ') - ).rjust(25+len(stake_currency)) - if x['Total profit'] != 0.0 else '--'.rjust(25+len(stake_currency)), + ).rjust(25 + len(stake_currency)) + if x['Total profit'] != 0.0 else '--'.rjust(25 + len(stake_currency)), axis=1 ) trials = trials.drop(columns=['Total profit']) @@ -399,15 +404,15 @@ class HyperoptTools(): if print_colorized: for i in range(len(trials)): if trials.loc[i]['is_profit']: - for j in range(len(trials.loc[i])-3): + for j in range(len(trials.loc[i]) - 3): trials.iat[i, j] = "{}{}{}".format(Fore.GREEN, str(trials.loc[i][j]), Fore.RESET) if trials.loc[i]['is_best'] and highlight_best: - for j in range(len(trials.loc[i])-3): + for j in range(len(trials.loc[i]) - 3): trials.iat[i, j] = "{}{}{}".format(Style.BRIGHT, str(trials.loc[i][j]), Style.RESET_ALL) - trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit']) + trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit', 'is_random']) if remove_header > 0: table = tabulate.tabulate( trials.to_dict(orient='list'), tablefmt='orgtbl', @@ -459,7 +464,7 @@ class HyperoptTools(): 'loss', 'is_initial_point', 'is_best'] perc_multi = 100 - param_metrics = [("params_dict."+param) for param in results[0]['params_dict'].keys()] + param_metrics = [("params_dict." + param) for param in results[0]['params_dict'].keys()] trials = trials[base_metrics + param_metrics] base_columns = ['Best', 'Epoch', 'Trades', 'Avg profit', 'Median profit', 'Total profit', diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index c08fa07a1..42db366a1 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -9,10 +9,10 @@ from pandas import DataFrame, to_datetime from tabulate import tabulate from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT -from freqtrade.data.btanalysis import (calculate_csum, calculate_market_change, - calculate_max_drawdown) -from freqtrade.misc import (decimals_per_coin, file_dump_json, get_backtest_metadata_filename, - round_coin_value) +from freqtrade.data.metrics import (calculate_cagr, calculate_csum, calculate_market_change, + calculate_max_drawdown) +from freqtrade.misc import decimals_per_coin, file_dump_joblib, file_dump_json, round_coin_value +from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename logger = logging.getLogger(__name__) @@ -45,6 +45,29 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N file_dump_json(latest_filename, {'latest_backtest': str(filename.name)}) +def store_backtest_signal_candles(recordfilename: Path, candles: Dict[str, Dict]) -> Path: + """ + Stores backtest trade signal candles + :param recordfilename: Path object, which can either be a filename or a directory. + Filenames will be appended with a timestamp right before the suffix + while for directories, /backtest-result-_signals.pkl will be used + as filename + :param stats: Dict containing the backtesting signal candles + """ + if recordfilename.is_dir(): + filename = (recordfilename / + f'backtest-result-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}_signals.pkl') + else: + filename = Path.joinpath( + recordfilename.parent, + f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}_signals.pkl' + ) + + file_dump_joblib(filename, candles) + + return filename + + def _get_line_floatfmt(stake_currency: str) -> List[str]: """ Generate floatformat (goes in line with _generate_result_line()) @@ -241,7 +264,7 @@ def generate_edge_table(results: dict) -> str: # Ignore type as floatfmt does allow tuples but mypy does not know that return tabulate(tabular_data, headers=headers, - floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore + floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") def _get_resample_from_period(period: str) -> str: @@ -423,6 +446,7 @@ def generate_strategy_stats(pairlist: List[str], 'profit_total_abs': results['profit_abs'].sum(), 'profit_total_long_abs': results.loc[~results['is_short'], 'profit_abs'].sum(), 'profit_total_short_abs': results.loc[results['is_short'], 'profit_abs'].sum(), + 'cagr': calculate_cagr(backtest_days, start_balance, content['final_balance']), 'backtest_start': min_date.strftime(DATETIME_PRINT_FORMAT), 'backtest_start_ts': int(min_date.timestamp() * 1000), 'backtest_end': max_date.strftime(DATETIME_PRINT_FORMAT), @@ -474,9 +498,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, @@ -497,6 +524,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, @@ -705,6 +733,26 @@ 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']), + ]) + # 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. @@ -723,6 +771,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Absolute profit ', round_coin_value(strat_results['profit_total_abs'], strat_results['stake_currency'])), ('Total profit %', f"{strat_results['profit_total']:.2%}"), + ('CAGR %', f"{strat_results['cagr']:.2%}" if 'cagr' in strat_results else 'N/A'), ('Trades per day', strat_results['trades_per_day']), ('Avg. daily profit %', f"{(strat_results['profit_total'] / strat_results['backtest_days']):.2%}"), @@ -759,18 +808,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/migrations.py b/freqtrade/persistence/migrations.py index 9521eae69..03f3c3fb9 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -3,11 +3,13 @@ from typing import List from sqlalchemy import inspect, text +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)] @@ -19,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}' @@ -54,10 +56,22 @@ def set_sequence_ids(engine, order_id, trade_id): connection.execute(text(f"ALTER SEQUENCE trades_id_seq RESTART WITH {trade_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( decl_base, inspector, engine, trade_back_name: str, cols: List, order_back_name: str, cols_order: List): + base_currency = get_column_def(cols, 'base_currency', 'null') + stake_currency = get_column_def(cols, 'stake_currency', 'null') fee_open = get_column_def(cols, 'fee_open', 'fee') fee_open_cost = get_column_def(cols, 'fee_open_cost', 'null') fee_open_currency = get_column_def(cols, 'fee_open_currency', 'null') @@ -112,13 +126,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) @@ -130,7 +138,7 @@ def migrate_trades_and_orders_table( # Copy data back - following the correct schema with engine.begin() as connection: connection.execute(text(f"""insert into trades - (id, exchange, pair, is_open, + (id, exchange, pair, base_currency, stake_currency, is_open, fee_open, fee_open_cost, fee_open_currency, fee_close, fee_close_cost, fee_close_currency, open_rate, open_rate_requested, close_rate, close_rate_requested, close_profit, @@ -142,7 +150,8 @@ def migrate_trades_and_orders_table( trading_mode, leverage, liquidation_price, is_short, interest_rate, funding_fees ) - select id, lower(exchange), pair, + select id, lower(exchange), pair, {base_currency} base_currency, + {stake_currency} stake_currency, is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost, {fee_open_currency} fee_open_currency, {fee_close} fee_close, {fee_close_cost} fee_close_cost, {fee_close_currency} fee_close_currency, @@ -154,10 +163,10 @@ def migrate_trades_and_orders_table( {initial_stop_loss_pct} initial_stop_loss_pct, {stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update, {max_rate} max_rate, {min_rate} min_rate, - case when {exit_reason} == 'sell_signal' then 'exit_signal' - when {exit_reason} == 'custom_sell' then 'custom_exit' - when {exit_reason} == 'force_sell' then 'force_exit' - when {exit_reason} == 'emergency_sell' then 'emergency_exit' + case when {exit_reason} = 'sell_signal' then 'exit_signal' + when {exit_reason} = 'custom_sell' then 'custom_exit' + when {exit_reason} = 'force_sell' then 'force_exit' + when {exit_reason} = 'emergency_sell' then 'emergency_exit' else {exit_reason} end exit_reason, {exit_order_status} exit_order_status, @@ -173,23 +182,6 @@ def migrate_trades_and_orders_table( set_sequence_ids(engine, order_id, trade_id) -def migrate_open_orders_to_trades(engine): - with engine.begin() as connection: - connection.execute(text(""" - insert into orders (ft_trade_id, ft_pair, order_id, ft_order_side, ft_is_open) - select id ft_trade_id, pair ft_pair, open_order_id, - case when close_rate_requested is null then 'buy' - else 'sell' end ft_order_side, 1 ft_is_open - from trades - where open_order_id is not null - union all - select id ft_trade_id, pair ft_pair, stoploss_order_id order_id, - 'stoploss' ft_order_side, 1 ft_is_open - from trades - where stoploss_order_id is not null - """)) - - def drop_orders_table(engine, table_back_name: str): # Drop and recreate orders table as backup # This drops foreign keys, too. @@ -207,7 +199,7 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List): # sqlite does not support literals for booleans with engine.begin() as connection: connection.execute(text(f""" - insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, + insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status, symbol, order_type, side, price, amount, filled, average, remaining, cost, order_date, order_filled_date, order_update_date, ft_fee_base) select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, @@ -217,6 +209,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 @@ -230,24 +247,38 @@ def check_migrate(engine, decl_base, previous_tables) -> None: """ inspector = inspect(engine) - cols = inspector.get_columns('trades') + 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! # if ('orders' not in previous_tables # or not has_column(cols_orders, 'leverage')): - if not has_column(cols, 'exit_order_status'): + if not has_column(cols_trades, 'base_currency'): logger.info(f"Running database migration for trades - " f"backup: {table_back_name}, {order_table_bak_name}") migrate_trades_and_orders_table( - decl_base, inspector, engine, table_back_name, cols, order_table_bak_name, cols_orders) + 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: - logger.info('Moving open orders to Orders table.') - migrate_open_orders_to_trades(engine) + raise OperationalException( + "Your database seems to be very old. " + "Please update to freqtrade 2022.3 to migrate this database or " + "start with a fresh database.") + set_sqlite_to_wal(engine) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index cdc189c7e..2d1b3834a 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -8,13 +8,14 @@ from math import isclose from typing import Any, Dict, List, Optional from sqlalchemy import (Boolean, Column, DateTime, Enum, Float, ForeignKey, Integer, String, - create_engine, desc, func, inspect) + create_engine, desc, func, inspect, or_) from sqlalchemy.exc import NoSuchModuleError from sqlalchemy.orm import Query, declarative_base, relationship, scoped_session, sessionmaker from sqlalchemy.pool import StaticPool from sqlalchemy.sql.schema import UniqueConstraint -from freqtrade.constants import DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPEN_EXCHANGE_STATES +from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPEN_EXCHANGE_STATES, + LongShort) from freqtrade.enums import ExitType, TradingMode from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.leverage import interest @@ -281,6 +282,8 @@ class LocalTrade(): 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 @@ -393,12 +396,32 @@ class LocalTrade(): return "sell" @property - def trade_direction(self) -> str: + 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]) @@ -409,12 +432,10 @@ class LocalTrade(): def __repr__(self): open_since = self.open_date.strftime(DATETIME_PRINT_FORMAT) if self.is_open else 'closed' - leverage = self.leverage or 1.0 - is_short = self.is_short or False return ( f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, ' - f'is_short={is_short}, leverage={leverage}, ' + 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})' ) @@ -425,6 +446,8 @@ class LocalTrade(): 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), @@ -1092,6 +1115,8 @@ class Trade(_DECL_BASE, LocalTrade): 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) @@ -1445,6 +1470,8 @@ class PairLock(_DECL_BASE): 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) @@ -1456,11 +1483,12 @@ class PairLock(_DECL_BASE): 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})') + 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) -> Query: + 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 @@ -1471,6 +1499,11 @@ class PairLock(_DECL_BASE): 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 ) @@ -1485,5 +1518,6 @@ class PairLock(_DECL_BASE): '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/plot/plotting.py b/freqtrade/plot/plotting.py index 747248be7..37758d05f 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -5,12 +5,13 @@ from typing import Any, Dict, List, Optional import pandas as pd from freqtrade.configuration import TimeRange -from freqtrade.data.btanalysis import (analyze_trade_parallelism, calculate_max_drawdown, - calculate_underwater, combine_dataframes_with_mean, - create_cum_profit, extract_trades_of_period, load_trades) +from freqtrade.data.btanalysis import (analyze_trade_parallelism, extract_trades_of_period, + load_trades) from freqtrade.data.converter import trim_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import get_timerange, load_data +from freqtrade.data.metrics import (calculate_max_drawdown, calculate_underwater, + combine_dataframes_with_mean, create_cum_profit) from freqtrade.enums import CandleType from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_prev_date, timeframe_to_seconds @@ -158,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], @@ -188,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 @@ -506,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") @@ -530,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", @@ -539,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') @@ -546,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}' @@ -610,6 +633,7 @@ 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() plot_elements = init_plotscript(config, list(exchange.markets), strategy.startup_candle_count) timerange = plot_elements['timerange'] trades = plot_elements['trades'] @@ -668,7 +692,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/VolatilityFilter.py b/freqtrade/plugins/pairlist/VolatilityFilter.py index 7a355c291..6aa857c2c 100644 --- a/freqtrade/plugins/pairlist/VolatilityFilter.py +++ b/freqtrade/plugins/pairlist/VolatilityFilter.py @@ -107,7 +107,7 @@ class VolatilityFilter(IPairList): returns = (np.log(daily_candles.close / daily_candles.close.shift(-1))) returns.fillna(0, inplace=True) - volatility_series = returns.rolling(window=self._days).std()*np.sqrt(self._days) + volatility_series = returns.rolling(window=self._days).std() * np.sqrt(self._days) volatility_avg = volatility_series.mean() if self._min_volatility <= volatility_avg <= self._max_volatility: 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..7d5d6054d 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 @@ -35,7 +36,7 @@ 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) -> Optional[ProtectionReturn]: """ Evaluate recent trades for pair """ @@ -51,7 +52,7 @@ 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) if profit < self._required_profit: @@ -60,20 +61,25 @@ class LowProfitPairs(IProtection): 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), + ) - 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". diff --git a/freqtrade/plugins/protections/max_drawdown_protection.py b/freqtrade/plugins/protections/max_drawdown_protection.py index b6ef92bd5..e0b016cb8 100644 --- a/freqtrade/plugins/protections/max_drawdown_protection.py +++ b/freqtrade/plugins/protections/max_drawdown_protection.py @@ -1,11 +1,12 @@ import logging from datetime import datetime, timedelta -from typing import Any, Dict +from typing import Any, Dict, Optional import pandas as pd -from freqtrade.data.btanalysis import calculate_max_drawdown +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..f9fe039d6 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: str) -> 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/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index e3c234f60..bcfe5e1d8 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -23,7 +23,7 @@ class HyperOptLossResolver(IResolver): object_type = IHyperOptLoss object_type_str = "HyperoptLoss" user_subdir = USERPATH_HYPEROPTS - initial_search_path = Path(__file__).parent.parent.joinpath('optimize').resolve() + initial_search_path = Path(__file__).parent.parent.joinpath('optimize/hyperopt_loss').resolve() @staticmethod def load_hyperoptloss(config: Dict) -> IHyperOptLoss: diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index 3ab461041..74b28dffe 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -44,7 +44,7 @@ class IResolver: @classmethod def build_search_paths(cls, config: Dict[str, Any], user_subdir: Optional[str] = None, - extra_dir: Optional[str] = None) -> List[Path]: + extra_dirs: List[str] = []) -> List[Path]: abs_paths: List[Path] = [] if cls.initial_search_path: @@ -53,9 +53,9 @@ class IResolver: if user_subdir: abs_paths.insert(0, config['user_data_dir'].joinpath(user_subdir)) - if extra_dir: - # Add extra directory to the top of the search paths - abs_paths.insert(0, Path(extra_dir).resolve()) + # Add extra directory to the top of the search paths + for dir in extra_dirs: + abs_paths.insert(0, Path(dir).resolve()) return abs_paths @@ -164,9 +164,13 @@ class IResolver: :return: Object instance or None """ + extra_dirs: List[str] = [] + if extra_dir: + extra_dirs.append(extra_dir) + abs_paths = cls.build_search_paths(config, user_subdir=cls.user_subdir, - extra_dir=extra_dir) + extra_dirs=extra_dirs) found_object = cls._load_object(paths=abs_paths, object_name=object_name, kwargs=kwargs) @@ -178,18 +182,25 @@ class IResolver: ) @classmethod - def search_all_objects(cls, directory: Path, - enum_failed: bool) -> List[Dict[str, Any]]: + def search_all_objects(cls, directory: Path, enum_failed: bool, + recursive: bool = False) -> List[Dict[str, Any]]: """ Searches a directory for valid objects :param directory: Path to search :param enum_failed: If True, will return None for modules which fail. Otherwise, failing modules are skipped. + :param recursive: Recursively walk directory tree searching for strategies :return: List of dicts containing 'name', 'class' and 'location' entries """ logger.debug(f"Searching for {cls.object_type.__name__} '{directory}'") objects = [] for entry in directory.iterdir(): + if ( + recursive and entry.is_dir() + and not entry.name.startswith('__') + and not entry.name.startswith('.') + ): + objects.extend(cls.search_all_objects(entry, enum_failed, recursive=recursive)) # Only consider python files if entry.suffix != '.py': logger.debug('Ignoring %s', entry) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 76515026c..44d590b67 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -7,8 +7,9 @@ import logging import tempfile from base64 import urlsafe_b64decode from inspect import getfullargspec +from os import walk from pathlib import Path -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional from freqtrade.configuration.config_validation import validate_migrated_strategy_settings from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES @@ -216,15 +217,19 @@ class StrategyResolver(IResolver): raise OperationalException( "`populate_exit_trend` or `populate_sell_trend` must be implemented.") - strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) - strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) - strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) + _populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) + _buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) + _sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) if any(x == 2 for x in [ - strategy._populate_fun_len, - strategy._buy_fun_len, - strategy._sell_fun_len + _populate_fun_len, + _buy_fun_len, + _sell_fun_len ]): - strategy.INTERFACE_VERSION = 1 + raise OperationalException( + "Strategy Interface v1 is no longer supported. " + "Please update your strategy to implement " + "`populate_indicators`, `populate_entry_trend` and `populate_exit_trend` " + "with the metadata argument. ") return strategy @staticmethod @@ -237,10 +242,19 @@ class StrategyResolver(IResolver): :param extra_dir: additional directory to search for the given strategy :return: Strategy instance or None """ + if config.get('recursive_strategy_search', False): + extra_dirs: List[str] = [ + path[0] for path in walk(f"{config['user_data_dir']}/{USERPATH_STRATEGIES}") + ] # sub-directories + else: + extra_dirs = [] + + if extra_dir: + extra_dirs.append(extra_dir) abs_paths = StrategyResolver.build_search_paths(config, user_subdir=USERPATH_STRATEGIES, - extra_dir=extra_dir) + extra_dirs=extra_dirs) if ":" in strategy_name: logger.info("loading base64 encoded strategy") diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 757ed8aac..41712632b 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -1,13 +1,16 @@ import asyncio import logging from copy import deepcopy +from typing import Any, Dict, List from fastapi import APIRouter, BackgroundTasks, Depends from freqtrade.configuration.config_validation import validate_config_consistency +from freqtrade.data.btanalysis import get_backtest_resultlist, load_and_merge_backtest_result from freqtrade.enums import BacktestState from freqtrade.exceptions import DependencyException -from freqtrade.rpc.api_server.api_schemas import BacktestRequest, BacktestResponse +from freqtrade.rpc.api_server.api_schemas import (BacktestHistoryEntry, BacktestRequest, + BacktestResponse) from freqtrade.rpc.api_server.deps import get_config, is_webserver_mode from freqtrade.rpc.api_server.webserver import ApiServer from freqtrade.rpc.rpc import RPCException @@ -81,6 +84,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac lastconfig['enable_protections'] = btconfig.get('enable_protections') lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet') + ApiServer._bt.strategylist = [strat] ApiServer._bt.results = {} ApiServer._bt.load_prior_backtest() @@ -200,3 +204,30 @@ def api_backtest_abort(ws_mode=Depends(is_webserver_mode)): "progress": 0, "status_msg": "Backtest ended", } + + +@router.get('/backtest/history', response_model=List[BacktestHistoryEntry], tags=['webserver', 'backtest']) +def api_backtest_history(config=Depends(get_config), ws_mode=Depends(is_webserver_mode)): + # Get backtest result history, read from metadata files + return get_backtest_resultlist(config['user_data_dir'] / 'backtest_results') + + +@router.get('/backtest/history/result', response_model=BacktestResponse, tags=['webserver', 'backtest']) +def api_backtest_history_result(filename: str, strategy: str, config=Depends(get_config), ws_mode=Depends(is_webserver_mode)): + # Get backtest result history, read from metadata files + fn = config['user_data_dir'] / 'backtest_results' / filename + results: Dict[str, Any] = { + 'metadata': {}, + 'strategy': {}, + 'strategy_comparison': [], + } + + load_and_merge_backtest_result(strategy, fn, results) + return { + "status": "ended", + "running": False, + "step": "", + "progress": 1, + "status_msg": "Historic result", + "backtest_result": results, + } diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 03049e0f4..d78ea8b78 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -203,6 +203,8 @@ class OrderSchema(BaseModel): class TradeSchema(BaseModel): trade_id: int pair: str + base_currency: str + quote_currency: str is_open: bool is_short: bool exchange: str @@ -289,6 +291,7 @@ class LockModel(BaseModel): lock_time: str lock_timestamp: int pair: str + side: str reason: str @@ -419,6 +422,13 @@ class BacktestResponse(BaseModel): backtest_result: Optional[Dict[str, Any]] +class BacktestHistoryEntry(BaseModel): + filename: str + strategy: str + run_id: str + backtest_start_time: int + + class SysInfo(BaseModel): cpu_pct: List[float] ram_pct: float diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 69338d665..a8b9873d7 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -35,7 +35,8 @@ logger = logging.getLogger(__name__) # 1.13: forcebuy supports stake_amount # versions 2.xx -> futures/short branch # 2.14: Add entry/exit orders to trade response -API_VERSION = 2.14 +# 2.15: Add backtest history endpoints +API_VERSION = 2.15 # Public API, requires no auth. router_public = APIRouter() @@ -157,7 +158,7 @@ def force_entry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)): # /forcesell is deprecated with short addition. use /forceexit instead @router.post('/forceexit', response_model=ResultMsg, tags=['trading']) @router.post('/forcesell', response_model=ResultMsg, tags=['trading']) -def forcesell(payload: ForceExitPayload, rpc: RPC = Depends(get_rpc)): +def forceexit(payload: ForceExitPayload, rpc: RPC = Depends(get_rpc)): ordertype = payload.ordertype.value if payload.ordertype else None return rpc._rpc_force_exit(payload.tradeid, ordertype) @@ -252,7 +253,8 @@ def list_strategies(config=Depends(get_config)): directory = Path(config.get( 'strategy_path', config['user_data_dir'] / USERPATH_STRATEGIES)) from freqtrade.resolvers.strategy_resolver import StrategyResolver - strategies = StrategyResolver.search_all_objects(directory, False) + strategies = StrategyResolver.search_all_objects( + directory, False, config.get('recursive_strategy_search', False)) strategies = sorted(strategies, key=lambda x: x['name']) return {'strategies': [x['name'] for x in strategies]} diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index 63812f52f..0da129583 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -2,7 +2,7 @@ import logging from ipaddress import IPv4Address from typing import Any, Dict -import rapidjson +import orjson import uvicorn from fastapi import Depends, FastAPI from fastapi.middleware.cors import CORSMiddleware @@ -24,7 +24,7 @@ class FTJSONResponse(JSONResponse): Use rapidjson for responses Handles NaN and Inf / -Inf in a javascript way by default. """ - return rapidjson.dumps(content).encode("utf-8") + return orjson.dumps(content, option=orjson.OPT_SERIALIZE_NUMPY) class ApiServer(RPCHandler): diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 6db1f51de..4cba746b0 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -197,7 +197,6 @@ class RPC: trade_dict = trade.to_json() trade_dict.update(dict( - base_currency=self._freqtrade.config['stake_currency'], close_profit=trade.close_profit if not trade.is_open else None, current_rate=current_rate, current_profit=current_profit, # Deprecated @@ -223,6 +222,7 @@ class RPC: def _rpc_status_table(self, stake_currency: str, fiat_display_currency: str) -> Tuple[List, List, float]: trades: List[Trade] = Trade.get_open_trades() + nonspot = self._config.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT if not trades: raise RPCException('no active trade') else: @@ -237,7 +237,7 @@ class RPC: current_rate = NAN trade_profit = trade.calc_profit(current_rate) profit_str = f'{trade.calc_profit_ratio(current_rate):.2%}' - direction_str = 'S' if trade.is_short else 'L' + direction_str = ('S' if trade.is_short else 'L') if nonspot else '' if self._fiat_converter: fiat_profit = self._fiat_converter.convert_amount( trade_profit, @@ -267,7 +267,11 @@ class RPC: if self._fiat_converter: profitcol += " (" + fiat_display_currency + ")" - columns = ['ID L/S', 'Pair', 'Since', profitcol] + columns = [ + 'ID L/S' if nonspot else 'ID', + 'Pair', + 'Since', + profitcol] if self._config.get('position_adjustment_enable', False): columns.append('# Entries') return trades_list, columns, fiat_profit_sum @@ -686,10 +690,10 @@ class RPC: def _rpc_force_exit(self, trade_id: str, ordertype: Optional[str] = None) -> Dict[str, str]: """ - Handler for forcesell . + Handler for forceexit . Sells the given trade at current price """ - def _exec_forcesell(trade: Trade) -> None: + def _exec_force_exit(trade: Trade) -> None: # Check if there is there is an open order fully_canceled = False if trade.open_order_id: @@ -722,7 +726,7 @@ class RPC: if trade_id == 'all': # Execute sell for all open orders for trade in Trade.get_open_trades(): - _exec_forcesell(trade) + _exec_force_exit(trade) Trade.commit() self._freqtrade.wallets.update() return {'result': 'Created sell orders for all open trades.'} @@ -735,7 +739,7 @@ class RPC: logger.warning('force_exit: Invalid argument received') raise RPCException('invalid argument') - _exec_forcesell(trade) + _exec_force_exit(trade) Trade.commit() self._freqtrade.wallets.update() return {'result': f'Created sell order for trade {trade_id}.'} diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 52286e4c9..9b4900b51 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -103,7 +103,6 @@ class Telegram(RPCHandler): ['/count', '/start', '/stop', '/help'] ] # do not allow commands with mandatory arguments and critical cmds - # like /forcesell and /forcebuy # TODO: DRY! - its not good to list all valid cmds here. But otherwise # this needs refactoring of the whole telegram module (same # problem in _help()). @@ -116,6 +115,7 @@ class Telegram(RPCHandler): r'/logs$', r'/whitelist$', r'/blacklist$', r'/bl_delete$', r'/weekly$', r'/weekly \d+$', r'/monthly$', r'/monthly \d+$', r'/forcebuy$', r'/forcelong$', r'/forceshort$', + r'/forcesell$', r'/forceexit$', r'/edge$', r'/health$', r'/help$', r'/version$'] # Create keys for generation valid_keys_print = [k.replace('$', '') for k in valid_keys] @@ -197,7 +197,8 @@ class Telegram(RPCHandler): pattern='update_exit_reason_performance'), CallbackQueryHandler(self._mix_tag_performance, pattern='update_mix_tag_performance'), CallbackQueryHandler(self._count, pattern='update_count'), - CallbackQueryHandler(self._force_enter_inline), + CallbackQueryHandler(self._force_exit_inline, pattern=r"force_exit__\S+"), + CallbackQueryHandler(self._force_enter_inline, pattern=r"\S+\/\S+"), ] for handle in handles: self._updater.dispatcher.add_handler(handle) @@ -287,7 +288,7 @@ class Telegram(RPCHandler): else "") # Check if all sell properties are available. - # This might not be the case if the message origin is triggered by /forcesell + # This might not be the case if the message origin is triggered by /forceexit if (all(prop in msg for prop in ['gain', 'fiat_currency', 'stake_currency']) and self._rpc._fiat_converter): msg['profit_fiat'] = self._rpc._fiat_converter.convert_amount( @@ -431,7 +432,7 @@ class Telegram(RPCHandler): else: return "\N{CROSS MARK}" - def _prepare_entry_details(self, filled_orders: List, base_currency: str, is_open: bool): + def _prepare_entry_details(self, filled_orders: List, quote_currency: str, is_open: bool): """ Prepare details of trade with entry adjustment enabled """ @@ -449,7 +450,7 @@ class Telegram(RPCHandler): if x == 0: lines.append(f"*Entry #{x+1}:*") lines.append( - f"*Entry Amount:* {cur_entry_amount} ({order['cost']:.8f} {base_currency})") + f"*Entry Amount:* {cur_entry_amount} ({order['cost']:.8f} {quote_currency})") lines.append(f"*Average Entry Price:* {cur_entry_average}") else: sumA = 0 @@ -464,7 +465,8 @@ class Telegram(RPCHandler): if prev_avg_price: minus_on_entry = (cur_entry_average - prev_avg_price) / prev_avg_price - dur_entry = cur_entry_datetime - arrow.get(filled_orders[x-1]["order_filled_date"]) + dur_entry = cur_entry_datetime - arrow.get( + filled_orders[x - 1]["order_filled_date"]) days = dur_entry.days hours, remainder = divmod(dur_entry.seconds, 3600) minutes, seconds = divmod(remainder, 60) @@ -473,7 +475,7 @@ class Telegram(RPCHandler): lines.append("({})".format(cur_entry_datetime .humanize(granularity=["day", "hour", "minute"]))) lines.append( - f"*Entry Amount:* {cur_entry_amount} ({order['cost']:.8f} {base_currency})") + f"*Entry Amount:* {cur_entry_amount} ({order['cost']:.8f} {quote_currency})") lines.append(f"*Average Entry Price:* {cur_entry_average} " f"({price_to_1st_entry:.2%} from 1st entry rate)") lines.append(f"*Order filled at:* {order['order_filled_date']}") @@ -516,7 +518,7 @@ class Telegram(RPCHandler): "*Current Pair:* {pair}", "*Direction:* " + ("`Short`" if r.get('is_short') else "`Long`"), "*Leverage:* `{leverage}`" if r.get('leverage') else "", - "*Amount:* `{amount} ({stake_amount} {base_currency})`", + "*Amount:* `{amount} ({stake_amount} {quote_currency})`", "*Enter Tag:* `{enter_tag}`" if r['enter_tag'] else "", "*Exit Reason:* `{exit_reason}`" if r['exit_reason'] else "", ] @@ -556,7 +558,7 @@ class Telegram(RPCHandler): lines.append("*Open Order:* `{open_order}`") lines_detail = self._prepare_entry_details( - r['orders'], r['base_currency'], r['is_open']) + r['orders'], r['quote_currency'], r['is_open']) lines.extend(lines_detail if lines_detail else "") # Filter empty lines using list-comprehension @@ -976,23 +978,58 @@ class Telegram(RPCHandler): @authorized_only def _force_exit(self, update: Update, context: CallbackContext) -> None: """ - Handler for /forcesell . + Handler for /forceexit . Sells the given trade at current price :param bot: telegram bot :param update: message update :return: None """ - trade_id = context.args[0] if context.args and len(context.args) > 0 else None - if not trade_id: - self._send_msg("You must specify a trade-id or 'all'.") - return - try: - msg = self._rpc._rpc_force_exit(trade_id) - self._send_msg('Force_exit Result: `{result}`'.format(**msg)) + if context.args: + trade_id = context.args[0] + self._force_exit_action(trade_id) + else: + fiat_currency = self._config.get('fiat_display_currency', '') + try: + statlist, _, _ = self._rpc._rpc_status_table( + self._config['stake_currency'], fiat_currency) + except RPCException: + self._send_msg(msg='No open trade found.') + return + trades = [] + for trade in statlist: + trades.append((trade[0], f"{trade[0]} {trade[1]} {trade[2]} {trade[3]}")) - except RPCException as e: - self._send_msg(str(e)) + trade_buttons = [ + InlineKeyboardButton(text=trade[1], callback_data=f"force_exit__{trade[0]}") + for trade in trades] + buttons_aligned = self._layout_inline_keyboard_onecol(trade_buttons) + + buttons_aligned.append([InlineKeyboardButton( + text='Cancel', callback_data='force_exit__cancel')]) + self._send_msg(msg="Which trade?", keyboard=buttons_aligned) + + def _force_exit_action(self, trade_id): + if trade_id != 'cancel': + try: + self._rpc._rpc_force_exit(trade_id) + except RPCException as e: + self._send_msg(str(e)) + + def _force_exit_inline(self, update: Update, _: CallbackContext) -> None: + if update.callback_query: + query = update.callback_query + if query.data and '__' in query.data: + # Input data is "force_exit__" + trade_id = query.data.split("__")[1].split(' ')[0] + if trade_id == 'cancel': + query.answer() + query.edit_message_text(text="Force exit canceled.") + return + trade: Trade = Trade.get_trades(trade_filter=Trade.id == trade_id).first() + query.answer() + query.edit_message_text(text=f"Manually exiting Trade #{trade_id}, {trade.pair}") + self._force_exit_action(trade_id) def _force_enter_action(self, pair, price: Optional[float], order_side: SignalDirection): if pair != 'cancel': @@ -1012,8 +1049,13 @@ class Telegram(RPCHandler): self._force_enter_action(pair, None, order_side) @staticmethod - def _layout_inline_keyboard(buttons: List[InlineKeyboardButton], - cols=3) -> List[List[InlineKeyboardButton]]: + def _layout_inline_keyboard( + buttons: List[InlineKeyboardButton], cols=3) -> List[List[InlineKeyboardButton]]: + return [buttons[i:i + cols] for i in range(0, len(buttons), cols)] + + @staticmethod + def _layout_inline_keyboard_onecol( + buttons: List[InlineKeyboardButton], cols=1) -> List[List[InlineKeyboardButton]]: return [buttons[i:i + cols] for i in range(0, len(buttons), cols)] @authorized_only @@ -1421,7 +1463,6 @@ class Telegram(RPCHandler): "*/start:* `Starts the trader`\n" "*/stop:* Stops the trader\n" "*/stopbuy:* `Stops buying, but handles open trades gracefully` \n" - # TODO: forceenter forceshort forcelong missing "*/forceexit |all:* `Instantly exits the given trade or all trades, " "regardless of profit`\n" "*/fe |all:* `Alias to /forceexit`" diff --git a/freqtrade/strategy/informative_decorator.py b/freqtrade/strategy/informative_decorator.py index 0dd5320cd..7dfdf5a8c 100644 --- a/freqtrade/strategy/informative_decorator.py +++ b/freqtrade/strategy/informative_decorator.py @@ -23,7 +23,7 @@ class InformativeData: def informative(timeframe: str, asset: str = '', fmt: Optional[Union[str, Callable[[Any], str]]] = None, *, - candle_type: Optional[CandleType] = None, + candle_type: Optional[Union[CandleType, str]] = None, ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]: """ A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 001fa5e2c..d38970bfa 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -3,7 +3,6 @@ IStrategy interface This module defines the interface to apply for strategies """ import logging -import warnings from abc import ABC, abstractmethod from datetime import datetime, timedelta, timezone from typing import Dict, List, Optional, Tuple, Union @@ -44,14 +43,11 @@ class IStrategy(ABC, HyperStrategyMixin): """ # Strategy interface version # Default to version 2 - # Version 1 is the initial interface without metadata dict + # Version 1 is the initial interface without metadata dict - deprecated and no longer supported. # Version 2 populate_* include metadata dict # Version 3 - First version with short and leverage support INTERFACE_VERSION: int = 3 - _populate_fun_len: int = 0 - _buy_fun_len: int = 0 - _sell_fun_len: int = 0 _ft_params_from_file: Dict # associated minimal roi minimal_roi: Dict = {} @@ -114,7 +110,7 @@ class IStrategy(ABC, HyperStrategyMixin): # Class level variables (intentional) containing # the dataprovider (dp) (access to other candles, historic data, ...) # and wallets - access to the current balance. - dp: Optional[DataProvider] + dp: DataProvider wallets: Optional[Wallets] = None # Filled from configuration stake_currency: str @@ -197,6 +193,13 @@ class IStrategy(ABC, HyperStrategyMixin): """ return self.populate_sell_trend(dataframe, metadata) + def bot_start(self, **kwargs) -> None: + """ + Called only once after bot instantiation. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + """ + pass + def bot_loop_start(self, **kwargs) -> None: """ Called at the start of the bot iteration (one loop). @@ -206,18 +209,18 @@ class IStrategy(ABC, HyperStrategyMixin): """ pass - def check_buy_timeout(self, pair: str, trade: Trade, order: dict, + def check_buy_timeout(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> bool: """ DEPRECATED: Please use `check_entry_timeout` instead. """ return False - def check_entry_timeout(self, pair: str, trade: Trade, order: dict, + def check_entry_timeout(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> bool: """ Check entry timeout function callback. - This method can be used to override the enter-timeout. + This method can be used to override the entry-timeout. It is called whenever a limit entry order has been created, and is not yet fully filled. Configuration options in `unfilledtimeout` will be verified before this, @@ -225,8 +228,8 @@ class IStrategy(ABC, HyperStrategyMixin): When not implemented by a strategy, this simply returns False. :param pair: Pair the trade is for - :param trade: trade object. - :param order: Order dictionary as returned from CCXT. + :param trade: Trade object. + :param order: Order object. :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the entry order is cancelled. @@ -234,30 +237,30 @@ class IStrategy(ABC, HyperStrategyMixin): return self.check_buy_timeout( pair=pair, trade=trade, order=order, current_time=current_time) - def check_sell_timeout(self, pair: str, trade: Trade, order: dict, + def check_sell_timeout(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> bool: """ DEPRECATED: Please use `check_exit_timeout` instead. """ return False - def check_exit_timeout(self, pair: str, trade: Trade, order: dict, + def check_exit_timeout(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> bool: """ - Check sell timeout function callback. + Check exit timeout function callback. This method can be used to override the exit-timeout. - It is called whenever a (long) limit sell order or (short) limit buy - has been created, and is not yet fully filled. + It is called whenever a limit exit order has been created, + and is not yet fully filled. Configuration options in `unfilledtimeout` will be verified before this, so ensure to set these timeouts high enough. When not implemented by a strategy, this simply returns False. :param pair: Pair the trade is for - :param trade: trade object. - :param order: Order dictionary as returned from CCXT. + :param trade: Trade object. + :param order: Order object :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. - :return bool: When True is returned, then the (long)sell/(short)buy-order is cancelled. + :return bool: When True is returned, then the exit-order is cancelled. """ return self.check_sell_timeout( pair=pair, trade=trade, order=order, current_time=current_time) @@ -359,7 +362,7 @@ class IStrategy(ABC, HyperStrategyMixin): def custom_exit_price(self, pair: str, trade: Trade, current_time: datetime, proposed_rate: float, - current_profit: float, **kwargs) -> float: + current_profit: float, exit_tag: Optional[str], **kwargs) -> float: """ Custom exit price logic, returning the new exit price. @@ -372,6 +375,7 @@ class IStrategy(ABC, HyperStrategyMixin): :param current_time: datetime object, containing the current datetime :param proposed_rate: Rate, calculated based on pricing settings in exit_pricing. :param current_profit: Current profit (as ratio), calculated based on current_rate. + :param exit_tag: Exit reason. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: New exit price value if provided """ @@ -541,7 +545,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. @@ -551,8 +555,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: """ @@ -572,7 +577,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, @@ -580,15 +585,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: """ @@ -881,14 +887,10 @@ class IStrategy(ABC, HyperStrategyMixin): current_rate = rate current_profit = trade.calc_profit_ratio(current_rate) - if (self.exit_profit_only and current_profit <= self.exit_profit_offset): - # exit_profit_only and profit doesn't reach the offset - ignore sell signal - pass - elif self.use_exit_signal and not enter: - if exit_: + if self.use_exit_signal: + if exit_ and not enter: exit_signal = ExitType.EXIT_SIGNAL else: - trade_type = "exit_short" if trade.is_short else "sell" custom_reason = 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) @@ -896,13 +898,17 @@ class IStrategy(ABC, HyperStrategyMixin): exit_signal = ExitType.CUSTOM_EXIT if isinstance(custom_reason, str): if len(custom_reason) > CUSTOM_EXIT_MAX_LENGTH: - logger.warning(f'Custom {trade_type} reason returned from ' + 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] else: custom_reason = None - if exit_signal in (ExitType.CUSTOM_EXIT, ExitType.EXIT_SIGNAL): + if ( + exit_signal == ExitType.CUSTOM_EXIT + or (exit_signal == ExitType.EXIT_SIGNAL + and (not self.exit_profit_only or current_profit > self.exit_profit_offset)) + ): logger.debug(f"{trade.pair} - Sell signal received. " f"exit_type=ExitType.{exit_signal.name}" + (f", custom_reason={custom_reason}" if custom_reason else "")) @@ -1095,12 +1101,7 @@ class IStrategy(ABC, HyperStrategyMixin): dataframe = _create_and_merge_informative_pair( self, dataframe, metadata, inf_data, populate_fn) - if self._populate_fun_len == 2: - warnings.warn("deprecated - check out the Sample strategy to see " - "the current function headers!", DeprecationWarning) - return self.populate_indicators(dataframe) # type: ignore - else: - return self.populate_indicators(dataframe, metadata) + return self.populate_indicators(dataframe, metadata) def advise_entry(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ @@ -1114,12 +1115,7 @@ class IStrategy(ABC, HyperStrategyMixin): logger.debug(f"Populating enter signals for pair {metadata.get('pair')}.") - if self._buy_fun_len == 2: - warnings.warn("deprecated - check out the Sample strategy to see " - "the current function headers!", DeprecationWarning) - df = self.populate_buy_trend(dataframe) # type: ignore - else: - df = self.populate_entry_trend(dataframe, metadata) + df = self.populate_entry_trend(dataframe, metadata) if 'enter_long' not in df.columns: df = df.rename({'buy': 'enter_long', 'buy_tag': 'enter_tag'}, axis='columns') @@ -1134,14 +1130,8 @@ class IStrategy(ABC, HyperStrategyMixin): currently traded pair :return: DataFrame with exit column """ - logger.debug(f"Populating exit signals for pair {metadata.get('pair')}.") - if self._sell_fun_len == 2: - warnings.warn("deprecated - check out the Sample strategy to see " - "the current function headers!", DeprecationWarning) - df = self.populate_sell_trend(dataframe) # type: ignore - else: - df = self.populate_exit_trend(dataframe, metadata) + df = self.populate_exit_trend(dataframe, metadata) if 'exit_long' not in df.columns: df = df.rename({'sell': 'exit_long'}, axis='columns') return df diff --git a/freqtrade/strategy/strategy_helper.py b/freqtrade/strategy/strategy_helper.py index f07c14e24..43728dc1f 100644 --- a/freqtrade/strategy/strategy_helper.py +++ b/freqtrade/strategy/strategy_helper.py @@ -56,12 +56,18 @@ def merge_informative_pair(dataframe: pd.DataFrame, informative: pd.DataFrame, # Combine the 2 dataframes # all indicators on the informative sample MUST be calculated before this point - dataframe = pd.merge(dataframe, informative, left_on='date', - right_on=date_merge, how='left') + if ffill: + # https://pandas.pydata.org/docs/user_guide/merging.html#timeseries-friendly-merging + # merge_ordered - ffill method is 2.5x faster than seperate ffill() + dataframe = pd.merge_ordered(dataframe, informative, fill_method="ffill", left_on='date', + right_on=date_merge, how='left') + else: + dataframe = pd.merge(dataframe, informative, left_on='date', + right_on=date_merge, how='left') dataframe = dataframe.drop(date_merge, axis=1) - if ffill: - dataframe = dataframe.ffill() + # if ffill: + # dataframe = dataframe.ffill() return dataframe @@ -93,9 +99,9 @@ def stoploss_from_open( return 1 if is_short is True: - stoploss = -1+((1-open_relative_stop)/(1-current_profit)) + stoploss = -1 + ((1 - open_relative_stop) / (1 - current_profit)) else: - stoploss = 1-((1+open_relative_stop)/(1+current_profit)) + stoploss = 1 - ((1 + open_relative_stop) / (1 + current_profit)) # negative stoploss values indicate the requested stop price is higher/lower # (long/short) than the current price diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index 17dfa0873..ed40ef509 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -32,7 +32,7 @@ def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: def custom_exit_price(self, pair: str, trade: 'Trade', current_time: 'datetime', proposed_rate: float, - current_profit: float, **kwargs) -> float: + current_profit: float, exit_tag: Optional[str], **kwargs) -> float: """ Custom exit price logic, returning the new exit price. @@ -45,6 +45,7 @@ def custom_exit_price(self, pair: str, trade: 'Trade', :param current_time: datetime object, containing the current datetime :param proposed_rate: Rate, calculated based on pricing settings in exit_pricing. :param current_profit: Current profit (as ratio), calculated based on current_rate. + :param exit_tag: Exit reason. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: New exit price value if provided """ @@ -170,7 +171,8 @@ def confirm_trade_exit(self, pair: str, trade: 'Trade', order_type: str, amount: """ return True -def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool: +def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order', + current_time: datetime, **kwargs) -> bool: """ Check entry timeout function callback. This method can be used to override the entry-timeout. @@ -183,14 +185,16 @@ def check_entry_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) When not implemented by a strategy, this simply returns False. :param pair: Pair the trade is for - :param trade: trade object. - :param order: Order dictionary as returned from CCXT. + :param trade: Trade object. + :param order: Order object. + :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. - :return bool: When True is returned, then the buy-order is cancelled. + :return bool: When True is returned, then the entry order is cancelled. """ return False -def check_exit_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool: +def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order', + current_time: datetime, **kwargs) -> bool: """ Check exit timeout function callback. This method can be used to override the exit-timeout. @@ -203,8 +207,9 @@ def check_exit_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) - When not implemented by a strategy, this simply returns False. :param pair: Pair the trade is for - :param trade: trade object. - :param order: Order dictionary as returned from CCXT. + :param trade: Trade object. + :param order: Order object. + :param current_time: datetime object, containing the current datetime :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the exit-order is cancelled. """ diff --git a/mkdocs.yml b/mkdocs.yml index 8b36ba699..a43322f78 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -29,6 +29,7 @@ nav: - Data Analysis: - Jupyter Notebooks: data-analysis.md - Strategy analysis: strategy_analysis_example.md + - Backtest analysis: advanced-backtesting.md - Advanced Topics: - Advanced Post-installation Tasks: advanced-setup.md - Edge Positioning: edge.md diff --git a/pyproject.toml b/pyproject.toml index 50f0242a8..e8d5ed47e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ exclude = ''' line_length = 100 multi_line_output=0 lines_after_imports=2 -skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*"] +skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*", "**/user_data/*"] [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/requirements-dev.txt b/requirements-dev.txt index 20fd420bd..9458be1ef 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,13 +2,14 @@ -r requirements.txt -r requirements-plot.txt -r requirements-hyperopt.txt +-r docs/requirements-docs.txt coveralls==3.3.1 flake8==4.0.1 flake8-tidy-imports==4.6.0 -mypy==0.942 +mypy==0.950 pre-commit==2.18.1 -pytest==7.1.1 +pytest==7.1.2 pytest-asyncio==0.18.3 pytest-cov==3.0.0 pytest-mock==3.7.0 @@ -18,13 +19,11 @@ isort==5.10.1 time-machine==2.6.0 # Convert jupyter notebooks to markdown documents -nbconvert==6.4.5 +nbconvert==6.5.0 # mypy types -types-cachetools==5.0.0 +types-cachetools==5.0.1 types-filelock==3.2.5 -types-requests==2.27.16 -types-tabulate==0.8.6 - -# Extensions to datetime library -types-python-dateutil==2.8.10 +types-requests==2.27.25 +types-tabulate==0.8.8 +types-python-dateutil==2.8.14 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index aeb7be035..32fc3f4b9 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -6,5 +6,4 @@ scipy==1.8.0 scikit-learn==1.0.2 scikit-optimize==0.9.0 filelock==3.6.0 -joblib==1.1.0 progressbar2==4.0.0 diff --git a/requirements-plot.txt b/requirements-plot.txt index bb2132f87..d9faed301 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,4 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.6.0 - +plotly==5.7.0 diff --git a/requirements.txt b/requirements.txt index eee88001d..709408aeb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,11 @@ numpy==1.22.3 pandas==1.4.2 pandas-ta==0.3.14b -ccxt==1.77.98 +ccxt==1.81.16 # Pin cryptography for now due to rust build errors with piwheels -cryptography==36.0.2 +cryptography==37.0.1 aiohttp==3.8.1 -SQLAlchemy==1.4.34 +SQLAlchemy==1.4.36 python-telegram-bot==13.11 arrow==1.2.2 cachetools==4.2.2 @@ -17,21 +17,24 @@ 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 # find first, C search in arrays py_find_1st==1.1.5 # Load ticker files 30% faster python-rapidjson==1.6 +# Properly format api responses +orjson==3.6.8 # Notify systemd sdnotify==0.3.2 # API Server -fastapi==0.75.1 +fastapi==0.75.2 uvicorn==0.17.6 pyjwt==2.3.0 aiofiles==0.8.0 @@ -41,7 +44,7 @@ psutil==5.9.0 colorama==0.4.4 # Building config files interactively questionary==1.10.0 -prompt-toolkit==3.0.28 +prompt-toolkit==3.0.29 # Extensions to datetime library python-dateutil==2.8.2 diff --git a/scripts/rest_client.py b/scripts/rest_client.py index 9c5f820b9..ecbb65253 100755 --- a/scripts/rest_client.py +++ b/scripts/rest_client.py @@ -275,14 +275,14 @@ class FtRestClient(): } return self._post("force_enter", data=data) - def forcesell(self, tradeid): - """Force-sell a trade. + def forceexit(self, tradeid): + """Force-exit a trade. :param tradeid: Id of the trade (can be received via status command) :return: json object """ - return self._post("forcesell", data={"tradeid": tradeid}) + return self._post("forceexit", data={"tradeid": tradeid}) def strategies(self): """Lists available strategies diff --git a/setup.cfg b/setup.cfg index 6aaec9d73..edbd320c3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,7 +39,9 @@ console_scripts = freqtrade = freqtrade.main:main [flake8] -#ignore = +# Default from https://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-ignore +# minus E226 +ignore = E121,E123,E126,E24,E704,W503,W504 max-line-length = 100 max-complexity = 12 exclude = @@ -50,6 +52,11 @@ exclude = [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 640c8cc7b..c5e418d0d 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ setup( ], install_requires=[ # from requirements.txt - 'ccxt>=1.77.29', + 'ccxt>=1.79.69', 'SQLAlchemy', 'python-telegram-bot>=13.4', 'arrow>=0.17.0', @@ -57,6 +57,7 @@ setup( 'pycoingecko', 'py_find_1st', 'python-rapidjson', + 'orjson', 'sdnotify', 'colorama', 'jinja2', diff --git a/setup.sh b/setup.sh index 2c3a6710b..dcf6c02c7 100755 --- a/setup.sh +++ b/setup.sh @@ -89,12 +89,13 @@ function updateenv() { fi echo "pip install completed" echo - if [[ $dev =~ ^[Yy]$ ]] then - ${PYTHON} -m pre-commit install + if [[ $dev =~ ^[Yy]$ ]]; then + ${PYTHON} -m pre_commit install if [ $? -ne 0 ]; then echo "Failed installing pre-commit" exit 1 fi + fi } # Install tab lib @@ -154,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 22869638b..37eeda86a 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -847,7 +847,7 @@ def test_start_convert_trades(mocker, caplog): assert convert_mock.call_count == 1 -def test_start_list_strategies(mocker, caplog, capsys): +def test_start_list_strategies(capsys): args = [ "list-strategies", @@ -859,8 +859,8 @@ def test_start_list_strategies(mocker, caplog, capsys): # pargs['config'] = None start_list_strategies(pargs) captured = capsys.readouterr() - assert "TestStrategyLegacyV1" in captured.out - assert "legacy_strategy_v1.py" not in captured.out + assert "StrategyTestV2" in captured.out + assert "strategy_test_v2.py" not in captured.out assert CURRENT_TEST_STRATEGY in captured.out # Test regular output @@ -874,8 +874,8 @@ def test_start_list_strategies(mocker, caplog, capsys): # pargs['config'] = None start_list_strategies(pargs) captured = capsys.readouterr() - assert "TestStrategyLegacyV1" in captured.out - assert "legacy_strategy_v1.py" in captured.out + assert "StrategyTestV2" in captured.out + assert "strategy_test_v2.py" in captured.out assert CURRENT_TEST_STRATEGY in captured.out # Test color output @@ -888,10 +888,30 @@ def test_start_list_strategies(mocker, caplog, capsys): # pargs['config'] = None start_list_strategies(pargs) captured = capsys.readouterr() - assert "TestStrategyLegacyV1" in captured.out - assert "legacy_strategy_v1.py" in captured.out + assert "StrategyTestV2" in captured.out + assert "strategy_test_v2.py" in captured.out assert CURRENT_TEST_STRATEGY in captured.out assert "LOAD FAILED" in captured.out + # Recursive + assert "TestStrategyNoImplements" not in captured.out + + # Test recursive + args = [ + "list-strategies", + "--strategy-path", + str(Path(__file__).parent.parent / "strategy" / "strats"), + '--no-color', + '--recursive-strategy-search' + ] + pargs = get_args(args) + # pargs['config'] = None + start_list_strategies(pargs) + captured = capsys.readouterr() + assert "StrategyTestV2" in captured.out + assert "strategy_test_v2.py" in captured.out + assert "StrategyTestV2" in captured.out + assert "TestStrategyNoImplements" in captured.out + assert str(Path("broken_strats/broken_futures_strategies.py")) in captured.out def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): @@ -1429,7 +1449,7 @@ def test_backtesting_show(mocker, testdatadir, capsys): args = [ "backtesting-show", "--export-filename", - f"{testdatadir / 'backtest-result_new.json'}", + f"{testdatadir / 'backtest_results/backtest-result_new.json'}", "--show-pair-list" ] pargs = get_args(args) diff --git a/tests/conftest.py b/tests/conftest.py index f53ec4bc7..36297552a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1633,40 +1633,6 @@ def limit_buy_order(limit_buy_order_open): return order -@pytest.fixture(scope='function') -def market_buy_order(): - return { - 'id': 'mocked_market_buy', - 'type': 'market', - 'side': 'buy', - 'symbol': 'mocked', - 'timestamp': arrow.utcnow().int_timestamp * 1000, - 'datetime': arrow.utcnow().isoformat(), - 'price': 0.00004099, - 'amount': 91.99181073, - 'filled': 91.99181073, - 'remaining': 0.0, - 'status': 'closed' - } - - -@pytest.fixture -def market_sell_order(): - return { - 'id': 'mocked_limit_sell', - 'type': 'market', - 'side': 'sell', - 'symbol': 'mocked', - 'timestamp': arrow.utcnow().int_timestamp * 1000, - 'datetime': arrow.utcnow().isoformat(), - 'price': 0.00004173, - 'amount': 91.99181073, - 'filled': 91.99181073, - 'remaining': 0.0, - 'status': 'closed' - } - - @pytest.fixture def limit_buy_order_old(): return { @@ -2673,6 +2639,7 @@ def saved_hyperopt_results(): 'total_profit': -0.00125625, 'current_epoch': 1, 'is_initial_point': True, + 'is_random': False, 'is_best': True, }, { @@ -2689,6 +2656,7 @@ def saved_hyperopt_results(): 'total_profit': 6.185e-05, 'current_epoch': 2, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': 14.241196856510731, @@ -2699,6 +2667,7 @@ def saved_hyperopt_results(): 'total_profit': -0.13639474, 'current_epoch': 3, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': 100000, @@ -2706,7 +2675,7 @@ def saved_hyperopt_results(): 'params_details': {'buy': {'mfi-value': 13, 'fastd-value': 35, 'adx-value': 39, 'rsi-value': 29, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'macd_cross_signal'}, 'sell': {'sell-mfi-value': 87, 'sell-fastd-value': 54, 'sell-adx-value': 63, 'sell-rsi-value': 93, 'sell-mfi-enabled': False, 'sell-fastd-enabled': True, 'sell-adx-enabled': True, 'sell-rsi-enabled': True, 'sell-trigger': 'sell-bb_upper'}, 'roi': {0: 0.411946348378729, 215: 0.2052334363683207, 891: 0.06264755784937427, 2293: 0}, 'stoploss': {'stoploss': -0.11818343570194478}}, # noqa: E501 'results_metrics': {'total_trades': 0, 'wins': 0, 'draws': 0, 'losses': 0, 'profit_mean': None, 'profit_median': None, 'profit_total': 0, 'profit': 0.0, 'holding_avg': timedelta()}, # noqa: E501 'results_explanation': ' 0 trades. Avg profit nan%. Total profit 0.00000000 BTC ( 0.00Σ%). Avg duration nan min.', # noqa: E501 - 'total_profit': 0, 'current_epoch': 4, 'is_initial_point': True, 'is_best': False + 'total_profit': 0, 'current_epoch': 4, 'is_initial_point': True, 'is_random': False, 'is_best': False # noqa: E501 }, { 'loss': 0.22195522184191518, 'params_dict': {'mfi-value': 17, 'fastd-value': 21, 'adx-value': 38, 'rsi-value': 33, 'mfi-enabled': True, 'fastd-enabled': False, 'adx-enabled': True, 'rsi-enabled': False, 'trigger': 'macd_cross_signal', 'sell-mfi-value': 87, 'sell-fastd-value': 82, 'sell-adx-value': 78, 'sell-rsi-value': 69, 'sell-mfi-enabled': True, 'sell-fastd-enabled': False, 'sell-adx-enabled': True, 'sell-rsi-enabled': False, 'sell-trigger': 'sell-macd_cross_signal', 'roi_t1': 1269, 'roi_t2': 601, 'roi_t3': 444, 'roi_p1': 0.07280999507931168, 'roi_p2': 0.08946698095898986, 'roi_p3': 0.1454876733325284, 'stoploss': -0.18181041180901014}, # noqa: E501 @@ -2716,6 +2685,7 @@ def saved_hyperopt_results(): 'total_profit': -0.002480140000000001, 'current_epoch': 5, 'is_initial_point': True, + 'is_random': False, 'is_best': True }, { 'loss': 0.545315889154162, @@ -2726,6 +2696,7 @@ def saved_hyperopt_results(): 'total_profit': -0.0041773, 'current_epoch': 6, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': 4.713497421432944, @@ -2738,6 +2709,7 @@ def saved_hyperopt_results(): 'total_profit': -0.06339929, 'current_epoch': 7, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': 20.0, # noqa: E501 @@ -2748,6 +2720,7 @@ def saved_hyperopt_results(): 'total_profit': 0.0, 'current_epoch': 8, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': 2.4731817780991223, @@ -2758,6 +2731,7 @@ def saved_hyperopt_results(): 'total_profit': -0.044050070000000004, # noqa: E501 'current_epoch': 9, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': -0.2604606005845212, # noqa: E501 @@ -2768,6 +2742,7 @@ def saved_hyperopt_results(): 'total_profit': 0.00021629, 'current_epoch': 10, 'is_initial_point': True, + 'is_random': False, 'is_best': True }, { 'loss': 4.876465945994304, # noqa: E501 @@ -2779,6 +2754,7 @@ def saved_hyperopt_results(): 'total_profit': -0.07436117, 'current_epoch': 11, 'is_initial_point': True, + 'is_random': False, 'is_best': False }, { 'loss': 100000, @@ -2789,6 +2765,7 @@ def saved_hyperopt_results(): 'total_profit': 0, 'current_epoch': 12, 'is_initial_point': True, + 'is_random': False, 'is_best': False } ] @@ -2937,14 +2914,6 @@ def limit_order(limit_buy_order_usdt, limit_sell_order_usdt): } -@pytest.fixture(scope='function') -def market_order(market_buy_order_usdt, market_sell_order_usdt): - return { - 'buy': market_buy_order_usdt, - 'sell': market_sell_order_usdt - } - - @pytest.fixture(scope='function') def limit_order_open(limit_buy_order_usdt_open, limit_sell_order_usdt_open): return { diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index f4275edd9..4157bd899 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -8,14 +8,14 @@ from pandas import DataFrame, DateOffset, Timestamp, to_datetime from freqtrade.configuration import TimeRange from freqtrade.constants import LAST_BT_RESULT_FN -from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, analyze_trade_parallelism, calculate_csum, - calculate_market_change, calculate_max_drawdown, - calculate_underwater, combine_dataframes_with_mean, - create_cum_profit, extract_trades_of_period, - get_latest_backtest_filename, get_latest_hyperopt_file, - load_backtest_data, load_backtest_metadata, load_trades, - load_trades_from_db) +from freqtrade.data.btanalysis import (BT_DATA_COLUMNS, analyze_trade_parallelism, + extract_trades_of_period, get_latest_backtest_filename, + get_latest_hyperopt_file, load_backtest_data, + load_backtest_metadata, load_trades, load_trades_from_db) from freqtrade.data.history import load_data, load_pair_history +from freqtrade.data.metrics import (calculate_cagr, calculate_csum, calculate_market_change, + calculate_max_drawdown, calculate_underwater, + combine_dataframes_with_mean, create_cum_profit) from freqtrade.exceptions import OperationalException from tests.conftest import CURRENT_TEST_STRATEGY, create_mock_trades from tests.conftest_trades import MOCK_TRADE_COUNT @@ -27,18 +27,19 @@ def test_get_latest_backtest_filename(testdatadir, mocker): with pytest.raises(ValueError, match=r"Directory .* does not seem to contain .*"): - get_latest_backtest_filename(testdatadir.parent) + get_latest_backtest_filename(testdatadir) - res = get_latest_backtest_filename(testdatadir) + testdir_bt = testdatadir / "backtest_results" + res = get_latest_backtest_filename(testdir_bt) assert res == 'backtest-result_new.json' - res = get_latest_backtest_filename(str(testdatadir)) + res = get_latest_backtest_filename(str(testdir_bt)) assert res == 'backtest-result_new.json' mocker.patch("freqtrade.data.btanalysis.json_load", return_value={}) with pytest.raises(ValueError, match=r"Invalid '.last_result.json' format."): - get_latest_backtest_filename(testdatadir) + get_latest_backtest_filename(testdir_bt) def test_get_latest_hyperopt_file(testdatadir): @@ -81,7 +82,7 @@ def test_load_backtest_data_old_format(testdatadir, mocker): def test_load_backtest_data_new_format(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) assert set(bt_data.columns) == set(BT_DATA_COLUMNS + ['close_timestamp', 'open_timestamp']) @@ -92,19 +93,19 @@ def test_load_backtest_data_new_format(testdatadir): assert bt_data.equals(bt_data2) # Test loading from folder (must yield same result) - bt_data3 = load_backtest_data(testdatadir) + bt_data3 = load_backtest_data(testdatadir / "backtest_results") assert bt_data.equals(bt_data3) with pytest.raises(ValueError, match=r"File .* does not exist\."): load_backtest_data(str("filename") + "nofile") with pytest.raises(ValueError, match=r"Unknown dataformat."): - load_backtest_data(testdatadir / LAST_BT_RESULT_FN) + load_backtest_data(testdatadir / "backtest_results" / LAST_BT_RESULT_FN) def test_load_backtest_data_multi(testdatadir): - filename = testdatadir / "backtest-result_multistrat.json" + filename = testdatadir / "backtest_results/backtest-result_multistrat.json" for strategy in ('StrategyTestV2', 'TestStrategy'): bt_data = load_backtest_data(filename, strategy=strategy) assert isinstance(bt_data, DataFrame) @@ -182,7 +183,7 @@ def test_extract_trades_of_period(testdatadir): def test_analyze_trade_parallelism(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) res = analyze_trade_parallelism(bt_data, "5m") @@ -256,7 +257,7 @@ def test_combine_dataframes_with_mean_no_data(testdatadir): def test_create_cum_profit(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") @@ -272,7 +273,7 @@ def test_create_cum_profit(testdatadir): def test_create_cum_profit1(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) # Move close-time to "off" the candle, to make sure the logic still works bt_data.loc[:, 'close_date'] = bt_data.loc[:, 'close_date'] + DateOffset(seconds=20) @@ -294,7 +295,7 @@ def test_create_cum_profit1(testdatadir): def test_calculate_max_drawdown(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) _, hdate, lowdate, hval, lval, drawdown = calculate_max_drawdown( bt_data, value_col="profit_abs") @@ -318,7 +319,7 @@ def test_calculate_max_drawdown(testdatadir): def test_calculate_csum(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) csum_min, csum_max = calculate_csum(bt_data) @@ -335,6 +336,19 @@ def test_calculate_csum(testdatadir): csum_min, csum_max = calculate_csum(DataFrame()) +@pytest.mark.parametrize('start,end,days, expected', [ + (64900, 176000, 3 * 365, 0.3945), + (64900, 176000, 365, 1.7119), + (1000, 1000, 365, 0.0), + (1000, 1500, 365, 0.5), + (1000, 1500, 100, 3.3927), # sub year + (0.01000000, 0.01762792, 120, 4.6087), # sub year BTC values +]) +def test_calculate_cagr(start, end, days, expected): + + assert round(calculate_cagr(days, start, end), 4) == expected + + def test_calculate_max_drawdown2(): values = [0.011580, 0.010048, 0.011340, 0.012161, 0.010416, 0.010009, 0.020024, -0.024662, -0.022350, 0.020496, -0.029859, -0.030511, 0.010041, 0.010872, @@ -362,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..82d4a841c 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 ) @@ -223,42 +223,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/edge/test_edge.py b/tests/edge/test_edge.py index a43e82b22..b30d6f998 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -8,7 +8,7 @@ from unittest.mock import MagicMock import arrow import numpy as np import pytest -from pandas import DataFrame, to_datetime +from pandas import DataFrame from freqtrade.data.converter import ohlcv_to_dataframe from freqtrade.edge import Edge, PairInfo @@ -30,49 +30,6 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe, tests_start_time = arrow.get(2018, 10, 3) timeframe_in_minute = 60 -# Helpers for this test file - - -def _validate_ohlc(buy_ohlc_sell_matrice): - for index, ohlc in enumerate(buy_ohlc_sell_matrice): - # if not high < open < low or not high < close < low - if not ohlc[3] >= ohlc[2] >= ohlc[4] or not ohlc[3] >= ohlc[5] >= ohlc[4]: - raise Exception('Line ' + str(index + 1) + ' of ohlc has invalid values!') - return True - - -def _build_dataframe(buy_ohlc_sell_matrice): - _validate_ohlc(buy_ohlc_sell_matrice) - data = [] - for ohlc in buy_ohlc_sell_matrice: - d = { - 'date': tests_start_time.shift( - minutes=( - ohlc[0] * - timeframe_in_minute)).int_timestamp * - 1000, - 'buy': ohlc[1], - 'open': ohlc[2], - 'high': ohlc[3], - 'low': ohlc[4], - 'close': ohlc[5], - 'sell': ohlc[6]} - data.append(d) - - frame = DataFrame(data) - frame['date'] = to_datetime(frame['date'], - unit='ms', - utc=True, - infer_datetime_format=True) - - return frame - - -def _time_on_candle(number): - return np.datetime64(tests_start_time.shift( - minutes=(number * timeframe_in_minute)).int_timestamp * 1000, 'ms') - - # End helper functions # Open trade should be removed from the end tc0 = BTContainer(data=[ diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index c3950e459..5c8d7d3b0 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -169,90 +169,90 @@ def test_fill_leverage_tiers_binance(default_conf, mocker): 'ADA/BUSD': [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 100000, - "notionalCap": 500000, + "minNotional": 100000, + "maxNotional": 500000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "maxNotional": "500000", + "minNotional": "100000", "maintMarginRatio": "0.05", "cum": "2500.0" } }, { "tier": 3, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "3", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.1", "cum": "27500.0" } }, { "tier": 4, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.15, "maxLeverage": 3, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.15", "cum": "77500.0" } }, { "tier": 5, - "notionalFloor": 2000000, - "notionalCap": 5000000, + "minNotional": 2000000, + "maxNotional": 5000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "maxNotional": "5000000", + "minNotional": "2000000", "maintMarginRatio": "0.25", "cum": "277500.0" } }, { "tier": 6, - "notionalFloor": 5000000, - "notionalCap": 30000000, + "minNotional": 5000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "5000000", + "maxNotional": "30000000", + "minNotional": "5000000", "maintMarginRatio": "0.5", "cum": "1527500.0" } @@ -261,105 +261,105 @@ def test_fill_leverage_tiers_binance(default_conf, mocker): "ZEC/USDT": [ { "tier": 1, - "notionalFloor": 0, - "notionalCap": 50000, + "minNotional": 0, + "maxNotional": 50000, "maintenanceMarginRate": 0.01, "maxLeverage": 50, "info": { "bracket": "1", "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", + "maxNotional": "50000", + "minNotional": "0", "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2, - "notionalFloor": 50000, - "notionalCap": 150000, + "minNotional": 50000, + "maxNotional": 150000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "150000", - "notionalFloor": "50000", + "maxNotional": "150000", + "minNotional": "50000", "maintMarginRatio": "0.025", "cum": "750.0" } }, { "tier": 3, - "notionalFloor": 150000, - "notionalCap": 250000, + "minNotional": 150000, + "maxNotional": 250000, "maintenanceMarginRate": 0.05, "maxLeverage": 10, "info": { "bracket": "3", "initialLeverage": "10", - "notionalCap": "250000", - "notionalFloor": "150000", + "maxNotional": "250000", + "minNotional": "150000", "maintMarginRatio": "0.05", "cum": "4500.0" } }, { "tier": 4, - "notionalFloor": 250000, - "notionalCap": 500000, + "minNotional": 250000, + "maxNotional": 500000, "maintenanceMarginRate": 0.1, "maxLeverage": 5, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "250000", + "maxNotional": "500000", + "minNotional": "250000", "maintMarginRatio": "0.1", "cum": "17000.0" } }, { "tier": 5, - "notionalFloor": 500000, - "notionalCap": 1000000, + "minNotional": 500000, + "maxNotional": 1000000, "maintenanceMarginRate": 0.125, "maxLeverage": 4, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "500000", + "maxNotional": "1000000", + "minNotional": "500000", "maintMarginRatio": "0.125", "cum": "29500.0" } }, { "tier": 6, - "notionalFloor": 1000000, - "notionalCap": 2000000, + "minNotional": 1000000, + "maxNotional": 2000000, "maintenanceMarginRate": 0.25, "maxLeverage": 2, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "maxNotional": "2000000", + "minNotional": "1000000", "maintMarginRatio": "0.25", "cum": "154500.0" } }, { "tier": 7, - "notionalFloor": 2000000, - "notionalCap": 30000000, + "minNotional": 2000000, + "maxNotional": 30000000, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "2000000", + "maxNotional": "30000000", + "minNotional": "2000000", "maintMarginRatio": "0.5", "cum": "654500.0" } diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index 14e45c8b0..2a148c388 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -369,25 +369,25 @@ class TestCCXTExchange(): pair_tiers = leverage_tiers[futures_pair] assert len(pair_tiers) > 0 oldLeverage = float('inf') - oldMaintenanceMarginRate = oldNotionalFloor = oldNotionalCap = -1 + oldMaintenanceMarginRate = oldminNotional = oldmaxNotional = -1 for tier in pair_tiers: for key in [ 'maintenanceMarginRate', - 'notionalFloor', - 'notionalCap', + 'minNotional', + 'maxNotional', 'maxLeverage' ]: assert key in tier assert tier[key] >= 0.0 - assert tier['notionalCap'] > tier['notionalFloor'] + assert tier['maxNotional'] > tier['minNotional'] assert tier['maxLeverage'] <= oldLeverage assert tier['maintenanceMarginRate'] >= oldMaintenanceMarginRate - assert tier['notionalFloor'] > oldNotionalFloor - assert tier['notionalCap'] > oldNotionalCap + assert tier['minNotional'] > oldminNotional + assert tier['maxNotional'] > oldmaxNotional oldLeverage = tier['maxLeverage'] oldMaintenanceMarginRate = tier['maintenanceMarginRate'] - oldNotionalFloor = tier['notionalFloor'] - oldNotionalCap = tier['notionalCap'] + oldminNotional = tier['minNotional'] + oldmaxNotional = tier['maxNotional'] def test_ccxt_dry_run_liquidation_price(self, exchange_futures): futures, futures_name = exchange_futures diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 1d560e2dd..250a97886 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -282,6 +282,10 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog): (2.34559, 2, 3, 1, 2.345, 'spot'), (2.9999, 2, 3, 1, 2.999, 'spot'), (2.9909, 2, 3, 1, 2.990, 'spot'), + (2.9909, 2, 0, 1, 2, 'spot'), + (29991.5555, 2, 0, 1, 29991, 'spot'), + (29991.5555, 2, -1, 1, 29990, 'spot'), + (29991.5555, 2, -2, 1, 29900, 'spot'), # Tests for Tick-size (2.34559, 4, 0.0001, 1, 2.3455, 'spot'), (2.34559, 4, 0.00001, 1, 2.34559, 'spot'), @@ -433,11 +437,11 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: ) # min result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss) - expected_result = 2 * (1+0.05) / (1-abs(stoploss)) + expected_result = 2 * (1 + 0.05) / (1 - abs(stoploss)) assert isclose(result, expected_result) # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss, 3.0) - assert isclose(result, expected_result/3) + assert isclose(result, expected_result / 3) # max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 10000 @@ -452,11 +456,11 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: PropertyMock(return_value=markets) ) result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = 2 * 2 * (1+0.05) / (1-abs(stoploss)) + expected_result = 2 * 2 * (1 + 0.05) / (1 - abs(stoploss)) assert isclose(result, expected_result) # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0) - assert isclose(result, expected_result/5) + assert isclose(result, expected_result / 5) # max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 20000 @@ -471,11 +475,11 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: PropertyMock(return_value=markets) ) result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = max(2, 2 * 2) * (1+0.05) / (1-abs(stoploss)) + expected_result = max(2, 2 * 2) * (1 + 0.05) / (1 - abs(stoploss)) assert isclose(result, expected_result) # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 10) - assert isclose(result, expected_result/10) + assert isclose(result, expected_result / 10) # min amount and cost are set (amount is minial) markets["ETH/BTC"]["limits"] = { @@ -487,11 +491,11 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: PropertyMock(return_value=markets) ) result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = max(8, 2 * 2) * (1+0.05) / (1-abs(stoploss)) + expected_result = max(8, 2 * 2) * (1 + 0.05) / (1 - abs(stoploss)) assert isclose(result, expected_result) # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 7.0) - assert isclose(result, expected_result/7.0) + assert isclose(result, expected_result / 7.0) # Max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 1000 @@ -501,7 +505,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: assert isclose(result, expected_result) # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4, 8.0) - assert isclose(result, expected_result/8.0) + assert isclose(result, expected_result / 8.0) # Max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 1000 @@ -512,7 +516,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: assert isclose(result, expected_result) # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0) - assert isclose(result, expected_result/12) + assert isclose(result, expected_result / 12) # Max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 1000 @@ -540,7 +544,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: ) # With Leverage, Contract size 10 result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0) - assert isclose(result, (expected_result/12) * 10.0) + assert isclose(result, (expected_result / 12) * 10.0) # Max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 10000 @@ -561,7 +565,7 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None: PropertyMock(return_value=markets) ) result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss) - expected_result = max(0.0001, 0.001 * 0.020405) * (1+0.05) / (1-abs(stoploss)) + expected_result = max(0.0001, 0.001 * 0.020405) * (1 + 0.05) / (1 - abs(stoploss)) assert round(result, 8) == round(expected_result, 8) # Max result = exchange.get_max_pair_stake_amount('ETH/BTC', 2.0) @@ -569,12 +573,12 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None: # Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0) - assert round(result, 8) == round(expected_result/3, 8) + assert round(result, 8) == round(expected_result / 3, 8) # Contract_size markets["ETH/BTC"]["contractSize"] = 0.1 result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0) - assert round(result, 8) == round((expected_result/3), 8) + assert round(result, 8) == round((expected_result / 3), 8) # Max result = exchange.get_max_pair_stake_amount('ETH/BTC', 12.0) @@ -956,7 +960,7 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') with pytest.raises(OperationalException, match=r'The ccxt library does not provide the list of timeframes ' - r'for the exchange ".*" and this exchange ' + r'for the exchange .* and this exchange ' r'is therefore not supported. *'): Exchange(default_conf) @@ -977,7 +981,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') with pytest.raises(OperationalException, match=r'The ccxt library does not provide the list of timeframes ' - r'for the exchange ".*" and this exchange ' + r'for the exchange .* and this exchange ' r'is therefore not supported. *'): Exchange(default_conf) @@ -2030,6 +2034,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') + 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: @@ -2766,9 +2784,10 @@ async def test__async_get_trade_history_time(default_conf, mocker, caplog, excha # Monkey-patch async function exchange._api_async.fetch_trades = MagicMock(side_effect=mock_get_trade_hist) pair = 'ETH/BTC' - ret = await exchange._async_get_trade_history_time(pair, - since=fetch_trades_result[0]['timestamp'], - until=fetch_trades_result[-1]['timestamp']-1) + ret = await exchange._async_get_trade_history_time( + pair, + since=fetch_trades_result[0]['timestamp'], + until=fetch_trades_result[-1]['timestamp'] - 1) assert type(ret) is tuple assert ret[0] == pair assert type(ret[1]) is list @@ -2804,7 +2823,7 @@ async def test__async_get_trade_history_time_empty(default_conf, mocker, caplog, exchange._async_fetch_trades = MagicMock(side_effect=mock_get_trade_hist) pair = 'ETH/BTC' ret = await exchange._async_get_trade_history_time(pair, since=trades_history[0][0], - until=trades_history[-1][0]-1) + until=trades_history[-1][0] - 1) assert type(ret) is tuple assert ret[0] == pair assert type(ret[1]) is list @@ -4577,8 +4596,8 @@ def test_load_leverage_tiers(mocker, default_conf, leverage_tiers, exchange_name 'ADA/USDT:USDT': [ { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 500, + 'minNotional': 0, + 'maxNotional': 500, 'maintenanceMarginRate': 0.02, 'maxLeverage': 75, 'info': { @@ -4618,8 +4637,8 @@ def test_load_leverage_tiers(mocker, default_conf, leverage_tiers, exchange_name 'ADA/USDT:USDT': [ { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 500, + 'minNotional': 0, + 'maxNotional': 500, 'maintenanceMarginRate': 0.02, 'maxLeverage': 75, 'info': { @@ -4654,15 +4673,15 @@ def test_parse_leverage_tier(mocker, default_conf): tier = { "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, + "minNotional": 0, + "maxNotional": 100000, "maintenanceMarginRate": 0.025, "maxLeverage": 20, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", + "maxNotional": "100000", + "minNotional": "0", "maintMarginRatio": "0.025", "cum": "0.0" } @@ -4678,8 +4697,8 @@ def test_parse_leverage_tier(mocker, default_conf): tier2 = { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 2000, + 'minNotional': 0, + 'maxNotional': 2000, 'maintenanceMarginRate': 0.01, 'maxLeverage': 75, 'info': { diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 8ecdf6904..37c1ea974 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -19,8 +19,8 @@ def test_get_maintenance_ratio_and_amt_okx( 'ETH/USDT:USDT': [ { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 2000, + 'minNotional': 0, + 'maxNotional': 2000, 'maintenanceMarginRate': 0.01, 'maxLeverage': 75, 'info': { @@ -39,8 +39,8 @@ def test_get_maintenance_ratio_and_amt_okx( }, { 'tier': 2, - 'notionalFloor': 2001, - 'notionalCap': 4000, + 'minNotional': 2001, + 'maxNotional': 4000, 'maintenanceMarginRate': 0.015, 'maxLeverage': 50, 'info': { @@ -59,8 +59,8 @@ def test_get_maintenance_ratio_and_amt_okx( }, { 'tier': 3, - 'notionalFloor': 4001, - 'notionalCap': 8000, + 'minNotional': 4001, + 'maxNotional': 8000, 'maintenanceMarginRate': 0.02, 'maxLeverage': 20, 'info': { @@ -81,8 +81,8 @@ def test_get_maintenance_ratio_and_amt_okx( 'ADA/USDT:USDT': [ { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 500, + 'minNotional': 0, + 'maxNotional': 500, 'maintenanceMarginRate': 0.02, 'maxLeverage': 75, 'info': { @@ -101,8 +101,8 @@ def test_get_maintenance_ratio_and_amt_okx( }, { 'tier': 2, - 'notionalFloor': 501, - 'notionalCap': 1000, + 'minNotional': 501, + 'maxNotional': 1000, 'maintenanceMarginRate': 0.025, 'maxLeverage': 50, 'info': { @@ -121,8 +121,8 @@ def test_get_maintenance_ratio_and_amt_okx( }, { 'tier': 3, - 'notionalFloor': 1001, - 'notionalCap': 2000, + 'minNotional': 1001, + 'maxNotional': 2000, 'maintenanceMarginRate': 0.03, 'maxLeverage': 20, 'info': { @@ -180,8 +180,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets): [ { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 500, + 'minNotional': 0, + 'maxNotional': 500, 'maintenanceMarginRate': 0.02, 'maxLeverage': 75, 'info': { @@ -200,8 +200,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets): }, { 'tier': 2, - 'notionalFloor': 501, - 'notionalCap': 1000, + 'minNotional': 501, + 'maxNotional': 1000, 'maintenanceMarginRate': 0.025, 'maxLeverage': 50, 'info': { @@ -220,8 +220,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets): }, { 'tier': 3, - 'notionalFloor': 1001, - 'notionalCap': 2000, + 'minNotional': 1001, + 'maxNotional': 2000, 'maintenanceMarginRate': 0.03, 'maxLeverage': 20, 'info': { @@ -242,8 +242,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets): [ { 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 2000, + 'minNotional': 0, + 'maxNotional': 2000, 'maintenanceMarginRate': 0.01, 'maxLeverage': 75, 'info': { @@ -262,8 +262,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets): }, { 'tier': 2, - 'notionalFloor': 2001, - 'notionalCap': 4000, + 'minNotional': 2001, + 'maxNotional': 4000, 'maintenanceMarginRate': 0.015, 'maxLeverage': 50, 'info': { @@ -282,8 +282,8 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets): }, { 'tier': 3, - 'notionalFloor': 4001, - 'notionalCap': 8000, + 'minNotional': 4001, + 'maxNotional': 8000, 'maintenanceMarginRate': 0.02, 'maxLeverage': 20, 'info': { diff --git a/tests/leverage/test_interest.py b/tests/leverage/test_interest.py index c7e787bdb..6b189ce50 100644 --- a/tests/leverage/test_interest.py +++ b/tests/leverage/test_interest.py @@ -6,7 +6,7 @@ import pytest from freqtrade.leverage import interest -ten_mins = Decimal(1/6) +ten_mins = Decimal(1 / 6) five_hours = Decimal(5.0) twentyfive_hours = Decimal(25.0) diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index fca204b52..ea13de4c8 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -251,7 +251,7 @@ tc15 = BTContainer(data=[ BTrade(exit_reason=ExitType.STOP_LOSS, open_tick=2, close_tick=2)] ) -# Test 16: Buy, hold for 65 min, then forcesell using roi=-1 +# Test 16: Buy, hold for 65 min, then forceexit using roi=-1 # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 65 minutes (limits trade duration) tc16 = BTContainer(data=[ @@ -259,14 +259,14 @@ tc16 = BTContainer(data=[ [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) + [3, 4975, 5000, 4940, 4962, 6172, 0, 0], # Forceexit on ROI (roi=-1) [4, 4962, 4987, 4950, 4950, 6172, 0, 0], [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "65": -1}, profit_perc=-0.012, trades=[BTrade(exit_reason=ExitType.ROI, open_tick=1, close_tick=3)] ) -# Test 17: Buy, hold for 120 mins, then forcesell using roi=-1 +# Test 17: Buy, hold for 120 mins, then forceexit using roi=-1 # Causes negative profit even though sell-reason is ROI. # stop-loss: 10%, ROI: 10% (should not apply), -100% after 100 minutes (limits trade duration) # Uses open as sell-rate (special case) - since the roi-time is a multiple of the timeframe. @@ -275,7 +275,7 @@ tc17 = BTContainer(data=[ [0, 5000, 5025, 4975, 4987, 6172, 1, 0], [1, 5000, 5025, 4975, 4987, 6172, 0, 0], [2, 4987, 5300, 4950, 5050, 6172, 0, 0], - [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # ForceSell on ROI (roi=-1) + [3, 4980, 5000, 4940, 4962, 6172, 0, 0], # Forceexit on ROI (roi=-1) [4, 4962, 4987, 4950, 4950, 6172, 0, 0], [5, 4950, 4975, 4925, 4950, 6172, 0, 0]], stop_loss=-0.10, roi={"0": 0.10, "120": -1}, profit_perc=-0.004, diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 21b5fcb09..0abf80043 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -22,7 +22,7 @@ from freqtrade.data.history import get_timerange from freqtrade.enums import ExitType, RunMode from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.exchange.exchange import timeframe_to_next_date -from freqtrade.misc import get_strategy_run_id +from freqtrade.optimize.backtest_caching import get_strategy_run_id from freqtrade.optimize.backtesting import Backtesting from freqtrade.persistence import LocalTrade from freqtrade.resolvers import StrategyResolver @@ -312,6 +312,7 @@ def test_backtesting_init(mocker, default_conf, order_types) -> None: get_fee.assert_called() assert backtesting.fee == 0.5 assert not backtesting.strategy.order_types["stoploss_on_exchange"] + assert backtesting.strategy.bot_started is True def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None: @@ -384,14 +385,16 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None: mocker.patch('freqtrade.optimize.backtesting.generate_backtest_stats') mocker.patch('freqtrade.optimize.backtesting.show_backtest_results') sbs = mocker.patch('freqtrade.optimize.backtesting.store_backtest_stats') + sbc = mocker.patch('freqtrade.optimize.backtesting.store_backtest_signal_candles') mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', PropertyMock(return_value=['UNITTEST/BTC'])) default_conf['timeframe'] = '1m' default_conf['datadir'] = testdatadir - default_conf['export'] = 'trades' + default_conf['export'] = 'signals' default_conf['exportfilename'] = 'export.txt' default_conf['timerange'] = '-1510694220' + default_conf['runmode'] = RunMode.BACKTEST backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) @@ -407,6 +410,7 @@ def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None: assert backtesting.strategy.dp._pairlists is not None assert backtesting.strategy.bot_loop_start.call_count == 1 assert sbs.call_count == 1 + assert sbc.call_count == 1 def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) -> None: @@ -497,7 +501,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti Backtesting(default_conf) # Multiple strategies - default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'TestStrategyLegacyV1'] + default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'StrategyTestV2'] with pytest.raises(OperationalException, match='PrecisionFilter not allowed for backtesting multiple strategies.'): Backtesting(default_conf) @@ -711,7 +715,7 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None: ) # No data available. - res = backtesting._get_sell_trade_entry(trade, row_sell) + res = backtesting._get_exit_trade_entry(trade, row_sell) assert res is not None assert res.exit_reason == ExitType.ROI.value assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc) @@ -724,13 +728,13 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None: [], columns=['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long', 'enter_short', 'exit_short', 'long_tag', 'short_tag', 'exit_tag']) - res = backtesting._get_sell_trade_entry(trade, row) + res = backtesting._get_exit_trade_entry(trade, row) assert res is None # Assign backtest-detail data backtesting.detail_data[pair] = row_detail - res = backtesting._get_sell_trade_entry(trade, row_sell) + res = backtesting._get_exit_trade_entry(trade, row_sell) assert res is not None assert res.exit_reason == ExitType.ROI.value @@ -1196,7 +1200,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): '--disable-max-market-positions', '--strategy-list', CURRENT_TEST_STRATEGY, - 'TestStrategyLegacyV1', + 'StrategyTestV2', ] args = get_args(args) start_backtesting(args) @@ -1219,14 +1223,13 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'up to 2017-11-14 22:58:00 (0 days).', 'Parameter --enable-position-stacking detected ...', f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}', - 'Running backtesting for Strategy TestStrategyLegacyV1', + 'Running backtesting for Strategy StrategyTestV2', ] for line in exists: assert log_has(line, caplog) -@pytest.mark.filterwarnings("ignore:deprecated") def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys): default_conf.update({ "use_exit_signal": True, @@ -1308,7 +1311,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat '--breakdown', 'day', '--strategy-list', CURRENT_TEST_STRATEGY, - 'TestStrategyLegacyV1', + 'StrategyTestV2', ] args = get_args(args) start_backtesting(args) @@ -1325,7 +1328,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'up to 2017-11-14 22:58:00 (0 days).', 'Parameter --enable-position-stacking detected ...', f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}', - 'Running backtesting for Strategy TestStrategyLegacyV1', + 'Running backtesting for Strategy StrategyTestV2', ] for line in exists: @@ -1340,6 +1343,39 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat assert 'STRATEGY SUMMARY' in captured.out +@pytest.mark.filterwarnings("ignore:deprecated") +def test_backtest_start_futures_noliq(default_conf_usdt, mocker, + caplog, testdatadir, capsys): + # Tests detail-data loading + default_conf_usdt.update({ + "trading_mode": "futures", + "margin_mode": "isolated", + "use_exit_signal": True, + "exit_profit_only": False, + "exit_profit_offset": 0.0, + "ignore_roi_if_entry_signal": False, + "strategy": CURRENT_TEST_STRATEGY, + }) + patch_exchange(mocker) + + mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', + PropertyMock(return_value=['HULUMULU/USDT', 'XRP/USDT'])) + # mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) + + patched_configuration_load_config_file(mocker, default_conf_usdt) + + args = [ + 'backtesting', + '--config', 'config.json', + '--datadir', str(testdatadir), + '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), + '--timeframe', '1h', + ] + args = get_args(args) + with pytest.raises(OperationalException, match=r"Pairs .* got no leverage tiers available\."): + start_backtesting(args) + + @pytest.mark.filterwarnings("ignore:deprecated") def test_backtest_start_nomock_futures(default_conf_usdt, mocker, caplog, testdatadir, capsys): @@ -1590,7 +1626,7 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda min_backtest_date = now - timedelta(weeks=4) load_backtest_metadata = MagicMock(return_value={ 'StrategyTestV2': {'run_id': '1', 'backtest_start_time': now.timestamp()}, - 'TestStrategyLegacyV1': {'run_id': run_id, 'backtest_start_time': start_time.timestamp()} + 'StrategyTestV3': {'run_id': run_id, 'backtest_start_time': start_time.timestamp()} }) load_backtest_stats = MagicMock(side_effect=[ { @@ -1599,9 +1635,9 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda 'strategy_comparison': [{'key': 'StrategyTestV2'}] }, { - 'metadata': {'TestStrategyLegacyV1': {'run_id': '2'}}, - 'strategy': {'TestStrategyLegacyV1': {}}, - 'strategy_comparison': [{'key': 'TestStrategyLegacyV1'}] + 'metadata': {'StrategyTestV3': {'run_id': '2'}}, + 'strategy': {'StrategyTestV3': {}}, + 'strategy_comparison': [{'key': 'StrategyTestV3'}] } ]) mocker.patch('pathlib.Path.glob', return_value=[ @@ -1625,7 +1661,7 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda '--cache', cache, '--strategy-list', 'StrategyTestV2', - 'TestStrategyLegacyV1', + 'StrategyTestV3', ] args = get_args(args) start_backtesting(args) @@ -1647,7 +1683,7 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda assert backtestmock.call_count == 2 exists = [ 'Running backtesting for Strategy StrategyTestV2', - 'Running backtesting for Strategy TestStrategyLegacyV1', + 'Running backtesting for Strategy StrategyTestV3', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).', ] @@ -1655,12 +1691,12 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda assert backtestmock.call_count == 0 exists = [ 'Reusing result of previous backtest for StrategyTestV2', - 'Reusing result of previous backtest for TestStrategyLegacyV1', + 'Reusing result of previous backtest for StrategyTestV3', ] else: exists = [ 'Reusing result of previous backtest for StrategyTestV2', - 'Running backtesting for Strategy TestStrategyLegacyV1', + 'Running backtesting for Strategy StrategyTestV3', 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', 'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).', ] diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index f0f436a43..8241a5362 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -94,6 +94,7 @@ def test_edge_init(mocker, edge_conf) -> None: assert edge_cli.config == edge_conf assert edge_cli.config['stake_amount'] == 'unlimited' assert callable(edge_cli.edge.calculate) + assert edge_cli.strategy.bot_started is True def test_edge_init_fee(mocker, edge_conf) -> None: diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index ce6ae1880..75944390e 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -41,6 +41,7 @@ def generate_result_metrics(): 'max_drawdown_abs': 0.001, 'loss': 0.001, 'is_initial_point': 0.001, + 'is_random': False, 'is_best': 1, } @@ -247,6 +248,7 @@ def test_log_results_if_loss_improves(hyperopt, capsys) -> None: 'total_profit': 0, 'current_epoch': 2, # This starts from 1 (in a human-friendly manner) 'is_initial_point': False, + 'is_random': False, 'is_best': True } ) diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index e3f6daf6c..be1c313f6 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock import pytest from freqtrade.exceptions import OperationalException -from freqtrade.optimize.hyperopt_loss_short_trade_dur import ShortTradeDurHyperOptLoss +from freqtrade.optimize.hyperopt_loss.hyperopt_loss_short_trade_dur import ShortTradeDurHyperOptLoss from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver @@ -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 ad9bcd978..ff8d420b3 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -2,6 +2,7 @@ import re from datetime import timedelta from pathlib import Path +import joblib import pandas as pd import pytest from arrow import Arrow @@ -19,6 +20,7 @@ from freqtrade.optimize.optimize_reports import (_get_resample_from_period, gene generate_periodic_breakdown_stats, generate_strategy_comparison, generate_trading_stats, show_sorted_pairlist, + store_backtest_signal_candles, store_backtest_stats, text_table_bt_results, text_table_exit_reason, text_table_strategy) from freqtrade.resolvers.strategy_resolver import StrategyResolver @@ -190,7 +192,7 @@ def test_store_backtest_stats(testdatadir, mocker): assert dump_mock.call_count == 3 assert isinstance(dump_mock.call_args_list[0][0][0], Path) - assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir/'backtest-result')) + assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / 'backtest-result')) dump_mock.reset_mock() filename = testdatadir / 'testresult.json' @@ -201,6 +203,62 @@ def test_store_backtest_stats(testdatadir, mocker): assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / 'testresult')) +def test_store_backtest_candles(testdatadir, mocker): + + dump_mock = mocker.patch('freqtrade.optimize.optimize_reports.file_dump_joblib') + + candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}} + + # mock directory exporting + store_backtest_signal_candles(testdatadir, candle_dict) + + assert dump_mock.call_count == 1 + assert isinstance(dump_mock.call_args_list[0][0][0], Path) + assert str(dump_mock.call_args_list[0][0][0]).endswith(str('_signals.pkl')) + + dump_mock.reset_mock() + # mock file exporting + filename = Path(testdatadir / 'testresult') + store_backtest_signal_candles(filename, candle_dict) + assert dump_mock.call_count == 1 + assert isinstance(dump_mock.call_args_list[0][0][0], Path) + # result will be testdatadir / testresult-_signals.pkl + assert str(dump_mock.call_args_list[0][0][0]).endswith(str('_signals.pkl')) + dump_mock.reset_mock() + + +def test_write_read_backtest_candles(tmpdir): + + candle_dict = {'DefStrat': {'UNITTEST/BTC': pd.DataFrame()}} + + # test directory exporting + stored_file = store_backtest_signal_candles(Path(tmpdir), candle_dict) + scp = open(stored_file, "rb") + pickled_signal_candles = joblib.load(scp) + scp.close() + + assert pickled_signal_candles.keys() == candle_dict.keys() + assert pickled_signal_candles['DefStrat'].keys() == pickled_signal_candles['DefStrat'].keys() + assert pickled_signal_candles['DefStrat']['UNITTEST/BTC'] \ + .equals(pickled_signal_candles['DefStrat']['UNITTEST/BTC']) + + _clean_test_file(stored_file) + + # test file exporting + filename = Path(tmpdir / 'testresult') + stored_file = store_backtest_signal_candles(filename, candle_dict) + scp = open(stored_file, "rb") + pickled_signal_candles = joblib.load(scp) + scp.close() + + assert pickled_signal_candles.keys() == candle_dict.keys() + assert pickled_signal_candles['DefStrat'].keys() == pickled_signal_candles['DefStrat'].keys() + assert pickled_signal_candles['DefStrat']['UNITTEST/BTC'] \ + .equals(pickled_signal_candles['DefStrat']['UNITTEST/BTC']) + + _clean_test_file(stored_file) + + def test_generate_pair_metrics(): results = pd.DataFrame( @@ -228,7 +286,7 @@ def test_generate_pair_metrics(): def test_generate_daily_stats(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) res = generate_daily_stats(bt_data) assert isinstance(res, dict) @@ -248,7 +306,7 @@ def test_generate_daily_stats(testdatadir): def test_generate_trading_stats(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) res = generate_trading_stats(bt_data) assert isinstance(res, dict) @@ -332,7 +390,7 @@ def test_generate_sell_reason_stats(): def test_text_table_strategy(testdatadir): - filename = testdatadir / "backtest-result_multistrat.json" + filename = testdatadir / "backtest_results/backtest-result_multistrat.json" bt_res_data = load_backtest_stats(filename) bt_res_data_comparison = bt_res_data.pop('strategy_comparison') @@ -364,7 +422,7 @@ def test_generate_edge_table(): def test_generate_periodic_breakdown_stats(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename).to_dict(orient='records') res = generate_periodic_breakdown_stats(bt_data, 'day') @@ -392,7 +450,7 @@ def test__get_resample_from_period(): def test_show_sorted_pairlist(testdatadir, default_conf, capsys): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_stats(filename) default_conf['backtest_show_pair_list'] = True 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..b2dc99610 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, )) @@ -242,7 +268,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 +279,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,14 +291,14 @@ 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, + 'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value, min_ago_open=20, min_ago_close=10, profit_rate=1.15, )) assert not freqtrade.protections.stop_per_pair('XRP/BTC') assert not PairLocks.is_pair_locked('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=110, min_ago_close=20, profit_rate=0.8, )) @@ -300,15 +326,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 +342,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 +352,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 +365,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 +375,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 b852ab6e7..2495636bf 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -52,7 +52,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: assert results[0] == { 'trade_id': 1, 'pair': 'ETH/BTC', - 'base_currency': 'BTC', + 'base_currency': 'ETH', + 'quote_currency': 'BTC', 'open_date': ANY, 'open_timestamp': ANY, 'is_open': ANY, @@ -136,7 +137,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: assert results[0] == { 'trade_id': 1, 'pair': 'ETH/BTC', - 'base_currency': 'BTC', + 'base_currency': 'ETH', + 'quote_currency': 'BTC', 'open_date': ANY, 'open_timestamp': ANY, 'is_open': ANY, diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 3e1710c8e..ac2f1c3ec 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -13,7 +13,6 @@ import uvicorn from fastapi import FastAPI from fastapi.exceptions import HTTPException from fastapi.testclient import TestClient -from numpy import isnan from requests.auth import _basic_auth_str from freqtrade.__init__ import __version__ @@ -931,6 +930,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short, 'open_order': None, 'open_rate': 0.123, 'pair': 'ETH/BTC', + 'base_currency': 'ETH', + 'quote_currency': 'BTC', 'stake_amount': 0.001, 'stop_loss_abs': ANY, 'stop_loss_pct': ANY, @@ -983,7 +984,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short, assert_response(rc) resp_values = rc.json() assert len(resp_values) == 4 - assert isnan(resp_values[0]['profit_abs']) + assert resp_values[0]['profit_abs'] is None def test_api_version(botclient): @@ -1097,7 +1098,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): # Test creating trade fbuy_mock = MagicMock(return_value=Trade( - pair='ETH/ETH', + pair='ETH/BTC', amount=1, amount_requested=1, exchange='binance', @@ -1130,7 +1131,9 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): 'open_date': ANY, 'open_timestamp': ANY, 'open_rate': 0.245441, - 'pair': 'ETH/ETH', + 'pair': 'ETH/BTC', + 'base_currency': 'ETH', + 'quote_currency': 'BTC', 'stake_amount': 1, 'stop_loss_abs': None, 'stop_loss_pct': None, @@ -1178,7 +1181,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): } -def test_api_forcesell(botclient, mocker, ticker, fee, markets): +def test_api_forceexit(botclient, mocker, ticker, fee, markets): ftbot, client = botclient mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -1190,15 +1193,15 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): ) patch_get_signal(ftbot) - rc = client_post(client, f"{BASE_URI}/forcesell", + rc = client_post(client, f"{BASE_URI}/forceexit", data='{"tradeid": "1"}') assert_response(rc, 502) - assert rc.json() == {"error": "Error querying /api/v1/forcesell: invalid argument"} + assert rc.json() == {"error": "Error querying /api/v1/forceexit: invalid argument"} Trade.query.session.rollback() ftbot.enter_positions() - rc = client_post(client, f"{BASE_URI}/forcesell", + rc = client_post(client, f"{BASE_URI}/forceexit", data='{"tradeid": "1"}') assert_response(rc) assert rc.json() == {'result': 'Created sell order for trade 1.'} @@ -1385,7 +1388,6 @@ def test_api_strategies(botclient): 'StrategyTestV2', 'StrategyTestV3', 'StrategyTestV3Futures', - 'TestStrategyLegacyV1', ]} @@ -1481,7 +1483,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir): assert not result['running'] assert result['status_msg'] == 'Backtest reset' ftbot.config['export'] = 'trades' - ftbot.config['backtest_cache'] = 'none' + ftbot.config['backtest_cache'] = 'day' ftbot.config['user_data_dir'] = Path(tmpdir) ftbot.config['exportfilename'] = Path(tmpdir) / "backtest_results" ftbot.config['exportfilename'].mkdir() @@ -1554,19 +1556,19 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir): ApiServer._bgtask_running = False - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy', - side_effect=DependencyException()) - rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data)) - assert log_has("Backtesting caused an error: ", caplog) - - ftbot.config['backtest_cache'] = 'day' - # Rerun backtest (should get previous result) rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data)) assert_response(rc) result = rc.json() assert log_has_re('Reusing result of previous backtest.*', caplog) + data['stake_amount'] = 101 + + mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest_one_strategy', + side_effect=DependencyException()) + rc = client_post(client, f"{BASE_URI}/backtest", data=json.dumps(data)) + assert log_has("Backtesting caused an error: ", caplog) + # Delete backtesting to avoid leakage since the backtest-object may stick around. rc = client_delete(client, f"{BASE_URI}/backtest") assert_response(rc) @@ -1577,6 +1579,38 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir): assert result['status_msg'] == 'Backtest reset' +def test_api_backtest_history(botclient, mocker, testdatadir): + ftbot, client = botclient + mocker.patch('freqtrade.data.btanalysis._get_backtest_files', + return_value=[ + testdatadir / 'backtest_results/backtest-result_multistrat.json', + testdatadir / 'backtest_results/backtest-result_new.json' + ]) + + rc = client_get(client, f"{BASE_URI}/backtest/history") + assert_response(rc, 502) + ftbot.config['user_data_dir'] = testdatadir + ftbot.config['runmode'] = RunMode.WEBSERVER + + rc = client_get(client, f"{BASE_URI}/backtest/history") + assert_response(rc) + result = rc.json() + assert len(result) == 3 + fn = result[0]['filename'] + assert fn == "backtest-result_multistrat.json" + strategy = result[0]['strategy'] + rc = client_get(client, f"{BASE_URI}/backtest/history/result?filename={fn}&strategy={strategy}") + assert_response(rc) + result2 = rc.json() + assert result2 + assert result2['status'] == 'ended' + assert not result2['running'] + assert result2['progress'] == 1 + # Only one strategy loaded - even though we use multiresult + assert len(result2['backtest_result']['strategy']) == 1 + assert result2['backtest_result']['strategy'][strategy] + + def test_health(botclient): ftbot, client = botclient diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index af4ad089e..dc392c748 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -184,7 +184,8 @@ def test_telegram_status(default_conf, update, mocker) -> None: _rpc_trade_status=MagicMock(return_value=[{ 'trade_id': 1, 'pair': 'ETH/BTC', - 'base_currency': 'BTC', + 'base_currency': 'ETH', + 'quote_currency': 'BTC', 'open_date': arrow.utcnow(), 'close_date': None, 'open_rate': 1.099e-05, @@ -398,8 +399,8 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ') assert int(fields[0]) == 1 - assert 'L' in fields[1] - assert 'ETH/BTC' in fields[2] + # assert 'L' in fields[1] + assert 'ETH/BTC' in fields[1] assert msg_mock.call_count == 1 @@ -1016,7 +1017,7 @@ def test_reload_config_handle(default_conf, update, mocker) -> None: assert 'Reloading config' in msg_mock.call_args_list[0][0][0] -def test_telegram_forcesell_handle(default_conf, update, ticker, fee, +def test_telegram_forceexit_handle(default_conf, update, ticker, fee, ticker_sell_up, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) @@ -1044,7 +1045,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, # Increase the price and sell it mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', ticker_sell_up) - # /forcesell 1 + # /forceexit 1 context = MagicMock() context.args = ["1"] telegram._force_exit(update=update, context=context) @@ -1081,8 +1082,8 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, } == last_msg -def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, - ticker_sell_down, mocker) -> None: +def test_telegram_force_exit_down_handle(default_conf, update, ticker, fee, + ticker_sell_down, mocker) -> None: mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) @@ -1114,7 +1115,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, trade = Trade.query.first() assert trade - # /forcesell 1 + # /forceexit 1 context = MagicMock() context.args = ["1"] telegram._force_exit(update=update, context=context) @@ -1152,7 +1153,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, } == last_msg -def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None: +def test_forceexit_all_handle(default_conf, update, ticker, fee, mocker) -> None: patch_exchange(mocker) mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) @@ -1175,7 +1176,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None freqtradebot.enter_positions() msg_mock.reset_mock() - # /forcesell all + # /forceexit all context = MagicMock() context.args = ["all"] telegram._force_exit(update=update, context=context) @@ -1213,7 +1214,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None } == msg -def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: +def test_forceexit_handle_invalid(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) @@ -1222,26 +1223,17 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: # Trader is not running freqtradebot.state = State.STOPPED - # /forcesell 1 + # /forceexit 1 context = MagicMock() context.args = ["1"] telegram._force_exit(update=update, context=context) assert msg_mock.call_count == 1 assert 'not running' in msg_mock.call_args_list[0][0][0] - # No argument - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - context = MagicMock() - context.args = [] - telegram._force_exit(update=update, context=context) - assert msg_mock.call_count == 1 - assert "You must specify a trade-id or 'all'." in msg_mock.call_args_list[0][0][0] - # Invalid argument msg_mock.reset_mock() freqtradebot.state = State.RUNNING - # /forcesell 123456 + # /forceexit 123456 context = MagicMock() context.args = ["123456"] telegram._force_exit(update=update, context=context) @@ -1249,6 +1241,59 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: assert 'invalid argument' in msg_mock.call_args_list[0][0][0] +def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) -> None: + default_conf['max_open_trades'] = 4 + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + fetch_ticker=ticker, + get_fee=fee, + _is_dry_limit_order_filled=MagicMock(return_value=True), + ) + femock = mocker.patch('freqtrade.rpc.rpc.RPC._rpc_force_exit') + telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) + + patch_get_signal(freqtradebot) + + # /forceexit + context = MagicMock() + context.args = [] + telegram._force_exit(update=update, context=context) + # No pair + assert msg_mock.call_args_list[0][1]['msg'] == 'No open trade found.' + + # Create some test data + freqtradebot.enter_positions() + msg_mock.reset_mock() + + # /forceexit + telegram._force_exit(update=update, context=context) + keyboard = msg_mock.call_args_list[0][1]['keyboard'] + # 4 pairs + cancel + assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 5 + assert keyboard[-1][0].text == "Cancel" + + assert keyboard[1][0].callback_data == 'force_exit__2 ' + update = MagicMock() + update.callback_query = MagicMock() + update.callback_query.data = keyboard[1][0].callback_data + telegram._force_exit_inline(update, None) + assert update.callback_query.answer.call_count == 1 + assert update.callback_query.edit_message_text.call_count == 1 + assert femock.call_count == 1 + assert femock.call_args_list[0][0][0] == '2' + + # Retry exiting - but cancel instead + update.callback_query.reset_mock() + telegram._force_exit(update=update, context=context) + # Use cancel button + update.callback_query.data = keyboard[-1][0].callback_data + telegram._force_exit_inline(update, None) + query = update.callback_query + assert query.answer.call_count == 1 + assert query.edit_message_text.call_count == 1 + assert query.edit_message_text.call_args_list[-1][1]['text'] == "Force exit canceled." + + def test_force_enter_handle(default_conf, update, mocker) -> None: mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) diff --git a/tests/strategy/strats/broken_strats/legacy_strategy_v1.py b/tests/strategy/strats/broken_strats/legacy_strategy_v1.py new file mode 100644 index 000000000..f3b8c2696 --- /dev/null +++ b/tests/strategy/strats/broken_strats/legacy_strategy_v1.py @@ -0,0 +1,30 @@ +# type: ignore +from pandas import DataFrame + +from freqtrade.strategy import IStrategy + + +# Dummy strategy - no longer loads but raises an exception. +class TestStrategyLegacyV1(IStrategy): + + minimal_roi = { + "40": 0.0, + "30": 0.01, + "20": 0.02, + "0": 0.04 + } + stoploss = -0.10 + + timeframe = '5m' + + def populate_indicators(self, dataframe: DataFrame) -> DataFrame: + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame: + + return dataframe diff --git a/tests/strategy/strats/legacy_strategy_v1.py b/tests/strategy/strats/legacy_strategy_v1.py deleted file mode 100644 index bad2aa40d..000000000 --- a/tests/strategy/strats/legacy_strategy_v1.py +++ /dev/null @@ -1,85 +0,0 @@ - -# --- Do not remove these libs --- -# Add your lib to import here -import talib.abstract as ta -from pandas import DataFrame - -from freqtrade.strategy import IStrategy - - -# -------------------------------- - -# This class is a sample. Feel free to customize it. -class TestStrategyLegacyV1(IStrategy): - """ - This is a test strategy using the legacy function headers, which will be - removed in a future update. - Please do not use this as a template, but refer to user_data/strategy/sample_strategy.py - for a uptodate version of this template. - """ - - # Minimal ROI designed for the strategy. - # This attribute will be overridden if the config file contains "minimal_roi" - minimal_roi = { - "40": 0.0, - "30": 0.01, - "20": 0.02, - "0": 0.04 - } - - # Optimal stoploss designed for the strategy - # This attribute will be overridden if the config file contains "stoploss" - stoploss = -0.10 - - timeframe = '5m' - - def populate_indicators(self, dataframe: DataFrame) -> DataFrame: - """ - Adds several different TA indicators to the given DataFrame - - Performance Note: For the best performance be frugal on the number of indicators - you are using. Let uncomment only the indicator you are using in your strategies - or your hyperopt configuration, otherwise you will waste your memory and CPU usage. - """ - - # Momentum Indicator - # ------------------------------------ - - # ADX - dataframe['adx'] = ta.ADX(dataframe) - - # TEMA - Triple Exponential Moving Average - dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9) - - return dataframe - - def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: - """ - Based on TA indicators, populates the buy signal for the given dataframe - :param dataframe: DataFrame - :return: DataFrame with buy column - """ - dataframe.loc[ - ( - (dataframe['adx'] > 30) & - (dataframe['tema'] > dataframe['tema'].shift(1)) & - (dataframe['volume'] > 0) - ), - 'buy'] = 1 - - return dataframe - - def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame: - """ - Based on TA indicators, populates the sell signal for the given dataframe - :param dataframe: DataFrame - :return: DataFrame with buy column - """ - dataframe.loc[ - ( - (dataframe['adx'] > 70) & - (dataframe['tema'] < dataframe['tema'].shift(1)) & - (dataframe['volume'] > 0) - ), - 'sell'] = 1 - return dataframe diff --git a/tests/strategy/strats/strategy_test_v2.py b/tests/strategy/strats/strategy_test_v2.py index 8996b227a..85ff856e1 100644 --- a/tests/strategy/strats/strategy_test_v2.py +++ b/tests/strategy/strats/strategy_test_v2.py @@ -56,19 +56,6 @@ class StrategyTestV2(IStrategy): # By default this strategy does not use Position Adjustments position_adjustment_enable = False - def informative_pairs(self): - """ - Define additional, informative pair/interval combinations to be cached from the exchange. - These pair/interval combinations are non-tradeable, unless they are part - of the whitelist as well. - For more information, please consult the documentation - :return: List of tuples in the format (pair, interval) - Sample: return [("ETH/USDT", "5m"), - ("BTC/USDT", "15m"), - ] - """ - return [] - def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index 372e29412..df83d3663 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -82,6 +82,11 @@ class StrategyTestV3(IStrategy): # }) # return prot + bot_started = False + + def bot_start(self): + self.bot_started = True + def informative_pairs(self): return [] diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 44a17ac02..6e57a3182 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -523,7 +523,7 @@ def test_custom_exit(default_conf, fee, caplog) -> None: assert res.exit_type == ExitType.CUSTOM_EXIT assert res.exit_flag is True assert res.exit_reason == 'h' * 64 - assert log_has_re('Custom sell reason returned from custom_exit is too long.*', caplog) + assert log_has_re('Custom exit reason returned from custom_exit is too long.*', caplog) @pytest.mark.parametrize('side', TRADE_SIDES) @@ -666,27 +666,27 @@ 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): - default_conf.update({'strategy': 'TestStrategyLegacyV1'}) + default_conf.update({'strategy': 'StrategyTestV2'}) strategy = StrategyResolver.load_strategy(default_conf) # Should return empty # Uses fallback to base implementation diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 205fb4dac..244fd3919 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -68,6 +68,21 @@ def test_merge_informative_pair(): assert result.iloc[7]['date_1h'] == result.iloc[4]['date'] assert result.iloc[8]['date_1h'] == result.iloc[4]['date'] + informative = generate_test_data('1h', 40) + result = merge_informative_pair(data, informative, '15m', '1h', ffill=False) + # First 3 rows are empty + assert result.iloc[0]['date_1h'] is pd.NaT + assert result.iloc[1]['date_1h'] is pd.NaT + assert result.iloc[2]['date_1h'] is pd.NaT + # Next 4 rows contain the starting date (0:00) + assert result.iloc[3]['date_1h'] == result.iloc[0]['date'] + assert result.iloc[4]['date_1h'] is pd.NaT + assert result.iloc[5]['date_1h'] is pd.NaT + assert result.iloc[6]['date_1h'] is pd.NaT + # Next 4 rows contain the next Hourly date original date row 4 + assert result.iloc[7]['date_1h'] == result.iloc[4]['date'] + assert result.iloc[8]['date_1h'] is pd.NaT + def test_merge_informative_pair_same(): data = generate_test_data('15m', 40) @@ -164,7 +179,7 @@ def test_stoploss_from_absolute(): assert pytest.approx(stoploss_from_absolute(90, 100, True)) == 0 assert pytest.approx(stoploss_from_absolute(100, 100, True)) == 0 - assert pytest.approx(stoploss_from_absolute(110, 100, True)) == -(1 - (110/100)) + assert pytest.approx(stoploss_from_absolute(110, 100, True)) == -(1 - (110 / 100)) assert pytest.approx(stoploss_from_absolute(110, 100, True)) == 0.1 assert pytest.approx(stoploss_from_absolute(105, 100, True)) == 0.05 assert pytest.approx(stoploss_from_absolute(100, 0, True)) == 1 diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index e74a2a022..3ed1eb0ce 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -1,6 +1,5 @@ # pragma pylint: disable=missing-docstring, protected-access, C0103 import logging -import warnings from base64 import urlsafe_b64encode from pathlib import Path @@ -35,7 +34,7 @@ def test_search_all_strategies_no_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=False) assert isinstance(strategies, list) - assert len(strategies) == 6 + assert len(strategies) == 5 assert isinstance(strategies[0], dict) @@ -43,10 +42,10 @@ def test_search_all_strategies_with_failed(): directory = Path(__file__).parent / "strats" strategies = StrategyResolver.search_all_objects(directory, enum_failed=True) assert isinstance(strategies, list) - assert len(strategies) == 7 + assert len(strategies) == 6 # with enum_failed=True search_all_objects() shall find 2 good strategies # and 1 which fails to load - assert len([x for x in strategies if x['class'] is not None]) == 6 + assert len([x for x in strategies if x['class'] is not None]) == 5 assert len([x for x in strategies if x['class'] is None]) == 1 @@ -100,7 +99,7 @@ def test_load_strategy_noname(default_conf): @pytest.mark.filterwarnings("ignore:deprecated") -@pytest.mark.parametrize('strategy_name', ['StrategyTestV2', 'TestStrategyLegacyV1']) +@pytest.mark.parametrize('strategy_name', ['StrategyTestV2']) def test_strategy_pre_v3(result, default_conf, strategy_name): default_conf.update({'strategy': strategy_name}) @@ -346,40 +345,6 @@ def test_strategy_override_use_exit_profit_only(caplog, default_conf): assert log_has("Override strategy 'exit_profit_only' with value in config file: True.", caplog) -@pytest.mark.filterwarnings("ignore:deprecated") -def test_deprecate_populate_indicators(result, default_conf): - default_location = Path(__file__).parent / "strats" - default_conf.update({'strategy': 'TestStrategyLegacyV1', - 'strategy_path': default_location}) - strategy = StrategyResolver.load_strategy(default_conf) - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - indicators = strategy.advise_indicators(result, {'pair': 'ETH/BTC'}) - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "deprecated - check out the Sample strategy to see the current function headers!" \ - in str(w[-1].message) - - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - strategy.advise_entry(indicators, {'pair': 'ETH/BTC'}) - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "deprecated - check out the Sample strategy to see the current function headers!" \ - in str(w[-1].message) - - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - strategy.advise_exit(indicators, {'pair': 'ETH_BTC'}) - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "deprecated - check out the Sample strategy to see the current function headers!" \ - in str(w[-1].message) - - @pytest.mark.filterwarnings("ignore:deprecated") def test_missing_implements(default_conf, caplog): @@ -438,33 +403,14 @@ def test_missing_implements(default_conf, caplog): StrategyResolver.load_strategy(default_conf) -@pytest.mark.filterwarnings("ignore:deprecated") -def test_call_deprecated_function(result, default_conf, caplog): - default_location = Path(__file__).parent / "strats" +def test_call_deprecated_function(default_conf): + default_location = Path(__file__).parent / "strats/broken_strats/" del default_conf['timeframe'] default_conf.update({'strategy': 'TestStrategyLegacyV1', 'strategy_path': default_location}) - strategy = StrategyResolver.load_strategy(default_conf) - metadata = {'pair': 'ETH/BTC'} - - # Make sure we are using a legacy function - assert strategy._populate_fun_len == 2 - assert strategy._buy_fun_len == 2 - assert strategy._sell_fun_len == 2 - assert strategy.INTERFACE_VERSION == 1 - assert strategy.timeframe == '5m' - - indicator_df = strategy.advise_indicators(result, metadata=metadata) - assert isinstance(indicator_df, DataFrame) - assert 'adx' in indicator_df.columns - - enterdf = strategy.advise_entry(result, metadata=metadata) - assert isinstance(enterdf, DataFrame) - assert 'enter_long' in enterdf.columns - - exitdf = strategy.advise_exit(result, metadata=metadata) - assert isinstance(exitdf, DataFrame) - assert 'exit_long' in exitdf + with pytest.raises(OperationalException, + match=r"Strategy Interface v1 is no longer supported.*"): + StrategyResolver.load_strategy(default_conf) def test_strategy_interface_versioning(result, default_conf): @@ -472,10 +418,6 @@ def test_strategy_interface_versioning(result, default_conf): strategy = StrategyResolver.load_strategy(default_conf) metadata = {'pair': 'ETH/BTC'} - # Make sure we are using a legacy function - assert strategy._populate_fun_len == 3 - assert strategy._buy_fun_len == 3 - assert strategy._sell_fun_len == 3 assert strategy.INTERFACE_VERSION == 2 indicator_df = strategy.advise_indicators(result, metadata=metadata) diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 19355b9eb..db87c405f 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -18,7 +18,8 @@ from freqtrade.configuration.deprecated_settings import (check_conflicting_setti process_removed_setting, process_temporary_deprecated_settings) from freqtrade.configuration.environment_vars import flat_vars_to_nested_dict -from freqtrade.configuration.load_config import load_config_file, load_file, log_config_error_range +from freqtrade.configuration.load_config import (load_config_file, load_file, load_from_files, + log_config_error_range) from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL, ENV_VAR_PREFIX from freqtrade.enums import RunMode from freqtrade.exceptions import OperationalException @@ -206,6 +207,33 @@ def test_from_config(default_conf, mocker, caplog) -> None: assert isinstance(validated_conf['user_data_dir'], Path) +def test_from_recursive_files(testdatadir) -> None: + files = testdatadir / "testconfigs/testconfig.json" + + conf = Configuration.from_files([files]) + + assert conf + # Exchange comes from "the first config" + assert conf['exchange'] + # Pricing comes from the 2nd config + assert conf['entry_pricing'] + assert conf['entry_pricing']['price_side'] == "same" + assert conf['exit_pricing'] + # The other key comes from pricing2, which is imported by pricing.json. + # pricing.json is a level higher, therefore wins. + assert conf['exit_pricing']['price_side'] == "same" + + assert len(conf['config_files']) == 4 + assert 'testconfig.json' in conf['config_files'][0] + assert 'test_pricing_conf.json' in conf['config_files'][1] + assert 'test_base_config.json' in conf['config_files'][2] + assert 'test_pricing2_conf.json' in conf['config_files'][3] + + files = testdatadir / "testconfigs/recursive.json" + with pytest.raises(OperationalException, match="Config loop detected."): + load_from_files([files]) + + def test_print_config(default_conf, mocker, caplog) -> None: conf1 = deepcopy(default_conf) # Delete non-json elements from default_conf diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index c3ed0e3d8..98052ccc0 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -20,6 +20,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, @@ -421,7 +422,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) @@ -442,9 +443,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 @@ -718,12 +719,12 @@ def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker) (True, 'spot', 'gateio', None, 0.0, None), (False, 'spot', 'okx', None, 0.0, None), (True, 'spot', 'okx', None, 0.0, None), - (True, 'futures', 'binance', 'isolated', 0.0, 11.89108910891089), - (False, 'futures', 'binance', 'isolated', 0.0, 8.070707070707071), + (True, 'futures', 'binance', 'isolated', 0.0, 11.88151815181518), + (False, 'futures', 'binance', 'isolated', 0.0, 8.080471380471382), (True, 'futures', 'gateio', 'isolated', 0.0, 11.87413417771621), (False, 'futures', 'gateio', 'isolated', 0.0, 8.085708510208207), - (True, 'futures', 'binance', 'isolated', 0.05, 11.796534653465345), - (False, 'futures', 'binance', 'isolated', 0.05, 8.167171717171717), + (True, 'futures', 'binance', 'isolated', 0.05, 11.7874422442244), + (False, 'futures', 'binance', 'isolated', 0.05, 8.17644781144781), (True, 'futures', 'gateio', 'isolated', 0.05, 11.7804274688304), (False, 'futures', 'gateio', 'isolated', 0.05, 8.181423084697796), (True, 'futures', 'okx', 'isolated', 0.0, 11.87413417771621), @@ -846,6 +847,7 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order, assert trade.open_order_id is None assert trade.open_rate == 10 assert trade.stake_amount == round(order['average'] * order['filled'] / leverage, 8) + assert pytest.approx(trade.liquidation_price) == liq_price # In case of rejected or expired order and partially filled order['status'] = 'expired' @@ -933,8 +935,6 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order, assert trade.open_rate_requested == 10 # In case of custom entry price not float type - freqtrade.exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(0.01, 0.01)) - freqtrade.exchange.name = exchange_name order['status'] = 'open' order['id'] = '5568' freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price" @@ -947,7 +947,6 @@ def test_execute_entry(mocker, default_conf_usdt, fee, limit_order, trade.is_short = is_short assert trade assert trade.open_rate_requested == 10 - assert trade.liquidation_price == liq_price # In case of too high stake amount @@ -3232,7 +3231,7 @@ def test_execute_trade_exit_custom_exit_price( freqtrade.execute_trade_exit( trade=trade, limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'], - exit_check=ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL) + exit_check=ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL, exit_reason='foo') ) # Sell price must be different to default bid price @@ -3260,8 +3259,8 @@ def test_execute_trade_exit_custom_exit_price( 'profit_ratio': profit_ratio, 'stake_currency': 'USDT', 'fiat_currency': 'USD', - 'sell_reason': ExitType.EXIT_SIGNAL.value, - 'exit_reason': ExitType.EXIT_SIGNAL.value, + 'sell_reason': 'foo', + 'exit_reason': 'foo', 'open_date': ANY, 'close_date': ANY, 'close_rate': ANY, @@ -3680,6 +3679,7 @@ def test_exit_profit_only( }) freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short) + freqtrade.strategy.custom_exit = MagicMock(return_value=None) if exit_type == ExitType.EXIT_SIGNAL.value: freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) else: @@ -3688,10 +3688,15 @@ def test_exit_profit_only( freqtrade.enter_positions() trade = Trade.query.first() - trade.is_short = is_short + assert trade.is_short == is_short oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside) trade.update_trade(oobj) freqtrade.wallets.update() + if profit_only: + assert freqtrade.handle_trade(trade) is False + # Custom-exit is called + freqtrade.strategy.custom_exit.call_count == 1 + patch_get_signal(freqtrade, enter_long=False, exit_short=is_short, exit_long=not is_short) assert freqtrade.handle_trade(trade) is handle_first @@ -3806,13 +3811,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]) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 467a27566..166e25cb5 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -15,6 +15,7 @@ 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.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 @@ -119,7 +120,7 @@ def test_set_stop_loss_isolated_liq(fee): assert trade.stop_loss is None assert trade.initial_stop_loss is None - trade._set_stop_loss(0.1, (1.0/9.0)) + trade._set_stop_loss(0.1, (1.0 / 9.0)) assert trade.liquidation_price == 0.09 assert trade.stop_loss == 0.1 assert trade.initial_stop_loss == 0.1 @@ -160,7 +161,7 @@ def test_set_stop_loss_isolated_liq(fee): assert trade.stop_loss is None assert trade.initial_stop_loss is None - trade._set_stop_loss(0.08, (1.0/9.0)) + trade._set_stop_loss(0.08, (1.0 / 9.0)) assert trade.liquidation_price == 0.09 assert trade.stop_loss == 0.08 assert trade.initial_stop_loss == 0.08 @@ -171,13 +172,13 @@ def test_set_stop_loss_isolated_liq(fee): assert trade.initial_stop_loss == 0.08 trade.set_isolated_liq(0.07) - trade._set_stop_loss(0.1, (1.0/8.0)) + trade._set_stop_loss(0.1, (1.0 / 8.0)) assert trade.liquidation_price == 0.07 assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.08 # Stop doesn't move stop higher - trade._set_stop_loss(0.1, (1.0/9.0)) + trade._set_stop_loss(0.1, (1.0 / 9.0)) assert trade.liquidation_price == 0.07 assert trade.stop_loss == 0.07 assert trade.initial_stop_loss == 0.08 @@ -1209,6 +1210,27 @@ def test_migrate_new(mocker, default_conf, fee, caplog): PRIMARY KEY (id), CHECK (is_open IN (0, 1)) );""" + create_table_order = """CREATE TABLE orders ( + id INTEGER NOT NULL, + ft_trade_id INTEGER, + ft_order_side VARCHAR(25) NOT NULL, + ft_pair VARCHAR(25) NOT NULL, + ft_is_open BOOLEAN NOT NULL, + order_id VARCHAR(255) NOT NULL, + status VARCHAR(255), + symbol VARCHAR(25), + order_type VARCHAR(50), + side VARCHAR(25), + price FLOAT, + amount FLOAT, + filled FLOAT, + remaining FLOAT, + cost FLOAT, + order_date DATETIME, + order_filled_date DATETIME, + order_update_date DATETIME, + PRIMARY KEY (id) + );""" insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee, open_rate, stake_amount, amount, open_date, stop_loss, initial_stop_loss, max_rate, ticker_interval, @@ -1222,15 +1244,66 @@ def test_migrate_new(mocker, default_conf, fee, caplog): stake=default_conf.get("stake_amount"), amount=amount ) + insert_orders = f""" + insert into orders ( + ft_trade_id, + ft_order_side, + ft_pair, + ft_is_open, + order_id, + status, + symbol, + order_type, + side, + price, + amount, + filled, + remaining, + cost) + values ( + 1, + 'buy', + 'ETC/BTC', + 0, + 'buy_order', + 'closed', + 'ETC/BTC', + 'limit', + 'buy', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ), + ( + 1, + 'stoploss', + 'ETC/BTC', + 0, + 'stop_order_id222', + 'closed', + 'ETC/BTC', + 'limit', + 'sell', + 0.00258580, + {amount}, + {amount}, + 0, + {amount * 0.00258580} + ) + """ 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(create_table_order)) connection.execute(text("create index ix_trades_is_open on trades(is_open)")) connection.execute(text("create index ix_trades_pair on trades(pair)")) connection.execute(text(insert_table_old)) + connection.execute(text(insert_orders)) # fake previous backup connection.execute(text("create table trades_bak as select * from trades")) @@ -1267,8 +1340,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert trade.open_trade_value == trade._calc_open_trade_value() assert trade.close_profit_abs is None - assert log_has("Moving open orders to Orders table.", caplog) - orders = Order.query.all() + orders = trade.orders assert len(orders) == 2 assert orders[0].order_id == 'buy_order' assert orders[0].ft_order_side == 'buy' @@ -1277,7 +1349,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog): assert orders[1].ft_order_side == 'stoploss' -def test_migrate_mid_state(mocker, default_conf, fee, caplog): +def test_migrate_too_old(mocker, default_conf, fee, caplog): """ Test Database migration (starting with new pairformat) """ @@ -1301,6 +1373,7 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog): PRIMARY KEY (id), CHECK (is_open IN (0, 1)) );""" + insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date) VALUES ('binance', 'ETC/BTC', 1, {fee}, {fee}, @@ -1319,26 +1392,8 @@ def test_migrate_mid_state(mocker, default_conf, fee, caplog): connection.execute(text(insert_table_old)) # Run init to test migration - init_db(default_conf['db_url'], default_conf['dry_run']) - - assert len(Trade.query.filter(Trade.id == 1).all()) == 1 - trade = Trade.query.filter(Trade.id == 1).first() - assert trade.fee_open == fee.return_value - assert trade.fee_close == fee.return_value - assert trade.open_rate_requested is None - assert trade.close_rate_requested is None - assert trade.is_open == 1 - assert trade.amount == amount - assert trade.stake_amount == default_conf.get("stake_amount") - assert trade.pair == "ETC/BTC" - assert trade.exchange == "binance" - assert trade.max_rate == 0.0 - assert trade.stop_loss == 0.0 - assert trade.initial_stop_loss == 0.0 - assert trade.open_trade_value == trade._calc_open_trade_value() - assert log_has("trying trades_bak0", caplog) - assert log_has("Running database migration for trades - backup: trades_bak0, orders_bak0", - caplog) + with pytest.raises(OperationalException, match=r'Your database seems to be very old'): + init_db(default_conf['db_url'], default_conf['dry_run']) def test_migrate_get_last_sequence_ids(): @@ -1373,6 +1428,55 @@ def test_migrate_set_sequence_ids(): 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'], default_conf['dry_run']) + + 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', @@ -1561,6 +1665,8 @@ def test_to_json(fee): assert result == {'trade_id': None, 'pair': 'ADA/USDT', + 'base_currency': 'ADA', + 'quote_currency': 'USDT', 'is_open': None, 'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), 'open_timestamp': int(trade.open_date.timestamp() * 1000), @@ -1638,6 +1744,8 @@ def test_to_json(fee): assert result == {'trade_id': None, 'pair': 'XRP/BTC', + 'base_currency': 'XRP', + 'quote_currency': 'BTC', 'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), 'open_timestamp': int(trade.open_date.timestamp() * 1000), 'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"), diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 940639465..9ee7a75c6 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -10,7 +10,8 @@ from plotly.subplots import make_subplots from freqtrade.commands import start_plot_dataframe, start_plot_profit from freqtrade.configuration import TimeRange from freqtrade.data import history -from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data +from freqtrade.data.btanalysis import load_backtest_data +from freqtrade.data.metrics import create_cum_profit from freqtrade.exceptions import OperationalException from freqtrade.plot.plotting import (add_areas, add_indicators, add_profit, create_plotconfig, generate_candlestick_graph, generate_plot_filename, @@ -157,7 +158,7 @@ def test_plot_trades(testdatadir, caplog): assert fig == fig1 assert log_has("No trades found.", caplog) pair = "ADA/BTC" - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" trades = load_backtest_data(filename) trades = trades.loc[trades['pair'] == pair] @@ -298,7 +299,7 @@ def test_generate_plot_file(mocker, caplog): def test_add_profit(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" bt_data = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") @@ -318,7 +319,7 @@ def test_add_profit(testdatadir): def test_generate_profit_graph(testdatadir): - filename = testdatadir / "backtest-result_new.json" + filename = testdatadir / "backtest_results/backtest-result_new.json" trades = load_backtest_data(filename) timerange = TimeRange.parse_timerange("20180110-20180112") pairs = ["TRX/BTC", "XLM/BTC"] @@ -331,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" @@ -340,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) @@ -355,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) @@ -362,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): @@ -456,7 +466,7 @@ def test_plot_profit(default_conf, mocker, testdatadir): match=r"No trades found, cannot generate Profit-plot.*"): plot_profit(default_conf) - default_conf['exportfilename'] = testdatadir / "backtest-result_new.json" + default_conf['exportfilename'] = testdatadir / "backtest_results/backtest-result_new.json" plot_profit(default_conf) diff --git a/tests/testdata/.last_result.json b/tests/testdata/backtest_results/.last_result.json similarity index 100% rename from tests/testdata/.last_result.json rename to tests/testdata/backtest_results/.last_result.json diff --git a/tests/testdata/backtest-result_multistrat.json b/tests/testdata/backtest_results/backtest-result_multistrat.json similarity index 100% rename from tests/testdata/backtest-result_multistrat.json rename to tests/testdata/backtest_results/backtest-result_multistrat.json diff --git a/tests/testdata/backtest_results/backtest-result_multistrat.meta.json b/tests/testdata/backtest_results/backtest-result_multistrat.meta.json new file mode 100644 index 000000000..906edcece --- /dev/null +++ b/tests/testdata/backtest_results/backtest-result_multistrat.meta.json @@ -0,0 +1,10 @@ +{ + "StrategyTestV2": { + "run_id": "430d0271075ef327edbb23088f4db4ebe51a3dbf", + "backtest_start_time": 1648904006 + }, + "TestStrategy": { + "run_id": "110d0271075ef327edbb23085102b4ebe51a3d55", + "backtest_start_time": 1648904006 + } +} diff --git a/tests/testdata/backtest-result_new.json b/tests/testdata/backtest_results/backtest-result_new.json similarity index 100% rename from tests/testdata/backtest-result_new.json rename to tests/testdata/backtest_results/backtest-result_new.json diff --git a/tests/testdata/backtest_results/backtest-result_new.meta.json b/tests/testdata/backtest_results/backtest-result_new.meta.json new file mode 100644 index 000000000..57ecdb19d --- /dev/null +++ b/tests/testdata/backtest_results/backtest-result_new.meta.json @@ -0,0 +1,6 @@ +{ + "StrategyTestV3": { + "run_id": "430d0271075ef327edbb23088f4db4ebe51a3dbf", + "backtest_start_time": 1648904006 + } +} diff --git a/tests/testdata/testconfigs/recursive.json b/tests/testdata/testconfigs/recursive.json new file mode 100644 index 000000000..33ab12008 --- /dev/null +++ b/tests/testdata/testconfigs/recursive.json @@ -0,0 +1,6 @@ +{ + // This file fails as it's loading itself over and over + "add_config_files": [ + "./recursive.json" + ] +} diff --git a/tests/testdata/testconfigs/test_base_config.json b/tests/testdata/testconfigs/test_base_config.json new file mode 100644 index 000000000..d15c5890b --- /dev/null +++ b/tests/testdata/testconfigs/test_base_config.json @@ -0,0 +1,12 @@ +{ + "stake_currency": "", + "dry_run": true, + "exchange": { + "name": "", + "key": "", + "secret": "", + "pair_whitelist": [], + "ccxt_async_config": { + } + } +} diff --git a/tests/testdata/testconfigs/test_pricing2_conf.json b/tests/testdata/testconfigs/test_pricing2_conf.json new file mode 100644 index 000000000..094783a60 --- /dev/null +++ b/tests/testdata/testconfigs/test_pricing2_conf.json @@ -0,0 +1,18 @@ +{ + "entry_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "exit_pricing":{ + "price_side": "other", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0 + } +} diff --git a/tests/testdata/testconfigs/test_pricing_conf.json b/tests/testdata/testconfigs/test_pricing_conf.json new file mode 100644 index 000000000..59516d65e --- /dev/null +++ b/tests/testdata/testconfigs/test_pricing_conf.json @@ -0,0 +1,21 @@ +{ + "entry_pricing": { + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0, + "check_depth_of_market": { + "enabled": false, + "bids_to_ask_delta": 1 + } + }, + "exit_pricing":{ + "price_side": "same", + "use_order_book": true, + "order_book_top": 1, + "price_last_balance": 0.0 + }, + "add_config_files": [ + "./test_pricing2_conf.json" + ] +} diff --git a/tests/testdata/testconfigs/testconfig.json b/tests/testdata/testconfigs/testconfig.json new file mode 100644 index 000000000..87ed6daef --- /dev/null +++ b/tests/testdata/testconfigs/testconfig.json @@ -0,0 +1,6 @@ +{ + "add_config_files": [ + "test_base_config.json", + "test_pricing_conf.json" + ] +}