diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc8dbecbe21..56d02e5bfa9 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: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 992e9b37358..6844925af6e 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] @@ -31,7 +31,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.4.7' + rev: 'v0.4.9' hooks: - id: ruff diff --git a/Dockerfile b/Dockerfile index 9491f2e0589..cedeafbe6d1 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.30-cp310-cp310-win_amd64.whl b/build_helpers/TA_Lib-0.4.30-cp310-cp310-win_amd64.whl deleted file mode 100644 index 68d6df6b37f..00000000000 Binary files a/build_helpers/TA_Lib-0.4.30-cp310-cp310-win_amd64.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.30-cp311-cp311-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.30-cp311-cp311-linux_armv7l.whl deleted file mode 100644 index 941a2aa9d24..00000000000 Binary files a/build_helpers/TA_Lib-0.4.30-cp311-cp311-linux_armv7l.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.30-cp312-cp312-win_amd64.whl b/build_helpers/TA_Lib-0.4.30-cp312-cp312-win_amd64.whl deleted file mode 100644 index 2a8253e3924..00000000000 Binary files a/build_helpers/TA_Lib-0.4.30-cp312-cp312-win_amd64.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.30-cp39-cp39-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.30-cp39-cp39-linux_armv7l.whl deleted file mode 100644 index a5cab7919df..00000000000 Binary files a/build_helpers/TA_Lib-0.4.30-cp39-cp39-linux_armv7l.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.30-cp39-cp39-win_amd64.whl b/build_helpers/TA_Lib-0.4.30-cp39-cp39-win_amd64.whl deleted file mode 100644 index a03c3ca8cef..00000000000 Binary files a/build_helpers/TA_Lib-0.4.30-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 00000000000..4ce492a4067 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 00000000000..a664f12e654 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.30-cp311-cp311-win_amd64.whl b/build_helpers/TA_Lib-0.4.31-cp311-cp311-win_amd64.whl similarity index 88% rename from build_helpers/TA_Lib-0.4.30-cp311-cp311-win_amd64.whl rename to build_helpers/TA_Lib-0.4.31-cp311-cp311-win_amd64.whl index 702f51559ba..e5b8cb4ef8f 100644 Binary files a/build_helpers/TA_Lib-0.4.30-cp311-cp311-win_amd64.whl 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 00000000000..79596cf0c94 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 00000000000..bceb2177380 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 00000000000..01d3c626eae 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 4aa070992c1..5f0c643ace5 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 6882541223e..fbd95211151 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 5fdfd65565e..5adeae54bba 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 0f01717ab20..fbf8f4be084 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 6ae0c3f1161..f0a52415cd4 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 c290fb85208..845aba7bba8 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.25 +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 2b55c25631b..3b5c8928f49 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 b1e46d35603..74eef53c150 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 fa8b2dbb1c9..ec9cf14ecce 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 932f9b70156..5c4e6df5d21 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -30,5 +30,5 @@ 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 1e771a372cc..a5ab8cb4161 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 cc8b5407e41..184f9decf03 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 @@ def _process_optimize_options(self, config: Config) -> None: # 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 bdca599c663..d13fa6fca6b 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 95698846774..d88ac26b803 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 71744bf36d6..2e8b07ab1ad 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, + "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": "3", + "bracket": "4", "initialLeverage": "10", - "notionalCap": "300000", - "notionalFloor": "25000", + "notionalCap": "1000000", + "notionalFloor": "100000", "maintMarginRatio": "0.05", - "cum": "650.0" + "cum": "2775.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 300000.0, - "maxNotional": 800000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "800000", - "notionalFloor": "300000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "15650.0" + "cum": "52775.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 1000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "800000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "35650.0" + "cum": "102775.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 3000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "2", - "notionalCap": "3000000", - "notionalFloor": "1000000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "160650.0" + "cum": "415275.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 5000000.0, + "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, + "minNotional": 80000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "800000", + "notionalFloor": "80000", + "maintMarginRatio": "0.05", + "cum": "2150.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "5675.0" + "cum": "42150.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.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": "5", + "bracket": "7", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "332150.0" } }, { - "tier": 6.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "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": 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": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "10", - "notionalCap": "100000", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "650.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5650.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11900.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "3", + "initialLeverage": "20", + "notionalCap": "80000", + "notionalFloor": "25000", + "maintMarginRatio": "0.025", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", + "notionalCap": "800000", + "notionalFloor": "80000", "maintMarginRatio": "0.05", - "cum": "675.0" + "cum": "2150.0" } }, { - "tier": 4.0, + "tier": 5.0, "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 200000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "200000", - "notionalFloor": "100000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "5675.0" + "cum": "42150.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "200000", + "notionalCap": "2000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.125", - "cum": "10675.0" + "cum": "82150.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "73175.0" + "cum": "332150.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "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": 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": "2", + "bracket": "3", "initialLeverage": "20", "notionalCap": "25000", - "notionalFloor": "5000", + "notionalFloor": "20000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "125.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "10", - "notionalCap": "100000", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "675.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5675.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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": 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": 10.0, + "maxLeverage": 20.0, "info": { - "bracket": "2", - "initialLeverage": "10", + "bracket": "3", + "initialLeverage": "20", "notionalCap": "25000", - "notionalFloor": "5000", + "notionalFloor": "20000", "maintMarginRatio": "0.025", - "cum": "75.0" + "cum": "125.0" } }, { - "tier": 3.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": "3", - "initialLeverage": "8", - "notionalCap": "100000", + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "700.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5700.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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": 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": "2", + "bracket": "3", "initialLeverage": "20", "notionalCap": "25000", - "notionalFloor": "5000", + "notionalFloor": "20000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "125.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "10", - "notionalCap": "100000", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "675.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5675.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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" } } ], @@ -22353,6 +22691,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 +24290,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, + "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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "200000", - "notionalFloor": "100000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "5675.0" + "cum": "52775.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "5", + "bracket": "6", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "200000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "10675.0" + "cum": "102775.0" } }, { - "tier": 6.0, + "tier": 7.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "73175.0" + "cum": "415275.0" } }, { - "tier": 7.0, + "tier": 8.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "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 +24598,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 +24617,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 +24632,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": 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": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "maxLeverage": 10.0, "info": { - "bracket": "3", - "initialLeverage": "8", - "notionalCap": "100000", + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "650.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5650.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11900.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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 +25235,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 +25250,112 @@ "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": 10.0, + "maxLeverage": 20.0, "info": { - "bracket": "2", - "initialLeverage": "10", + "bracket": "3", + "initialLeverage": "20", "notionalCap": "25000", - "notionalFloor": "5000", + "notionalFloor": "20000", "maintMarginRatio": "0.025", - "cum": "75.0" + "cum": "125.0" } }, { - "tier": 3.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": "3", - "initialLeverage": "8", - "notionalCap": "100000", + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "700.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5700.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11950.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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 +28472,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 +28487,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 +33208,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 +33223,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 +33707,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 +34834,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 +34849,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 +34934,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 +35028,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 +37338,112 @@ "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": "2", + "bracket": "3", "initialLeverage": "20", "notionalCap": "25000", - "notionalFloor": "5000", + "notionalFloor": "20000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "125.0" } }, { - "tier": 3.0, + "tier": 4.0, "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "4", "initialLeverage": "10", - "notionalCap": "100000", + "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "675.0" + "cum": "750.0" } }, { - "tier": 4.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": "4", + "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.1", - "cum": "5675.0" + "cum": "10750.0" } }, { - "tier": 5.0, + "tier": 6.0, "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "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": "5", + "bracket": "7", "initialLeverage": "2", "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83250.0" } }, { - "tier": 6.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 +37691,136 @@ } } ], + "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" + } + } + ], "ZRX/USDT:USDT": [ { "tier": 1.0, diff --git a/freqtrade/exchange/bingx.py b/freqtrade/exchange/bingx.py index 2d81643a1b3..4dcff8a21cb 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 ffc8ac67aec..ab509c7863f 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 c8b05d1de4f..252f0a29b14 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 5771682b525..c7139c8fa30 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", @@ -258,7 +259,7 @@ def __del__(self): 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 ): diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 2408e306eb8..9ed5a73668e 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 f939534e915..fa26a5ffdd5 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 4fbbe113c6c..f0562ecafd6 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 1704117e62f..94a81b45271 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 602c8e95b7d..54c42a284d5 100644 --- a/freqtrade/freqai/torch/PyTorchModelTrainer.py +++ b/freqtrade/freqai/torch/PyTorchModelTrainer.py @@ -148,7 +148,8 @@ def calc_n_epochs(self, n_obs: int) -> int: 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 acec9932e5c..efe268f9e12 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -243,7 +243,7 @@ def cleanup(self) -> None: 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 2f9c151adb0..766c77ddcd6 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 7ae2791bfd7..b19fca9dc6e 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -489,7 +489,7 @@ def run_optimizer_parallel(self, parallel: Parallel, asked: List[List]) -> List[ ) 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 26997e11d38..09096461239 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") @@ -365,6 +375,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] ): @@ -383,19 +419,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 74e8ce14539..2da688d4240 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) @@ -379,12 +385,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, @@ -426,8 +438,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 0be04d7b81b..917dad45c33 100644 --- a/freqtrade/plugins/pairlist/AgeFilter.py +++ b/freqtrade/plugins/pairlist/AgeFilter.py @@ -5,11 +5,11 @@ 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 @@ 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 11d98abc554..13586611dad 100644 --- a/freqtrade/plugins/pairlist/FullTradesFilter.py +++ b/freqtrade/plugins/pairlist/FullTradesFilter.py @@ -3,9 +3,8 @@ """ 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 @@ 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 0db38ff2f57..a2e70e64930 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -3,7 +3,7 @@ """ 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 @@ def name(self) -> str: """ 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 709b6100bd1..648766e201e 100644 --- a/freqtrade/plugins/pairlist/MarketCapPairList.py +++ b/freqtrade/plugins/pairlist/MarketCapPairList.py @@ -5,11 +5,10 @@ """ 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 @@ 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 @@ def __init__( '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 1fa9e1bd011..bd981358e5f 100644 --- a/freqtrade/plugins/pairlist/OffsetFilter.py +++ b/freqtrade/plugins/pairlist/OffsetFilter.py @@ -3,9 +3,8 @@ """ 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 @@ 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 930c7833450..c10ae7394ca 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -3,11 +3,10 @@ """ 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 @@ 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) - - self._minutes = pairlistconfig.get("minutes", 0) - self._min_profit = pairlistconfig.get("min_profit") + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + 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 0e8c5084914..b2f767a6792 100644 --- a/freqtrade/plugins/pairlist/PrecisionFilter.py +++ b/freqtrade/plugins/pairlist/PrecisionFilter.py @@ -3,9 +3,8 @@ """ 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 @@ 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 81dbdfc3358..d651533ce6e 100644 --- a/freqtrade/plugins/pairlist/PriceFilter.py +++ b/freqtrade/plugins/pairlist/PriceFilter.py @@ -3,9 +3,8 @@ """ 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 @@ 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) - - self._low_price_ratio = pairlistconfig.get("low_price_ratio", 0) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + 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 771f873802c..09a0c49d23e 100644 --- a/freqtrade/plugins/pairlist/ProducerPairList.py +++ b/freqtrade/plugins/pairlist/ProducerPairList.py @@ -5,7 +5,7 @@ """ 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 b15cfa96eb0..26fadb9ae6c 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -14,7 +14,6 @@ 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 @@ 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 d7f8a60bc00..59ac1ac7c6b 100644 --- a/freqtrade/plugins/pairlist/ShuffleFilter.py +++ b/freqtrade/plugins/pairlist/ShuffleFilter.py @@ -4,9 +4,8 @@ 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 @@ 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 4aca98f3e82..736903abd9a 100644 --- a/freqtrade/plugins/pairlist/SpreadFilter.py +++ b/freqtrade/plugins/pairlist/SpreadFilter.py @@ -3,9 +3,8 @@ """ 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 @@ 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 ac1201ca38e..922d0fd9447 100644 --- a/freqtrade/plugins/pairlist/StaticPairList.py +++ b/freqtrade/plugins/pairlist/StaticPairList.py @@ -6,9 +6,8 @@ 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 @@ 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 c4088196d8d..f5af2d0a793 100644 --- a/freqtrade/plugins/pairlist/VolatilityFilter.py +++ b/freqtrade/plugins/pairlist/VolatilityFilter.py @@ -5,13 +5,13 @@ 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) - - 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) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + 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 f9a0dd6b15e..ea172f14094 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -10,7 +10,7 @@ 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 @@ 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 @@ def __init__( '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 @@ def __init__( 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 54c6a536e1b..473e003b6fc 100644 --- a/freqtrade/plugins/pairlist/rangestabilityfilter.py +++ b/freqtrade/plugins/pairlist/rangestabilityfilter.py @@ -4,12 +4,12 @@ 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 @@ 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) - - 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) + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + + 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 0e054220b57..24e04a905de 100644 --- a/freqtrade/rpc/api_server/api_auth.py +++ b/freqtrade/rpc/api_server/api_auth.py @@ -31,7 +31,7 @@ def verify_auth(api_config, username: str, password: str): 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 f711e6456fa..b86575ab481 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -1486,6 +1486,8 @@ def _rpc_plot_config_with_strategy(config: Config) -> Dict[str, Any]: 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 de3d0f34910..e6d86926bb4 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -6,6 +6,7 @@ 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 @@ def ft_stoploss_adjust( 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 9ede4dd124a..60b5d11642a 100644 --- a/ft_client/freqtrade_client/__init__.py +++ b/ft_client/freqtrade_client/__init__.py @@ -31,7 +31,7 @@ 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 d51858fde2d..3c450d1df8f 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 6e5f7e6c5dd..f7c89590170 100755 --- a/ft_client/freqtrade_client/ft_rest_client.py +++ b/ft_client/freqtrade_client/ft_rest_client.py @@ -54,7 +54,7 @@ def _call(self, method, apipath, params: Optional[dict] = None, data=None, files # 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 @@ def forcebuy(self, pair, price=None): 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/test_client/test_rest_client.py b/ft_client/test_client/test_rest_client.py index 08ccee7658b..541577e594a 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/pyproject.toml b/pyproject.toml index 0c92225300e..55ff28db622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -138,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 @@ -151,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 b0ce069da42..379ca4c38a2 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.7 +ruff==0.4.9 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 diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 546c0b62a9e..c278716fbdb 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 cdc32e11bbc..d864b45e8b3 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 99d4ee5c6a4..3d367211b11 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.1 diff --git a/requirements.txt b/requirements.txt index c24a33d0da9..058b18d5709 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,19 +6,19 @@ pandas-ta==0.3.14b finta>=1.3 ta>=0.10.1 -ccxt==4.3.38 -cryptography==42.0.7 +ccxt==4.3.46 +cryptography==42.0.8 aiohttp==3.9.5 SQLAlchemy==2.0.30 -python-telegram-bot==21.2 +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.3 -urllib3==2.2.1 +urllib3==2.2.2 jsonschema==4.22.0 -TA-Lib==0.4.30 +TA-Lib==0.4.31 technical==1.4.3 tabulate==0.9.0 pycoingecko==3.1.0 @@ -34,14 +34,14 @@ 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.2 +pydantic==2.7.4 uvicorn==0.30.1 pyjwt==2.8.0 aiofiles==23.2.1 @@ -64,4 +64,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 18f7682d86c..2c5eadabbbe 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.1" wheel setuptools REQUIREMENTS_HYPEROPT="" REQUIREMENTS_PLOT="" REQUIREMENTS_FREQAI="" diff --git a/tests/data/test_download_data.py b/tests/data/test_download_data.py index 4922a213f51..08d56458f37 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 49ef74c0ab5..1a53091900b 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 fe47f821c3e..6c3d62dde18 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -7,7 +7,7 @@ import ccxt import pytest -from numpy import NaN +from numpy import nan from pandas import DataFrame from freqtrade.enums import CandleType, MarginMode, RunMode, TradingMode @@ -4860,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/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 5576b312fbb..6e182e6e8aa 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 d2ff5b8d3e0..2e9ae8d3546 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, ], } ), diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 88e846d9daa..b26f15be514 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 531d05c0c09..ab87e110ea9 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 d0e0aa2c64c..f9368246ae3 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 45297fba83f..d64ff9c18ae 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)