diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b359ac458..56d02e5bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-20.04, ubuntu-22.04 ] + os: [ "ubuntu-20.04", "ubuntu-22.04", "ubuntu-24.04" ] python-version: ["3.9", "3.10", "3.11", "3.12"] steps: @@ -55,7 +55,7 @@ jobs: - name: Installation - *nix run: | - python -m pip install --upgrade pip wheel + python -m pip install --upgrade "pip<=24.0" wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include @@ -192,7 +192,7 @@ jobs: - name: Installation (python) run: | - python -m pip install --upgrade pip wheel + python -m pip install --upgrade "pip<=24.0" wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include @@ -322,7 +322,7 @@ jobs: run: | $PSVersionTable Set-PSRepository psgallery -InstallationPolicy trusted - Install-Module -Name Pester -RequiredVersion 5.3.1 -Confirm:$false -Force + Install-Module -Name Pester -RequiredVersion 5.3.1 -Confirm:$false -Force -SkipPublisherCheck $Error.clear() Invoke-Pester -Path "tests" -CI if ($Error.Length -gt 0) {exit 1} @@ -422,7 +422,7 @@ jobs: - name: Installation - *nix run: | - python -m pip install --upgrade pip wheel + python -m pip install --upgrade "pip<=24.0" wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include @@ -533,12 +533,12 @@ jobs: - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.8.14 + uses: pypa/gh-action-pypi-publish@v1.9.0 with: repository-url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.8.14 + uses: pypa/gh-action-pypi-publish@v1.9.0 deploy-docker: @@ -577,11 +577,11 @@ jobs: docker version -f '{{.Server.Experimental}}' - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Available platforms run: echo ${{ steps.buildx.outputs.platforms }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af54ccf7d..1b344d872 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pycqa/flake8 - rev: "7.0.0" + rev: "7.1.0" hooks: - id: flake8 additional_dependencies: [Flake8-pyproject] @@ -16,10 +16,10 @@ repos: additional_dependencies: - types-cachetools==5.3.0.7 - types-filelock==3.2.7 - - types-requests==2.32.0.20240523 + - types-requests==2.32.0.20240622 - types-tabulate==0.9.0.20240106 - types-python-dateutil==2.9.0.20240316 - - SQLAlchemy==2.0.30 + - SQLAlchemy==2.0.31 # stages: [push] - repo: https://github.com/pycqa/isort @@ -31,7 +31,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.4.5' + rev: 'v0.4.10' hooks: - id: ruff diff --git a/Dockerfile b/Dockerfile index 9491f2e05..cedeafbe6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.3-slim-bookworm as base +FROM python:3.12.4-slim-bookworm as base # Setup env ENV LANG C.UTF-8 @@ -25,7 +25,7 @@ FROM base as python-deps RUN apt-get update \ && apt-get -y install build-essential libssl-dev git libffi-dev libgfortran5 pkg-config cmake gcc \ && apt-get clean \ - && pip install --upgrade pip wheel + && pip install --upgrade "pip<=24.0" wheel # Install TA-lib COPY build_helpers/* /tmp/ @@ -35,7 +35,7 @@ ENV LD_LIBRARY_PATH /usr/local/lib # Install dependencies COPY --chown=ftuser:ftuser requirements.txt requirements-hyperopt.txt /freqtrade/ USER ftuser -RUN pip install --user --no-cache-dir numpy \ +RUN pip install --user --no-cache-dir "numpy<2.0" \ && pip install --user --no-cache-dir -r requirements-hyperopt.txt # Copy dependencies to runtime-image diff --git a/build_helpers/TA_Lib-0.4.29-cp310-cp310-win_amd64.whl b/build_helpers/TA_Lib-0.4.29-cp310-cp310-win_amd64.whl deleted file mode 100644 index f7b8ca8ee..000000000 Binary files a/build_helpers/TA_Lib-0.4.29-cp310-cp310-win_amd64.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.29-cp311-cp311-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.29-cp311-cp311-linux_armv7l.whl deleted file mode 100644 index 4ec20aa11..000000000 Binary files a/build_helpers/TA_Lib-0.4.29-cp311-cp311-linux_armv7l.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.29-cp311-cp311-win_amd64.whl b/build_helpers/TA_Lib-0.4.29-cp311-cp311-win_amd64.whl deleted file mode 100644 index 509a5e710..000000000 Binary files a/build_helpers/TA_Lib-0.4.29-cp311-cp311-win_amd64.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.29-cp312-cp312-win_amd64.whl b/build_helpers/TA_Lib-0.4.29-cp312-cp312-win_amd64.whl deleted file mode 100644 index 7a4ef378a..000000000 Binary files a/build_helpers/TA_Lib-0.4.29-cp312-cp312-win_amd64.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.29-cp39-cp39-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.29-cp39-cp39-linux_armv7l.whl deleted file mode 100644 index ec8d7c119..000000000 Binary files a/build_helpers/TA_Lib-0.4.29-cp39-cp39-linux_armv7l.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.29-cp39-cp39-win_amd64.whl b/build_helpers/TA_Lib-0.4.29-cp39-cp39-win_amd64.whl deleted file mode 100644 index cc50b4be4..000000000 Binary files a/build_helpers/TA_Lib-0.4.29-cp39-cp39-win_amd64.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.31-cp310-cp310-win_amd64.whl b/build_helpers/TA_Lib-0.4.31-cp310-cp310-win_amd64.whl new file mode 100644 index 000000000..4ce492a40 Binary files /dev/null and b/build_helpers/TA_Lib-0.4.31-cp310-cp310-win_amd64.whl differ diff --git a/build_helpers/TA_Lib-0.4.31-cp311-cp311-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.31-cp311-cp311-linux_armv7l.whl new file mode 100644 index 000000000..a664f12e6 Binary files /dev/null and b/build_helpers/TA_Lib-0.4.31-cp311-cp311-linux_armv7l.whl differ diff --git a/build_helpers/TA_Lib-0.4.31-cp311-cp311-win_amd64.whl b/build_helpers/TA_Lib-0.4.31-cp311-cp311-win_amd64.whl new file mode 100644 index 000000000..e5b8cb4ef Binary files /dev/null and b/build_helpers/TA_Lib-0.4.31-cp311-cp311-win_amd64.whl differ diff --git a/build_helpers/TA_Lib-0.4.31-cp312-cp312-win_amd64.whl b/build_helpers/TA_Lib-0.4.31-cp312-cp312-win_amd64.whl new file mode 100644 index 000000000..79596cf0c Binary files /dev/null and b/build_helpers/TA_Lib-0.4.31-cp312-cp312-win_amd64.whl differ diff --git a/build_helpers/TA_Lib-0.4.31-cp39-cp39-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.31-cp39-cp39-linux_armv7l.whl new file mode 100644 index 000000000..bceb21773 Binary files /dev/null and b/build_helpers/TA_Lib-0.4.31-cp39-cp39-linux_armv7l.whl differ diff --git a/build_helpers/TA_Lib-0.4.31-cp39-cp39-win_amd64.whl b/build_helpers/TA_Lib-0.4.31-cp39-cp39-win_amd64.whl new file mode 100644 index 000000000..01d3c626e Binary files /dev/null and b/build_helpers/TA_Lib-0.4.31-cp39-cp39-win_amd64.whl differ diff --git a/build_helpers/install_windows.ps1 b/build_helpers/install_windows.ps1 index 4aa070992..5f0c643ac 100644 --- a/build_helpers/install_windows.ps1 +++ b/build_helpers/install_windows.ps1 @@ -1,6 +1,6 @@ # vendored Wheels compiled via https://github.com/xmatthias/ta-lib-python/tree/ta_bundled_040 -python -m pip install --upgrade pip wheel +python -m pip install --upgrade "pip<=24.0" wheel $pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index 688254122..fbd952111 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -17,7 +17,7 @@ RUN mkdir /freqtrade \ && chown ftuser:ftuser /freqtrade \ # Allow sudoers && echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers \ - && pip install --upgrade pip + && pip install --upgrade "pip<=24.0" WORKDIR /freqtrade diff --git a/docs/backtesting.md b/docs/backtesting.md index 5fdfd6556..5adeae54b 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -253,36 +253,36 @@ A backtesting result will look like that: ``` ================================================ BACKTESTING REPORT ================================================= -| Pair | Entries | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% | -|:---------|--------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:| -| ADA/BTC | 35 | -0.11 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 | -| ARK/BTC | 11 | -0.41 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 | -| BTS/BTC | 32 | 0.31 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 | -| DASH/BTC | 13 | -0.08 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 | -| ENG/BTC | 18 | 1.36 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 | -| EOS/BTC | 36 | 0.08 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 | -| ETC/BTC | 26 | 0.37 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 | -| ETH/BTC | 33 | 0.30 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 | -| IOTA/BTC | 32 | 0.03 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 | -| LSK/BTC | 15 | 1.75 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 | -| LTC/BTC | 32 | -0.04 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 | -| NANO/BTC | 17 | 1.26 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 | -| NEO/BTC | 23 | 0.82 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 | -| REQ/BTC | 9 | 1.17 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 | -| XLM/BTC | 16 | 1.22 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 | -| XMR/BTC | 23 | -0.18 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 | -| XRP/BTC | 35 | 0.66 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 | -| ZEC/BTC | 22 | -0.46 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 | -| TOTAL | 429 | 0.36 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 | +| Pair | Trades | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% | +|----------+--------+----------------+------------------+----------------+--------------+--------------------------| +| ADA/BTC | 35 | -0.11 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 | +| ARK/BTC | 11 | -0.41 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 | +| BTS/BTC | 32 | 0.31 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 | +| DASH/BTC | 13 | -0.08 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 | +| ENG/BTC | 18 | 1.36 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 | +| EOS/BTC | 36 | 0.08 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 | +| ETC/BTC | 26 | 0.37 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 | +| ETH/BTC | 33 | 0.30 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 | +| IOTA/BTC | 32 | 0.03 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 | +| LSK/BTC | 15 | 1.75 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 | +| LTC/BTC | 32 | -0.04 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 | +| NANO/BTC | 17 | 1.26 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 | +| NEO/BTC | 23 | 0.82 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 | +| REQ/BTC | 9 | 1.17 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 | +| XLM/BTC | 16 | 1.22 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 | +| XMR/BTC | 23 | -0.18 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 | +| XRP/BTC | 35 | 0.66 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 | +| ZEC/BTC | 22 | -0.46 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 | +| TOTAL | 429 | 0.36 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 | ============================================= LEFT OPEN TRADES REPORT ============================================= -| Pair | Entries | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% | -|:---------|---------:|---------------:|-----------------:|---------------:|:---------------|--------------------:| -| ADA/BTC | 1 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 | -| LTC/BTC | 1 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 | -| TOTAL | 2 | 0.78 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 | +| Pair | Trades | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|----------+---------+----------------+------------------+----------------+----------------+---------------------| +| ADA/BTC | 1 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 | +| LTC/BTC | 1 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 | +| TOTAL | 2 | 0.78 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 | ==================== EXIT REASON STATS ==================== | Exit Reason | Exits | Wins | Draws | Losses | -|:-------------------|--------:|------:|-------:|--------:| +|--------------------+---------+-------+--------+---------| | trailing_stop_loss | 205 | 150 | 0 | 55 | | stop_loss | 166 | 0 | 0 | 166 | | exit_signal | 56 | 36 | 0 | 20 | @@ -631,10 +631,10 @@ Detailed output for all strategies one after the other will be available, so mak ``` ================================================== STRATEGY SUMMARY =================================================================== -| Strategy | Entries | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % | -|:------------|---------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:| -| Strategy1 | 429 | 0.36 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 | -| Strategy2 | 1487 | -0.13 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 | +| Strategy | Trades | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % | +|-------------+---------+----------------+------------------+----------------+----------------+-------+--------+--------+------------| +| Strategy1 | 429 | 0.36 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 | +| Strategy2 | 1487 | -0.13 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 | ``` ## Next step diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 0f01717ab..fbf8f4be0 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -373,7 +373,7 @@ Filters low-value coins which would not allow setting stoplosses. Namely, pairs are blacklisted if a variance of one percent or more in the stop price would be caused by precision rounding on the exchange, i.e. `rounded(stop_price) <= rounded(stop_price * 0.99)`. The idea is to avoid coins with a value VERY close to their lower trading boundary, not allowing setting of proper stoploss. -!!! Tip "PerformanceFilter is pointless for futures trading" +!!! Tip "PrecisionFilter is pointless for futures trading" The above does not apply to shorts. And for longs, in theory the trade will be liquidated first. !!! Warning "Backtesting" diff --git a/docs/plotting.md b/docs/plotting.md index 6ae0c3f11..f0a52415c 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -2,6 +2,14 @@ This page explains how to plot prices, indicators and profits. +!!! Warning "Deprecated" + The commands described in this page (`plot-dataframe`, `plot-profit`) should be considered deprecated and are in maintenance mode. + This is mostly for the performance problems even medium sized plots can cause, but also because "store a file and open it in a browser" isn't very intuitive from a UI perspective. + + While there are no immediate plans to remove them, they are not actively maintained - and may be removed short-term should major changes be required to keep them working. + + Please use [FreqUI](freq-ui.md) for plotting needs, which doesn't struggle with the same performance problems. + ## Installation / Setup Plotting modules use the Plotly library. You can install / upgrade this by running the following command: diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 489370b16..845aba7bb 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,6 +1,6 @@ markdown==3.6 mkdocs==1.6.0 -mkdocs-material==9.5.24 +mkdocs-material==9.5.27 mdx_truly_sane_lists==1.3 pymdown-extensions==10.8.1 jinja2==3.1.4 diff --git a/docs/rest-api.md b/docs/rest-api.md index 2b55c2563..3b5c8928f 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -118,6 +118,14 @@ By default, the script assumes `127.0.0.1` (localhost) and port `8080` to be use freqtrade-client --config rest_config.json [optional parameters] ``` +Commands with many arguments may require keyword arguments (for clarity) - which can be provided as follows: + +``` bash +freqtrade-client --config rest_config.json forceenter BTC/USDT long enter_tag=GutFeeling +``` + +This method will work for all arguments - check the "show" command for a list of available parameters. + ??? Note "Programmatic use" The `freqtrade-client` package (installable independent of freqtrade) can be used in your own scripts to interact with the freqtrade API. to do so, please use the following: diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index b1e46d356..74eef53c1 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -165,7 +165,9 @@ E.g. If the `current_rate` is 200 USD, then returning `0.02` will set the stoplo During backtesting, `current_rate` (and `current_profit`) are provided against the candle's high (or low for short trades) - while the resulting stoploss is evaluated against the candle's low (or high for short trades). The absolute value of the return value is used (the sign is ignored), so returning `0.05` or `-0.05` have the same result, a stoploss 5% below the current price. -Returning None will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss. +Returning `None` will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss. +`NaN` and `inf` values are considered invalid and will be ignored (identical to `None`). + Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)). @@ -467,7 +469,7 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab ??? Example "Returning a stoploss using absolute price from the custom stoploss function" - If we want to trail a stop price at 2xATR below current price we can call `stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), current_rate, is_short=trade.is_short, leverage=trade.leverage)`. + If we want to trail a stop price at 2xATR below current price we can call `stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), current_rate=current_rate, is_short=trade.is_short, leverage=trade.leverage)`. For futures, we need to adjust the direction (up or down), as well as adjust for leverage, since the [`custom_stoploss`](strategy-callbacks.md#custom-stoploss) callback returns the ["risk for this trade"](stoploss.md#stoploss-and-leverage) - not the relative price movement. ``` python @@ -492,7 +494,8 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab candle = dataframe.iloc[-1].squeeze() side = 1 if trade.is_short else -1 return stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), - current_rate, is_short=trade.is_short, + current_rate=current_rate, + is_short=trade.is_short, leverage=trade.leverage) ``` diff --git a/docs/trade-object.md b/docs/trade-object.md index fa8b2dbb1..ec9cf14ec 100644 --- a/docs/trade-object.md +++ b/docs/trade-object.md @@ -13,28 +13,28 @@ The following attributes / properties are available for each individual trade - | Attribute | DataType | Description | |------------|-------------|-------------| -`pair`| string | Pair of this trade -`is_open`| boolean | Is the trade currently open, or has it been concluded -`open_rate`| float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments) -`close_rate`| float | Close rate - only set when is_open = False -`stake_amount`| float | Amount in Stake (or Quote) currency. -`amount`| float | Amount in Asset / Base currency that is currently owned. -`open_date`| datetime | Timestamp when trade was opened **use `open_date_utc` instead** -`open_date_utc`| datetime | Timestamp when trade was opened - in UTC -`close_date`| datetime | Timestamp when trade was closed **use `close_date_utc` instead** -`close_date_utc`| datetime | Timestamp when trade was closed - in UTC -`close_profit`| float | Relative profit at the time of trade closure. `0.01` == 1% -`close_profit_abs`| float | Absolute profit (in stake currency) at the time of trade closure. -`leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. -`enter_tag`| string | Tag provided on entry via the `enter_tag` column in the dataframe -`is_short` | boolean | True for short trades, False otherwise -`orders` | Order[] | List of order objects attached to this trade (includes both filled and cancelled orders) -`date_last_filled_utc` | datetime | Time of the last filled order -`entry_side` | "buy" / "sell" | Order Side the trade was entered -`exit_side` | "buy" / "sell" | Order Side that will result in a trade exit / position reduction. -`trade_direction` | "long" / "short" | Trade direction in text - long or short. -`nr_of_successful_entries` | int | Number of successful (filled) entry orders -`nr_of_successful_exits` | int | Number of successful (filled) exit orders +| `pair` | string | Pair of this trade. | +| `is_open` | boolean | Is the trade currently open, or has it been concluded. | +| `open_rate` | float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments). | +| `close_rate` | float | Close rate - only set when is_open = False. | +| `stake_amount` | float | Amount in Stake (or Quote) currency. | +| `amount` | float | Amount in Asset / Base currency that is currently owned. | +| `open_date` | datetime | Timestamp when trade was opened **use `open_date_utc` instead** | +| `open_date_utc` | datetime | Timestamp when trade was opened - in UTC. | +| `close_date` | datetime | Timestamp when trade was closed **use `close_date_utc` instead** | +| `close_date_utc` | datetime | Timestamp when trade was closed - in UTC. | +| `close_profit` | float | Relative profit at the time of trade closure. `0.01` == 1% | +| `close_profit_abs` | float | Absolute profit (in stake currency) at the time of trade closure. | +| `leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. | +| `enter_tag` | string | Tag provided on entry via the `enter_tag` column in the dataframe. | +| `is_short` | boolean | True for short trades, False otherwise. | +| `orders` | Order[] | List of order objects attached to this trade (includes both filled and cancelled orders). | +| `date_last_filled_utc` | datetime | Time of the last filled order. | +| `entry_side` | "buy" / "sell" | Order Side the trade was entered. | +| `exit_side` | "buy" / "sell" | Order Side that will result in a trade exit / position reduction. | +| `trade_direction` | "long" / "short" | Trade direction in text - long or short. | +| `nr_of_successful_entries` | int | Number of successful (filled) entry orders. | +| `nr_of_successful_exits` | int | Number of successful (filled) exit orders. | ## Class methods diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 932f9b701..5c4e6df5d 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -30,5 +30,5 @@ if "dev" in __version__: versionfile = Path("./freqtrade_commit") if versionfile.is_file(): __version__ = f"docker-{__version__}-{versionfile.read_text()[:8]}" - except Exception: + except Exception: # noqa: S110 pass diff --git a/freqtrade/commands/build_config_commands.py b/freqtrade/commands/build_config_commands.py index 1e771a372..a5ab8cb41 100644 --- a/freqtrade/commands/build_config_commands.py +++ b/freqtrade/commands/build_config_commands.py @@ -187,7 +187,7 @@ def ask_user_config() -> Dict[str, Any]: "Insert Api server Listen Address (0.0.0.0 for docker, " "otherwise best left untouched)" ), - "default": "127.0.0.1" if not running_in_docker() else "0.0.0.0", + "default": "127.0.0.1" if not running_in_docker() else "0.0.0.0", # noqa: S104 "when": lambda x: x["api_server"], }, { diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index cc8b5407e..184f9decf 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -2,6 +2,7 @@ This module contains the configuration class """ +import ast import logging import warnings from copy import deepcopy @@ -301,7 +302,7 @@ class Configuration: # Edge section: if "stoploss_range" in self.args and self.args["stoploss_range"]: - txt_range = eval(self.args["stoploss_range"]) + txt_range = ast.literal_eval(self.args["stoploss_range"]) config["edge"].update({"stoploss_range_min": txt_range[0]}) config["edge"].update({"stoploss_range_max": txt_range[1]}) config["edge"].update({"stoploss_range_step": txt_range[2]}) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index bdca599c6..d13fa6fca 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -618,6 +618,11 @@ def download_data_main(config: Config) -> None: # Start downloading try: if config.get("download_trades"): + if not exchange.get_option("trades_has_history", True): + raise OperationalException( + f"Trade history not available for {exchange.name}. " + "You cannot use --dl-trades for this exchange." + ) pairs_not_available = refresh_backtest_trades_data( exchange, pairs=expanded_pairs, diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 07c4f9286..4c0eaf9ce 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -28,6 +28,7 @@ class Binance(Exchange): "ohlcv_candle_limit": 1000, "trades_pagination": "id", "trades_pagination_arg": "fromId", + "trades_has_history": True, "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], } _ft_has_futures: Dict = { diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index 71744bf36..be9f68d98 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -150,13 +150,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "25000", + "notionalCap": "50000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -165,97 +165,97 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "25000", + "notionalCap": "100000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", - "cum": "150.0" + "cum": "275.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 500000.0, + "minNotional": 100000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "50000", + "notionalCap": "1000000", + "notionalFloor": "100000", "maintMarginRatio": "0.05", - "cum": "1400.0" + "cum": "2775.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "26400.0" + "cum": "52775.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1250000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "1250000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "51400.0" + "cum": "102775.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 1250000.0, - "maxNotional": 2500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1250000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "207650.0" + "cum": "415275.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "832650.0" + "cum": "1665275.0" } } ], @@ -265,14 +265,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.015", "cum": "0.0" } }, @@ -280,96 +280,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 15.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "15", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "50000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", + "maintMarginRatio": "0.02", "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 300000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "300000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "650.0" + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "275.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 300000.0, - "maxNotional": 800000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 100000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "800000", - "notionalFloor": "300000", - "maintMarginRatio": "0.1", - "cum": "15650.0" + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2775.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "800000", - "maintMarginRatio": "0.125", - "cum": "35650.0" + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "52775.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "6", - "initialLeverage": "2", - "notionalCap": "3000000", - "notionalFloor": "1000000", - "maintMarginRatio": "0.25", - "cum": "160650.0" + "initialLeverage": "4", + "notionalCap": "2500000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "102775.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 3000000.0, + "minNotional": 2500000.0, "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "415275.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "3000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "910650.0" + "cum": "1665275.0" } } ], @@ -2246,13 +2262,13 @@ "tier": 7.0, "currency": "USDT", "minNotional": 6000000.0, - "maxNotional": 10000000.0, + "maxNotional": 6500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "10000000", + "notionalCap": "6500000", "notionalFloor": "6000000", "maintMarginRatio": "0.5", "cum": "1820650.0" @@ -3599,79 +3615,111 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "10", + "initialLeverage": "25", "notionalCap": "25000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "8", - "notionalCap": "100000", + "initialLeverage": "20", + "notionalCap": "80000", "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "maintMarginRatio": "0.025", + "cum": "150.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 80000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "initialLeverage": "10", + "notionalCap": "800000", + "notionalFloor": "80000", + "maintMarginRatio": "0.05", + "cum": "2150.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "initialLeverage": "5", + "notionalCap": "1600000", + "notionalFloor": "800000", + "maintMarginRatio": "0.1", + "cum": "42150.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "2000000", + "notionalFloor": "1600000", + "maintMarginRatio": "0.125", + "cum": "82150.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "332150.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "386925.0" + "cum": "1332150.0" } } ], @@ -4267,14 +4315,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.015", "cum": "0.0" } }, @@ -4282,80 +4330,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 15.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "15", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", + "maintMarginRatio": "0.02", "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "650.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5650.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11900.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386900.0" + "cum": "333250.0" } } ], @@ -5877,95 +5957,111 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "25", "notionalCap": "25000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "100000", + "initialLeverage": "20", + "notionalCap": "80000", "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "maintMarginRatio": "0.025", + "cum": "150.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 80000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "200000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "initialLeverage": "10", + "notionalCap": "800000", + "notionalFloor": "80000", + "maintMarginRatio": "0.05", + "cum": "2150.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "200000", - "maintMarginRatio": "0.125", - "cum": "10675.0" + "initialLeverage": "5", + "notionalCap": "1600000", + "notionalFloor": "800000", + "maintMarginRatio": "0.1", + "cum": "42150.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "6", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.25", - "cum": "73175.0" + "initialLeverage": "4", + "notionalCap": "2000000", + "notionalFloor": "1600000", + "maintMarginRatio": "0.125", + "cum": "82150.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "332150.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "323175.0" + "cum": "1332150.0" } } ], @@ -10758,80 +10854,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386925.0" + "cum": "333250.0" } } ], @@ -12498,13 +12626,13 @@ "tier": 3.0, "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 100000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "100000", + "notionalCap": "150000", "notionalFloor": "50000", "maintMarginRatio": "0.025", "cum": "275.0" @@ -12513,81 +12641,81 @@ { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 1000000.0, + "minNotional": 150000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "1000000", - "notionalFloor": "100000", + "notionalCap": "1500000", + "notionalFloor": "150000", "maintMarginRatio": "0.05", - "cum": "2775.0" + "cum": "4025.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "52775.0" + "cum": "79025.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "102775.0" + "cum": "154025.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "415275.0" + "cum": "622775.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1665275.0" + "cum": "2497775.0" } } ], @@ -14318,10 +14446,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -14334,10 +14462,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.02", @@ -14350,10 +14478,10 @@ "minNotional": 25000.0, "maxNotional": 80000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "80000", "notionalFloor": "25000", "maintMarginRatio": "0.025", @@ -14366,10 +14494,10 @@ "minNotional": 80000.0, "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "800000", "notionalFloor": "80000", "maintMarginRatio": "0.05", @@ -14428,13 +14556,13 @@ "tier": 8.0, "currency": "USDT", "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "8000000", + "notionalCap": "5000000", "notionalFloor": "4000000", "maintMarginRatio": "0.5", "cum": "1332150.0" @@ -17797,6 +17925,136 @@ } } ], + "IO/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "50000", + "notionalFloor": "5000", + "maintMarginRatio": "0.02", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "275.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2775.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "52775.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "2500000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "102775.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "415275.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1665275.0" + } + } + ], "IOST/USDT:USDT": [ { "tier": 1.0, @@ -18015,14 +18273,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.01", + "maintMarginRatio": "0.015", "cum": "0.0" } }, @@ -18030,80 +18288,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "10", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "8", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386950.0" + "cum": "333250.0" } } ], @@ -18731,14 +19021,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "25", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.01", + "maintMarginRatio": "0.015", "cum": "0.0" } }, @@ -18746,96 +19036,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, + "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "10000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", "maintMarginRatio": "0.02", - "cum": "50.0" + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, + "minNotional": 20000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "20", "notionalCap": "25000", - "notionalFloor": "10000", + "notionalFloor": "20000", "maintMarginRatio": "0.025", - "cum": "100.0" + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "8", - "notionalCap": "100000", + "initialLeverage": "10", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "725.0" + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5725.0" + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 3000000.0, + "minNotional": 400000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "maxLeverage": 4.0, "info": { "bracket": "6", - "initialLeverage": "2", - "notionalCap": "3000000", - "notionalFloor": "250000", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", "maintMarginRatio": "0.125", - "cum": "11975.0" + "cum": "20750.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 8000000.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "8000000", - "notionalFloor": "3000000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "1136975.0" + "cum": "333250.0" } } ], @@ -19186,80 +19492,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386925.0" + "cum": "333250.0" } } ], @@ -19945,6 +20283,136 @@ } } ], + "LISTA/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "5000", + "maintMarginRatio": "0.02", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333250.0" + } + } + ], "LIT/USDT:USDT": [ { "tier": 1.0, @@ -22353,6 +22821,136 @@ } } ], + "MEW/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "5000", + "maintMarginRatio": "0.02", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333250.0" + } + } + ], "MINA/USDT:USDT": [ { "tier": 1.0, @@ -23822,96 +24420,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "50000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 50000.0, "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "20", "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "275.0" } }, { "tier": 4.0, "currency": "USDT", "minNotional": 100000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "200000", + "initialLeverage": "10", + "notionalCap": "1000000", "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "maintMarginRatio": "0.05", + "cum": "2775.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "200000", - "maintMarginRatio": "0.125", - "cum": "10675.0" + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "52775.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "6", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.25", - "cum": "73175.0" + "initialLeverage": "4", + "notionalCap": "2500000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "102775.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "415275.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "323175.0" + "cum": "1665275.0" } } ], @@ -24114,13 +24728,13 @@ "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "398150.0" @@ -24133,14 +24747,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.015", "cum": "0.0" } }, @@ -24148,80 +24762,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "10", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", + "maintMarginRatio": "0.02", "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "8", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "650.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5650.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11900.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386900.0" + "cum": "333250.0" } } ], @@ -24719,14 +25365,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.01", + "maintMarginRatio": "0.015", "cum": "0.0" } }, @@ -24734,80 +25380,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "10", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "8", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5700.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386950.0" + "cum": "333250.0" } } ], @@ -27924,13 +28602,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "25000", + "notionalCap": "50000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -27939,97 +28617,97 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 80000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "80000", - "notionalFloor": "25000", + "notionalCap": "100000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", - "cum": "150.0" + "cum": "275.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 800000.0, + "minNotional": 100000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "800000", - "notionalFloor": "80000", + "notionalCap": "1000000", + "notionalFloor": "100000", "maintMarginRatio": "0.05", - "cum": "2150.0" + "cum": "2775.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 1600000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "1600000", - "notionalFloor": "800000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "42150.0" + "cum": "52775.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1600000.0, - "maxNotional": 2000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1600000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "82150.0" + "cum": "102775.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "332150.0" + "cum": "415275.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "8000000", - "notionalFloor": "4000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "1332150.0" + "cum": "1665275.0" } } ], @@ -32660,13 +33338,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "25000", + "notionalCap": "50000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -32675,97 +33353,97 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 80000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "80000", - "notionalFloor": "25000", + "notionalCap": "100000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", - "cum": "150.0" + "cum": "275.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 800000.0, + "minNotional": 100000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "800000", - "notionalFloor": "80000", + "notionalCap": "1000000", + "notionalFloor": "100000", "maintMarginRatio": "0.05", - "cum": "2150.0" + "cum": "2775.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 1600000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "1600000", - "notionalFloor": "800000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "42150.0" + "cum": "52775.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1600000.0, - "maxNotional": 2000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1600000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "82150.0" + "cum": "102775.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "332150.0" + "cum": "415275.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "8000000", - "notionalFloor": "4000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "1332150.0" + "cum": "1665275.0" } } ], @@ -33159,6 +33837,136 @@ } } ], + "TURBO/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "5000", + "maintMarginRatio": "0.02", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333250.0" + } + } + ], "TWT/USDT:USDT": [ { "tier": 1.0, @@ -34156,13 +34964,13 @@ "tier": 3.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 50000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "50000", + "notionalCap": "80000", "notionalFloor": "25000", "maintMarginRatio": "0.025", "cum": "150.0" @@ -34171,81 +34979,81 @@ { "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 500000.0, + "minNotional": 80000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "50000", + "notionalCap": "800000", + "notionalFloor": "80000", "maintMarginRatio": "0.05", - "cum": "1400.0" + "cum": "2150.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "26400.0" + "cum": "42150.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1250000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "1250000", - "notionalFloor": "1000000", + "notionalCap": "2000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.125", - "cum": "51400.0" + "cum": "82150.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 1250000.0, - "maxNotional": 2500000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1250000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "207650.0" + "cum": "332150.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "832650.0" + "cum": "1332150.0" } } ], @@ -34256,10 +35064,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 21.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "21", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -34350,13 +35158,13 @@ "tier": 7.0, "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 5000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.5", "cum": "991300.0" @@ -36660,80 +37468,112 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "25", + "notionalCap": "20000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.02", + "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" } }, { "tier": 6.0, "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "386925.0" + "cum": "333250.0" } } ], @@ -36981,6 +37821,266 @@ } } ], + "ZK/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "5000", + "maintMarginRatio": "0.02", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333250.0" + } + } + ], + "ZRO/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.015", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "5000", + "maintMarginRatio": "0.02", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "125.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "750.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "5", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10750.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "6", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "20750.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333250.0" + } + } + ], "ZRX/USDT:USDT": [ { "tier": 1.0, diff --git a/freqtrade/exchange/bingx.py b/freqtrade/exchange/bingx.py index 2d81643a1..4dcff8a21 100644 --- a/freqtrade/exchange/bingx.py +++ b/freqtrade/exchange/bingx.py @@ -20,4 +20,5 @@ class Bingx(Exchange): "stoploss_on_exchange": True, "stoploss_order_types": {"limit": "limit", "market": "market"}, "order_time_in_force": ["GTC", "IOC", "PO"], + "trades_has_history": False, # Endpoint doesn't seem to support pagination } diff --git a/freqtrade/exchange/bitmart.py b/freqtrade/exchange/bitmart.py index ffc8ac67a..ab509c786 100644 --- a/freqtrade/exchange/bitmart.py +++ b/freqtrade/exchange/bitmart.py @@ -18,4 +18,5 @@ class Bitmart(Exchange): _ft_has: Dict = { "stoploss_on_exchange": False, # Bitmart API does not support stoploss orders "ohlcv_candle_limit": 200, + "trades_has_history": False, # Endpoint doesn't seem to support pagination } diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index c8b05d1de..252f0a29b 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -33,6 +33,7 @@ class Bybit(Exchange): "ohlcv_candle_limit": 1000, "ohlcv_has_history": True, "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], + "trades_has_history": False, # Endpoint doesn't support pagination } _ft_has_futures: Dict = { "ohlcv_has_history": True, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 9d7c4eabb..c7139c8fa 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -117,6 +117,7 @@ class Exchange: "tickers_have_price": True, "trades_pagination": "time", # Possible are "time" or "id" "trades_pagination_arg": "since", + "trades_has_history": False, "l2_limit_range": None, "l2_limit_range_required": True, # Allow Empty L2 limit (kucoin) "mark_ohlcv_price": "mark", @@ -151,7 +152,7 @@ class Exchange: :return: None """ self._api: ccxt.Exchange - self._api_async: ccxt_async.Exchange = None + self._api_async: ccxt_async.Exchange self._markets: Dict = {} self._trading_fees: Dict[str, Any] = {} self._leverage_tiers: Dict[str, List[Dict]] = {} @@ -233,7 +234,7 @@ class Exchange: self.required_candle_call_count = 1 if validate: # Initial markets load - self._load_markets() + self.reload_markets(True, load_leverage_tiers=False) self.validate_config(config) self._startup_candle_count: int = config.get("startup_candle_count", 0) self.required_candle_call_count = self.validate_required_startup_candles( @@ -258,7 +259,7 @@ class Exchange: def close(self): logger.debug("Exchange object destroyed, closing async loop") if ( - self._api_async + getattr(self, "_api_async", None) and inspect.iscoroutinefunction(self._api_async.close) and self._api_async.session ): @@ -354,7 +355,7 @@ class Exchange: """exchange ccxt markets""" if not self._markets: logger.info("Markets were not loaded. Loading them now..") - self._load_markets() + self.reload_markets(True) return self._markets @property @@ -530,30 +531,26 @@ class Exchange: amount, self.get_precision_amount(pair), self.precisionMode, contract_size ) - def _load_async_markets(self, reload: bool = False) -> None: + def _load_async_markets(self, reload: bool = False) -> Dict[str, Any]: try: - if self._api_async: - self.loop.run_until_complete(self._api_async.load_markets(reload=reload, params={})) + markets = self.loop.run_until_complete( + self._api_async.load_markets(reload=reload, params={}) + ) - except (asyncio.TimeoutError, ccxt.BaseError) as e: - logger.warning("Could not load async markets. Reason: %s", e) - return + if isinstance(markets, Exception): + raise markets + return markets + except asyncio.TimeoutError as e: + logger.warning("Could not load markets. Reason: %s", e) + raise TemporaryError from e - def _load_markets(self) -> None: - """Initialize markets both sync and async""" - try: - self._markets = self._api.load_markets(params={}) - self._load_async_markets() - self._last_markets_refresh = dt_ts() - if self._ft_has["needs_trading_fees"]: - self._trading_fees = self.fetch_trading_fees() + def reload_markets(self, force: bool = False, *, load_leverage_tiers: bool = True) -> None: + """ + Reload / Initialize markets both sync and async if refresh interval has passed - except ccxt.BaseError: - logger.exception("Unable to initialize markets.") - - def reload_markets(self, force: bool = False) -> None: - """Reload markets both sync and async if refresh interval has passed""" + """ # Check whether markets have to be reloaded + is_initial = self._last_markets_refresh == 0 if ( not force and self._last_markets_refresh > 0 @@ -562,13 +559,18 @@ class Exchange: return None logger.debug("Performing scheduled market reload..") try: - self._markets = self._api.load_markets(reload=True, params={}) - # Also reload async markets to avoid issues with newly listed pairs - self._load_async_markets(reload=True) + # Reload async markets, then assign them to sync api + self._markets = self._load_async_markets(reload=True) + self._api.set_markets(self._api_async.markets, self._api_async.currencies) self._last_markets_refresh = dt_ts() - self.fill_leverage_tiers() - except ccxt.BaseError: - logger.exception("Could not reload markets.") + + if is_initial and self._ft_has["needs_trading_fees"]: + self._trading_fees = self.fetch_trading_fees() + + if load_leverage_tiers and self.trading_mode == TradingMode.FUTURES: + self.fill_leverage_tiers() + except (ccxt.BaseError, TemporaryError): + logger.exception("Could not load markets.") def validate_stakecurrency(self, stake_currency: str) -> None: """ diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 2408e306e..9ed5a7366 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -31,6 +31,7 @@ class Gate(Exchange): "stop_price_param": "stopPrice", "stop_price_prop": "stopPrice", "marketOrderRequiresPrice": True, + "trades_has_history": False, # Endpoint would support this - but ccxt doesn't. } _ft_has_futures: Dict = { diff --git a/freqtrade/exchange/htx.py b/freqtrade/exchange/htx.py index f939534e9..fa26a5ffd 100644 --- a/freqtrade/exchange/htx.py +++ b/freqtrade/exchange/htx.py @@ -28,6 +28,7 @@ class Htx(Exchange): "1w": 500, "1M": 500, }, + "trades_has_history": False, # Endpoint doesn't have a "since" parameter } def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict: diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 4fbbe113c..f0562ecaf 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -31,6 +31,7 @@ class Kraken(Exchange): "trades_pagination": "id", "trades_pagination_arg": "since", "trades_pagination_overlap": False, + "trades_has_history": True, "mark_ohlcv_timeframe": "4h", } diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 1704117e6..94a81b452 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -33,6 +33,7 @@ class Okx(Exchange): "funding_fee_timeframe": "8h", "stoploss_order_types": {"limit": "limit"}, "stoploss_on_exchange": True, + "trades_has_history": False, # Endpoint doesn't have a "since" parameter } _ft_has_futures: Dict = { "tickers_have_quoteVolume": False, diff --git a/freqtrade/freqai/torch/PyTorchModelTrainer.py b/freqtrade/freqai/torch/PyTorchModelTrainer.py index 602c8e95b..54c42a284 100644 --- a/freqtrade/freqai/torch/PyTorchModelTrainer.py +++ b/freqtrade/freqai/torch/PyTorchModelTrainer.py @@ -148,7 +148,8 @@ class PyTorchModelTrainer(PyTorchTrainerInterface): the motivation here is that `n_steps` is easier to optimize and keep stable, across different n_obs - the number of data points. """ - assert isinstance(self.n_steps, int), "Either `n_steps` or `n_epochs` should be set." + if not isinstance(self.n_steps, int): + raise ValueError("Either `n_steps` or `n_epochs` should be set.") n_batches = n_obs // self.batch_size n_epochs = max(self.n_steps // n_batches, 1) if n_epochs <= 10: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8d53097a5..8e4fa1d5f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -217,7 +217,7 @@ class FreqtradeBot(LoggingMixin): except Exception: # Exceptions here will be happening if the db disappeared. # At which point we can no longer commit anyway. - pass + logger.exception("Error during cleanup") def startup(self) -> None: """ diff --git a/freqtrade/optimize/backtest_caching.py b/freqtrade/optimize/backtest_caching.py index 2f9c151ad..766c77ddc 100644 --- a/freqtrade/optimize/backtest_caching.py +++ b/freqtrade/optimize/backtest_caching.py @@ -13,7 +13,7 @@ def get_strategy_run_id(strategy) -> str: :param strategy: strategy object. :return: hex string id. """ - digest = hashlib.sha1() + digest = hashlib.sha1() # noqa: S324 config = deepcopy(strategy.config) # Options that have no impact on results of individual backtest. diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 7ae2791bf..b19fca9dc 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -489,7 +489,7 @@ class Hyperopt: ) def _set_random_state(self, random_state: Optional[int]) -> int: - return random_state or random.randint(1, 2**16 - 1) + return random_state or random.randint(1, 2**16 - 1) # noqa: S311 def advise_and_trim(self, data: Dict[str, DataFrame]) -> Dict[str, DataFrame]: preprocessed = self.backtesting.strategy.advise_all_indicators(data) diff --git a/freqtrade/optimize/optimize_reports/bt_output.py b/freqtrade/optimize/optimize_reports/bt_output.py index 00e980e6b..f20d7f190 100644 --- a/freqtrade/optimize/optimize_reports/bt_output.py +++ b/freqtrade/optimize/optimize_reports/bt_output.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Dict, List +from typing import Any, Dict, List, Union from tabulate import tabulate @@ -20,13 +20,13 @@ def _get_line_floatfmt(stake_currency: str) -> List[str]: def _get_line_header( - first_column: str, stake_currency: str, direction: str = "Entries" + first_column: Union[str, List[str]], stake_currency: str, direction: str = "Trades" ) -> List[str]: """ Generate header lines (goes in line with _generate_result_line()) """ return [ - first_column, + *([first_column] if isinstance(first_column, str) else first_column), direction, "Avg Profit %", f"Tot Profit {stake_currency}", @@ -54,7 +54,7 @@ def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: st :return: pretty printed table with tabulate as string """ - headers = _get_line_header("Pair", stake_currency) + headers = _get_line_header("Pair", stake_currency, "Trades") floatfmt = _get_line_floatfmt(stake_currency) output = [ [ @@ -79,20 +79,30 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr :param stake_currency: stake-currency - used to correctly name headers :return: pretty printed table with tabulate as string """ + floatfmt = _get_line_floatfmt(stake_currency) fallback: str = "" + is_list = False if tag_type == "enter_tag": - headers = _get_line_header("TAG", stake_currency) - else: + headers = _get_line_header("Enter Tag", stake_currency, "Entries") + elif tag_type == "exit_tag": headers = _get_line_header("Exit Reason", stake_currency, "Exits") fallback = "exit_reason" + else: + # Mix tag + headers = _get_line_header(["Enter Tag", "Exit Reason"], stake_currency, "Trades") + floatfmt.insert(0, "s") + is_list = True - floatfmt = _get_line_floatfmt(stake_currency) output = [ [ - ( - t["key"] + *( + ( + (t["key"] if isinstance(t["key"], list) else [t["key"], ""]) + if is_list + else [t["key"]] + ) if t.get("key") is not None and len(str(t["key"])) > 0 - else t.get(fallback, "OTHER") + else [t.get(fallback, "OTHER")] ), t["trades"], t["profit_mean_pct"], @@ -144,7 +154,7 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str: :return: pretty printed table with tabulate as string """ floatfmt = _get_line_floatfmt(stake_currency) - headers = _get_line_header("Strategy", stake_currency) + headers = _get_line_header("Strategy", stake_currency, "Trades") # _get_line_header() is also used for per-pair summary. Per-pair drawdown is mostly useless # therefore we slip this column in only for strategy summary here. headers.append("Drawdown") @@ -380,6 +390,32 @@ def text_table_add_metrics(strat_results: Dict) -> str: return message +def _show_tag_subresults(results: Dict[str, Any], stake_currency: str): + """ + Print tag subresults (enter_tag, exit_reason_summary, mix_tag_stats) + """ + if (enter_tags := results.get("results_per_enter_tag")) is not None: + table = text_table_tags("enter_tag", enter_tags, stake_currency) + + if isinstance(table, str) and len(table) > 0: + print(" ENTER TAG STATS ".center(len(table.splitlines()[0]), "=")) + print(table) + + if (exit_reasons := results.get("exit_reason_summary")) is not None: + table = text_table_tags("exit_tag", exit_reasons, stake_currency) + + if isinstance(table, str) and len(table) > 0: + print(" EXIT REASON STATS ".center(len(table.splitlines()[0]), "=")) + print(table) + + if (mix_tag := results.get("mix_tag_stats")) is not None: + table = text_table_tags("mix_tag", mix_tag, stake_currency) + + if isinstance(table, str) and len(table) > 0: + print(" MIXED TAG STATS ".center(len(table.splitlines()[0]), "=")) + print(table) + + def show_backtest_result( strategy: str, results: Dict[str, Any], stake_currency: str, backtest_breakdown: List[str] ): @@ -398,19 +434,7 @@ def show_backtest_result( print(" LEFT OPEN TRADES REPORT ".center(len(table.splitlines()[0]), "=")) print(table) - if (enter_tags := results.get("results_per_enter_tag")) is not None: - table = text_table_tags("enter_tag", enter_tags, stake_currency) - - if isinstance(table, str) and len(table) > 0: - print(" ENTER TAG STATS ".center(len(table.splitlines()[0]), "=")) - print(table) - - if (exit_reasons := results.get("exit_reason_summary")) is not None: - table = text_table_tags("exit_tag", exit_reasons, stake_currency) - - if isinstance(table, str) and len(table) > 0: - print(" EXIT REASON STATS ".center(len(table.splitlines()[0]), "=")) - print(table) + _show_tag_subresults(results, stake_currency) for period in backtest_breakdown: if period in results.get("periodic_breakdown", {}): diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 2ca467eb6..ef5fce0e1 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -1,7 +1,7 @@ import logging from copy import deepcopy from datetime import datetime, timedelta, timezone -from typing import Any, Dict, List, Tuple, Union +from typing import Any, Dict, List, Literal, Tuple, Union import numpy as np from pandas import DataFrame, Series, concat, to_datetime @@ -68,7 +68,9 @@ def generate_rejected_signals( return rejected_candles_only -def _generate_result_line(result: DataFrame, starting_balance: int, first_column: str) -> Dict: +def _generate_result_line( + result: DataFrame, starting_balance: int, first_column: Union[str, List[str]] +) -> Dict: """ Generate one result dict, with "first_column" as key. """ @@ -141,7 +143,10 @@ def generate_pair_metrics( def generate_tag_metrics( - tag_type: str, starting_balance: int, results: DataFrame, skip_nan: bool = False + tag_type: Union[Literal["enter_tag", "exit_reason"], List[Literal["enter_tag", "exit_reason"]]], + starting_balance: int, + results: DataFrame, + skip_nan: bool = False, ) -> List[Dict]: """ Generates and returns a list of metrics for the given tag trades and the results dataframe @@ -153,13 +158,14 @@ def generate_tag_metrics( tabular_data = [] - if tag_type in results.columns: - for tag, count in results[tag_type].value_counts().items(): - result = results[results[tag_type] == tag] - if skip_nan and result["profit_abs"].isnull().all(): + if all( + tag in results.columns for tag in (tag_type if isinstance(tag_type, list) else [tag_type]) + ): + for tags, group in results.groupby(tag_type): + if skip_nan and group["profit_abs"].isnull().all(): continue - tabular_data.append(_generate_result_line(result, starting_balance, tag)) + tabular_data.append(_generate_result_line(group, starting_balance, tags)) # Sort by total profit %: tabular_data = sorted(tabular_data, key=lambda k: k["profit_total_abs"], reverse=True) @@ -378,12 +384,18 @@ def generate_strategy_stats( skip_nan=False, ) - enter_tag_results = generate_tag_metrics( + enter_tag_stats = generate_tag_metrics( "enter_tag", starting_balance=start_balance, results=results, skip_nan=False ) exit_reason_stats = generate_tag_metrics( "exit_reason", starting_balance=start_balance, results=results, skip_nan=False ) + mix_tag_stats = generate_tag_metrics( + ["enter_tag", "exit_reason"], + starting_balance=start_balance, + results=results, + skip_nan=False, + ) left_open_results = generate_pair_metrics( pairlist, stake_currency=stake_currency, @@ -425,8 +437,9 @@ def generate_strategy_stats( "best_pair": best_pair, "worst_pair": worst_pair, "results_per_pair": pair_results, - "results_per_enter_tag": enter_tag_results, + "results_per_enter_tag": enter_tag_stats, "exit_reason_summary": exit_reason_stats, + "mix_tag_stats": mix_tag_stats, "left_open_trades": left_open_results, "total_trades": len(results), "trade_count_long": len(results.loc[~results["is_short"]]), diff --git a/freqtrade/plugins/pairlist/AgeFilter.py b/freqtrade/plugins/pairlist/AgeFilter.py index 0be04d7b8..917dad45c 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -5,11 +5,11 @@ Minimum age (days listed) pair list filter import logging from copy import deepcopy from datetime import timedelta -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional from pandas import DataFrame -from freqtrade.constants import Config, ListPairsWithTimeframes +from freqtrade.constants import ListPairsWithTimeframes from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.misc import plural @@ -21,24 +21,17 @@ logger = logging.getLogger(__name__) class AgeFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) # Checked symbols cache (dictionary of ticker symbol => timestamp) self._symbolsChecked: Dict[str, int] = {} self._symbolsCheckFailed = PeriodicCache(maxsize=1000, ttl=86_400) - self._min_days_listed = pairlistconfig.get("min_days_listed", 10) - self._max_days_listed = pairlistconfig.get("max_days_listed") + self._min_days_listed = self._pairlistconfig.get("min_days_listed", 10) + self._max_days_listed = self._pairlistconfig.get("max_days_listed") - candle_limit = exchange.ohlcv_candle_limit("1d", self._config["candle_type_def"]) + candle_limit = self._exchange.ohlcv_candle_limit("1d", self._config["candle_type_def"]) if self._min_days_listed < 1: raise OperationalException("AgeFilter requires min_days_listed to be >= 1") if self._min_days_listed > candle_limit: diff --git a/freqtrade/plugins/pairlist/FullTradesFilter.py b/freqtrade/plugins/pairlist/FullTradesFilter.py index 11d98abc5..13586611d 100644 --- a/freqtrade/plugins/pairlist/FullTradesFilter.py +++ b/freqtrade/plugins/pairlist/FullTradesFilter.py @@ -3,9 +3,8 @@ Full trade slots pair list filter """ import logging -from typing import Any, Dict, List +from typing import List -from freqtrade.constants import Config from freqtrade.exchange.types import Tickers from freqtrade.persistence import Trade from freqtrade.plugins.pairlist.IPairList import IPairList @@ -15,16 +14,6 @@ logger = logging.getLogger(__name__) class FullTradesFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) - @property def needstickers(self) -> bool: """ diff --git a/freqtrade/plugins/pairlist/IPairList.py b/freqtrade/plugins/pairlist/IPairList.py index 0db38ff2f..a2e70e649 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -3,7 +3,7 @@ PairList Handler base class """ import logging -from abc import ABC, abstractmethod, abstractproperty +from abc import ABC, abstractmethod from copy import deepcopy from typing import Any, Dict, List, Literal, Optional, TypedDict, Union @@ -87,7 +87,8 @@ class IPairList(LoggingMixin, ABC): """ return self.__class__.__name__ - @abstractproperty + @property + @abstractmethod def needstickers(self) -> bool: """ Boolean property defining if tickers are necessary. diff --git a/freqtrade/plugins/pairlist/MarketCapPairList.py b/freqtrade/plugins/pairlist/MarketCapPairList.py index 709b6100b..648766e20 100644 --- a/freqtrade/plugins/pairlist/MarketCapPairList.py +++ b/freqtrade/plugins/pairlist/MarketCapPairList.py @@ -5,11 +5,10 @@ Provides dynamic pair list based on Market Cap """ import logging -from typing import Any, Dict, List +from typing import Dict, List from cachetools import TTLCache -from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -22,15 +21,8 @@ logger = logging.getLogger(__name__) class MarketCapPairList(IPairList): is_pairlist_generator = True - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) if "number_assets" not in self._pairlistconfig: raise OperationalException( @@ -38,14 +30,14 @@ class MarketCapPairList(IPairList): 'for "pairlist.config.number_assets"' ) - self._stake_currency = config["stake_currency"] + self._stake_currency = self._config["stake_currency"] self._number_assets = self._pairlistconfig["number_assets"] self._max_rank = self._pairlistconfig.get("max_rank", 30) self._refresh_period = self._pairlistconfig.get("refresh_period", 86400) self._marketcap_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) self._def_candletype = self._config["candle_type_def"] - _coingecko_config = config.get("coingecko", {}) + _coingecko_config = self._config.get("coingecko", {}) self._coingecko: FtCoinGeckoApi = FtCoinGeckoApi( api_key=_coingecko_config.get("api_key", ""), diff --git a/freqtrade/plugins/pairlist/OffsetFilter.py b/freqtrade/plugins/pairlist/OffsetFilter.py index 1fa9e1bd0..bd981358e 100644 --- a/freqtrade/plugins/pairlist/OffsetFilter.py +++ b/freqtrade/plugins/pairlist/OffsetFilter.py @@ -3,9 +3,8 @@ Offset pair list filter """ import logging -from typing import Any, Dict, List +from typing import Dict, List -from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -15,18 +14,11 @@ logger = logging.getLogger(__name__) class OffsetFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) - self._offset = pairlistconfig.get("offset", 0) - self._number_pairs = pairlistconfig.get("number_assets", 0) + self._offset = self._pairlistconfig.get("offset", 0) + self._number_pairs = self._pairlistconfig.get("number_assets", 0) if self._offset < 0: raise OperationalException("OffsetFilter requires offset to be >= 0") diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 930c78334..c10ae7394 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -3,11 +3,10 @@ Performance pair list filter """ import logging -from typing import Any, Dict, List +from typing import Dict, List import pandas as pd -from freqtrade.constants import Config from freqtrade.exchange.types import Tickers from freqtrade.persistence import Trade from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -17,18 +16,11 @@ logger = logging.getLogger(__name__) class PerformanceFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) - self._minutes = pairlistconfig.get("minutes", 0) - self._min_profit = pairlistconfig.get("min_profit") + self._minutes = self._pairlistconfig.get("minutes", 0) + self._min_profit = self._pairlistconfig.get("min_profit") @property def needstickers(self) -> bool: diff --git a/freqtrade/plugins/pairlist/PrecisionFilter.py b/freqtrade/plugins/pairlist/PrecisionFilter.py index 0e8c50849..b2f767a67 100644 --- a/freqtrade/plugins/pairlist/PrecisionFilter.py +++ b/freqtrade/plugins/pairlist/PrecisionFilter.py @@ -3,9 +3,8 @@ Precision pair list filter """ import logging -from typing import Any, Dict, Optional +from typing import Optional -from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange import ROUND_UP from freqtrade.exchange.types import Ticker @@ -16,15 +15,8 @@ logger = logging.getLogger(__name__) class PrecisionFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) if "stoploss" not in self._config: raise OperationalException( diff --git a/freqtrade/plugins/pairlist/PriceFilter.py b/freqtrade/plugins/pairlist/PriceFilter.py index 81dbdfc33..d651533ce 100644 --- a/freqtrade/plugins/pairlist/PriceFilter.py +++ b/freqtrade/plugins/pairlist/PriceFilter.py @@ -3,9 +3,8 @@ Price pair list filter """ import logging -from typing import Any, Dict, Optional +from typing import Dict, Optional -from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Ticker from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -15,26 +14,19 @@ logger = logging.getLogger(__name__) class PriceFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) - self._low_price_ratio = pairlistconfig.get("low_price_ratio", 0) + self._low_price_ratio = self._pairlistconfig.get("low_price_ratio", 0) if self._low_price_ratio < 0: raise OperationalException("PriceFilter requires low_price_ratio to be >= 0") - self._min_price = pairlistconfig.get("min_price", 0) + self._min_price = self._pairlistconfig.get("min_price", 0) if self._min_price < 0: raise OperationalException("PriceFilter requires min_price to be >= 0") - self._max_price = pairlistconfig.get("max_price", 0) + self._max_price = self._pairlistconfig.get("max_price", 0) if self._max_price < 0: raise OperationalException("PriceFilter requires max_price to be >= 0") - self._max_value = pairlistconfig.get("max_value", 0) + self._max_value = self._pairlistconfig.get("max_value", 0) if self._max_value < 0: raise OperationalException("PriceFilter requires max_value to be >= 0") self._enabled = ( diff --git a/freqtrade/plugins/pairlist/ProducerPairList.py b/freqtrade/plugins/pairlist/ProducerPairList.py index 771f87380..09a0c49d2 100644 --- a/freqtrade/plugins/pairlist/ProducerPairList.py +++ b/freqtrade/plugins/pairlist/ProducerPairList.py @@ -5,7 +5,7 @@ Provides pair list from Leader data """ import logging -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers @@ -32,19 +32,12 @@ class ProducerPairList(IPairList): is_pairlist_generator = True - def __init__( - self, - exchange, - pairlistmanager, - config: Dict[str, Any], - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) self._num_assets: int = self._pairlistconfig.get("number_assets", 0) self._producer_name = self._pairlistconfig.get("producer_name", "default") - if not config.get("external_message_consumer", {}).get("enabled"): + if not self._config.get("external_message_consumer", {}).get("enabled"): raise OperationalException( "ProducerPairList requires external_message_consumer to be enabled." ) diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index b15cfa96e..26fadb9ae 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -14,7 +14,6 @@ from cachetools import TTLCache from freqtrade import __version__ from freqtrade.configuration.load_config import CONFIG_PARSE_MODE -from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -27,15 +26,8 @@ logger = logging.getLogger(__name__) class RemotePairList(IPairList): is_pairlist_generator = True - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) if "number_assets" not in self._pairlistconfig: raise OperationalException( diff --git a/freqtrade/plugins/pairlist/ShuffleFilter.py b/freqtrade/plugins/pairlist/ShuffleFilter.py index d7f8a60bc..59ac1ac7c 100644 --- a/freqtrade/plugins/pairlist/ShuffleFilter.py +++ b/freqtrade/plugins/pairlist/ShuffleFilter.py @@ -4,9 +4,8 @@ Shuffle pair list filter import logging import random -from typing import Any, Dict, List, Literal +from typing import Dict, List, Literal -from freqtrade.constants import Config from freqtrade.enums import RunMode from freqtrade.exchange import timeframe_to_seconds from freqtrade.exchange.types import Tickers @@ -20,27 +19,20 @@ ShuffleValues = Literal["candle", "iteration"] class ShuffleFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) # Apply seed in backtesting mode to get comparable results, # but not in live modes to get a non-repeating order of pairs during live modes. - if config.get("runmode") in (RunMode.LIVE, RunMode.DRY_RUN): + if self._config.get("runmode") in (RunMode.LIVE, RunMode.DRY_RUN): self._seed = None logger.info("Live mode detected, not applying seed.") else: - self._seed = pairlistconfig.get("seed") + self._seed = self._pairlistconfig.get("seed") logger.info(f"Backtesting mode detected, applying seed value: {self._seed}") - self._random = random.Random(self._seed) - self._shuffle_freq: ShuffleValues = pairlistconfig.get("shuffle_frequency", "candle") + self._random = random.Random(self._seed) # noqa: S311 + self._shuffle_freq: ShuffleValues = self._pairlistconfig.get("shuffle_frequency", "candle") self.__pairlist_cache = PeriodicCache( maxsize=1000, ttl=timeframe_to_seconds(self._config["timeframe"]) ) diff --git a/freqtrade/plugins/pairlist/SpreadFilter.py b/freqtrade/plugins/pairlist/SpreadFilter.py index 4aca98f3e..736903abd 100644 --- a/freqtrade/plugins/pairlist/SpreadFilter.py +++ b/freqtrade/plugins/pairlist/SpreadFilter.py @@ -3,9 +3,8 @@ Spread pair list filter """ import logging -from typing import Any, Dict, Optional +from typing import Dict, Optional -from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Ticker from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -15,17 +14,10 @@ logger = logging.getLogger(__name__) class SpreadFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) - self._max_spread_ratio = pairlistconfig.get("max_spread_ratio", 0.005) + self._max_spread_ratio = self._pairlistconfig.get("max_spread_ratio", 0.005) self._enabled = self._max_spread_ratio != 0 if not self._exchange.get_option("tickers_have_bid_ask"): diff --git a/freqtrade/plugins/pairlist/StaticPairList.py b/freqtrade/plugins/pairlist/StaticPairList.py index ac1201ca3..922d0fd94 100644 --- a/freqtrade/plugins/pairlist/StaticPairList.py +++ b/freqtrade/plugins/pairlist/StaticPairList.py @@ -6,9 +6,8 @@ Provides pair white list as it configured in config import logging from copy import deepcopy -from typing import Any, Dict, List +from typing import Dict, List -from freqtrade.constants import Config from freqtrade.exchange.types import Tickers from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter @@ -19,15 +18,8 @@ logger = logging.getLogger(__name__) class StaticPairList(IPairList): is_pairlist_generator = True - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) self._allow_inactive = self._pairlistconfig.get("allow_inactive", False) diff --git a/freqtrade/plugins/pairlist/VolatilityFilter.py b/freqtrade/plugins/pairlist/VolatilityFilter.py index c4088196d..f5af2d0a7 100644 --- a/freqtrade/plugins/pairlist/VolatilityFilter.py +++ b/freqtrade/plugins/pairlist/VolatilityFilter.py @@ -5,13 +5,13 @@ Volatility pairlist filter import logging import sys from datetime import timedelta -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional import numpy as np from cachetools import TTLCache from pandas import DataFrame -from freqtrade.constants import Config, ListPairsWithTimeframes +from freqtrade.constants import ListPairsWithTimeframes from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.misc import plural @@ -27,26 +27,19 @@ class VolatilityFilter(IPairList): Filters pairs by volatility """ - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) - self._days = pairlistconfig.get("lookback_days", 10) - self._min_volatility = pairlistconfig.get("min_volatility", 0) - self._max_volatility = pairlistconfig.get("max_volatility", sys.maxsize) - self._refresh_period = pairlistconfig.get("refresh_period", 1440) + self._days = self._pairlistconfig.get("lookback_days", 10) + self._min_volatility = self._pairlistconfig.get("min_volatility", 0) + self._max_volatility = self._pairlistconfig.get("max_volatility", sys.maxsize) + self._refresh_period = self._pairlistconfig.get("refresh_period", 1440) self._def_candletype = self._config["candle_type_def"] - self._sort_direction: Optional[str] = pairlistconfig.get("sort_direction", None) + self._sort_direction: Optional[str] = self._pairlistconfig.get("sort_direction", None) self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) - candle_limit = exchange.ohlcv_candle_limit("1d", self._config["candle_type_def"]) + candle_limit = self._exchange.ohlcv_candle_limit("1d", self._config["candle_type_def"]) if self._days < 1: raise OperationalException("VolatilityFilter requires lookback_days to be >= 1") if self._days > candle_limit: diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index f9a0dd6b1..ea172f140 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -10,7 +10,7 @@ from typing import Any, Dict, List, Literal from cachetools import TTLCache -from freqtrade.constants import Config, ListPairsWithTimeframes +from freqtrade.constants import ListPairsWithTimeframes from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date from freqtrade.exchange.types import Tickers @@ -27,15 +27,8 @@ SORT_VALUES = ["quoteVolume"] class VolumePairList(IPairList): is_pairlist_generator = True - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) if "number_assets" not in self._pairlistconfig: raise OperationalException( @@ -43,7 +36,7 @@ class VolumePairList(IPairList): 'for "pairlist.config.number_assets"' ) - self._stake_currency = config["stake_currency"] + self._stake_currency = self._config["stake_currency"] self._number_pairs = self._pairlistconfig["number_assets"] self._sort_key: Literal["quoteVolume"] = self._pairlistconfig.get("sort_key", "quoteVolume") self._min_value = self._pairlistconfig.get("min_value", 0) @@ -94,7 +87,7 @@ class VolumePairList(IPairList): if not self._validate_keys(self._sort_key): raise OperationalException(f"key {self._sort_key} not in {SORT_VALUES}") - candle_limit = exchange.ohlcv_candle_limit( + candle_limit = self._exchange.ohlcv_candle_limit( self._lookback_timeframe, self._config["candle_type_def"] ) if self._lookback_period < 0: diff --git a/freqtrade/plugins/pairlist/rangestabilityfilter.py b/freqtrade/plugins/pairlist/rangestabilityfilter.py index 54c6a536e..473e003b6 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -4,12 +4,12 @@ Rate of change pairlist filter import logging from datetime import timedelta -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional from cachetools import TTLCache from pandas import DataFrame -from freqtrade.constants import Config, ListPairsWithTimeframes +from freqtrade.constants import ListPairsWithTimeframes from freqtrade.exceptions import OperationalException from freqtrade.exchange.types import Tickers from freqtrade.misc import plural @@ -21,26 +21,19 @@ logger = logging.getLogger(__name__) class RangeStabilityFilter(IPairList): - def __init__( - self, - exchange, - pairlistmanager, - config: Config, - pairlistconfig: Dict[str, Any], - pairlist_pos: int, - ) -> None: - super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) - self._days = pairlistconfig.get("lookback_days", 10) - self._min_rate_of_change = pairlistconfig.get("min_rate_of_change", 0.01) - self._max_rate_of_change = pairlistconfig.get("max_rate_of_change") - self._refresh_period = pairlistconfig.get("refresh_period", 86400) + self._days = self._pairlistconfig.get("lookback_days", 10) + self._min_rate_of_change = self._pairlistconfig.get("min_rate_of_change", 0.01) + self._max_rate_of_change = self._pairlistconfig.get("max_rate_of_change") + self._refresh_period = self._pairlistconfig.get("refresh_period", 86400) self._def_candletype = self._config["candle_type_def"] - self._sort_direction: Optional[str] = pairlistconfig.get("sort_direction", None) + self._sort_direction: Optional[str] = self._pairlistconfig.get("sort_direction", None) self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period) - candle_limit = exchange.ohlcv_candle_limit("1d", self._config["candle_type_def"]) + candle_limit = self._exchange.ohlcv_candle_limit("1d", self._config["candle_type_def"]) if self._days < 1: raise OperationalException("RangeStabilityFilter requires lookback_days to be >= 1") if self._days > candle_limit: diff --git a/freqtrade/rpc/api_server/api_auth.py b/freqtrade/rpc/api_server/api_auth.py index 0e054220b..24e04a905 100644 --- a/freqtrade/rpc/api_server/api_auth.py +++ b/freqtrade/rpc/api_server/api_auth.py @@ -31,7 +31,7 @@ security = HTTPBasic() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False) -def get_user_from_token(token, secret_key: str, token_type: str = "access") -> str: +def get_user_from_token(token, secret_key: str, token_type: str = "access") -> str: # noqa: S107 credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", @@ -86,11 +86,11 @@ async def validate_ws_token( await ws.close(code=status.WS_1008_POLICY_VIOLATION) -def create_token(data: dict, secret_key: str, token_type: str = "access") -> str: +def create_token(data: dict, secret_key: str, token_type: str = "access") -> str: # noqa: S107 to_encode = data.copy() - if token_type == "access": + if token_type == "access": # noqa: S105 expire = datetime.now(timezone.utc) + timedelta(minutes=15) - elif token_type == "refresh": + elif token_type == "refresh": # noqa: S105 expire = datetime.now(timezone.utc) + timedelta(days=30) else: raise ValueError() @@ -127,9 +127,15 @@ def token_login( ): if verify_auth(api_config, form_data.username, form_data.password): token_data = {"identity": {"u": form_data.username}} - access_token = create_token(token_data, api_config.get("jwt_secret_key", "super-secret")) + access_token = create_token( + token_data, + api_config.get("jwt_secret_key", "super-secret"), + token_type="access", # noqa: S106 + ) refresh_token = create_token( - token_data, api_config.get("jwt_secret_key", "super-secret"), token_type="refresh" + token_data, + api_config.get("jwt_secret_key", "super-secret"), + token_type="refresh", # noqa: S106 ) return { "access_token": access_token, @@ -148,6 +154,8 @@ def token_refresh(token: str = Depends(oauth2_scheme), api_config=Depends(get_ap u = get_user_from_token(token, api_config.get("jwt_secret_key", "super-secret"), "refresh") token_data = {"identity": {"u": u}} access_token = create_token( - token_data, api_config.get("jwt_secret_key", "super-secret"), token_type="access" + token_data, + api_config.get("jwt_secret_key", "super-secret"), + token_type="access", # noqa: S106 ) return {"access_token": access_token} diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 4cafa12ad..3513b7207 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1466,6 +1466,8 @@ class RPC: from freqtrade.resolvers.strategy_resolver import StrategyResolver strategy = StrategyResolver.load_strategy(config) + # Manually load hyperparameters, as we don't call the bot-start callback. + strategy.ft_load_hyper_params(False) if strategy.plot_config and "subplots" not in strategy.plot_config: strategy.plot_config["subplots"] = {} diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index de3d0f349..e6d86926b 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -6,6 +6,7 @@ This module defines the interface to apply for strategies import logging from abc import ABC, abstractmethod from datetime import datetime, timedelta, timezone +from math import isinf, isnan from typing import Dict, List, Optional, Tuple, Union from pandas import DataFrame @@ -1423,7 +1424,9 @@ class IStrategy(ABC, HyperStrategyMixin): after_fill=after_fill, ) # Sanity check - error cases will return None - if stop_loss_value_custom: + if stop_loss_value_custom and not ( + isnan(stop_loss_value_custom) or isinf(stop_loss_value_custom) + ): stop_loss_value = stop_loss_value_custom trade.adjust_stop_loss( bound or current_rate, stop_loss_value, allow_refresh=after_fill diff --git a/ft_client/freqtrade_client/__init__.py b/ft_client/freqtrade_client/__init__.py index 9ede4dd12..60b5d1164 100644 --- a/ft_client/freqtrade_client/__init__.py +++ b/ft_client/freqtrade_client/__init__.py @@ -31,7 +31,7 @@ if "dev" in __version__: versionfile = Path("./freqtrade_commit") if versionfile.is_file(): __version__ = f"docker-{__version__}-{versionfile.read_text()[:8]}" - except Exception: + except Exception: # noqa: S110 pass __all__ = ["FtRestClient"] diff --git a/ft_client/freqtrade_client/ft_client.py b/ft_client/freqtrade_client/ft_client.py index d51858fde..3c450d1df 100644 --- a/ft_client/freqtrade_client/ft_client.py +++ b/ft_client/freqtrade_client/ft_client.py @@ -81,12 +81,12 @@ def print_commands(): print(f"{x}\n\t{doc}\n") -def main_exec(args: Dict[str, Any]): - if args.get("show"): +def main_exec(parsed: Dict[str, Any]): + if parsed.get("show"): print_commands() sys.exit() - config = load_config(args["config"]) + config = load_config(parsed["config"]) url = config.get("api_server", {}).get("listen_ip_address", "127.0.0.1") port = config.get("api_server", {}).get("listen_port", "8080") username = config.get("api_server", {}).get("username") @@ -96,13 +96,24 @@ def main_exec(args: Dict[str, Any]): client = FtRestClient(server_url, username, password) m = [x for x, y in inspect.getmembers(client) if not x.startswith("_")] - command = args["command"] + command = parsed["command"] if command not in m: logger.error(f"Command {command} not defined") print_commands() return - print(json.dumps(getattr(client, command)(*args["command_arguments"]))) + # Split arguments with = into key/value pairs + kwargs = {x.split("=")[0]: x.split("=")[1] for x in parsed["command_arguments"] if "=" in x} + args = [x for x in parsed["command_arguments"] if "=" not in x] + try: + res = getattr(client, command)(*args, **kwargs) + print(json.dumps(res)) + except TypeError as e: + logger.error(f"Error executing command {command}: {e}") + sys.exit(1) + except Exception as e: + logger.error(f"Fatal Error executing command {command}: {e}") + sys.exit(1) def main(): diff --git a/ft_client/freqtrade_client/ft_rest_client.py b/ft_client/freqtrade_client/ft_rest_client.py index 6e5f7e6c5..f7c895901 100755 --- a/ft_client/freqtrade_client/ft_rest_client.py +++ b/ft_client/freqtrade_client/ft_rest_client.py @@ -54,7 +54,7 @@ class FtRestClient: # return resp.text return resp.json() except ConnectionError: - logger.warning("Connection error") + logger.warning(f"Connection error - could not connect to {netloc}.") def _get(self, apipath, params: ParamsT = None): return self._call("GET", apipath, params=params) @@ -312,20 +312,48 @@ class FtRestClient: data = {"pair": pair, "price": price} return self._post("forcebuy", data=data) - def forceenter(self, pair, side, price=None): + def forceenter( + self, + pair, + side, + price=None, + *, + order_type=None, + stake_amount=None, + leverage=None, + enter_tag=None, + ): """Force entering a trade :param pair: Pair to buy (ETH/BTC) :param side: 'long' or 'short' :param price: Optional - price to buy + :param order_type: Optional keyword argument - 'limit' or 'market' + :param stake_amount: Optional keyword argument - stake amount (as float) + :param leverage: Optional keyword argument - leverage (as float) + :param enter_tag: Optional keyword argument - entry tag (as string, default: 'force_enter') :return: json object of the trade """ data = { "pair": pair, "side": side, } + if price: data["price"] = price + + if order_type: + data["ordertype"] = order_type + + if stake_amount: + data["stakeamount"] = stake_amount + + if leverage: + data["leverage"] = leverage + + if enter_tag: + data["entry_tag"] = enter_tag + return self._post("forceenter", data=data) def forceexit(self, tradeid, ordertype=None, amount=None): diff --git a/ft_client/requirements.txt b/ft_client/requirements.txt index 36cd79635..35406c1d0 100644 --- a/ft_client/requirements.txt +++ b/ft_client/requirements.txt @@ -1,3 +1,3 @@ # Requirements for freqtrade client library -requests==2.32.2 +requests==2.32.3 python-rapidjson==1.17 diff --git a/ft_client/test_client/test_rest_client.py b/ft_client/test_client/test_rest_client.py index 08ccee765..541577e59 100644 --- a/ft_client/test_client/test_rest_client.py +++ b/ft_client/test_client/test_rest_client.py @@ -1,5 +1,5 @@ import re -from unittest.mock import MagicMock +from unittest.mock import ANY, MagicMock import pytest from requests.exceptions import ConnectionError @@ -52,70 +52,89 @@ def test_FtRestClient_call_invalid(caplog): @pytest.mark.parametrize( - "method,args", + "method,args,kwargs", [ - ("start", []), - ("stop", []), - ("stopbuy", []), - ("reload_config", []), - ("balance", []), - ("count", []), - ("entries", []), - ("exits", []), - ("mix_tags", []), - ("locks", []), - ("lock_add", ["XRP/USDT", "2024-01-01 20:00:00Z", "*", "rand"]), - ("delete_lock", [2]), - ("daily", []), - ("daily", [15]), - ("weekly", []), - ("weekly", [15]), - ("monthly", []), - ("monthly", [12]), - ("edge", []), - ("profit", []), - ("stats", []), - ("performance", []), - ("status", []), - ("version", []), - ("show_config", []), - ("ping", []), - ("logs", []), - ("logs", [55]), - ("trades", []), - ("trades", [5]), - ("trades", [5, 5]), # With offset - ("trade", [1]), - ("delete_trade", [1]), - ("cancel_open_order", [1]), - ("whitelist", []), - ("blacklist", []), - ("blacklist", ["XRP/USDT"]), - ("blacklist", ["XRP/USDT", "BTC/USDT"]), - ("forcebuy", ["XRP/USDT"]), - ("forcebuy", ["XRP/USDT", 1.5]), - ("forceenter", ["XRP/USDT", "short"]), - ("forceenter", ["XRP/USDT", "short", 1.5]), - ("forceexit", [1]), - ("forceexit", [1, "limit"]), - ("forceexit", [1, "limit", 100]), - ("strategies", []), - ("strategy", ["sampleStrategy"]), - ("pairlists_available", []), - ("plot_config", []), - ("available_pairs", []), - ("available_pairs", ["5m"]), - ("pair_candles", ["XRP/USDT", "5m"]), - ("pair_candles", ["XRP/USDT", "5m", 500]), - ("pair_history", ["XRP/USDT", "5m", "SampleStrategy"]), - ("sysinfo", []), - ("health", []), + ("start", [], {}), + ("stop", [], {}), + ("stopbuy", [], {}), + ("reload_config", [], {}), + ("balance", [], {}), + ("count", [], {}), + ("entries", [], {}), + ("exits", [], {}), + ("mix_tags", [], {}), + ("locks", [], {}), + ("lock_add", ["XRP/USDT", "2024-01-01 20:00:00Z", "*", "rand"], {}), + ("delete_lock", [2], {}), + ("daily", [], {}), + ("daily", [15], {}), + ("weekly", [], {}), + ("weekly", [15], {}), + ("monthly", [], {}), + ("monthly", [12], {}), + ("edge", [], {}), + ("profit", [], {}), + ("stats", [], {}), + ("performance", [], {}), + ("status", [], {}), + ("version", [], {}), + ("show_config", [], {}), + ("ping", [], {}), + ("logs", [], {}), + ("logs", [55], {}), + ("trades", [], {}), + ("trades", [5], {}), + ("trades", [5, 5], {}), # With offset + ("trade", [1], {}), + ("delete_trade", [1], {}), + ("cancel_open_order", [1], {}), + ("whitelist", [], {}), + ("blacklist", [], {}), + ("blacklist", ["XRP/USDT"], {}), + ("blacklist", ["XRP/USDT", "BTC/USDT"], {}), + ("forcebuy", ["XRP/USDT"], {}), + ("forcebuy", ["XRP/USDT", 1.5], {}), + ("forceenter", ["XRP/USDT", "short"], {}), + ("forceenter", ["XRP/USDT", "short", 1.5], {}), + ("forceenter", ["XRP/USDT", "short", 1.5], {"order_type": "market"}), + ("forceenter", ["XRP/USDT", "short", 1.5], {"order_type": "market", "stake_amount": 100}), + ( + "forceenter", + ["XRP/USDT", "short", 1.5], + {"order_type": "market", "stake_amount": 100, "leverage": 10.0}, + ), + ( + "forceenter", + ["XRP/USDT", "short", 1.5], + { + "order_type": "market", + "stake_amount": 100, + "leverage": 10.0, + "enter_tag": "test_force_enter", + }, + ), + ("forceexit", [1], {}), + ("forceexit", [1, "limit"], {}), + ("forceexit", [1, "limit", 100], {}), + ("strategies", [], {}), + ("strategy", ["sampleStrategy"], {}), + ("pairlists_available", [], {}), + ("plot_config", [], {}), + ("available_pairs", [], {}), + ("available_pairs", ["5m"], {}), + ("pair_candles", ["XRP/USDT", "5m"], {}), + ("pair_candles", ["XRP/USDT", "5m", 500], {}), + ("pair_candles", ["XRP/USDT", "5m", 500], {"columns": ["close_time,close"]}), + ("pair_history", ["XRP/USDT", "5m", "SampleStrategy"], {}), + ("pair_history", ["XRP/USDT", "5m"], {"strategy": "SampleStrategy"}), + ("sysinfo", [], {}), + ("health", [], {}), ], ) -def test_FtRestClient_call_explicit_methods(method, args): +def test_FtRestClient_call_explicit_methods(method, args, kwargs): client, mock = get_rest_client() exec = getattr(client, method) - exec(*args) + exec(*args, **kwargs) assert mock.call_count == 1 @@ -148,3 +167,40 @@ def test_ft_client(mocker, capsys, caplog): ) main_exec(args) assert log_has_re("Command whatever not defined", caplog) + + +@pytest.mark.parametrize( + "params, expected_args, expected_kwargs", + [ + ("forceenter BTC/USDT long", ["BTC/USDT", "long"], {}), + ("forceenter BTC/USDT long limit", ["BTC/USDT", "long", "limit"], {}), + ( + # Skip most parameters, only providing enter_tag + "forceenter BTC/USDT long enter_tag=deadBeef", + ["BTC/USDT", "long"], + {"enter_tag": "deadBeef"}, + ), + ( + "forceenter BTC/USDT long invalid_key=123", + [], + SystemExit, + # {"invalid_key": "deadBeef"}, + ), + ], +) +def test_ft_client_argparsing(mocker, params, expected_args, expected_kwargs, caplog): + mocked_method = params.split(" ")[0] + mocker.patch("freqtrade_client.ft_client.load_config", return_value={}, autospec=True) + mm = mocker.patch( + f"freqtrade_client.ft_client.FtRestClient.{mocked_method}", return_value={}, autospec=True + ) + args = add_arguments(params.split(" ")) + if isinstance(expected_kwargs, dict): + main_exec(args) + mm.assert_called_once_with(ANY, *expected_args, **expected_kwargs) + else: + with pytest.raises(expected_kwargs): + main_exec(args) + + assert log_has_re(f"Error executing command {mocked_method}: got an unexpected .*", caplog) + mm.assert_not_called() diff --git a/mkdocs.yml b/mkdocs.yml index ef8b7181b..896dcd3f4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,6 @@ site_name: Freqtrade site_url: !ENV [READTHEDOCS_CANONICAL_URL, 'https://www.freqtrade.io/en/latest/'] +site_description: Freqtrade is a free and open source crypto trading bot written in Python, designed to support all major exchanges and be controlled via Telegram or builtin Web UI repo_url: https://github.com/freqtrade/freqtrade edit_uri: edit/develop/docs/ use_directory_urls: True diff --git a/pyproject.toml b/pyproject.toml index e96768c5b..55ff28db6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,6 +82,9 @@ skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*", "**/user_data/*" known_first_party = ["freqtrade_client"] [tool.pytest.ini_options] +log_format = "%(asctime)s %(levelname)s %(message)s" +log_date_format = "%Y-%m-%d %H:%M:%S" + asyncio_mode = "auto" addopts = "--dist loadscope" @@ -135,7 +138,7 @@ extend-select = [ # "EXE", # flake8-executable # "C4", # flake8-comprehensions "YTT", # flake8-2020 - # "S", # flake8-bandit + "S", # flake8-bandit # "DTZ", # flake8-datetimez # "RSE", # flake8-raise # "TCH", # flake8-type-checking @@ -148,13 +151,30 @@ extend-ignore = [ "E272", # Multiple spaces before keyword "E221", # Multiple spaces before operator "B007", # Loop control variable not used + "S603", # `subprocess` call: check for execution of untrusted input + "S607", # Starting a process with a partial executable path + "S608", # Possible SQL injection vector through string-based query construction ] [tool.ruff.lint.mccabe] max-complexity = 12 [tool.ruff.lint.per-file-ignores] -"tests/*" = ["S"] +"freqtrade/freqai/**/*.py" = [ + "S311" # Standard pseudo-random generators are not suitable for cryptographic purposes +] +"tests/**/*.py" = [ + "S101", # allow assert in tests + "S104", # Possible binding to all interfaces + "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes + "S105", # Possible hardcoded password assigned to: "secret" + "S106", # Possible hardcoded password assigned to argument: "token_type" + "S110", # `try`-`except`-`pass` detected, consider logging the exception + ] + +"ft_client/test_client/**/*.py" = [ + "S101", # allow assert in tests +] [tool.ruff.lint.flake8-bugbear] # Allow default arguments like, e.g., `data: List[str] = fastapi.Query(None)`. diff --git a/requirements-dev.txt b/requirements-dev.txt index c9d561b40..f06316438 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,10 +7,10 @@ -r docs/requirements-docs.txt coveralls==4.0.1 -ruff==0.4.5 +ruff==0.4.10 mypy==1.10.0 pre-commit==3.7.1 -pytest==8.2.1 +pytest==8.2.2 pytest-asyncio==0.23.7 pytest-cov==5.0.0 pytest-mock==3.14.0 @@ -26,6 +26,6 @@ nbconvert==7.16.4 # mypy types types-cachetools==5.3.0.7 types-filelock==3.2.7 -types-requests==2.32.0.20240523 +types-requests==2.32.0.20240622 types-tabulate==0.9.0.20240106 types-python-dateutil==2.9.0.20240316 diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 546c0b62a..c278716fb 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -2,7 +2,8 @@ -r requirements-freqai.txt # Required for freqai-rl -torch==2.2.2 +torch==2.3.1; sys_platform != 'darwin' or platform_machine != 'x86_64' +torch==2.2.2; sys_platform == 'darwin' and platform_machine == 'x86_64' gymnasium==0.29.1 stable_baselines3==2.3.2 sb3_contrib>=2.2.1 diff --git a/requirements-freqai.txt b/requirements-freqai.txt index cdc32e11b..d864b45e8 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -6,7 +6,7 @@ scikit-learn==1.5.0 joblib==1.4.2 catboost==1.2.5; 'arm' not in platform_machine -lightgbm==4.3.0 +lightgbm==4.4.0 xgboost==2.0.3 -tensorboard==2.16.2 +tensorboard==2.17.0 datasieve==0.1.7 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 99d4ee5c6..ffde73b84 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -5,4 +5,4 @@ scipy==1.13.1 scikit-learn==1.5.0 ft-scikit-optimize==0.9.2 -filelock==3.14.0 +filelock==3.15.4 diff --git a/requirements.txt b/requirements.txt index 8adb2f84e..5dec7bb5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,22 @@ numpy==1.26.4 pandas==2.2.2 -bottleneck==1.3.8 -numexpr==2.10.0 +bottleneck==1.4.0 +numexpr==2.10.1 pandas-ta==0.3.14b -ccxt==4.3.35 -cryptography==42.0.7 +ccxt==4.3.50 +cryptography==42.0.8 aiohttp==3.9.5 -SQLAlchemy==2.0.30 -python-telegram-bot==21.2 +SQLAlchemy==2.0.31 +python-telegram-bot==21.3 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 humanize==4.9.0 cachetools==5.3.3 -requests==2.32.2 -urllib3==2.2.1 +requests==2.32.3 +urllib3==2.2.2 jsonschema==4.22.0 -TA-Lib==0.4.29 +TA-Lib==0.4.31 technical==1.4.3 tabulate==0.9.0 pycoingecko==3.1.0 @@ -32,18 +32,18 @@ py_find_1st==1.1.6 # Load ticker files 30% faster python-rapidjson==1.17 # Properly format api responses -orjson==3.10.3 +orjson==3.10.5 # Notify systemd sdnotify==0.3.2 # API Server fastapi==0.111.0 -pydantic==2.7.1 -uvicorn==0.29.0 +pydantic==2.7.4 +uvicorn==0.30.1 pyjwt==2.8.0 aiofiles==23.2.1 -psutil==5.9.8 +psutil==6.0.0 # Support for colorized terminal output colorama==0.4.6 @@ -62,4 +62,4 @@ websockets==12.0 janus==1.0.0 ast-comments==1.2.2 -packaging==24.0 +packaging==24.1 diff --git a/setup.sh b/setup.sh index 18f7682d8..f1317c02c 100755 --- a/setup.sh +++ b/setup.sh @@ -49,7 +49,7 @@ function updateenv() { source .venv/bin/activate SYS_ARCH=$(uname -m) echo "pip install in-progress. Please wait..." - ${PYTHON} -m pip install --upgrade pip wheel setuptools + ${PYTHON} -m pip install --upgrade "pip<=24.0" wheel setuptools REQUIREMENTS_HYPEROPT="" REQUIREMENTS_PLOT="" REQUIREMENTS_FREQAI="" diff --git a/tests/conftest.py b/tests/conftest.py index 3686a548a..c9f923c13 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -238,7 +238,6 @@ def patched_configuration_load_config_file(mocker, config) -> None: def patch_exchange( mocker, api_mock=None, id="binance", mock_markets=True, mock_supported_modes=True ) -> None: - mocker.patch(f"{EXMS}._load_async_markets", return_value={}) mocker.patch(f"{EXMS}.validate_config", MagicMock()) mocker.patch(f"{EXMS}.validate_timeframes", MagicMock()) mocker.patch(f"{EXMS}.id", PropertyMock(return_value=id)) @@ -248,6 +247,7 @@ def patch_exchange( mocker.patch("freqtrade.exchange.bybit.Bybit.cache_leverage_tiers") if mock_markets: + mocker.patch(f"{EXMS}._load_async_markets", return_value={}) if isinstance(mock_markets, bool): mock_markets = get_markets() mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=mock_markets)) diff --git a/tests/data/test_download_data.py b/tests/data/test_download_data.py index 4922a213f..08d56458f 100644 --- a/tests/data/test_download_data.py +++ b/tests/data/test_download_data.py @@ -83,6 +83,12 @@ def test_download_data_main_trades(mocker): assert dl_mock.call_count == 1 assert convert_mock.call_count == 1 + # Exchange that doesn't support historic downloads + config["exchange"]["name"] = "bybit" + with pytest.raises(OperationalException, match=r"Trade history not available for .*"): + config + download_data_main(config) + def test_download_data_main_data_invalid(mocker): patch_exchange(mocker, id="kraken") diff --git a/tests/data/test_entryexitanalysis.py b/tests/data/test_entryexitanalysis.py index 49ef74c0a..1a5309190 100644 --- a/tests/data/test_entryexitanalysis.py +++ b/tests/data/test_entryexitanalysis.py @@ -67,10 +67,10 @@ def test_backtest_analysis_nomock(default_conf, mocker, caplog, testdatadir, use "enter_tag_long_b", ], "exit_reason": [ - ExitType.ROI, - ExitType.EXIT_SIGNAL, - ExitType.STOP_LOSS, - ExitType.TRAILING_STOP_LOSS, + ExitType.ROI.value, + ExitType.EXIT_SIGNAL.value, + ExitType.STOP_LOSS.value, + ExitType.TRAILING_STOP_LOSS.value, ], } ) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 520de1c0f..6c3d62dde 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -7,7 +7,7 @@ from unittest.mock import MagicMock, Mock, PropertyMock, patch import ccxt import pytest -from numpy import NaN +from numpy import nan from pandas import DataFrame from freqtrade.enums import CandleType, MarginMode, RunMode, TradingMode @@ -181,7 +181,7 @@ def test_remove_exchange_credentials(default_conf) -> None: def test_init_ccxt_kwargs(default_conf, mocker, caplog): - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") aei_mock = mocker.patch(f"{EXMS}.additional_exchange_init") @@ -518,7 +518,7 @@ def test__load_async_markets(default_conf, mocker, caplog): mocker.patch(f"{EXMS}._init_ccxt") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_markets") + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") exchange = Exchange(default_conf) @@ -527,28 +527,26 @@ def test__load_async_markets(default_conf, mocker, caplog): assert exchange._api_async.load_markets.call_count == 1 caplog.set_level(logging.DEBUG) - exchange._api_async.load_markets = Mock(side_effect=ccxt.BaseError("deadbeef")) - exchange._load_async_markets() - - assert log_has("Could not load async markets. Reason: deadbeef", caplog) + exchange._api_async.load_markets = get_mock_coro(side_effect=ccxt.BaseError("deadbeef")) + with pytest.raises(ccxt.BaseError, match="deadbeef"): + exchange._load_async_markets() def test__load_markets(default_conf, mocker, caplog): caplog.set_level(logging.INFO) api_mock = MagicMock() - api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError("SomeError")) + api_mock.load_markets = get_mock_coro(side_effect=ccxt.BaseError("SomeError")) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") Exchange(default_conf) - assert log_has("Unable to initialize markets.", caplog) + assert log_has("Could not load markets.", caplog) expected_return = {"ETH/BTC": "available"} api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value=expected_return) + api_mock.load_markets = get_mock_coro(return_value=expected_return) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) default_conf["exchange"]["pair_whitelist"] = ["ETH/BTC"] ex = Exchange(default_conf) @@ -563,12 +561,12 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine): start_dt = dt_now() time_machine.move_to(start_dt, tick=False) api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value=initial_markets) + api_mock.load_markets = get_mock_coro(return_value=initial_markets) default_conf["exchange"]["markets_refresh_interval"] = 10 exchange = get_patched_exchange( mocker, default_conf, api_mock, id="binance", mock_markets=False ) - exchange._load_async_markets = MagicMock() + lam_spy = mocker.spy(exchange, "_load_async_markets") assert exchange._last_markets_refresh == dt_ts() assert exchange.markets == initial_markets @@ -577,42 +575,45 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine): # less than 10 minutes have passed, no reload exchange.reload_markets() assert exchange.markets == initial_markets - assert exchange._load_async_markets.call_count == 0 + assert lam_spy.call_count == 0 - api_mock.load_markets = MagicMock(return_value=updated_markets) + api_mock.load_markets = get_mock_coro(return_value=updated_markets) # more than 10 minutes have passed, reload is executed time_machine.move_to(start_dt + timedelta(minutes=11), tick=False) exchange.reload_markets() assert exchange.markets == updated_markets - assert exchange._load_async_markets.call_count == 1 + assert lam_spy.call_count == 1 assert log_has("Performing scheduled market reload..", caplog) # Not called again - exchange._load_async_markets.reset_mock() + lam_spy.reset_mock() exchange.reload_markets() - assert exchange._load_async_markets.call_count == 0 + assert lam_spy.call_count == 0 def test_reload_markets_exception(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) api_mock = MagicMock() - api_mock.load_markets = MagicMock(side_effect=ccxt.NetworkError("LoadError")) + api_mock.load_markets = get_mock_coro(side_effect=ccxt.NetworkError("LoadError")) default_conf["exchange"]["markets_refresh_interval"] = 10 - exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") + exchange = get_patched_exchange( + mocker, default_conf, api_mock, id="binance", mock_markets=False + ) + exchange._last_markets_refresh = 2 # less than 10 minutes have passed, no reload exchange.reload_markets() - assert exchange._last_markets_refresh == 0 - assert log_has_re(r"Could not reload markets.*", caplog) + assert exchange._last_markets_refresh == 2 + assert log_has_re(r"Could not load markets\..*", caplog) @pytest.mark.parametrize("stake_currency", ["ETH", "BTC", "USDT"]) def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog): default_conf["stake_currency"] = stake_currency api_mock = MagicMock() - type(api_mock).load_markets = MagicMock( + type(api_mock).load_markets = get_mock_coro( return_value={ "ETH/BTC": {"quote": "BTC"}, "LTC/BTC": {"quote": "BTC"}, @@ -623,7 +624,6 @@ def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") mocker.patch(f"{EXMS}.validate_pricing") Exchange(default_conf) @@ -631,7 +631,7 @@ def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog): def test_validate_stakecurrency_error(default_conf, mocker, caplog): default_conf["stake_currency"] = "XRP" api_mock = MagicMock() - type(api_mock).load_markets = MagicMock( + type(api_mock).load_markets = get_mock_coro( return_value={ "ETH/BTC": {"quote": "BTC"}, "LTC/BTC": {"quote": "BTC"}, @@ -642,14 +642,13 @@ def test_validate_stakecurrency_error(default_conf, mocker, caplog): mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") with pytest.raises( ConfigurationError, match=r"XRP is not available as stake on .*Available currencies are: BTC, ETH, USDT", ): Exchange(default_conf) - type(api_mock).load_markets = MagicMock(side_effect=ccxt.NetworkError("No connection.")) + type(api_mock).load_markets = get_mock_coro(side_effect=ccxt.NetworkError("No connection.")) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) with pytest.raises( @@ -694,24 +693,26 @@ def test_get_pair_base_currency(default_conf, mocker, pair, expected): assert ex.get_pair_base_currency(pair) == expected -def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly +def test_validate_pairs(default_conf, mocker): api_mock = MagicMock() - type(api_mock).load_markets = MagicMock( - return_value={ - "ETH/BTC": {"quote": "BTC"}, - "LTC/BTC": {"quote": "BTC"}, - "XRP/BTC": {"quote": "BTC"}, - "NEO/BTC": {"quote": "BTC"}, - } - ) id_mock = PropertyMock(return_value="test_exchange") type(api_mock).id = id_mock mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") + mocker.patch( + f"{EXMS}._load_async_markets", + return_value={ + "ETH/BTC": {"quote": "BTC"}, + "LTC/BTC": {"quote": "BTC"}, + "XRP/BTC": {"quote": "BTC"}, + "NEO/BTC": {"quote": "BTC"}, + }, + ) mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") + # test exchange.validate_pairs directly + # No assert - but this should not fail (!) Exchange(default_conf) @@ -751,7 +752,7 @@ def test_validate_pairs_exception(default_conf, mocker, caplog): def test_validate_pairs_restricted(default_conf, mocker, caplog): api_mock = MagicMock() - type(api_mock).load_markets = MagicMock( + type(api_mock).load_markets = get_mock_coro( return_value={ "ETH/BTC": {"quote": "BTC"}, "LTC/BTC": {"quote": "BTC"}, @@ -761,7 +762,6 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog): ) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") mocker.patch(f"{EXMS}.validate_pricing") mocker.patch(f"{EXMS}.validate_stakecurrency") @@ -774,9 +774,9 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog): ) -def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog): +def test_validate_pairs_stakecompatibility(default_conf, mocker): api_mock = MagicMock() - type(api_mock).load_markets = MagicMock( + type(api_mock).load_markets = get_mock_coro( return_value={ "ETH/BTC": {"quote": "BTC"}, "LTC/BTC": {"quote": "BTC"}, @@ -787,17 +787,16 @@ def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog): ) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") Exchange(default_conf) -def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, caplog): +def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker): api_mock = MagicMock() default_conf["stake_currency"] = "" - type(api_mock).load_markets = MagicMock( + type(api_mock).load_markets = get_mock_coro( return_value={ "ETH/BTC": {"quote": "BTC"}, "LTC/BTC": {"quote": "BTC"}, @@ -808,7 +807,6 @@ def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, ca ) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -816,10 +814,10 @@ def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, ca assert type(api_mock).load_markets.call_count == 1 -def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog): +def test_validate_pairs_stakecompatibility_fail(default_conf, mocker): default_conf["exchange"]["pair_whitelist"].append("HELLO-WORLD") api_mock = MagicMock() - type(api_mock).load_markets = MagicMock( + type(api_mock).load_markets = get_mock_coro( return_value={ "ETH/BTC": {"quote": "BTC"}, "LTC/BTC": {"quote": "BTC"}, @@ -830,7 +828,6 @@ def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog): ) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) mocker.patch(f"{EXMS}.validate_timeframes") - mocker.patch(f"{EXMS}._load_async_markets") mocker.patch(f"{EXMS}.validate_stakecurrency") with pytest.raises(OperationalException, match=r"Stake-currency 'BTC' not compatible with.*"): @@ -847,7 +844,7 @@ def test_validate_timeframes(default_conf, mocker, timeframe): type(api_mock).timeframes = timeframes mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -865,7 +862,7 @@ def test_validate_timeframes_failed(default_conf, mocker): type(api_mock).timeframes = timeframes mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -895,7 +892,7 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker): del api_mock.timeframes mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") with pytest.raises( @@ -917,7 +914,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): del api_mock.timeframes mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={"timeframes": None})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs", MagicMock()) mocker.patch(f"{EXMS}.validate_stakecurrency") with pytest.raises( @@ -939,7 +936,7 @@ def test_validate_timeframes_not_in_config(default_conf, mocker): type(api_mock).timeframes = timeframes mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_stakecurrency") mocker.patch(f"{EXMS}.validate_pricing") @@ -955,7 +952,7 @@ def test_validate_pricing(default_conf, mocker): } type(api_mock).has = PropertyMock(return_value=has) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_trading_mode_and_margin_mode") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") @@ -991,7 +988,7 @@ def test_validate_ordertypes(default_conf, mocker): type(api_mock).has = PropertyMock(return_value={"createMarketOrder": True}) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") @@ -1050,7 +1047,7 @@ def test_validate_ordertypes_stop_advanced(default_conf, mocker, exchange_name, default_conf["margin_mode"] = MarginMode.ISOLATED type(api_mock).has = PropertyMock(return_value={"createMarketOrder": True}) mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_stakecurrency") @@ -1075,7 +1072,7 @@ def test_validate_ordertypes_stop_advanced(default_conf, mocker, exchange_name, def test_validate_order_types_not_in_config(default_conf, mocker): api_mock = MagicMock() mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) - mocker.patch(f"{EXMS}._load_markets", MagicMock(return_value={})) + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.validate_pairs") mocker.patch(f"{EXMS}.validate_timeframes") mocker.patch(f"{EXMS}.validate_pricing") @@ -1947,7 +1944,9 @@ def test_fetch_trading_fees(default_conf, mocker): assert api_mock.fetch_trading_fees.call_count == 1 api_mock.fetch_trading_fees.reset_mock() - + # Reload-markets calls fetch_trading_fees, too - so the explicit calls in the below + # exception test would be called twice. + mocker.patch(f"{EXMS}.reload_markets") ccxt_exceptionhandlers( mocker, default_conf, api_mock, exchange_name, "fetch_trading_fees", "fetch_trading_fees" ) @@ -4861,7 +4860,7 @@ def test_get_max_leverage_from_margin(default_conf, mocker, pair, nominal_value, (10, 0.0001, 2.0, 1.0, 0.002, 0.002), (10, 0.0002, 2.0, 0.01, 0.004, 0.00004), (10, 0.0002, 2.5, None, 0.005, None), - (10, 0.0002, NaN, None, 0.0, None), + (10, 0.0002, nan, None, 0.0, None), ], ) def test_calculate_funding_fees( diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index e793e3c50..db2e96b37 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -699,18 +699,20 @@ def test_process_trade_creation( def test_process_exchange_failures(default_conf_usdt, ticker_usdt, mocker) -> None: + # TODO: Move this test to test_worker patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( EXMS, fetch_ticker=ticker_usdt, - reload_markets=MagicMock(side_effect=TemporaryError), + reload_markets=MagicMock(), create_order=MagicMock(side_effect=TemporaryError), ) sleep_mock = mocker.patch("time.sleep") worker = Worker(args=None, config=default_conf_usdt) patch_get_signal(worker.freqtrade) + mocker.patch(f"{EXMS}.reload_markets", MagicMock(side_effect=TemporaryError)) worker._process_running() assert sleep_mock.called is True diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 5576b312f..6e182e6e8 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1650,11 +1650,11 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): ] args = get_args(args) start_backtesting(args) - # 2 backtests, 4 tables + # 2 backtests, 6 tables (entry, exit, mixed - each 2x) assert backtestmock.call_count == 2 assert text_table_mock.call_count == 4 assert strattable_mock.call_count == 1 - assert tag_metrics_mock.call_count == 4 + assert tag_metrics_mock.call_count == 6 assert strat_summary.call_count == 1 # check the logs, that will contain the backtest result @@ -1709,7 +1709,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat "open_rate": [0.104445, 0.10302485], "close_rate": [0.104969, 0.103541], "is_short": [False, False], - "exit_reason": [ExitType.ROI, ExitType.ROI], + "exit_reason": [ExitType.ROI.value, ExitType.ROI.value], } ) result2 = pd.DataFrame( @@ -1729,7 +1729,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat "open_rate": [0.104445, 0.10302485, 0.122541], "close_rate": [0.104969, 0.103541, 0.123541], "is_short": [False, False, False], - "exit_reason": [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS], + "exit_reason": [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value], } ) backtestmock = MagicMock( diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 4d2f9ae36..2e9ae8d35 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -415,10 +415,10 @@ def test_hyperopt_format_results(hyperopt): "is_short": [False, False, False, False], "stake_amount": [0.01, 0.01, 0.01, 0.01], "exit_reason": [ - ExitType.ROI, - ExitType.STOP_LOSS, - ExitType.ROI, - ExitType.FORCE_EXIT, + ExitType.ROI.value, + ExitType.STOP_LOSS.value, + ExitType.ROI.value, + ExitType.FORCE_EXIT.value, ], } ), @@ -507,10 +507,10 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: "is_short": [False, False, False, False], "stake_amount": [0.01, 0.01, 0.01, 0.01], "exit_reason": [ - ExitType.ROI, - ExitType.STOP_LOSS, - ExitType.ROI, - ExitType.FORCE_EXIT, + ExitType.ROI.value, + ExitType.STOP_LOSS.value, + ExitType.ROI.value, + ExitType.FORCE_EXIT.value, ], } ), @@ -1063,7 +1063,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmp_path, fee) -> None def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmp_path, fee) -> None: mocker.patch(f"{EXMS}.validate_config", MagicMock()) mocker.patch(f"{EXMS}.get_fee", fee) - mocker.patch(f"{EXMS}._load_markets") + mocker.patch(f"{EXMS}.reload_markets") mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=get_markets())) (tmp_path / "hyperopt_results").mkdir(parents=True) # Dummy-reduce points to ensure scikit-learn is forced to generate new values diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 88e846d9d..b26f15be5 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -70,13 +70,13 @@ def test_text_table_bt_results(): ) result_str = ( - "| Pair | Entries | Avg Profit % | Tot Profit BTC | " + "| Pair | Trades | Avg Profit % | Tot Profit BTC | " "Tot Profit % | Avg Duration | Win Draw Loss Win% |\n" - "|---------+-----------+----------------+------------------+" + "|---------+----------+----------------+------------------+" "----------------+----------------+-------------------------|\n" - "| ETH/BTC | 3 | 8.33 | 0.50000000 | " + "| ETH/BTC | 3 | 8.33 | 0.50000000 | " "12.50 | 0:20:00 | 2 0 1 66.7 |\n" - "| TOTAL | 3 | 8.33 | 0.50000000 | " + "| TOTAL | 3 | 8.33 | 0.50000000 | " "12.50 | 0:20:00 | 2 0 1 66.7 |" ) @@ -116,10 +116,10 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path): "is_short": [False, False, False, False], "stake_amount": [0.01, 0.01, 0.01, 0.01], "exit_reason": [ - ExitType.ROI, - ExitType.STOP_LOSS, - ExitType.ROI, - ExitType.FORCE_EXIT, + ExitType.ROI.value, + ExitType.STOP_LOSS.value, + ExitType.ROI.value, + ExitType.FORCE_EXIT.value, ], } ), @@ -183,10 +183,10 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path): "is_short": [False, False, False, False], "stake_amount": [0.01, 0.01, 0.01, 0.01], "exit_reason": [ - ExitType.ROI, - ExitType.ROI, - ExitType.STOP_LOSS, - ExitType.FORCE_EXIT, + ExitType.ROI.value, + ExitType.ROI.value, + ExitType.STOP_LOSS.value, + ExitType.FORCE_EXIT.value, ], } ), @@ -444,7 +444,7 @@ def test_text_table_exit_reason(): "wins": [2, 0, 0], "draws": [0, 0, 0], "losses": [0, 0, 1], - "exit_reason": [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS], + "exit_reason": [ExitType.ROI.value, ExitType.ROI.value, ExitType.STOP_LOSS.value], } ) @@ -509,13 +509,13 @@ def test_text_table_strategy(testdatadir): bt_res_data_comparison = bt_res_data.pop("strategy_comparison") result_str = ( - "| Strategy | Entries | Avg Profit % | Tot Profit BTC |" + "| Strategy | Trades | Avg Profit % | Tot Profit BTC |" " Tot Profit % | Avg Duration | Win Draw Loss Win% | Drawdown |\n" - "|----------------+-----------+----------------+------------------+" + "|----------------+----------+----------------+------------------+" "----------------+----------------+-------------------------+-----------------------|\n" - "| StrategyTestV2 | 179 | 0.08 | 0.02608550 |" + "| StrategyTestV2 | 179 | 0.08 | 0.02608550 |" " 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |\n" - "| TestStrategy | 179 | 0.08 | 0.02608550 |" + "| TestStrategy | 179 | 0.08 | 0.02608550 |" " 260.85 | 3:40:00 | 170 0 9 95.0 | 0.00308222 BTC 8.67% |" ) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 531d05c0c..ab87e110e 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring, C0103 import logging +import math from datetime import datetime, timedelta, timezone from pathlib import Path from unittest.mock import MagicMock @@ -458,55 +459,66 @@ def test_min_roi_reached3(default_conf, fee) -> None: ExitType.TRAILING_STOP_LOSS, None, ), - (0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 1, ExitType.NONE, None), - (0.05, 1, ExitType.NONE, None, True, False, -0.01, 1, ExitType.TRAILING_STOP_LOSS, None), + (0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 0.998, ExitType.NONE, None), + ( + 0.05, + 0.998, + ExitType.NONE, + None, + True, + False, + -0.01, + 0.998, + ExitType.TRAILING_STOP_LOSS, + None, + ), # Default custom case - trails with 10% - (0.05, 0.95, ExitType.NONE, None, False, True, -0.02, 0.95, ExitType.NONE, None), + (0.05, 0.945, ExitType.NONE, None, False, True, -0.02, 0.945, ExitType.NONE, None), ( 0.05, - 0.95, + 0.945, ExitType.NONE, None, False, True, -0.06, - 0.95, + 0.945, ExitType.TRAILING_STOP_LOSS, None, ), ( 0.05, - 1, + 0.998, ExitType.NONE, None, False, True, -0.06, - 1, + 0.998, ExitType.TRAILING_STOP_LOSS, lambda **kwargs: -0.05, ), ( 0.05, - 1, + 0.998, ExitType.NONE, None, False, True, 0.09, - 1.04, + 1.036, ExitType.NONE, lambda **kwargs: -0.05, ), ( 0.05, - 0.95, + 0.945, ExitType.NONE, None, False, True, 0.09, - 0.98, + 0.981, ExitType.NONE, lambda current_profit, **kwargs: ( -0.1 if current_profit < 0.6 else -(current_profit * 2) @@ -525,6 +537,19 @@ def test_min_roi_reached3(default_conf, fee) -> None: ExitType.NONE, lambda **kwargs: None, ), + # Error case - Returning inf. + ( + 0.05, + 0.9, + ExitType.NONE, + None, + False, + True, + 0.09, + 0.9, + ExitType.NONE, + lambda **kwargs: math.inf, + ), ], ) def test_ft_stoploss_reached( @@ -552,6 +577,8 @@ def test_ft_stoploss_reached( exchange="binance", open_rate=1, liquidation_price=liq, + price_precision=4, + precision_mode=2, ) trade.adjust_min_max_rates(trade.open_rate, trade.open_rate) strategy.trailing_stop = trailing @@ -577,7 +604,7 @@ def test_ft_stoploss_reached( assert sl_flag.exit_flag is False else: assert sl_flag.exit_flag is True - assert round(trade.stop_loss, 2) == adjusted + assert round(trade.stop_loss, 3) == adjusted current_rate2 = trade.open_rate * (1 + profit2) sl_flag = strategy.ft_stoploss_reached( @@ -593,7 +620,7 @@ def test_ft_stoploss_reached( assert sl_flag.exit_flag is False else: assert sl_flag.exit_flag is True - assert round(trade.stop_loss, 2) == adjusted2 + assert round(trade.stop_loss, 3) == adjusted2 strategy.custom_stoploss = original_stopvalue diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d0e0aa2c6..f9368246a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -401,11 +401,11 @@ def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) -> assert validated_conf["runmode"] == (RunMode.DRY_RUN if expected else RunMode.LIVE) -def test_load_custom_strategy(default_conf, mocker) -> None: +def test_load_custom_strategy(default_conf, mocker, tmp_path) -> None: default_conf.update( { "strategy": "CustomStrategy", - "strategy_path": "/tmp/strategies", + "strategy_path": f"{tmp_path}/strategies", } ) patched_configuration_load_config_file(mocker, default_conf) @@ -415,7 +415,7 @@ def test_load_custom_strategy(default_conf, mocker) -> None: validated_conf = configuration.load_config() assert validated_conf.get("strategy") == "CustomStrategy" - assert validated_conf.get("strategy_path") == "/tmp/strategies" + assert validated_conf.get("strategy_path") == f"{tmp_path}/strategies" def test_show_info(default_conf, mocker, caplog) -> None: @@ -469,7 +469,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert "timerange" not in config -def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None: +def test_setup_configuration_with_arguments(mocker, default_conf, caplog, tmp_path) -> None: patched_configuration_load_config_file(mocker, default_conf) mocker.patch("freqtrade.configuration.configuration.create_datadir", lambda c, x: x) mocker.patch( @@ -485,7 +485,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non "--datadir", "/foo/bar", "--userdir", - "/tmp/freqtrade", + f"{tmp_path}/freqtrade", "--timeframe", "1m", "--enable-position-stacking", @@ -509,7 +509,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non assert "pair_whitelist" in config["exchange"] assert "datadir" in config assert log_has("Using data directory: {} ...".format("/foo/bar"), caplog) - assert log_has("Using user-data directory: {} ...".format(Path("/tmp/freqtrade")), caplog) + assert log_has(f"Using user-data directory: {tmp_path / 'freqtrade'} ...", caplog) assert "user_data_dir" in config assert "timeframe" in config diff --git a/tests/test_directory_operations.py b/tests/test_directory_operations.py index 45297fba8..d64ff9c18 100644 --- a/tests/test_directory_operations.py +++ b/tests/test_directory_operations.py @@ -24,16 +24,16 @@ def test_create_datadir(mocker, default_conf, caplog) -> None: assert log_has("Created data directory: /foo/bar", caplog) -def test_create_userdata_dir(mocker, default_conf, caplog) -> None: +def test_create_userdata_dir(mocker, tmp_path, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) md = mocker.patch.object(Path, "mkdir", MagicMock()) - x = create_userdata_dir("/tmp/bar", create_dir=True) + x = create_userdata_dir(tmp_path / "bar", create_dir=True) assert md.call_count == 10 assert md.call_args[1]["parents"] is False - assert log_has(f'Created user-data directory: {Path("/tmp/bar")}', caplog) + assert log_has(f'Created user-data directory: {tmp_path / "bar"}', caplog) assert isinstance(x, Path) - assert str(x) == str(Path("/tmp/bar")) + assert str(x) == str(tmp_path / "bar") def test_create_userdata_dir_and_chown(mocker, tmp_path, caplog) -> None: @@ -54,63 +54,57 @@ def test_create_userdata_dir_and_chown(mocker, tmp_path, caplog) -> None: del os.environ["FT_APP_ENV"] -def test_create_userdata_dir_exists(mocker, default_conf, caplog) -> None: +def test_create_userdata_dir_exists(mocker, tmp_path) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=True)) md = mocker.patch.object(Path, "mkdir", MagicMock()) - create_userdata_dir("/tmp/bar") + create_userdata_dir(f"{tmp_path}/bar") assert md.call_count == 0 -def test_create_userdata_dir_exists_exception(mocker, default_conf, caplog) -> None: +def test_create_userdata_dir_exists_exception(mocker, tmp_path) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) md = mocker.patch.object(Path, "mkdir", MagicMock()) - with pytest.raises( - OperationalException, match=r"Directory `.{1,2}tmp.{1,2}bar` does not exist.*" - ): - create_userdata_dir("/tmp/bar", create_dir=False) + with pytest.raises(OperationalException, match=r"Directory `.*.{1,2}bar` does not exist.*"): + create_userdata_dir(f"{tmp_path}/bar", create_dir=False) assert md.call_count == 0 -def test_copy_sample_files(mocker, default_conf, caplog) -> None: +def test_copy_sample_files(mocker, tmp_path) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=True)) mocker.patch.object(Path, "exists", MagicMock(return_value=False)) copymock = mocker.patch("shutil.copy", MagicMock()) - copy_sample_files(Path("/tmp/bar")) + copy_sample_files(Path(f"{tmp_path}/bar")) assert copymock.call_count == 3 - assert copymock.call_args_list[0][0][1] == str( - Path("/tmp/bar") / "strategies/sample_strategy.py" - ) + assert copymock.call_args_list[0][0][1] == str(tmp_path / "bar/strategies/sample_strategy.py") assert copymock.call_args_list[1][0][1] == str( - Path("/tmp/bar") / "hyperopts/sample_hyperopt_loss.py" + tmp_path / "bar/hyperopts/sample_hyperopt_loss.py" ) assert copymock.call_args_list[2][0][1] == str( - Path("/tmp/bar") / "notebooks/strategy_analysis_example.ipynb" + tmp_path / "bar/notebooks/strategy_analysis_example.ipynb" ) -def test_copy_sample_files_errors(mocker, default_conf, caplog) -> None: +def test_copy_sample_files_errors(mocker, tmp_path, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) mocker.patch.object(Path, "exists", MagicMock(return_value=False)) mocker.patch("shutil.copy", MagicMock()) - with pytest.raises( - OperationalException, match=r"Directory `.{1,2}tmp.{1,2}bar` does not exist\." - ): - copy_sample_files(Path("/tmp/bar")) + with pytest.raises(OperationalException, match=r"Directory `.*.{1,2}bar` does not exist\."): + copy_sample_files(Path(f"{tmp_path}/bar")) mocker.patch.object(Path, "is_dir", MagicMock(side_effect=[True, False])) with pytest.raises( OperationalException, - match=r"Directory `.{1,2}tmp.{1,2}bar.{1,2}strategies` does not exist\.", + match=r"Directory `.*.{1,2}bar.{1,2}strategies` does not exist\.", ): - copy_sample_files(Path("/tmp/bar")) + copy_sample_files(Path(f"{tmp_path}/bar")) mocker.patch.object(Path, "is_dir", MagicMock(return_value=True)) mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - copy_sample_files(Path("/tmp/bar")) + copy_sample_files(Path(f"{tmp_path}/bar")) assert log_has_re(r"File `.*` exists already, not deploying sample file\.", caplog) caplog.clear() - copy_sample_files(Path("/tmp/bar"), overwrite=True) + copy_sample_files(Path(f"{tmp_path}/bar"), overwrite=True) assert log_has_re(r"File `.*` exists already, overwriting\.", caplog)