Compare commits

..

3 Commits

Author SHA1 Message Date
Matthias 9f00a1d0d2 Merge pull request #12673 from freqtrade/new_release
New release 2025.12
2025-12-30 08:19:19 +01:00
Matthias 9a37d7bfbb chore: bump version to 2025.12 2025-12-29 13:19:31 +01:00
Matthias c9c08906e5 Merge branch 'stable' into new_release 2025-12-29 13:17:12 +01:00
274 changed files with 43997 additions and 59070 deletions
+1 -2
View File
@@ -46,9 +46,8 @@ runs:
id: tags
env:
BRANCH_NAME_INPUT: ${{ github.event.inputs.branch_name }}
EVENT_NAME: ${{ github.event_name }}
run: |
if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
BRANCH_NAME="${BRANCH_NAME_INPUT}"
else
BRANCH_NAME="${GITHUB_REF##*/}"
+5 -13
View File
@@ -1,10 +1,8 @@
version: 2
updates:
- package-ecosystem: docker # zizmor: ignore[dependabot-cooldown] Docker does not support cooldowns at the moment.
# Docker does not support cooldowns at the moment.
# https://github.com/dependabot/dependabot-core/issues/14044
# cooldown:
# default-days: 7
- package-ecosystem: docker
cooldown:
default-days: 7
directories:
- "/"
- "/docker"
@@ -49,11 +47,7 @@ updates:
patterns:
- "scipy"
- "scipy-stubs"
gymnasium:
patterns:
- "gymnasium"
- "stable-baselines3"
- "sb3-contrib"
- package-ecosystem: "github-actions"
directory: "/"
cooldown:
@@ -67,7 +61,5 @@ updates:
groups:
actions:
patterns:
# Combine updates for github provided actions
- "actions/*"
docker:
patterns:
- "docker/*"
+7 -15
View File
@@ -2,37 +2,29 @@ name: Binance Leverage tiers update
on:
schedule:
- cron: "25 2 * * 4"
- cron: "0 3 * * 4"
# on demand
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
permissions:
contents: read
jobs:
auto-update:
name: "Auto Update Binance Leverage Tiers"
runs-on: ubuntu-latest
environment:
name: develop
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- uses: actions/setup-python@v6
with:
activate-environment: true
enable-cache: false
python-version: "3.14"
python-version: "3.12"
- name: Install ccxt
run: uv pip install $(grep -E "^ccxt==" requirements.txt) $(grep -E "^orjson==" requirements.txt)
run: pip install ccxt
- name: Run leverage tier update
env:
@@ -42,7 +34,7 @@ jobs:
run: python build_helpers/binance_update_lev_tiers.py
- uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
- uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: freqtrade/exchange/binance_leverage_tiers.json
@@ -51,7 +43,7 @@ jobs:
Dependencies
branch: update/binance-leverage-tiers
title: Update Binance Leverage Tiers
commit-message: "chore: update binance leverage tiers"
commit-message: "chore: update pre-commit hooks"
committer: Freqtrade Bot <154552126+freqtrade-bot@users.noreply.github.com>
author: Freqtrade Bot <154552126+freqtrade-bot@users.noreply.github.com>
body: Update binance leverage tiers.
+89 -76
View File
@@ -16,8 +16,8 @@ on:
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}"
cancel-in-progress: true
permissions: {}
permissions:
repository-projects: read
jobs:
tests:
name: "Tests and Linting"
@@ -28,12 +28,17 @@ jobs:
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
with:
activate-environment: true
enable-cache: true
@@ -50,6 +55,7 @@ jobs:
- name: Installation (python)
run: |
uv pip install --upgrade wheel
uv pip install -r requirements-dev.txt
uv pip install -e ft_client/
uv pip install -e .
@@ -68,11 +74,11 @@ jobs:
run: |
pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc
- uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
- uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04')
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # zizmor: ignore[secrets-outside-env] Intentionally not using environment variable.
token: ${{ secrets.CODECOV_TOKEN }}
- name: Cleanup codecov dirty state files
if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04')
@@ -131,6 +137,10 @@ jobs:
freqtrade create-userdir --userdir user_data
freqtrade hyperopt --datadir tests/testdata -e 6 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
- name: Sort imports (isort)
run: |
isort --check .
- name: Run Ruff
run: |
ruff check --output-format=github
@@ -150,62 +160,56 @@ jobs:
run: |
$PSVersionTable
Get-PSRepository | Format-List *
if (-not (Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue)) {
Register-PSRepository -Default
}
Set-PSRepository PSGallery -InstallationPolicy Trusted
Install-Module -Name Pester -RequiredVersion 5.7.1 -Confirm:$false -Force -SkipPublisherCheck
Set-PSRepository psgallery -InstallationPolicy trusted
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}
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708 # v1.16.0
uses: rjstone/discord-webhook-notify@c2597273488aeda841dd1e891321952b51f7996f #v2.2.1
if: ${{ failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) }}
with:
color: '#FF0000' # red
title: Freqtrade CI failed on ${{ matrix.os }} with Python ${{ matrix.python-version }}!
webhook: ${{ secrets.DISCORD_WEBHOOK }} # zizmor: ignore[secrets-outside-env] Intentionally not using environment variable.
severity: error
details: Freqtrade CI failed on ${{ matrix.os }} with Python ${{ matrix.python-version }}!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
mypy-version-check:
name: "Mypy Version Check"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@v6
with:
activate-environment: true
python-version: "3.13"
python-version: "3.12"
- name: pre-commit dependencies
run: |
uv pip install $(grep -E "^pyyaml==" requirements-dev.txt)
pip install pyaml
python build_helpers/pre_commit_update.py
pre-commit:
name: "Pre-commit checks"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Set up Python 🐍
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/setup-python@v6
with:
python-version: "3.13"
python-version: "3.12"
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
docs-check:
name: "Documentation build"
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
@@ -213,49 +217,51 @@ jobs:
run: |
./tests/test_docs.sh
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@v6
with:
activate-environment: true
python-version: "3.13"
python-version: "3.12"
- name: Documentation build
run: |
uv pip install -r docs/requirements-docs.txt
pip install -r docs/requirements-docs.txt
mkdocs build
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708 # v1.16.0
uses: rjstone/discord-webhook-notify@c2597273488aeda841dd1e891321952b51f7996f #v2.2.1
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
color: '#FF0000' # red
title: Freqtrade doc test failed!
webhook: ${{ secrets.DISCORD_WEBHOOK }} # zizmor: ignore[secrets-outside-env] Intentionally not using environment variable.
severity: error
details: Freqtrade doc test failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
build-linux-online:
# Run pytest with "live" checks
name: "Online / live tests"
name: "Tests and Linting - Online tests"
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
with:
activate-environment: true
enable-cache: true
python-version: "${{ matrix.python-version }}"
python-version: "3.12"
cache-dependency-glob: "requirements**.txt"
cache-suffix: "3.12"
- name: Installation - *nix
run: |
uv pip install --upgrade wheel
uv pip install -r requirements-dev.txt
uv pip install -e ft_client/
uv pip install -e .
@@ -269,7 +275,6 @@ jobs:
# Notify only once - when CI completes (and after deploy) in case it's successful
notify-complete:
name: "Notify CI Completion"
needs: [
build,
build-linux-online
@@ -277,15 +282,26 @@ jobs:
runs-on: ubuntu-22.04
# Discord notification can't handle schedule events
if: github.event_name != 'schedule' && github.repository == 'freqtrade/freqtrade'
permissions:
repository-projects: read
steps:
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708 # v1.16.0
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
- name: Check user permission
id: check
continue-on-error: true
uses: prince-chrismc/check-actor-permissions-action@d504e74ba31658f4cdf4fcfeb509d4c09736d88e # v3.0.2
with:
color: '#00FF00' # green
title: Test Completed!
webhook: ${{ secrets.DISCORD_WEBHOOK }} # zizmor: ignore[secrets-outside-env] Intentionally not using environment variable.
permission: "write"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Discord notification
uses: rjstone/discord-webhook-notify@c2597273488aeda841dd1e891321952b51f7996f #v2.2.1
if: steps.check.outputs.permitted == 'true' && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
severity: info
details: Test Completed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
build:
if: always()
@@ -297,9 +313,6 @@ jobs:
pre-commit,
]
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ["3.13"]
steps:
@@ -308,23 +321,22 @@ jobs:
with:
jobs: ${{ toJSON(needs) }}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@v6
with:
activate-environment: true
python-version: "${{ matrix.python-version }}"
python-version: "3.12"
- name: Build distribution
run: |
uv pip install $(grep -E "^build==" requirements-dev.txt)
pip install -U build
python -m build --sdist --wheel
- name: Upload artifacts 📦
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v6
with:
name: freqtrade-build
path: |
@@ -333,10 +345,11 @@ jobs:
- name: Build Client distribution
run: |
pip install -U build
python -m build --sdist --wheel ft_client
- name: Upload artifacts 📦
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v6
with:
name: freqtrade-client-build
path: |
@@ -352,22 +365,22 @@ jobs:
name: testpypi
url: https://test.pypi.org/p/freqtrade
permissions:
id-token: write # Needed for pypa/gh-action-pypi-publish
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Download artifact 📦
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@v7
with:
pattern: freqtrade*-build
path: dist
merge-multiple: true
- name: Publish to PyPI (Test)
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
@@ -381,22 +394,22 @@ jobs:
name: pypi
url: https://pypi.org/p/freqtrade
permissions:
id-token: write # Needed for pypa/gh-action-pypi-publish
id-token: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Download artifact 📦
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@v7
with:
pattern: freqtrade*-build
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
docker-build:
@@ -407,12 +420,12 @@ jobs:
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade'
uses: ./.github/workflows/docker-build.yml
permissions:
packages: write # Needed to push package versions
packages: write
contents: read
secrets:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} # zizmor: ignore[secrets-outside-env] Intentionally not using environment variable.
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
packages-cleanup:
@@ -421,6 +434,6 @@ jobs:
# Only run on push, schedule, or release events
if: (github.event_name == 'push' || github.event_name == 'schedule') && github.repository == 'freqtrade/freqtrade'
permissions:
packages: write # Needed to delete package versions
packages: write
with:
package_name: 'freqtrade'
+6 -9
View File
@@ -11,9 +11,6 @@ on:
# disable permissions for all of the available permissions
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-docs:
@@ -22,19 +19,19 @@ jobs:
name: Deploy Docs through mike
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: true
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Set up Python
uses: actions/setup-python@v6
with:
activate-environment: true
python-version: '3.13'
python-version: '3.12'
- name: Install dependencies
run: |
uv pip install -r docs/requirements-docs.txt
python -m pip install --upgrade pip
pip install -r docs/requirements-docs.txt
- name: Fetch gh-pages branch
run: |
+4 -18
View File
@@ -17,40 +17,26 @@ concurrency:
group: "${{ github.workflow }}"
cancel-in-progress: true
permissions:
contents: read
jobs:
build-and-push:
name: "Build and Push Devcontainer Image"
permissions:
packages: write # Needed to push package versions
packages: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pre-build dev container image
uses: devcontainers/ci@b63b30de439b47a52267f241112c5b453b673db5 # v0.3.1900000449
uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.19
with:
subFolder: .github
imageName: ghcr.io/${{ github.repository }}-devcontainer
cacheFrom: ghcr.io/${{ github.repository }}-devcontainer
push: always
packages-cleanup:
name: "Docker Package Cleanup"
uses: ./.github/workflows/packages-cleanup.yml
# Only run on push, schedule, or release events
if: (github.event_name == 'push' || github.event_name == 'schedule') && github.repository == 'freqtrade/freqtrade'
permissions:
packages: write # Needed to delete package versions
with:
package_name: 'freqtrade-devcontainer'
+18 -25
View File
@@ -3,9 +3,9 @@ name: Docker Build and Deploy
on:
workflow_call:
secrets:
DOCKERHUB_USERNAME:
DOCKER_PASSWORD:
required: true
DOCKERHUB_TOKEN:
DOCKER_USERNAME:
required: true
DISCORD_WEBHOOK:
required: false
@@ -17,10 +17,6 @@ on:
default: 'develop'
type: string
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
@@ -35,11 +31,9 @@ jobs:
name: "Deploy Docker x64 and armv7l"
runs-on: ubuntu-22.04
if: github.repository == 'freqtrade/freqtrade'
environment:
name: docker
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
@@ -59,19 +53,19 @@ jobs:
uses: ./.github/actions/docker-tags
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
cache-image: false
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd #v4.0.0
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f #v3.12.0
- name: Available platforms
run: echo ${PLATFORMS}
@@ -165,16 +159,14 @@ jobs:
deploy-arm:
name: "Deploy Docker ARM64"
permissions:
packages: write # Needed to push package versions
packages: write
needs: [ deploy-docker ]
# Only run on 64bit machines
runs-on: [self-hosted, linux, ARM64]
if: github.repository == 'freqtrade/freqtrade'
environment:
name: docker
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
@@ -183,13 +175,13 @@ jobs:
uses: ./.github/actions/docker-tags
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to github
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -310,8 +302,9 @@ jobs:
docker image prune -a --force --filter "until=24h"
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708 # v1.16.0
uses: rjstone/discord-webhook-notify@c2597273488aeda841dd1e891321952b51f7996f #v2.2.1
if: always() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) && (github.event_name != 'schedule')
with:
title: Deploy Succeeded!
webhook: ${{ secrets.DISCORD_WEBHOOK }}
severity: info
details: Deploy Succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+3 -11
View File
@@ -3,29 +3,21 @@ on:
push:
branches:
- stable
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
# disable permissions for all of the available permissions
permissions: {}
jobs:
dockerHubDescription:
name: "Update Docker Hub Description"
runs-on: ubuntu-latest
environment:
name: docker
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: freqtradeorg/freqtrade
+2 -8
View File
@@ -25,26 +25,20 @@ on:
default: true
type: boolean
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
env:
PACKAGE_NAME: "freqtrade"
permissions: {}
jobs:
deploy-docker:
name: "Delete Packages"
runs-on: ubuntu-24.04
if: github.repository == 'freqtrade/freqtrade'
permissions:
packages: write # Needed to delete package versions
packages: write
steps:
- name: "Delete untagged Package Versions"
uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0
uses: actions/delete-package-versions@v5
with:
package-name: ${{ inputs.package_name || env.PACKAGE_NAME }}
package-type: 'container'
@@ -1,53 +0,0 @@
name: Pre-commit Types update
on:
pull_request:
branches:
- "develop"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}"
cancel-in-progress: true
permissions: {}
jobs:
mypy-version-update:
name: "Pre-commit mypy type versions update"
runs-on: ubuntu-24.04
# Only run this job for pull requests created by dependabot[bot]
if: >
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.repository == github.event.pull_request.head.repo.full_name &&
github.event_name == 'pull_request' &&
startsWith(github.head_ref, 'dependabot/')
environment:
name: dependabot-pulls
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true
token: ${{ secrets.REPO_SCOPED_TOKEN_DEP }}
ref: ${{ github.head_ref || github.ref }}
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
activate-environment: true
python-version: "3.13"
- name: Install PyYAML
run: |
- name: pre-commit dependencies
run: |
uv pip install $(grep -E "^pyyaml==" requirements-dev.txt)
python build_helpers/pre_commit_update.py --update
- uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7
with:
commit_message: "chore(deps): Apply pre-commit types update"
commit_user_name: Freqtrade Bot
commit_user_email: 154552126+freqtrade-bot@users.noreply.github.com
commit_author: Freqtrade Bot <154552126+freqtrade-bot@users.noreply.github.com>
+7 -16
View File
@@ -2,42 +2,33 @@ name: Pre-commit auto-update
on:
schedule:
- cron: "13 1 * * 2"
- cron: "0 3 * * 2"
# on demand
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
auto-update:
name: Auto-update pre-commit hooks
runs-on: ubuntu-latest
environment:
name: develop
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- uses: actions/setup-python@v6
with:
activate-environment: true
python-version: "3.13"
python-version: "3.12"
- name: Install pre-commit
run: uv pip install $(grep -E "^pre-commit==" requirements-dev.txt)
run: pip install pre-commit
- name: Run auto-update
run: pre-commit autoupdate
- uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
- uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: .pre-commit-config.yaml
+30
View File
@@ -0,0 +1,30 @@
name: GitHub Actions Security Analysis with zizmor 🌈
on:
push:
branches:
- develop
- stable
pull_request:
branches:
- develop
- stable
permissions: {}
jobs:
zizmor:
name: Run zizmor 🌈
runs-on: ubuntu-latest
permissions:
security-events: write
# contents: read # only needed for private repos
# actions: read # only needed for private repos
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.1
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
-34
View File
@@ -1,34 +0,0 @@
name: GitHub Actions Security Analysis with zizmor 🌈
on:
push:
branches:
- develop
- stable
pull_request:
branches:
- develop
- stable
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: false
permissions: {}
jobs:
zizmor:
name: Run zizmor 🌈
runs-on: ubuntu-latest
permissions:
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
# contents: read # Only needed for private repos. Needed to clone the repo.
# actions: read # Only needed for private repos. Needed for upload-sarif to read workflow run info.
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
+23 -10
View File
@@ -13,25 +13,38 @@ repos:
pass_filenames: false
additional_dependencies: ["python-rapidjson", "jsonschema"]
- repo: https://github.com/pycqa/flake8
rev: "7.3.0"
hooks:
- id: flake8
additional_dependencies: [Flake8-pyproject]
# stages: [push]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v2.1.0"
rev: "v1.19.1"
hooks:
- id: mypy
exclude: build_helpers
additional_dependencies:
- types-cachetools==7.0.0.20260503
- types-cachetools==6.2.0.20251022
- types-filelock==3.2.7
- types-requests==2.33.0.20260508
- types-tabulate==0.10.0.20260508
- types-python-dateutil==2.9.0.20260508
- scipy-stubs==1.17.1.4
- SQLAlchemy==2.0.49
- types-requests==2.32.4.20250913
- types-tabulate==0.9.0.20241207
- types-python-dateutil==2.9.0.20251115
- scipy-stubs==1.16.3.3
- SQLAlchemy==2.0.45
# stages: [push]
- repo: https://github.com/pycqa/isort
rev: "7.0.0"
hooks:
- id: isort
name: isort (python)
# stages: [push]
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.15.13'
rev: 'v0.14.10'
hooks:
- id: ruff
- id: ruff-format
@@ -62,7 +75,7 @@ repos:
- id: strip-exif
- repo: https://github.com/codespell-project/codespell
rev: v2.4.2
rev: v2.4.1
hooks:
- id: codespell
additional_dependencies:
@@ -70,6 +83,6 @@ repos:
# Ensure github actions remain safe
- repo: https://github.com/woodruffw/zizmor-pre-commit
rev: v1.24.1
rev: v1.19.0
hooks:
- id: zizmor
+3 -3
View File
@@ -12,8 +12,7 @@ Few pointers for contributions:
- Stick to english in both commit messages, PR descriptions and code comments and variable names.
- New features need to contain unit tests, must pass CI (run pre-commit and pytest to get an early feedback) and should be documented with the introduction PR.
- PR's can be declared as draft - signaling Work in Progress for Pull Requests (which are not finished). We'll still aim to provide feedback on draft PR's in a timely manner.
- If you're using AI for your PR, please both mention it in the PR description and do a thorough review of the generated code yourself.
The final responsibility for the code with the PR author, not with the AI, which also means that commits must be linked to your (human) account, not some generic AI account.
- If you're using AI for your PR, please both mention it in the PR description and do a thorough review of the generated code. The final responsibility for the code with the PR author, not with the AI.
If you are unsure, discuss the feature on our [discord server](https://discord.gg/p7nuUNVfP7) or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a Pull Request.
@@ -25,7 +24,8 @@ Best start by reading the [documentation](https://www.freqtrade.io/) to get a fe
### 1. Run unit tests
All unit tests must pass. If a unit test is broken, change your code to make it pass. It means you have introduced a regression.
All unit tests must pass. If a unit test is broken, change your code to
make it pass. It means you have introduced a regression.
#### Test the whole project
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.14.5-slim-trixie AS base
FROM python:3.13.11-slim-bookworm AS base
# Setup env
ENV LANG=C.UTF-8
+5 -9
View File
@@ -2,9 +2,8 @@
[![Freqtrade CI](https://github.com/freqtrade/freqtrade/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/freqtrade/freqtrade/actions/workflows/ci.yml)
[![DOI](https://joss.theoj.org/papers/10.21105/joss.04864/status.svg)](https://doi.org/10.21105/joss.04864)
[![codecov](https://codecov.io/gh/freqtrade/freqtrade/branch/develop/graph/badge.svg?token=AD5BG3ATKI)](https://codecov.io/gh/freqtrade/freqtrade)
[![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
[![Documentation](https://readthedocs.org/projects/freqtrade/badge/)](https://www.freqtrade.io)
[![Discord Server](https://img.shields.io/badge/Freqtrade_Discord-4E4E4E?logo=discord)](https://discord.gg/p7nuUNVfP7)
Freqtrade is a free and open source crypto trading bot written in Python. It is designed to support all major exchanges and be controlled via Telegram or webUI. It contains backtesting, plotting and money management tools as well as strategy optimization by machine learning.
@@ -25,9 +24,7 @@ hesitate to read the source code and understand the mechanism of this bot.
## Supported Exchange marketplaces
Please read the [exchange-specific notes](https://www.freqtrade.io/en/stable/exchanges/) to learn about special configurations that maybe needed for each exchange.
### Supported Spot Exchanges
Please read the [exchange-specific notes](docs/exchanges.md) to learn about special configurations that maybe needed for each exchange.
- [X] [Binance](https://www.binance.com/)
- [X] [BingX](https://bingx.com/invite/0EM9RX)
@@ -42,7 +39,7 @@ Please read the [exchange-specific notes](https://www.freqtrade.io/en/stable/exc
- [X] [MyOKX](https://okx.com/) (OKX EEA)
- [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_
### Supported Futures Exchanges
### Supported Futures Exchanges (experimental)
- [X] [Binance](https://www.binance.com/)
- [X] [Bitget](https://www.bitget.com/)
@@ -50,9 +47,8 @@ Please read the [exchange-specific notes](https://www.freqtrade.io/en/stable/exc
- [X] [Hyperliquid](https://hyperliquid.xyz/) (A decentralized exchange, or DEX)
- [X] [OKX](https://okx.com/)
- [X] [Bybit](https://bybit.com/)
- [X] [Kraken](https://www.kraken.com/features/futures)
Please make sure to read the [exchange specific notes](https://www.freqtrade.io/en/stable/exchanges/), as well as the [trading with leverage](https://www.freqtrade.io/en/stable/leverage/) documentation before diving in.
Please make sure to read the [exchange specific notes](docs/exchanges.md), as well as the [trading with leverage](docs/leverage.md) documentation before diving in.
### Community tested
@@ -144,7 +140,7 @@ options:
### Telegram RPC commands
Telegram is not mandatory. However, this is a great way to control your bot. More details and the full command list on the [documentation](https://www.freqtrade.io/en/stable/telegram-usage/)
Telegram is not mandatory. However, this is a great way to control your bot. More details and the full command list on the [documentation](https://www.freqtrade.io/en/latest/telegram-usage/)
- `/start`: Starts the trader.
- `/stop`: Stops the trader.
+1 -1
View File
@@ -87,7 +87,7 @@ def extract_command_partials():
help_output = _get_help_output(subparser)
_write_partial_file(f"docs/commands/{command}.md", help_output)
else:
print(f" Warning: subcommand '{command}' not found in parser")
print(f" Warning: subcommand '{command}' not found in parser")
# freqtrade-client still uses subprocess as requested
print("Running for freqtrade-client")
+2 -42
View File
@@ -1,7 +1,5 @@
# File used in CI to ensure pre-commit dependencies are kept up-to-date.
import argparse
import re
import sys
from pathlib import Path
@@ -12,24 +10,6 @@ pre_commit_file = Path(".pre-commit-config.yaml")
require_dev = Path("requirements-dev.txt")
require = Path("requirements.txt")
parser = argparse.ArgumentParser()
parser.add_argument("--update", action="store_true")
args = parser.parse_args()
def replace_dependency_version(pre_commit_text: str, dependency: str) -> tuple[str, bool]:
"""
Regex-based replacement of a dependency version in the pre-commit config file.
using regex here ensures we only replace the version of the dependency while
keeping the overall file intact.
"""
package_name = dependency.split("==", 1)[0]
pattern = re.compile(rf"^(\s*-\s+){re.escape(package_name)}==.*$", re.MULTILINE)
updated_text, replacements = pattern.subn(rf"\1{dependency}", pre_commit_text, count=1)
return updated_text, replacements > 0 and updated_text != pre_commit_text
with require_dev.open("r") as rfile:
requirements = rfile.readlines()
@@ -43,18 +23,6 @@ supported = ("types-", "SQLAlchemy", "scipy-stubs")
# Only keep the first part of the line up to the first space
type_reqs = [r.strip("\n").split()[0] for r in requirements if r.startswith(supported)]
with pre_commit_file.open("r") as file:
pre_commit_text = file.read()
updated = False
for req in type_reqs:
pre_commit_text, req_updated = replace_dependency_version(pre_commit_text, req)
updated = updated or req_updated
if args.update and updated:
with pre_commit_file.open("w") as file:
file.write(pre_commit_text)
with pre_commit_file.open("r") as file:
f = yaml.load(file, Loader=yaml.SafeLoader)
@@ -72,20 +40,12 @@ for hook in hooks:
for req in type_reqs:
if req not in hooks:
errors.append(f"{req} is missing in pre-commit config file.")
if updated:
if args.update:
errors.append(".pre-commit-config.yaml was updated to match the requirements files.")
else:
errors.append(
".pre-commit-config.yaml is outdated. Run build_helpers/pre_commit_update.py --update."
)
errors.append(f"{req} is missing in pre-config file.")
if errors:
for e in errors:
print(e)
sys.exit(1 if not (args.update and updated) else 0)
sys.exit(1)
sys.exit(0)
+2 -10
View File
@@ -283,10 +283,6 @@
"month"
]
},
"skip_wallet_history_migration": {
"description": "Disable wallet history migration.",
"type": "boolean"
},
"hyperopt_path": {
"description": "Specify additional lookup path for Hyperopt Loss functions.",
"type": "string"
@@ -653,7 +649,6 @@
"ProducerPairList",
"RemotePairList",
"MarketCapPairList",
"CrossMarketPairList",
"AgeFilter",
"DelistFilter",
"FullTradesFilter",
@@ -1062,9 +1057,7 @@
},
"jwt_secret_key": {
"description": "Secret key for JWT authentication.",
"type": "string",
"default": "somethingRandomSomethingRandom123",
"minLength": 32
"type": "string"
},
"CORS_origins": {
"description": "List of allowed CORS origins.",
@@ -1087,8 +1080,7 @@
"listen_ip_address",
"listen_port",
"username",
"password",
"jwt_secret_key"
"password"
]
},
"db_url": {
+1 -1
View File
@@ -70,7 +70,7 @@
"listen_ip_address": "127.0.0.1",
"listen_port": 8080,
"verbosity": "error",
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "freqtrader",
"password": "SuperSecurePassword"
+2 -2
View File
@@ -177,7 +177,7 @@
"listen_port": 8080,
"verbosity": "error",
"enable_openapi": false,
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "freqtrader",
"password": "SuperSecurePassword",
@@ -215,4 +215,4 @@
"reduce_df_footprint": false,
"dataformat_ohlcv": "feather",
"dataformat_trades": "feather"
}
}
+1 -1
View File
@@ -75,7 +75,7 @@
"listen_ip_address": "127.0.0.1",
"listen_port": 8080,
"verbosity": "error",
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "freqtrader",
"password": "SuperSecurePassword"
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.11.15-slim-bookworm AS base
FROM python:3.11.14-slim-bookworm AS base
# Setup env
ENV LANG=C.UTF-8
+29 -28
View File
@@ -41,7 +41,7 @@ ranging from the simplest (0) to the most detailed per pair, per buy and per sel
* 1: profit summaries grouped by enter_tag
* 2: profit summaries grouped by enter_tag and exit_tag
* 3: profit summaries grouped by pair and enter_tag
* 4: profit summaries grouped by pair, enter_tag and exit_tag (this can get quite large)
* 4: profit summaries grouped by pair, enter_ and exit_tag (this can get quite large)
* 5: profit summaries grouped by exit_tag
More options are available by running with the `-h` option.
@@ -52,10 +52,11 @@ By default, `backtesting-analysis` processes the most recent backtest results in
If you want to analyze results from an earlier backtest, use the `--backtest-filename` option to specify the desired file. This lets you revisit and re-analyze historical backtest outputs at any time by providing the filename of the relevant backtest result:
``` bash
freqtrade backtesting -c <config.json> --strategy <strategy_name> --timerange <timerange> --export signals --backtest-filename backtest-result-2025-03-05_20-38-34.zip
freqtrade backtesting-analysis -c <config.json> --timeframe <tf> --strategy <strategy_name> --timerange <timerange> --export signals --backtest-filename backtest-result-2025-03-05_20-38-34.zip
```
You should see some output similar to below in the logs with the name of the timestamped filename that was exported:
You should see some output similar to below in the logs with the name of the timestamped
filename that was exported:
```
2022-06-14 16:28:32,698 - freqtrade.misc - INFO - dumping json to "mystrat_backtest-2022-06-14_16-28-32.json"
@@ -63,14 +64,14 @@ You should see some output similar to below in the logs with the name of the tim
You can then use that filename in `backtesting-analysis`:
``` bash
freqtrade backtesting-analysis -c <config.json> --backtest-filename=backtest-result-2025-03-05_20-38-34.zip
```
freqtrade backtesting-analysis -c <config.json> --backtest-filename=mystrat_backtest-2022-06-14_16-28-32.json
```
To use a result from a different results directory, you can use `--backtest-directory` to specify the directory
``` bash
freqtrade backtesting-analysis -c <config.json> --backtest-directory custom_results/ --backtest-filename backtest-result-2025-03-05_20-38-34.zip
freqtrade backtesting-analysis -c <config.json> --backtest-directory custom_results/ --backtest-filename mystrat_backtest-2022-06-14_16-28-32.json
```
### Tuning the buy tags and sell tags to display
@@ -84,7 +85,7 @@ To show only certain buy and sell tags in the displayed output, use the followin
For example:
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --analysis-groups 0 2 --enter-reason-list enter_tag_a enter_tag_b --exit-reason-list roi custom_exit_tag_a stop_loss
```
@@ -95,7 +96,7 @@ values present on signal candles to allow fine-grained investigation and tuning
indicators. To print out a column for a given set of indicators, use the `--indicator-list`
option:
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --analysis-groups 0 2 --enter-reason-list enter_tag_a enter_tag_b --exit-reason-list roi custom_exit_tag_a stop_loss --indicator-list rsi rsi_1h bb_lowerband ema_9 macd macdsignal
```
@@ -107,24 +108,24 @@ output.
The indicator values will be displayed for both entry and exit points. If `--indicator-list all` is specified,
only the indicators at the entry point will be shown to avoid excessively large lists, which could occur depending on the strategy.
There are a range of candle and trade-related fields that are included in the analysis so are
There are a range of candle and trade-related fields that are included in the analysis so are
automatically accessible by including them on the indicator-list, and these include:
* **open_date :** trade open datetime
* **close_date :** trade close datetime
* **min_rate :** minimum price seen throughout the position
* **max_rate :** maximum price seen throughout the position
* **open :** signal candle open price
* **close :** signal candle close price
* **high :** signal candle high price
* **low :** signal candle low price
* **volume :** signal candle volume
* **profit_ratio :** trade profit ratio
* **profit_abs :** absolute profit return of the trade
- **open_date :** trade open datetime
- **close_date :** trade close datetime
- **min_rate :** minimum price seen throughout the position
- **max_rate :** maximum price seen throughout the position
- **open :** signal candle open price
- **close :** signal candle close price
- **high :** signal candle high price
- **low :** signal candle low price
- **volume :** signal candle volume
- **profit_ratio :** trade profit ratio
- **profit_abs :** absolute profit return of the trade
#### Sample Output for Indicator Values
``` bash
```bash
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen
```
@@ -157,13 +158,13 @@ The `--indicator-list` option, by default, displays indicator values for both en
Example: Display indicator values at entry signals:
``` bash
```bash
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --entry-only
```
Example: Display indicator values at exit signals:
``` bash
```bash
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --exit-only
```
@@ -180,7 +181,7 @@ To show only trades between dates within your backtested timerange, supply the u
For example, if your backtest timerange was `20220101-20221231` but you only want to output trades in January:
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --timerange 20220101-20220201
```
@@ -188,7 +189,7 @@ freqtrade backtesting-analysis -c <config.json> --timerange 20220101-20220201
Use the `--rejected-signals` option to print out rejected signals.
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --rejected-signals
```
@@ -197,13 +198,13 @@ freqtrade backtesting-analysis -c <config.json> --rejected-signals
Some of the tabular outputs can become large, so printing them out to the terminal is not preferable.
Use the `--analysis-to-csv` option to disable printing out of tables to standard out and write them to CSV files.
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --analysis-to-csv
```
By default this will write one file per output table you specified in the `backtesting-analysis` command, e.g.
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --analysis-to-csv --rejected-signals --analysis-groups 0 1
```
@@ -215,6 +216,6 @@ This will write to `user_data/backtest_results`:
To override where the files will be written, also specify the `--analysis-csv-path` option.
``` bash
```bash
freqtrade backtesting-analysis -c <config.json> --analysis-to-csv --analysis-csv-path another/data/path/
```
+4 -3
View File
@@ -133,7 +133,7 @@ class MyAwesomeStrategy(IStrategy):
]
# Define a custom max_open_trades space
def max_open_trades_space() -> List[Dimension]:
def max_open_trades_space(self) -> List[Dimension]:
return [
Integer(-1, 10, name='max_open_trades'),
]
@@ -142,7 +142,7 @@ class MyAwesomeStrategy(IStrategy):
!!! Note
All overrides are optional and can be mixed/matched as necessary.
## Dynamic parameters
### Dynamic parameters
Parameters can also be defined dynamically, but must be available to the instance once the [`bot_start()` callback](strategy-callbacks.md#bot-start) has been called.
@@ -159,7 +159,7 @@ class MyAwesomeStrategy(IStrategy):
!!! Warning
Parameters created this way will not show up in the `list-strategies` parameter count.
## Overriding Base estimator
### Overriding Base estimator
You can define your own optuna sampler for Hyperopt by implementing `generate_estimator()` in the Hyperopt subclass.
@@ -208,6 +208,7 @@ Some research will be necessary to find additional Samplers (from optunahub) for
Obviously the same approach will work for all other Samplers optuna supports.
## Space options
For the additional spaces, scikit-optimize (in combination with Freqtrade) provides the following space types:
+2 -2
View File
@@ -73,7 +73,7 @@ services:
volumes:
- "./user_data:/freqtrade/user_data"
# Expose api on port 8080 (localhost only)
# Please read the https://www.freqtrade.io/en/stable/rest-api/ documentation
# Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation
# before enabling this.
ports:
- "127.0.0.1:8080:8080"
@@ -100,7 +100,7 @@ services:
volumes:
- "./user_data:/freqtrade/user_data"
# Expose api on port 8080 (localhost only)
# Please read the https://www.freqtrade.io/en/stable/rest-api/ documentation
# Please read the https://www.freqtrade.io/en/latest/rest-api/ documentation
# before enabling this.
ports:
- "127.0.0.1:8081:8080"
+162 -203
View File
@@ -160,131 +160,117 @@ The most important in the backtesting is to understand the result.
A backtesting result will look like that:
```
BACKTESTING REPORT
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ LTC/USDT:USDT │ 16 │ 1.01 │ 56.882 │ 5.69 │ 16:16:00 │ 16 0 0 100 │
│ ETC/USDT:USDT │ 12 │ 0.7331.513 │ 3.15 │ 9:55:00 │ 11 0 1 91.7 │
│ ETH/USDT:USDT │ 8 │ 0.6918.659 │ 1.87 │ 1 day, 13:55:00 │ 7 0 1 87.5 │
│ XLM/USDT:USDT │ 10 │ 0.3 │ 10.694 │ 1.07 │ 12:08:00 │ 9 0 1 90.0 │
│ BTC/USDT:USDT │ 8 │ 0.22 │ 7.502 │ 0.75 │ 3 days, 1:24:00 │ 6 0 2 75.0 │
│ XRP/USDT:USDT │ 9 │ -0.13-6.837 │ -0.68 │ 21:18:00 │ 8 0 1 88.9 │
│ DOT/USDT:USDT │ 6 │ -0.39 │ -9.169 │ -0.92 │ 5:35:00 │ 4 0 2 66.7 │
│ ADA/USDT:USDT │ 8 │ -1.75 │ -52.089 │ -5.21 │ 11:38:00 │ 6 0 2 75.0 │
│ TOTAL │ 77 │ 0.2357.157 │ 5.72 │ 22:12:00 │ 67 0 10 87.0 │
└───────────────┴────────┴──────────────┴─────────────┴──────────────┴─────────────────┴────────────────────────┘
LEFT OPEN TRADES REPORT
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ BTC/USDT:USDT │ 1 │ -4.14 │ -9.930 │ -0.99 │ 17 days, 8:00:00 │ 0 0 1 0 │
│ ETC/USDT:USDT │ 1 │ -4.24 │ -15.365 │ -1.54 │ 10:40:00 │ 0 0 1 0 │
│ DOT/USDT:USDT │ 1 │ -5.29 │ -19.166 │ -1.92 │ 11:30:00 │ 0 0 1 0 │
│ TOTAL │ 3 │ -4.56 │ -44.461 │ -4.45 │ 6 days, 2:03:00 │ 0 0 3 0 │
└───────────────┴────────┴──────────────┴─────────────┴──────────────┴──────────────────┴────────────────────────┘
ENTER TAG STATS
┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Enter Tag ┃ Entries ┃ Avg Profit % ┃ Tot Profit ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ OTHER │ 77 │ 0.2357.157 │ 5.72 │ 22:12:00 │ 67 0 10 87.0 │
│ TOTAL │ 77 │ 0.2357.157 │ 5.72 │ 22:12:00 │ 67 0 10 87.0 │
└───────────┴─────────┴──────────────┴─────────────┴──────────────┴──────────────┴────────────────────────┘
EXIT REASON STATS
┏━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Exit Reason ┃ Exits ┃ Avg Profit % ┃ Tot Profit ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ roi │ 67 │ 1.06 │ 245.117 │ 24.51 │ 15:49:00 │ 67 0 0 100 │
│ exit_signal │ 4 │ -2.23 │ -31.226 │ -3.12 │ 1 day, 8:38:00 │ 0 0 4 0 │
│ force_exit │ 3 │ -4.56 │ -44.461 │ -4.45 │ 6 days, 2:03:00 │ 0 0 3 0 │
│ stop_loss │ 3 │ -10.14 │ -112.273 │ -11.23 │ 1 day, 3:05:00 │ 0 0 3 0 │
│ TOTAL │ 77 │ 0.2357.157 │ 5.72 │ 22:12:00 │ 67 0 10 87.0 │
└─────────────┴───────┴──────────────┴─────────────┴──────────────┴─────────────────┴────────────────────────┘
MIXED TAG STATS
┏━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Enter Tag ┃ Exit Reason ┃ Trades ┃ Avg Profit % ┃ Tot Profit ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ │ roi │ 67 │ 1.06 │ 245.117 │ 24.51 │ 15:49:00 │ 67 0 0 100 │
│ │ exit_signal │ 4 │ -2.23 │ -31.226 │ -3.12 │ 1 day, 8:38:00 │ 0 0 4 0 │
│ │ force_exit │ 3 │ -4.56 │ -44.461 │ -4.45 │ 6 days, 2:03:00 │ 0 0 3 0 │
│ │ stop_loss │ 3 │ -10.14 │ -112.273 │ -11.23 │ 1 day, 3:05:00 │ 0 0 3 0 │
│ TOTAL │ │ 77 │ 0.2357.157 │ 5.72 │ 22:12:00 │ 67 0 10 87.0 │
└───────────┴─────────────┴────────┴──────────────┴─────────────┴──────────────┴─────────────────┴────────────────────────┘
SUMMARY METRICS
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Value
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Backtesting from │ 2025-07-01 00:00:00
│ Backtesting to │ 2025-08-01 00:00:00
│ Trading Mode │ Isolated Futures
│ Max open trades │ 3
│ Total/Daily Avg Trades │ 77 / 2.48
│ Starting balance │ 1000 USDT
│ Final balance │ 1057.157 USDT
│ Absolute profit │ 57.157 USDT
│ Total profit % │ 5.72%
│ CAGR % │ 92.41%
│ Sharpe (closed trades)3.89
│ Sortino (closed trades)2.57
│ Calmar (closed trades) │ 43.03
│ SQN │ 0.71
│ Profit factor │ 1.30
│ Expectancy (Ratio) │ 0.74 (0.04)
│ Avg. daily profit │ 1.844 USDT
│ Avg. stake amount │ 345.478 USDT
Market change │ 30.51%
Total trade volume53390.788 USDT
│ Long / Short trades │ 67 / 10
│ Long / Short profit % │ 9.19% / -3.48%
Long / Short profit USDT91.940 / -34.783
Best Pair │ LTC/USDT:USDT 5.69%
Worst Pair ADA/USDT:USDT -5.21%
Best trade │ XRP/USDT:USDT 2.00%
Worst trade │ ADA/USDT:USDT -10.17%
Best day │ 27.031 USDT
Worst day │ -47.826 USDT
Days win/draw/lose │ 20 / 6 / 5
│ Min/Max/Avg. Duration Winners │ 0d 00:35 / 5d 18:15 / 0d 15:49
│ Min/Max/Avg. Duration Losers │ 0d 10:40 / 17d 08:00 / 2d 17:00
Max Consecutive Wins / Loss │ 36 / 3
Rejected Entry signals │ 258
Entry/Exit Timeouts 0 / 0
Min/Max balance (closed trades) │ 1003.205 USDT / 1151.425 USDT │
│ Max % of account underwater │ 8.19%
│ Absolute drawdown │ 94.268 USDT (8.19%)
│ Drawdown duration │ 9 days 08:50:00
│ Profit at drawdown start │ 151.425 USDT
│ Profit at drawdown end │ 57.157 USDT
│ Drawdown start │ 2025-07-22 15:10:00
│ Drawdown end │ 2025-08-01 00:00:00
│ Wallet based Metrics │ │
│ Min/Max balance (wallet balance) │ 1000 USDT / 1151.425 USDT │
│ Min/Max balance dates (wallet balance) │ 2025-07-01 00:05:00 / 2025-07-22 15:15:00 │
│ Max % of account underwater (balance) │ 5.01% │
│ Absolute drawdown (wallet balance) │ 54.76 USDT (4.76%) │
│ Drawdown duration │ 7 days 20:35:00 │
│ Profit at drawdown start │ 151.425 USDT │
│ Profit at drawdown end │ 96.664 USDT │
│ Drawdown start │ 2025-07-22 15:15:00 │
│ Drawdown end │ 2025-07-30 11:50:00 │
│ Sharpe (daily wallet balance) │ 4.42 │
│ Sortino (daily wallet balance) │ 4.35 │
│ Calmar (daily wallet balance) │ 136.07 │
└────────────────────────────────────────┴───────────────────────────────────────────┘
BACKTESTING REPORT
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ LTC/USDT:USDT │ 16 │ 1.0 │ 56.176 │ 5.62 │ 16:16:00 │ 16 0 0 100 │
│ ETC/USDT:USDT │ 12 │ 0.72 30.936 │ 3.09 │ 9:55:00 │ 11 0 1 91.7 │
│ ETH/USDT:USDT │ 8 │ 0.66 17.864 │ 1.79 │ 1 day, 13:55:00 │ 7 0 1 87.5 │
│ XLM/USDT:USDT │ 10 │ 0.31 11.054 │ 1.11 │ 12:08:00 │ 9 0 1 90.0 │
│ BTC/USDT:USDT │ 8 │ 0.21 7.289 │ 0.73 │ 3 days, 1:24:00 │ 6 0 2 75.0 │
│ XRP/USDT:USDT │ 9 │ -0.14 -7.261 │ -0.73 │ 21:18:00 │ 8 0 1 88.9 │
│ DOT/USDT:USDT │ 6 │ -0.4 │ -9.187 │ -0.92 │ 5:35:00 │ 4 0 2 66.7 │
│ ADA/USDT:USDT │ 8 │ -1.76 -52.098 │ -5.21 │ 11:38:00 │ 6 0 2 75.0 │
│ TOTAL │ 77 │ 0.22 54.774 │ 5.48 │ 22:12:00 │ 67 0 10 87.0 │
└───────────────┴────────┴──────────────┴─────────────────┴──────────────┴─────────────────┴────────────────────────┘
LEFT OPEN TRADES REPORT
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ BTC/USDT:USDT │ 1 │ -4.14 │ -9.930 │ -0.99 │ 17 days, 8:00:00 │ 0 0 1 0 │
│ ETC/USDT:USDT │ 1 │ -4.24 │ -15.365 │ -1.54 │ 10:40:00 │ 0 0 1 0 │
│ DOT/USDT:USDT │ 1 │ -5.29 │ -19.125 │ -1.91 │ 11:30:00 │ 0 0 1 0 │
│ TOTAL │ 3 │ -4.56 │ -44.420 │ -4.44 │ 6 days, 2:03:00 │ 0 0 3 0 │
└───────────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┘
ENTER TAG STATS
┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Enter Tag ┃ Entries ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ OTHER │ 77 │ 0.22 54.774 │ 5.48 │ 22:12:00 │ 67 0 10 87.0 │
│ TOTAL │ 77 │ 0.22 54.774 │ 5.48 │ 22:12:00 │ 67 0 10 87.0 │
└───────────┴─────────┴──────────────┴─────────────────┴──────────────┴──────────────┴────────────────────────┘
EXIT REASON STATS
┏━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Exit Reason ┃ Exits ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ roi │ 67 │ 1.05 242.179 │ 24.22 │ 15:49:00 │ 67 0 0 100 │
│ exit_signal │ 4 │ -2.23 │ -31.217 │ -3.12 │ 1 day, 8:38:00 │ 0 0 4 0 │
│ force_exit │ 3 │ -4.56 │ -44.420 │ -4.44 │ 6 days, 2:03:00 │ 0 0 3 0 │
│ stop_loss │ 3 │ -10.14 │ -111.768 │ -11.18 │ 1 day, 3:05:00 │ 0 0 3 0 │
│ TOTAL │ 77 │ 0.22 54.774 │ 5.48 │ 22:12:00 │ 67 0 10 87.0 │
└─────────────┴───────┴──────────────┴─────────────────┴──────────────┴─────────────────┴────────────────────────┘
MIXED TAG STATS
┏━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Enter Tag ┃ Exit Reason ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
│ │ roi │ 67 │ 1.05 242.179 │ 24.22 │ 15:49:00 │ 67 0 0 100 │
│ │ exit_signal │ 4 │ -2.23 │ -31.217 │ -3.12 │ 1 day, 8:38:00 │ 0 0 4 0 │
│ │ force_exit │ 3 │ -4.56 │ -44.420 │ -4.44 │ 6 days, 2:03:00 │ 0 0 3 0 │
│ │ stop_loss │ 3 │ -10.14 │ -111.768 │ -11.18 │ 1 day, 3:05:00 │ 0 0 3 0 │
│ TOTAL │ │ 77 │ 0.22 54.774 │ 5.48 │ 22:12:00 │ 67 0 10 87.0 │
└───────────┴─────────────┴────────┴──────────────┴─────────────────┴──────────────┴─────────────────┴────────────────────────┘
SUMMARY METRICS
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Value
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Backtesting from │ 2025-07-01 00:00:00 │
│ Backtesting to │ 2025-08-01 00:00:00 │
│ Trading Mode │ Isolated Futures │
│ Max open trades │ 3
│ Total/Daily Avg Trades │ 77 / 2.48 │
│ Starting balance │ 1000 USDT │
│ Final balance │ 1054.774 USDT
│ Absolute profit │ 54.774 USDT
│ Total profit % │ 5.48%
│ CAGR % │ 87.36%
│ Sortino 2.48
│ Sharpe 3.75
│ Calmar │ 40.99
│ SQN │ 0.69
│ Profit factor │ 1.29
│ Expectancy (Ratio) │ 0.71 (0.04) │
│ Avg. daily profit │ 1.767 USDT │
│ Avg. stake amount │ 345.016 USDT │
Total trade volume │ 53316.954 USDT
Long / Short trades │ 67 / 10
│ Long / Short profit % │ 8.94% / -3.47%
│ Long / Short profit USDT89.425 / -34.651
Best PairLTC/USDT:USDT 5.62%
Worst Pair │ ADA/USDT:USDT -5.21%
Best tradeETC/USDT:USDT 2.00%
Worst trade │ ADA/USDT:USDT -10.17%
Best day │ 26.91 USDT
Worst day │ -47.741 USDT
Days win/draw/lose │ 20 / 6 / 5
Min/Max/Avg. Duration Winners │ 0d 00:35 / 5d 18:15 / 0d 15:49
│ Min/Max/Avg. Duration Losers │ 0d 10:40 / 17d 08:00 / 2d 17:00
│ Max Consecutive Wins / Loss │ 36 / 3
Rejected Entry signals │ 258
Entry/Exit Timeouts │ 0 / 0
│ │
Min balance1003.168 USDT
│ Max balance 1149.421 USDT
│ Max % of account underwater │ 8.23%
│ Absolute drawdown │ 94.647 USDT (8.23%)
│ Drawdown duration │ 9 days 08:50:00 │
│ Profit at drawdown start │ 149.421 USDT │
│ Profit at drawdown end │ 54.774 USDT
│ Drawdown start │ 2025-07-22 15:10:00 │
│ Drawdown end │ 2025-08-01 00:00:00 │
Market change │ 30.51%
└───────────────────────────────┴─────────────────────────────────┘
Backtested 2025-07-01 00:00:00 -> 2025-08-01 00:00:00 | Max open trades : 3
STRATEGY SUMMARY
┏━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃ Strategy ┃ Trades ┃ Avg Profit % ┃ Tot Profit ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ Drawdown ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│ SampleStrategy │ 77 │ 0.2357.157 │ 5.72 │ 22:12:00 │ 67 0 10 87.0 │ 94.268 8.19% │
└────────────────┴────────┴──────────────┴─────────────┴──────────────┴──────────────┴────────────────────────┴────────────────┘
STRATEGY SUMMARY
┏━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━
┃ Strategy ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDT ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ Drawdown ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━
│ SampleStrategy │ 77 │ 0.22 54.774 │ 5.48 │ 22:12:00 │ 67 0 10 87.0 │ 94.647 USDT 8.23% │
└────────────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────┴────────────────────────┴────────────────────
```
### Backtesting report table
@@ -343,72 +329,54 @@ The last element of the backtest report is the summary metrics table.
It contains key metrics about the performance of your strategy on backtesting data.
```
SUMMARY METRICS
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Value ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Backtesting from │ 2025-07-01 00:00:00
Backtesting to │ 2025-08-01 00:00:00
Trading Mode │ Isolated Futures
Max open trades 3
Total/Daily Avg Trades │ 77 / 2.48
Starting balance │ 1000 USDT
Final balance │ 1057.157 USDT
Absolute profit 57.157 USDT
Total profit % 5.72%
CAGR % │ 92.41%
│ Sharpe (closed trades) │ 3.89
Sortino (closed trades) │ 2.57
Calmar (closed trades)43.03
SQN 0.71
Profit factor │ 1.30
Expectancy (Ratio) │ 0.74 (0.04)
│ Avg. daily profit 1.844 USDT
Avg. stake amount │ 345.478 USDT
Market change 30.51%
Total trade volume53390.788 USDT
Long / Short trades67 / 10
Long / Short profit %9.19% / -3.48%
Long / Short profit USDT │ 91.940 / -34.783
Best Pair │ LTC/USDT:USDT 5.69%
Worst Pair │ ADA/USDT:USDT -5.21%
Best trade │ XRP/USDT:USDT 2.00%
Worst trade │ ADA/USDT:USDT -10.17%
Best day │ 27.031 USDT
Worst day │ -47.826 USDT
Days win/draw/lose20 / 6 / 5
│ Min/Max/Avg. Duration Winners │ 0d 00:35 / 5d 18:15 / 0d 15:49
│ Min/Max/Avg. Duration Losers │ 0d 10:40 / 17d 08:00 / 2d 17:00
│ Max Consecutive Wins / Loss │ 36 / 3
Rejected Entry signals │ 258
Entry/Exit Timeouts │ 0 / 0
Min/Max balance (closed trades) │ 1003.205 USDT / 1151.425 USDT
Max % of account underwater │ 8.19%
Absolute drawdown │ 94.268 USDT (8.19%)
Drawdown duration │ 9 days 08:50:00
│ Profit at drawdown start │ 151.425 USDT │
│ Profit at drawdown end │ 57.157 USDT │
│ Drawdown start │ 2025-07-22 15:10:00 │
│ Drawdown end │ 2025-08-01 00:00:00 │
│ │ │
│ Wallet based Metrics │ │
│ Min/Max balance (wallet balance) │ 1000 USDT / 1151.425 USDT │
│ Min/Max balance dates (wallet balance) │ 2025-07-01 00:05:00 / 2025-07-22 15:15:00 │
│ Max % of account underwater (balance) │ 5.01% │
│ Absolute drawdown (wallet balance) │ 54.76 USDT (4.76%) │
│ Drawdown duration │ 7 days 20:35:00 │
│ Profit at drawdown start │ 151.425 USDT │
│ Profit at drawdown end │ 96.664 USDT │
│ Drawdown start │ 2025-07-22 15:15:00 │
│ Drawdown end │ 2025-07-30 11:50:00 │
│ Sharpe (daily wallet balance) │ 4.42 │
│ Sortino (daily wallet balance) │ 4.35 │
│ Calmar (daily wallet balance) │ 136.07 │
└────────────────────────────────────────┴───────────────────────────────────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric ┃ Value ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ Backtesting from │ 2025-07-01 00:00:00 │
│ Backtesting to │ 2025-08-01 00:00:00 │
Trading Mode │ Isolated Futures
Max open trades │ 3
│ │
Total/Daily Avg Trades │ 72 / 2.32
Starting balance │ 1000 USDT
Final balance │ 1106.734 USDT
Absolute profit │ 106.734 USDT
Total profit %10.67%
CAGR % │ 230.04%
Sortino │ 4.99
│ Sharpe │ 8.00
Calmar │ 77.76
SQN 1.52
Profit factor1.79
Expectancy (Ratio) │ 1.48 (0.07)
Avg. daily profit │ 3.443 USDT
│ Avg. stake amount363.133 USDT
Total trade volume │ 52466.174 USDT
│ │
Best PairLTC/USDT:USDT 4.48%
Worst PairADA/USDT:USDT -1.78%
Best trade │ ETC/USDT:USDT 2.00%
Worst trade ADA/USDT:USDT -10.17%
Best day │ 23.535 USDT
Worst day-49.813 USDT
Days win/draw/lose │ 21 / 6 / 4
Min/Max/Avg. Duration Winners │ 0d 00:35 / 5d 18:15 / 0d 15:30
Min/Max/Avg. Duration Losers │ 0d 12:00 / 17d 08:00 / 3d 23:28
Max Consecutive Wins / Loss │ 58 / 4
Rejected Entry signals │ 254
Entry/Exit Timeouts │ 0 / 0
│ │
│ Min balance │ 1003.168 USDT
│ Max balance │ 1209 USDT
│ Max % of account underwater │ 8.46%
Absolute drawdown │ 102.266 USDT (8.46%)
Drawdown duration │ 9 days 08:50:00
Profit at drawdown start │ 209 USDT
Profit at drawdown end │ 106.734 USDT
Drawdown start │ 2025-07-22 15:10:00
Drawdown end │ 2025-08-01 00:00:00
Market change │ 30.51%
└───────────────────────────────┴─────────────────────────────────┘
```
- `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option).
@@ -420,15 +388,14 @@ It contains key metrics about the performance of your strategy on backtesting da
- `Absolute profit`: Profit made in stake currency.
- `Total profit %`: Total profit. Aligned to the `TOTAL` row's `Tot Profit %` from the first table. Calculated as `(End capital Starting capital) / Starting capital`.
- `CAGR %`: Compound annual growth rate.
- `Sharpe (closed trades)`: Annualized Sharpe ratio including only closed trades (ignoring open trades with profits or losses).
- `Sortino (closed trades)`: Annualized Sortino ratio including only closed trades (ignoring open trades with profits or losses).
- `Calmar (closed trades)`: Annualized Calmar ratio including only closed trades (ignoring open trades with profits or losses).
- `Sortino`: Annualized Sortino ratio.
- `Sharpe`: Annualized Sharpe ratio.
- `Calmar`: Annualized Calmar ratio.
- `SQN`: System Quality Number (SQN) - by Van Tharp.
- `Profit factor`: Sum of the profits of all winning trades divided by the sum of the losses of all losing trades.
- `Expectancy (Ratio)`: Expectancy ratio, which is the average profit or loss per trade. A negative expectancy ratio means that your strategy is not profitable.
- `Avg. daily profit`: Average profit per day, calculated as `(Total Profit / Backtest Days)`.
- `Avg. stake amount`: Average stake amount, either `stake_amount` or the average when using dynamic stake amount.
- `Market change`: Change of the market during the backtest period. Calculated as the average of all pairs' changes from the first to the last candle using the "close" column.
- `Total trade volume`: Volume generated on the exchange to reach the above profit.
- `Long / Short trades`: Split long/short trade counts (only shown when short trades were made).
- `Long / Short profit %`: Profit percentage for long and short trades (only shown when short trades were made).
@@ -442,21 +409,13 @@ It contains key metrics about the performance of your strategy on backtesting da
- `Max Consecutive Wins / Loss`: Maximum consecutive wins/losses in a row.
- `Rejected Entry signals`: Trade entry signals that could not be acted upon due to `max_open_trades` being reached.
- `Entry/Exit Timeouts`: Entry/exit orders which did not fill (only applicable if custom pricing is used).
- `Min/Max balance (closed trades)`: Lowest and Highest Wallet balance during the backtest period based on closed trades trades.
- `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period.
- `Max % of account underwater`: Maximum percentage your account has decreased from the top since the simulation started. Calculated as the maximum of `(Max Balance - Current Balance) / (Max Balance)`.
- `Absolute drawdown`: Maximum absolute drawdown experienced, including percentage relative to the account calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`..
- `Absolute drawdown (wallet balance)`: Maximum absolute drawdown experienced based on the unrealized balance, including percentage relative to the account calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`.
- `Drawdown duration`: Duration of the largest drawdown period.
- `Profit at drawdown start` / `Profit at drawdown end`: Profit at the beginning and end of the largest drawdown period.
- `Drawdown start` / `Drawdown end`: Start and end datetime for the largest drawdown (can also be visualized via the `plot-dataframe` sub-command).
- `Min/Max balance (wallet balance)`: Lowest and Highest Wallet balance during the backtest period - including capital tied in open trades.
- `Min/Max balance dates (wallet balance)`: Dates when the minimum and maximum unrealized balance occurred.
- `Sharpe (wallet balance)` Annualized Sharpe ratio calculation including unrealized profits.
- `Sortino (wallet balance)` Annualized Sortino ratio calculation including unrealized profits.
- `Calmar (wallet balance)` Annualized Calmar ratio calculation including unrealized profits.
!!! Tip "Wallet based Metrics"
The metrics under the "Wallet based Metrics" section are calculated based on the unrealized balance, which includes the capital tied in open trades. This provides a more comprehensive view of the strategy's performance, as it accounts for both realized and unrealized profits and losses.
- `Market change`: Change of the market during the backtest period. Calculated as the average of all pairs' changes from the first to the last candle using the "close" column.
### Daily / Weekly / Monthly / Yearly breakdown
+9 -6
View File
@@ -64,15 +64,18 @@ options:
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a space-separated list of strategies to
backtest. Please note that timeframe needs to be set
either in config or via command line.
either in config or via command line. When using this
together with `--export trades`, the strategy-name is
injected into the filename (so `backtest-data.json`
becomes `backtest-data-SampleStrategy.json`
--export {none,trades,signals}
Export backtest results (default: trades).
--backtest-filename, --export-filename PATH
DEPRECATED: This option is deprecated for backtesting
and will be removed in a future release. Using a
custom filename for backtest results is no longer
supported. Use `--backtest-directory` to specify the
directory.
Use this filename for backtest results.Example:
`--backtest-
filename=backtest_results_2020-09-27_16-20-48.json`.
Assumes either `user_data/backtest_results/` or
`--export-directory` as base directory.
--backtest-directory, --export-directory PATH
Directory to use for backtest results. Example:
`--export-directory=user_data/backtest_results/`.
+4 -1
View File
@@ -62,7 +62,10 @@ options:
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a space-separated list of strategies to
backtest. Please note that timeframe needs to be set
either in config or via command line.
either in config or via command line. When using this
together with `--export trades`, the strategy-name is
injected into the filename (so `backtest-data.json`
becomes `backtest-data-SampleStrategy.json`
--export {none,trades,signals}
Export backtest results (default: trades).
--backtest-filename, --export-filename PATH
+4 -1
View File
@@ -10,7 +10,10 @@ options:
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a space-separated list of strategies to
backtest. Please note that timeframe needs to be set
either in config or via command line.
either in config or via command line. When using this
together with `--export trades`, the strategy-name is
injected into the filename (so `backtest-data.json`
becomes `backtest-data-SampleStrategy.json`
--strategy-path PATH Specify additional strategy lookup path.
--recursive-strategy-search
Recursively search for a strategy in the strategies
+5 -5
View File
@@ -191,7 +191,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| | **Unfilled timeout**
| `unfilledtimeout.entry` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled entry order to complete, after which the order will be cancelled. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
| `unfilledtimeout.exit` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled exit order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
| `unfilledtimeout.unit` | Unit to use in unfilledtimeout setting. Note: If you set `unfilledtimeout.unit` to "seconds", "internals.process_throttle_secs" must be inferior or equal to timeout [Strategy Override](#parameters-in-the-strategy). <br> *Defaults to `"minutes"`.* <br> **Datatype:** String
| `unfilledtimeout.unit` | Unit to use in unfilledtimeout setting. Note: If you set unfilledtimeout.unit to "seconds", "internals.process_throttle_secs" must be inferior or equal to timeout [Strategy Override](#parameters-in-the-strategy). <br> *Defaults to `"minutes"`.* <br> **Datatype:** String
| `unfilledtimeout.exit_timeout_count` | How many times can exit orders time out. Once this number of timeouts is reached, an emergency exit is triggered. 0 to disable and allow unlimited order cancels. [Strategy Override](#parameters-in-the-strategy).<br>*Defaults to `0`.* <br> **Datatype:** Integer
| | **Pricing**
| `entry_pricing.price_side` | Select the side of the spread the bot should look at to get the entry rate. [More information below](#entry-price).<br> *Defaults to `"same"`.* <br> **Datatype:** String (either `ask`, `bid`, `same` or `other`).
@@ -229,7 +229,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `exchange.enable_ws` | Enable the usage of Websockets for the exchange. <br>[More information](#consuming-exchange-websockets).<br>*Defaults to `true`.* <br> **Datatype:** Boolean
| `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded. <br>*Defaults to `60` minutes.* <br> **Datatype:** Positive Integer
| `exchange.skip_open_order_update` | Skips open order updates on startup should the exchange cause problems. Only relevant in live conditions.<br>*Defaults to `false`*<br> **Datatype:** Boolean
| `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".<br>*Defaults to `None`*<br> **Datatype:** float
| `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".<br>*Defaults to `None`<br> **Datatype:** float
| `exchange.log_responses` | Log relevant exchange responses. For debug mode only - use with care.<br>*Defaults to `false`*<br> **Datatype:** Boolean
| `exchange.only_from_ccxt` | Prevent data-download from data.binance.vision. Leaving this as false can greatly speed up downloads, but may be problematic if the site is not available.<br>*Defaults to `false`*<br> **Datatype:** Boolean
| `experimental.block_bad_exchanges` | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now. <br>*Defaults to `true`.* <br> **Datatype:** Boolean
@@ -240,7 +240,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `telegram.token` | Your Telegram bot token. Only required if `telegram.enabled` is `true`. <br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
| `telegram.chat_id` | Your personal Telegram account id. Only required if `telegram.enabled` is `true`. <br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
| `telegram.balance_dust_level` | Dust-level (in stake currency) - currencies with a balance below this will not be shown by `/balance`. <br> **Datatype:** float
| `telegram.reload` | Allow "reload" buttons on telegram messages. <br>*Defaults to `true`.*<br> **Datatype:** boolean
| `telegram.reload` | Allow "reload" buttons on telegram messages. <br>*Defaults to `true`.<br> **Datatype:** boolean
| `telegram.notification_settings.*` | Detailed notification settings. Refer to the [telegram documentation](telegram-usage.md) for details.<br> **Datatype:** dictionary
| `telegram.allow_custom_messages` | Enable the sending of Telegram messages from strategies via the dataprovider.send_msg() function. <br> **Datatype:** Boolean
| | **Webhook**
@@ -280,8 +280,8 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `add_config_files` | Additional config files. These files will be loaded and merged with the current config file. The files are resolved relative to the initial file.<br> *Defaults to `[]`*. <br> **Datatype:** List of strings
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `feather`*. <br> **Datatype:** String
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `feather`*. <br> **Datatype:** String
| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage (and decreasing train/inference timing backtesting/hyperopt and in FreqAI). <br> Default: `False`. <br> **Datatype:** Boolean.
| `log_config` | Dictionary containing the log config for python logging. [more info](advanced-setup.md#advanced-logging) <br> Default: `FtRichHandler` <br> **Datatype:** dict.
| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage (and decreasing train/inference timing backtesting/hyperopt and in FreqAI). <br> **Datatype:** Boolean. <br> Default: `False`.
| `log_config` | Dictionary containing the log config for python logging. [more info](advanced-setup.md#advanced-logging) <br> **Datatype:** dict. <br> Default: `FtRichHandler`
### Parameters in the strategy
-2
View File
@@ -269,8 +269,6 @@ If `--convert` is also provided, the resample step will happen automatically and
!!! Note "Kraken user"
Kraken users should read [this](exchanges.md#historic-kraken-data) before starting to download data.
Kraken Futures uses standard OHLCV downloads and does not require `--dl-trades`.
Example call:
```bash
+2 -2
View File
@@ -120,8 +120,8 @@ The script below should serve as an example - you may need to adjust the timefra
``` bash
# Cleanup no longer needed data
rm user_data/data/<exchange>/futures/*-mark*
rm user_data/data/<exchange>/futures/*-funding_rate*
rm user_data/data/<exchange>/futures/*-mark-*
rm user_data/data/<exchange>/futures/*-funding_rate-*
# download new data (only required once to fix the mark and funding fee data)
freqtrade download-data -t 1h --trading-mode futures --candle-types funding_rate mark [...] --timerange <full timerange you've got other data for>
+3 -2
View File
@@ -432,6 +432,7 @@ freqtrade download-data --timerange 20250625-20250801 --config tests/testdata/co
freqtrade backtesting --config tests/testdata/config.tests.usdt.json -s SampleStrategy --userdir user_data_bttest/ --cache none --timerange 20250701-20250801
```
## Continuous integration
This documents some decisions taken for the CI Pipeline.
@@ -463,10 +464,10 @@ git checkout -b new_release <commitid>
Determine if crucial bugfixes have been made between this commit and the current state, and eventually cherry-pick these.
* Merge the release branch (stable) into this branch.
* Edit `freqtrade/__init__.py` and add the version matching the current date (for example `2025.7` for July 2025). Minor versions can be `2025.7.1` should we need to do a second release that month. Version numbers must follow allowed versions from PEP0440 to avoid failures pushing to pypi.
* Edit `freqtrade/__init__.py` and add the version matching the current date (for example `2019.7` for July 2019). Minor versions can be `2019.7.1` should we need to do a second release that month. Version numbers must follow allowed versions from PEP0440 to avoid failures pushing to pypi.
* Commit this part.
* Push that branch to the remote and create a PR against the **stable branch**.
* Update develop version to next version following the pattern `2025.8-dev`.
* Update develop version to next version following the pattern `2019.8-dev`.
### Create changelog from git commits
+24 -115
View File
@@ -217,32 +217,6 @@ freqtrade download-data --exchange kraken --dl-trades -p BTC/EUR BCH/EUR
Please pay attention that rateLimit configuration entry holds delay in milliseconds between requests, NOT requests/sec rate.
So, in order to mitigate Kraken API "Rate limit exceeded" exception, this configuration should be increased, NOT decreased.
## Kraken Futures
Kraken Futures uses the exchange id `krakenfutures` and supports isolated futures mode.
```jsonc
"exchange": {
"name": "krakenfutures",
"key": "your_exchange_key",
"secret": "your_exchange_secret"
},
"trading_mode": "futures",
"margin_mode": "isolated",
"stake_currency": "USD"
```
!!! Tip "Stoploss on Exchange"
Kraken Futures supports `stoploss_on_exchange` with both `limit` and `market` stop orders.
Use `order_types.stoploss_price_type` to select the trigger price source (`mark`, `last`, or `index`).
!!! Note "Collateral"
Kraken Futures is USD-settled. Use USD as your stake currency.
!!! Note "Flex (Multi-collateral) Accounts"
Kraken Futures flex accounts allow collateral in multiple currencies, while trading remains USD-settled.
Freqtrade derives the `USD` balance from Kraken margin fields, so keep `stake_currency` set to `USD`.
## Kucoin
Kucoin requires a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows:
@@ -265,7 +239,7 @@ Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force)
### Kucoin Blacklists
For Kucoin, it is suggested to add `"KCS/<STAKE>"` to your blacklist to avoid issues, unless you are willing to maintain enough extra `KCS` on the account or unless you're willing to disable using `KCS` for fees.
For Kucoin, it is suggested to add `"KCS/<STAKE>"` to your blacklist to avoid issues, unless you are willing to maintain enough extra `KCS` on the account or unless you're willing to disable using `KCS` for fees.
Kucoin accounts may use `KCS` for fees, and if a trade happens to be on `KCS`, further trades may consume this position and make the initial `KCS` trade unsellable as the expected amount is not there anymore.
## HTX
@@ -345,14 +319,6 @@ API Keys for live futures trading must have the following permissions:
We do strongly recommend to limit all API keys to the IP you're going to use it from.
### Bybit Demo Mode
Bybit has a [demo mode](https://learn.bybit.com/en/bybit-guide/how-to-use-bybit-demo-trading) - which can be activated by setting `exchange.demo_trading` to `true` in the configuration.
Bybit uses live markets to simulate your trades (without market impact) - making it work very similar to freqtrade's dry-run mode.
You'll need to use separate API keys for demo trading, which you can create on bybit's demo page.
Demo mode is incompatible with dry-run.
## Bitmart
@@ -403,11 +369,6 @@ On startup, freqtrade will set the position mode to "One-way Mode" for the whole
!!! Tip "Stoploss on Exchange"
Hyperliquid supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it.
!!! Warning "Unified accounts"
Hyperliquid unified accounts are supported - though this relies freqtrade's assumption of "owning" the account, and being the only one trading on it (in this case, extended to both spot and futures).
We hence recommend the usage of subaccounts where possible, and to avoid manual trading on the same account while the bot is running.
Freqtrade will attempt to detect the account type on startup - changing the account type mid-trading is not supported and may lead to exceptions and errors.
Hyperliquid is a Decentralized Exchange (DEX). Decentralized exchanges work a bit different compared to normal exchanges. Instead of authenticating private API calls using an API key, private API calls need to be signed with the private key of your wallet (We recommend using an api Wallet for this, generated either on Hyperliquid or in your wallet of choice).
This needs to be configured like this:
@@ -438,87 +399,35 @@ Hyperliquid handles deposits and withdrawals on the Arbitrum One chain, a Layer
* Create a different software wallet, only transfer the funds you want to trade with to that wallet, and use that wallet to trade on Hyperliquid.
* If you have funds you don't want to use for trading (after making a profit for example), transfer them back to your hardware wallet.
### Hyperliquid Vault / Subaccount
!!! Warning "Vaults and Subaccounts"
Hyperliquid allows you to create either a vault or a subaccount.
To use these with Freqtrade, you will need to use the following configuration pattern:
``` json
"exchange": {
"name": "hyperliquid",
"walletAddress": "your_master_wallet_address", // Your master wallet address (not the API wallet address and not the vault/subaccount address).
"privateKey": "your_api_private_key", // API wallet private key (see https://app.hyperliquid.xyz/API). You'll only need the private key.
"ccxt_config": {
"options": {
"vaultAddress": "your_vault_address", // Optional, only if you want to use a vault ...
"subAccountAddress": "your_subaccount_address" // OR optional, only if you want to use a subaccount
}
},
// ...
}
```
Your balance and trades will now be used from your vault / subaccount - and no longer from your main account.
!!! Note
You can only use either a vault or a subaccount - not both at the same time.
### Hyperliquid Subaccount
Hyperliquid allows you to create subaccounts with sufficient previous trading volume.
To use subaccounts with Freqtrade, you will need to use the following configuration pattern:
``` json
"exchange": {
"name": "hyperliquid",
"walletAddress": "your_master_wallet_address", // Your master wallet address (not the API wallet or vault address - but not subaccount address).
"privateKey": "your_api_private_key", // API wallet private key (see https://app.hyperliquid.xyz/API). You'll only need the private key.
"ccxt_config": {
"options": {
"subAccountAddress": "your_subaccount_address" // Required if you want to use a subaccount.
}
},
// ...
}
```
Your balance and trades will now be used from your subaccount - and no longer from your main account.
### Hyperliquid Vault
Hyperliquid allows you to create vaults. To use vaults with Freqtrade, you will need to use the following configuration pattern:
``` json
"exchange": {
"name": "hyperliquid",
"walletAddress": "your_vault_address", // Your vault wallet address (Must also be added below in the ccxt_config.options.vaultAddress field)
"privateKey": "your_api_private_key", // API wallet private key (see https://app.hyperliquid.xyz/API). You'll only need the private key.
"ccxt_config": {
"options": {
"vaultAddress": "your_vault_address", // Optional, only if you want to use a vault ... (vault address must also be added to walletAdress)
}
},
// ...
}
```
Your balance and trades will now be used from your vault - and no longer from your main account.
### Historic Hyperliquid data
The Hyperliquid API does not provide historic data beyond the single call to fetch current data, so downloading data is not possible, as the downloaded data would not constitute proper historic data.
### HIP-3 DEXes
Hyperliquid supports HIP-3 decentralized exchanges (DEXes), which are independent exchanges built on top of the Hyperliquid infrastructure.
These DEXes operate similarly to the main Hyperliquid exchange but are community-created and managed.
To trade on HIP-3 DEXes with Freqtrade, you need to add them to your configuration using the `hip3_dexes` parameter:
```json
"exchange": {
"name": "hyperliquid",
"walletAddress": "your_master_wallet_address",
"privateKey": "your_api_private_key",
"hip3_dexes": ["dex_name_1", "dex_name_2"]
}
```
Replace `"dex_name_1"` and `"dex_name_2"` with the actual names of the HIP-3 DEXes you want to trade on (e.g. `vntl` and `xyz`).
!!! Warning "Performance and Rate Limit Impact"
Each HIP-3 DEX you add significantly impacts bot performance and rate limits.
* **Additional API Calls**: For each HIP-3 DEX configured, Freqtrade needs to make additional API calls.
* **Rate Limit Pressure**: Additional API calls contribute to Hyperliquid's strict rate limits. With multiple DEXes, you may hit rate limits faster, or rather, slow down bot operations due to enforced delays.
Please only add HIP-3 DEXes that you actively trade on. Monitor your logs for rate limit warnings or signs of slowed operations, and adjust your configuration accordingly.
Different HIP-3 DEXes may also use different quote currencies - so make sure to only add DEXes that are compatible with your stake currency to avoid unnecessary delays.
!!! Note
HIP-3 DEXes share the same wallet and free amount of collateral as your main Hyperliquid account. Trades on different DEXes will affect your overall account balance and margin.
The pair name for HIP-3 pairs will be slightly different than non HIP-3 pairs. Please use `list-pairs` subcommand to get the correct pair naming for all pairs for the specified dexes.
## Bitvavo
If your account is required to use an operatorId, you can set it in the configuration file as follows:
@@ -582,5 +491,5 @@ For example, to test the order type `FOK` with Kraken, and modify candle limit t
!!! Warning
Please make sure to fully understand the impacts of these settings before modifying them.
Using `_ft_has_params` overrides may lead to unexpected behavior, and may even break your bot.
Using `_ft_has_params` overrides may lead to unexpected behavior, and may even break your bot.
We will not be able to provide support for issues caused by custom settings in `_ft_has_params`.
+2 -9
View File
@@ -2,7 +2,7 @@
## Supported Markets
Freqtrade supports spot trading, as well as futures trading for some selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges) for an up-to-date list of supported exchanges.
Freqtrade supports spot trading, as well as (isolated) futures trading for some selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges-experimental) for an up-to-date list of supported exchanges.
### Can my bot open short positions?
@@ -14,7 +14,7 @@ In spot markets, you can in some cases use leveraged spot tokens, which reflect
### Can my bot trade options or futures?
Futures trading is supported for selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges) for an up-to-date list of supported exchanges.
Futures trading is supported for selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges-experimental) for an up-to-date list of supported exchanges.
## Beginner Tips & Tricks
@@ -29,13 +29,6 @@ You can however use the [`adjust_trade_position()` callback](strategy-callbacks.
Backtesting provides an option for this in `--eps` - however this is only there to highlight "hidden" signals, and will not work in live.
### Does freqtrade support sandbox accounts?
No, but you can use dry-run mode to simulate trading without risking real funds.
Sandbox markets are separate, simulated markets - which are not suitable to test your strategy in a realistic environment.
These markets usually have different order books, liquidity and trading behaviour (usually with very few participants) - which makes them unsuitable for realistic tests of your strategy.
### The bot does not start
Running the bot with `freqtrade trade --config config.json` shows the output `freqtrade: command not found`.
-17
View File
@@ -46,23 +46,6 @@ On this page, you can also interact with the bot by starting and stopping it and
![FreqUI - trade view](assets/freqUI-trade-pane-dark.png#only-dark)
![FreqUI - trade view](assets/freqUI-trade-pane-light.png#only-light)
### Dashboard
The dashboard view provides an overview of the bot's performance and status.
If multiple bots are connected, the dashboard will show an overview of all connected bots, allowing you to easily switch between them or show just a subset of available bots.
#### Wallet Balance
New in freqtrade 2026.4: This shows the balance of the bot over time.
Compared to the "cumulative Profit" chart, this chart will show the actual balance of the bot over time, including unrealized profit and losses, as well as deposits and withdrawals.
Historic data has re-populated based on available exchange data - however is assumed to be best-effort and may not be 100% accurate.
More specifically, it won't cover deposits and withdrawals, and will assume a starting balance of current balance - profit/losses.
For clarity - a "Capture start" marker line is shown on the chart, which indicates the point at which the migration to the new wallet balance tracking system happened.
Only beyond this point, the wallet balance is expected to be accurate.
### Plot Configurator
FreqUI Plots can be configured either via a `plot_config` configuration object in the strategy (which can be loaded via "from strategy" button) or via the UI.
+1 -5
View File
@@ -166,7 +166,7 @@ Below are the values you can expect to include/use inside a typical strategy dat
| `df['do_predict']` | Indication of an outlier data point. The return value is integer between -2 and 2, which lets you know if the prediction is trustworthy or not. `do_predict==1` means that the prediction is trustworthy. If the Dissimilarity Index (DI, see details [here](freqai-feature-engineering.md#identifying-outliers-with-the-dissimilarity-index-di)) of the input data point is above the threshold defined in the config, FreqAI will subtract 1 from `do_predict`, resulting in `do_predict==0`. If `use_SVM_to_remove_outliers` is active, the Support Vector Machine (SVM, see details [here](freqai-feature-engineering.md#identifying-outliers-using-a-support-vector-machine-svm)) may also detect outliers in training and prediction data. In this case, the SVM will also subtract 1 from `do_predict`. If the input data point was considered an outlier by the SVM but not by the DI, or vice versa, the result will be `do_predict==0`. If both the DI and the SVM considers the input data point to be an outlier, the result will be `do_predict==-1`. As with the SVM, if `use_DBSCAN_to_remove_outliers` is active, DBSCAN (see details [here](freqai-feature-engineering.md#identifying-outliers-with-dbscan)) may also detect outliers and subtract 1 from `do_predict`. Hence, if both the SVM and DBSCAN are active and identify a datapoint that was above the DI threshold as an outlier, the result will be `do_predict==-2`. A particular case is when `do_predict == 2`, which means that the model has expired due to exceeding `expired_hours`. <br> **Datatype:** Integer between -2 and 2.
| `df['DI_values']` | Dissimilarity Index (DI) values are proxies for the level of confidence FreqAI has in the prediction. A lower DI means the prediction is close to the training data, i.e., higher prediction confidence. See details about the DI [here](freqai-feature-engineering.md#identifying-outliers-with-the-dissimilarity-index-di). <br> **Datatype:** Float.
| `df['%*']` | Any dataframe column prepended with `%` in `feature_engineering_*()` is treated as a training feature. For example, you can include the RSI in the training feature set (similar to in `templates/FreqaiExampleStrategy.py`) by setting `df['%-rsi']`. See more details on how this is done [here](freqai-feature-engineering.md). <br> **Note:** Since the number of features prepended with `%` can multiply very quickly (10s of thousands of features are easily engineered using the multiplictative functionality of, e.g., `include_shifted_candles` and `include_timeframes` as described in the [parameter table](freqai-parameter-table.md)), these features are removed from the dataframe that is returned from FreqAI to the strategy. To keep a particular type of feature for plotting purposes, you would prepend it with `%%` (see details below). <br> **Datatype:** Depends on the feature created by the user.
| `df['%%*']` | Any dataframe column prepended with `%%` in `feature_engineering_*()` is treated as a training feature, just the same as the above `%` prepend. However, in this case, the features are returned back to the strategy for FreqUI/plot-dataframe plotting and monitoring in Dry/Live/Backtesting <br> **Datatype:** Depends on the feature created by the user. <br>*Please note* that features created in `feature_engineering_expand()` will have automatic FreqAI naming schemas depending on the expansions that you configured (i.e. `include_timeframes`, `include_corr_pairlist`, `indicators_periods_candles`, `include_shifted_candles`). So if you want to plot `%%-rsi` from `feature_engineering_expand_all()`, the final naming scheme for your plotting config would be: `%%-rsi-period_10_ETH/USDT:USDT_1h` for the `rsi` feature with `period=10`, `timeframe=1h`, and `pair=ETH/USDT:USDT` (the `:USDT` is added if you are using futures pairs). It is useful to simply add `print(dataframe.columns)` in your `populate_indicators()` after `self.freqai.start()` to see the full list of available features that are returned to the strategy for plotting purposes.
| `df['%%*']` | Any dataframe column prepended with `%%` in `feature_engineering_*()` is treated as a training feature, just the same as the above `%` prepend. However, in this case, the features are returned back to the strategy for FreqUI/plot-dataframe plotting and monitoring in Dry/Live/Backtesting <br> **Datatype:** Depends on the feature created by the user. Please note that features created in `feature_engineering_expand()` will have automatic FreqAI naming schemas depending on the expansions that you configured (i.e. `include_timeframes`, `include_corr_pairlist`, `indicators_periods_candles`, `include_shifted_candles`). So if you want to plot `%%-rsi` from `feature_engineering_expand_all()`, the final naming scheme for your plotting config would be: `%%-rsi-period_10_ETH/USDT:USDT_1h` for the `rsi` feature with `period=10`, `timeframe=1h`, and `pair=ETH/USDT:USDT` (the `:USDT` is added if you are using futures pairs). It is useful to simply add `print(dataframe.columns)` in your `populate_indicators()` after `self.freqai.start()` to see the full list of available features that are returned to the strategy for plotting purposes.
## Setting the `startup_candle_count`
@@ -260,10 +260,6 @@ freqtrade trade --config config_examples/config_freqai.example.json --strategy F
PyTorch dropped support for macOS x64 (intel based Apple devices) in version 2.3. Subsequently, freqtrade also dropped support for PyTorch on this platform.
!!! Danger "Security notice"
Loading saved models from disk can cause security issues if using remote model files (files you downloaded from the internet or received from an untrusted source) due to having the necessity to have `weights_only=False`, which can cause security problems.
As long as you only load models that you have trained yourself, there is no risk.
### Structure
#### Model
-1
View File
@@ -106,7 +106,6 @@ Mandatory parameters are marked as **Required** and have to be set in one of the
| `n_epochs` | The `n_epochs` parameter is a crucial setting in the PyTorch training loop that determines the number of times the entire training dataset will be used to update the model's parameters. An epoch represents one full pass through the entire training dataset. Overrides `n_steps`. Either `n_epochs` or `n_steps` must be set. <br><br> **Datatype:** int. optional. <br> Default: `10`.
| `n_steps` | An alternative way of setting `n_epochs` - the number of training iterations to run. Iteration here refer to the number of times we call `optimizer.step()`. Ignored if `n_epochs` is set. A simplified version of the function: <br><br> n_epochs = n_steps / (n_obs / batch_size) <br><br> The motivation here is that `n_steps` is easier to optimize and keep stable across different n_obs - the number of data points. <br> <br> **Datatype:** int. optional. <br> Default: `None`.
| `batch_size` | The size of the batches to use during training. <br><br> **Datatype:** int. <br> Default: `64`.
| `early_stopping_patience` | Number of epochs with no improvement in validation loss before training is stopped early. This helps prevent overfitting by halting training when the model stops improving. Set to `0` to disable early stopping. Requires a test/validation split (`test_size > 0`). <br><br> **Datatype:** int. <br> Default: `0` (disabled).
### Additional parameters
+1 -1
View File
@@ -45,7 +45,7 @@ where `ReinforcementLearner` will use the templated `ReinforcementLearner` from
More details about feature engineering available:
https://www.freqtrade.io/en/stable/freqai-feature-engineering
https://www.freqtrade.io/en/latest/freqai-feature-engineering
:param df: strategy dataframe which will receive the targets
usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
-4
View File
@@ -87,10 +87,6 @@ To save the models generated during a particular backtest so that you can start
To ensure that the model can be reused, freqAI will call your strategy with a dataframe of length 1.
If your strategy requires more data than this to generate the same features, you can't reuse backtest predictions for live deployment and need to update your `identifier` for each new backtest.
!!! Danger "Security notice"
Loading saved models from disk can cause security issues if using remote model files (files you downloaded from the internet or received from an untrusted source) due to having the necessity to have `weights_only=False`, which can cause security problems.
As long as you only load models that you have trained yourself, there is no risk.
### Backtest live collected predictions
FreqAI allow you to reuse live historic predictions through the backtest parameter `--freqai-backtest-live-models`. This can be useful when you want to reuse predictions generated in dry/run for comparison or other study.
+1 -1
View File
@@ -7,7 +7,7 @@
FreqAI is a software designed to automate a variety of tasks associated with training a predictive machine learning model to generate market forecasts given a set of input signals. In general, FreqAI aims to be a sandbox for easily deploying robust machine learning libraries on real-time data ([details](#freqai-position-in-open-source-machine-learning-landscape)).
!!! Note
FreqAI is, and always will be, a not-for-profit, open source project. FreqAI does *not* have a crypto token, FreqAI does *not* sell signals, and FreqAI does not have a domain besides the present [freqtrade documentation](https://www.freqtrade.io/en/stable/freqai/).
FreqAI is, and always will be, a not-for-profit, open source project. FreqAI does *not* have a crypto token, FreqAI does *not* sell signals, and FreqAI does not have a domain besides the present [freqtrade documentation](https://www.freqtrade.io/en/latest/freqai/).
Features include:
+2 -2
View File
@@ -15,7 +15,7 @@ Assuming your application is deployed as `https://frequi.freqtrade.io/home/` - t
```jsonc
{
//...
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": ["https://frequi.freqtrade.io"],
//...
}
@@ -29,7 +29,7 @@ The correct configuration for this case is `http://localhost:8080` - the main pa
```jsonc
{
//...
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": ["http://localhost:8080"],
//...
}
-1
View File
@@ -15,7 +15,6 @@
| [Hyperliquid](exchanges.md#hyperliquid) | spot | | ❌ (not supported) |
| [Hyperliquid](exchanges.md#hyperliquid) | futures | isolated, cross | limit |
| [Kraken](exchanges.md#kraken) | spot | | market, limit |
| [Kraken](exchanges.md#kraken-futures) | futures | isolated | market, limit |
| [OKX](exchanges.md#okx) | spot | | limit |
| [OKX](exchanges.md#okx) | futures | isolated | limit |
| [Bitvavo](exchanges.md#bitvavo) | spot | | ❌ (not supported) |
+2 -11
View File
@@ -2,11 +2,11 @@
Pairlist Handlers define the list of pairs (pairlist) that the bot should trade. They are configured in the `pairlists` section of the configuration settings.
In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list), [`CrossMarketPairList`](#crossmarketpairlist), [`MarketCapPairlist`](#marketcappairlist) and [`PercentChangePairList`](#percent-change-pair-list) Pairlist Handlers).
In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) and [`PercentChangePairList`](#percent-change-pair-list) Pairlist Handlers).
Additionally, [`AgeFilter`](#agefilter), [`DelistFilter`](#delistfilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist.
If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList`, `MarketCapPairList`, `PercentChangePairList` or `CrossMarketPairList` as the starting Pairlist Handler.
If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList`, `MarketCapPairList` or `PercentChangePairList` as the starting Pairlist Handler.
Inactive markets are always removed from the resulting pairlist. Explicitly blacklisted pairs (those in the `pair_blacklist` configuration setting) are also always removed from the resulting pairlist.
@@ -26,7 +26,6 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged
* [`ProducerPairList`](#producerpairlist)
* [`RemotePairList`](#remotepairlist)
* [`MarketCapPairList`](#marketcappairlist)
* [`CrossMarketPairList`](#crossmarketpairlist)
* [`AgeFilter`](#agefilter)
* [`DelistFilter`](#delistfilter)
* [`FullTradesFilter`](#fulltradesfilter)
@@ -304,8 +303,6 @@ The optional `mode` option specifies if the pairlist should be used as a `blackl
The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append". The default value is "filter".
The optional `number_assets` option in the RemotePairList configuration determines how many pairs will be returned if used in whitelist `mode`. By default, all pairs will be returned. In blacklist `mode`, this option will be ignored.
In "filter" mode, the retrieved pairlist is used as a filter. Only the pairs present in both the original pairlist and the retrieved pairlist are included in the final pairlist. Other pairs are filtered out.
In "append" mode, the retrieved pairlist is added to the original pairlist. All pairs from both lists are included in the final pairlist without any filtering.
@@ -405,12 +402,6 @@ Coins like 1000PEPE/USDT or KPEPE/USDT:USDT are detected on a best effort basis,
!!! Danger "Duplicate symbols in coingecko"
Coingecko often has duplicate symbols, where the same symbol is used for different coins. Freqtrade will use the symbol as is and try to search for it on the exchange. If the symbol exists - it will be used. Freqtrade will however not check if the _intended_ symbol is the one coingecko meant. This can sometimes lead to unexpected results, especially on low volume coins or with meme coin categories.
#### CrossMarketPairList
Generate or filter pairs based of their availability on the opposite market.
The `pairs_exist_on` setting defines whether the pairs should exists on both spot and futures market (`both_markets`) or only exist on the specified trading mode (`current_market_only`). By default, the plugin will be in `both_markets` setting, which means whitelisted pairs have to exists on both spot and futures markets.
#### AgeFilter
Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity).
+10 -22
View File
@@ -20,15 +20,15 @@ All protection end times are rounded up to the next candle to avoid sudden, unex
### Common settings to all Protections
| Parameter | Description |
| --------- | ---------- |
| `method` | Protection name to use. <br> **Datatype:** String, selected from [available Protections](#available-protections) |
| `stop_duration_candles` | For how many candles should the lock be set? <br> **Datatype:** Positive integer (in candles) |
| `stop_duration` | how many minutes should protections be locked. <br>Cannot be used together with `stop_duration_candles`. <br> **Datatype:** Float (in minutes) |
| `lookback_period_candles` | Only trades that completed within the last `lookback_period_candles` candles will be considered. This setting may be ignored by some Protections. <br> **Datatype:** Positive integer (in candles). |
| `lookback_period` | Only trades that completed after `current_time - lookback_period` will be considered. <br>Cannot be used together with `lookback_period_candles`. <br>This setting may be ignored by some Protections. <br> **Datatype:** Float (in minutes) |
| `trade_limit` | Number of trades required at minimum (not used by all Protections). <br> **Datatype:** Positive integer |
| `unlock_at` | Time when trading will be unlocked regularly (not used by all Protections). <br> **Datatype:** string <br>**Input Format:** "HH:MM" (24-hours) |
| Parameter| Description |
|------------|-------------|
| `method` | Protection name to use. <br> **Datatype:** String, selected from [available Protections](#available-protections)
| `stop_duration_candles` | For how many candles should the lock be set? <br> **Datatype:** Positive integer (in candles)
| `stop_duration` | how many minutes should protections be locked. <br>Cannot be used together with `stop_duration_candles`. <br> **Datatype:** Float (in minutes)
| `lookback_period_candles` | Only trades that completed within the last `lookback_period_candles` candles will be considered. This setting may be ignored by some Protections. <br> **Datatype:** Positive integer (in candles).
| `lookback_period` | Only trades that completed after `current_time - lookback_period` will be considered. <br>Cannot be used together with `lookback_period_candles`. <br>This setting may be ignored by some Protections. <br> **Datatype:** Float (in minutes)
| `trade_limit` | Number of trades required at minimum (not used by all Protections). <br> **Datatype:** Positive integer
| `unlock_at` | Time when trading will be unlocked regularly (not used by all Protections). <br> **Datatype:** string <br>**Input Format:** "HH:MM" (24-hours)
!!! Note "Durations"
Durations (`stop_duration*` and `lookback_period*` can be defined in either minutes or candles).
@@ -69,17 +69,7 @@ def protections(self):
#### MaxDrawdown
The `MaxDrawdown` protection evaluates trades that closed within the current `lookback_period` (or `lookback_period_candles`).
It supports 2 calculation modes:
- `calculation_mode: "ratios"` (default): Legacy approximation based on cumulative profit ratios.
- `calculation_mode: "equity"`: Standard peak-to-trough drawdown on the account equity curve, using starting balance and cumulative absolute profit.
With `calculation_mode: "ratios"`, drawdown is derived from cumulative trade profit ratios, not from the account equity curve. This is kept for backward compatibility and can differ from account-level drawdown when position sizing changes over time.
For new setups, `calculation_mode: "equity"` is recommended. Prefer `calculation_mode: "ratios"` only when you intentionally rely on legacy behavior, especially with fixed stake amount configurations where ratio-based behavior is easier to reason about.
If the observed drawdown exceeds `max_allowed_drawdown`, trading will stop for `stop_duration` after the last trade - assuming that the bot needs some time to let markets recover.
`MaxDrawdown` uses all trades within `lookback_period` in minutes (or in candles when using `lookback_period_candles`) to determine the maximum drawdown. If the drawdown is below `max_allowed_drawdown`, trading will stop for `stop_duration` in minutes (or in candles when using `stop_duration_candles`) after the last trade - assuming that the bot needs some time to let markets recover.
The below sample stops trading for 12 candles if max-drawdown is > 20% considering all pairs - with a minimum of `trade_limit` trades - within the last 48 candles. If desired, `lookback_period` and/or `stop_duration` can be used.
@@ -89,7 +79,6 @@ def protections(self):
return [
{
"method": "MaxDrawdown",
"calculation_mode": "equity",
"lookback_period_candles": 48,
"trade_limit": 20,
"stop_duration_candles": 12,
@@ -171,7 +160,6 @@ class AwesomeStrategy(IStrategy)
},
{
"method": "MaxDrawdown",
"calculation_mode": "equity",
"lookback_period_candles": 48,
"trade_limit": 20,
"stop_duration_candles": 4,
+7 -7
View File
@@ -1,28 +1,28 @@
# Highlighted changes
## Highlighted changes
- ...
## How to update
### How to update
As always, you can update your bot using one of the following commands:
### docker-compose
#### docker-compose
```bash
docker-compose pull
docker-compose up -d
```
### Installation via setup script
#### Installation via setup script
``` bash
```
# Deactivate venv and run
./setup.sh --update
```
### Plain native installation
#### Plain native installation
``` bash
```
git pull
pip install -U -r requirements.txt
```
+2 -7
View File
@@ -2,9 +2,7 @@
[![Freqtrade CI](https://github.com/freqtrade/freqtrade/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/freqtrade/freqtrade/actions/workflows/ci.yml)
[![DOI](https://joss.theoj.org/papers/10.21105/joss.04864/status.svg)](https://doi.org/10.21105/joss.04864)
[![codecov](https://codecov.io/gh/freqtrade/freqtrade/branch/develop/graph/badge.svg?token=AD5BG3ATKI)](https://codecov.io/gh/freqtrade/freqtrade)
[![Documentation](https://readthedocs.org/projects/freqtrade/badge/)](https://www.freqtrade.io)
[![Discord Server](https://img.shields.io/badge/Freqtrade_Discord-4E4E4E?logo=discord)](https://discord.gg/p7nuUNVfP7)
[![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
<!-- GitHub action buttons -->
[:octicons-star-16: Star](https://github.com/freqtrade/freqtrade){ .md-button .md-button--sm }
@@ -39,8 +37,6 @@ Freqtrade is a free and open source crypto trading bot written in Python. It is
Please read the [exchange specific notes](exchanges.md) to learn about eventual, special configurations needed for each exchange.
### Supported Spot Exchanges
- [X] [Binance](https://www.binance.com/)
- [X] [BingX](https://bingx.com/invite/0EM9RX)
- [X] [Bitget](https://www.bitget.com/)
@@ -54,7 +50,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual,
- [X] [MyOKX](https://okx.com/) (OKX EEA)
- [ ] [potentially many others through <img alt="ccxt" width="30px" src="assets/ccxt-logo.svg" />](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_
### Supported Futures Exchanges
### Supported Futures Exchanges (experimental)
- [X] [Binance](https://www.binance.com/)
- [X] [Bitget](https://www.bitget.com/)
@@ -62,7 +58,6 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual,
- [X] [Gate.io](https://www.gate.io/ref/6266643)
- [X] [Hyperliquid](https://hyperliquid.xyz/) (A decentralized exchange, or DEX)
- [X] [OKX](https://okx.com/)
- [X] [Kraken](https://www.kraken.com/features/futures)
Please make sure to read the [exchange specific notes](exchanges.md), as well as the [trading with leverage](leverage.md) documentation before diving in.
+18 -57
View File
@@ -9,25 +9,15 @@ The freqtrade documentation describes various ways to install freqtrade
* [Manual Installation](#manual-installation)
* [Installation with Conda](#installation-with-conda)
Please consider using the prebuilt [docker images](docker_quickstart.md) to get started quickly.
!!! Note "Updating"
Keeping freqtrade updated is important to [ensure ongoing compatibility](updating.md#why-update) with exchange API's.
Please refer to the [updating guide](updating.md) for details on how to update your installation.
!!! Note "Windows users"
We **strongly** recommend that Windows users use [Docker](docker_quickstart.md) as this will work much easier and smoother (also more secure).
If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu/Linux instructions will work.
If you really want to install freqtrade natively on Windows, best use the [`./setup.ps1` installation script](#use-setupps1-windows).
Please also make sure to use the 64bit version of Python, as 32bit versions have severe memory limitations, which can negatively impact your experience with backtesting/hyperopt.
Please consider using the prebuilt [docker images](docker_quickstart.md) to get started quickly while evaluating how freqtrade works.
------
## Information
The easiest way to install and run Freqtrade is to clone the bot Github repository and then run the `./setup.sh` (`./setup.ps1` for Windows) script, if it's available for your platform.
For Windows installation, please use the [windows installation guide](windows_installation.md).
The easiest way to install and run Freqtrade is to clone the bot Github repository and then run the `./setup.sh` script, if it's available for your platform.
!!! Note "Version considerations"
When cloning the repository the default working branch has the name `develop`. This branch contains all last features (can be considered as relatively stable, thanks to automated tests).
@@ -162,9 +152,20 @@ If you are on Debian, Ubuntu or MacOS, freqtrade provides the script to install
./setup.sh -i
```
#### Other options of /setup.sh script
### Activate your virtual environment
You can also update, configure and reset the codebase of your bot with `./setup.sh`
Each time you open a new terminal, you must run `source .venv/bin/activate` to activate your virtual environment.
```bash
# activate virtual environment
source ./.venv/bin/activate
```
[You are now ready](#you-are-ready) to run the bot.
### Other options of /setup.sh script
You can as well update, configure and reset the codebase of your bot with `./script.sh`
```bash
# --update, Command git pull to update.
@@ -193,34 +194,6 @@ This option will pull the last version of your current branch and update your vi
This option will hard reset your branch (only if you are on either `stable` or `develop`) and recreate your virtualenv.
```
#### Activate your virtual environment
Each time you open a new terminal, you must run `source .venv/bin/activate` to activate your virtual environment.
```bash
# activate virtual environment
source ./.venv/bin/activate
```
### Use ./setup.ps1 (Windows)
The script will ask you a few questions to determine which parts should be installed.
```powershell
Set-ExecutionPolicy -ExecutionPolicy Bypass
cd freqtrade
. .\setup.ps1
```
#### Activate your virtual environment (Windows)
```powershell
# activate virtual environment
. .\.venv\Scripts\Activate.ps1
```
[You are now ready](#you-are-ready) to run the bot.
-----
## Manual Installation
@@ -364,7 +337,7 @@ conda deactivate
Happy trading!
------
-----
## You are ready
@@ -421,15 +394,3 @@ open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10
```
If this file is inexistent, then you're probably on a different version of MacOS, so you may need to consult the internet for specific resolution details.
### Windows Installation error
```bash
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
```
Unfortunately, many packages requiring compilation don't provide a pre-built wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use.
You can download the Visual C++ build tools from [the Visual Studio website](https://visualstudio.microsoft.com/visual-cpp-build-tools/) and install "Desktop development with C++" in it's default configuration. Unfortunately, this is a heavy download / dependency so you might want to consider WSL2 or [docker compose](docker_quickstart.md) first.
![Windows installation](assets/windows_install.png)
+4 -1
View File
@@ -1,5 +1,8 @@
# Trading with Leverage
!!! Warning "Beta feature"
This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord or via Github Issue.
!!! Note "Multiple bots on one account"
You can't run 2 bots on the same account with leverage. For leveraged / margin trading, freqtrade assumes it's the only user of the account, and all liquidation levels are calculated based on this assumption.
@@ -52,7 +55,7 @@ Perpetual swaps (also known as Perpetual Futures) are contracts traded at a pric
In addition to the gains/losses from the change in price of the futures contract, traders also exchange _funding fees_, which are gains/losses worth an amount that is derived from the difference in price between the futures contract and the underlying asset. The difference in price between a futures contract and the underlying asset varies between exchanges.
To trade in futures markets, you'll have to set `trading_mode` to "futures".
You will also have to pick a "margin mode" (explanation below).
You will also have to pick a "margin mode" (explanation below) - with freqtrade currently only supporting isolated margin.
``` json
"trading_mode": "futures",
+4 -4
View File
@@ -111,10 +111,10 @@ It also allows multiple subplots to display both MACD and RSI at the same time.
Plot type can be configured using `type` key. Possible types are:
* `scatter` corresponding a scatter plot.
* `bar` corresponding to a bar plot.
* `scatter` corresponding to `plotly.graph_objects.Scatter` class (default).
* `bar` corresponding to `plotly.graph_objects.Bar` class.
Extra parameters to `plotly.graph_objects.*` constructor can be specified in `plotly` dict - these are only supported when using plotly as plotting library and will be ignored when using freq-ui.
Extra parameters to `plotly.graph_objects.*` constructor can be specified in `plotly` dict.
Sample configuration with inline comments explaining the process:
@@ -163,7 +163,7 @@ def plot_config(self):
```
??? Note "As attribute (former method)"
Assigning `plot_config` is also possible as Attribute (this used to be the default way).
Assigning plot_config is also possible as Attribute (this used to be the default way).
This has the disadvantage that strategy parameters are not available, preventing certain configurations from working.
``` python
+4 -4
View File
@@ -1,7 +1,7 @@
markdown==3.10.2
markdown==3.10
mkdocs==1.6.1
mkdocs-material==9.7.6
mkdocs-material==9.7.0
mdx_truly_sane_lists==1.3
pymdown-extensions==10.21.3
pymdown-extensions==10.19.1
jinja2==3.1.6
mike==2.2.0
mike==2.1.3
+12 -12
View File
@@ -17,7 +17,7 @@ Sample configuration:
"listen_port": 8080,
"verbosity": "error",
"enable_openapi": false,
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "Freqtrader",
"password": "SuperSecret1!",
@@ -56,7 +56,7 @@ secrets.token_hex()
!!! Danger "Password selection"
Please make sure to select a very strong, unique password to protect your bot from unauthorized access.
Also change `jwt_secret_key` to something random (no need to remember this, but it'll be used to encrypt your session, so it better be something unique!). This value should also be 32 characters or longer to be safe.
Also change `jwt_secret_key` to something random (no need to remember this, but it'll be used to encrypt your session, so it better be something unique!).
### Configuration with docker
@@ -202,20 +202,20 @@ All endpoints in the below table need to be prefixed with the base URL of the AP
| `/blacklist` | GET | Show the current blacklist.
| `/blacklist` | POST | Adds the specified pair to the blacklist.<br/>*Params:*<br/>- `blacklist` (`str`)
| `/blacklist` | DELETE | Deletes the specified list of pairs from the blacklist.<br/>*Params:*<br/>- `[pair,pair]` (`list[str]`)
| `/pair_candles` | GET | Returns dataframe for a pair / timeframe combination while the bot is running.
| `/pair_candles` | POST | Returns dataframe for a pair / timeframe combination while the bot is running, filtered by a provided list of columns to return.<br/>*Params:*<br/>- `<column_list>` (`list[str]`)
| `/pair_history` | GET | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy.
| `/pair_history` | POST | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy, filtered by a provided list of columns to return.<br/>*Params:*<br/>- `<column_list>` (`list[str]`)
| `/plot_config` | GET | Get plot config from the strategy (or nothing if not configured).
| `/strategies` | GET | List strategies in strategy directory.
| `/strategy/<strategy>` | GET | Get specific Strategy content by strategy class name.<br/>*Params:*<br/>- `<strategy>` (`str`)
| `/available_pairs` | GET | List available backtest data.
| `/pair_candles` | GET | Returns dataframe for a pair / timeframe combination while the bot is running. **Alpha**
| `/pair_candles` | POST | Returns dataframe for a pair / timeframe combination while the bot is running, filtered by a provided list of columns to return. **Alpha**<br/>*Params:*<br/>- `<column_list>` (`list[str]`)
| `/pair_history` | GET | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy. **Alpha**
| `/pair_history` | POST | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy, filtered by a provided list of columns to return. **Alpha**<br/>*Params:*<br/>- `<column_list>` (`list[str]`)
| `/plot_config` | GET | Get plot config from the strategy (or nothing if not configured). **Alpha**
| `/strategies` | GET | List strategies in strategy directory. **Alpha**
| `/strategy/<strategy>` | GET | Get specific Strategy content by strategy class name. **Alpha**<br/>*Params:*<br/>- `<strategy>` (`str`)
| `/available_pairs` | GET | List available backtest data. **Alpha**
| `/version` | GET | Show version.
| `/sysinfo` | GET | Show information about the system load.
| `/health` | GET | Show bot health (last bot loop).
!!! Warning "Alpha status"
Endpoints labeled with *Alpha status* or *Beta status* above may change at any time without notice.
Endpoints labeled with *Alpha status* above may change at any time without notice.
### Message WebSocket
@@ -245,7 +245,7 @@ You would then add that token under `ws_token` in your `api_server` config. Like
"listen_port": 8080,
"verbosity": "error",
"enable_openapi": false,
"jwt_secret_key": "somethingRandomSomethingRandom123",
"jwt_secret_key": "somethingrandom",
"CORS_origins": [],
"username": "Freqtrader",
"password": "SuperSecret1!",
+2 -7
View File
@@ -104,7 +104,7 @@ WHERE id=31;
### Remove trade from the database
!!! Tip "Use RPC Methods to delete trades"
Consider using `/delete <tradeid>` via telegram or rest API. That's the recommended way to deleting trades, as it will also remove the corresponding orders and custom data, and it will also trigger the necessary events in the bot to keep everything in sync.
Consider using `/delete <tradeid>` via telegram or rest API. That's the recommended way to deleting trades.
If you'd still like to remove a trade from the database directly, you can use the below query.
@@ -113,14 +113,9 @@ If you'd still like to remove a trade from the database directly, you can use th
```sql
DELETE FROM trades WHERE id = <tradeid>;
DELETE FROM orders WHERE ft_trade_id = <tradeid>;
DELETE FROM trade_custom_data WHERE ft_trade_id = <tradeid>;
DELETE FROM trades WHERE id = 31;
DELETE FROM orders WHERE ft_trade_id = 31;
DELETE FROM trade_custom_data WHERE ft_trade_id = 31;
```
!!! Warning
This will remove the specified trade from the database. Please make sure you got the correct id and **NEVER** run this query without the `where` clause.
This will remove this trade from the database. Please make sure you got the correct id and **NEVER** run this query without the `where` clause.
+2 -19
View File
@@ -39,22 +39,6 @@ The Order-type will be ignored if only one mode is available.
In that case, the bot will fallback to using the `emergency_exit` order type to place a market order as placing the stoploss order failed.
Freqtrade currently does not implement a limitation to avoid this situation, so please ensure your stoploss values are within reasonable limits for your exchange or disable stoploss on exchange.
### Which order type is used for stoploss on exchange?
The order type used for stoploss on exchange is determined by the `stoploss` value and the exchange capabilities.
If your selected exchange supports both stop-limit and stop-market orders, then the `stoploss` value will determine which order type is used for stoploss on exchange.
If your exchange only supports one of the two order types, you must configure your `stoploss` value accordingly, otherwise the bot will fail to start.
### Which order type should i use for stoploss on exchange?
If we translate the two stoploss order types into human words - they would be something like this:
* **stoploss-market** -> "when stop triggers, get me the hell out of here at whatever price".
* **stoploss-limit** -> "when stop triggers, place a limit order x% below the stoploss price. I accept a loss of "stoploss + 1%" at worst - but if price jumps further - i accept to wait for price to get back down to me, potentially resulting in a much bigger loss than "stoploss + 1%".
As a consequence, we recommend using stoploss-market orders whenever possible, as the main point of a stoploss is to get you out of a position when the market is crashing, and in such situations, you'll want to exit the position immediately at the best available price, rather than risking a limit order not getting filled and potentially incurring even greater losses.
The choice is ultimately up to you, but please be aware of the risk of using stoploss-limit orders, especially in volatile markets.
### stoploss_on_exchange and stoploss_on_exchange_limit_ratio
Enable or Disable stop loss on exchange.
@@ -82,10 +66,9 @@ This same logic will reapply a stoploss order on the exchange should you cancel
### stoploss_price_type
!!! Warning "Only applies to futures"
`stoploss_price_type` only applies to futures markets (on exchanges where it's available).
`stoploss_price_type` only applies to futures markets (on exchanges where it's available).
Freqtrade will perform a validation of this setting on startup, failing to start if an invalid setting for your exchange has been selected.
Supported price types are gonna differs between each exchanges. Please check with your exchange on which price types it supports.
In spot markets, this setting is ignored and not validated, as most exchanges only support one price type for stoploss orders on spot markets.
Supported price types are gonna differs between each exchanges. Please check with your exchange on which price types it supports.
Stoploss on exchange on futures markets can trigger on different price types.
The naming for these prices in exchange terminology often varies, but is usually something around "last" (or "contract price" ), "mark" and "index".
+1 -1
View File
@@ -33,7 +33,7 @@ class AwesomeStrategy(IStrategy):
trade_entry_type = trade.get_custom_data(key='entry_type')
if trade_entry_type is None:
trade_entry_type = 'breakout' if 'entry_1' in trade.enter_tag else 'dip'
elif len(fills) > 1:
elif fills > 1:
trade_entry_type = 'buy_up'
trade.set_custom_data(key='entry_type', value=trade_entry_type)
return super().bot_loop_start(**kwargs)
+6 -26
View File
@@ -225,7 +225,7 @@ class AwesomeStrategy(IStrategy):
e.g. returning -0.05 would create a stoploss 5% below current_rate.
The custom stoploss can never be below self.stoploss, which serves as a hard maximum loss.
For full documentation please go to https://www.freqtrade.io/en/stable/strategy-advanced/
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
When not implemented by a strategy, returns the initial stoploss value.
Only called when use_custom_stoploss is set to True.
@@ -696,9 +696,6 @@ However, freqtrade also offers a custom callback for both order types, which all
Backtesting fills orders if their price falls within the candle's low/high range.
The below callbacks will be called once per (detail) candle for orders that don't fill immediately (which use custom pricing).
!!! Tip "Replacing orders"
If you'd like to replace an order with a different price instead of just cancelling it, you might want to look at [`adjust_order_price()`](#adjust-order-price) instead, which will allow you to both cancel the order, as well as replace it with a new price.
### Custom order timeout example
Called for every open order until that order is either filled or cancelled.
@@ -808,7 +805,7 @@ class AwesomeStrategy(IStrategy):
Timing for this function is critical, so avoid doing heavy computations or
network requests in this method.
For full documentation please go to https://www.freqtrade.io/en/stable/strategy-advanced/
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
When not implemented by a strategy, returns True (always confirming).
@@ -856,7 +853,7 @@ class AwesomeStrategy(IStrategy):
Timing for this function is critical, so avoid doing heavy computations or
network requests in this method.
For full documentation please go to https://www.freqtrade.io/en/stable/strategy-advanced/
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
When not implemented by a strategy, returns True (always confirming).
@@ -994,7 +991,7 @@ class DigDeeperStrategy(IStrategy):
This means extra entry or exit orders with additional fees.
Only called when `position_adjustment_enable` is set to True.
For full documentation please go to https://www.freqtrade.io/en/stable/strategy-advanced/
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/
When not implemented by a strategy, returns None
@@ -1121,7 +1118,7 @@ class AwesomeStrategy(IStrategy):
This only executes when a order was already placed, still open (unfilled fully or partially)
and not timed out on subsequent candles after entry trigger.
For full documentation please go to https://www.freqtrade.io/en/stable/strategy-callbacks/
For full documentation please go to https://www.freqtrade.io/en/latest/strategy-callbacks/
When not implemented by a strategy, returns current_order_rate as default.
If current_order_rate is returned then the existing order is maintained.
@@ -1295,23 +1292,6 @@ Currently two types of annotations are supported, `area` and `line`.
}
```
#### Point
``` json
{
"type": "point", // Type of the annotation, currently only "point" is supported
"x": "2024-01-01 15:00:00", // Start date of the point
"y": 94000.2, // Price / y axis value
"color": "",
"z_level": 5, // z-level, higher values are drawn on top of lower values. Positions relative to the Chart elements need to be set in freqUI.
"label": "some label",
"size": 2, // Optional, line width in pixels. Defaults to 10
"shape": "circle", // Optional, can be "circle", "rect", "roundRect", "triangle", "pin", "arrow", "none".
"rotate": 0, // Optional, rotation of the shape/symbol in degrees. Defaults to 0
}
```
The below example will mark the chart with areas for the hours 8 and 15, with a grey color, highlighting the market open and close hours.
This is obviously a very basic example.
@@ -1389,7 +1369,7 @@ Entries will be validated, and won't be passed to the UI if they don't correspon
}
)
elif (start_dt.hour % 2) == 0:
price = dataframe.loc[dataframe["date"] == start_dt, "close"].mean()
price = dataframe.loc[dataframe["date"] == start_dt, ["close"]].mean()
annotations.append(
{
"type": "area",
-2
View File
@@ -910,8 +910,6 @@ if self.dp.runmode.value in ('live', 'dry_run'):
### *check_delisting(pair)*
Return Datetime of the pair delisting schedule if any, otherwise return None
```python
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs):
if self.dp.runmode.value in ('live', 'dry_run'):
+6 -6
View File
@@ -594,9 +594,9 @@ Features will now expand automatically. As such, the expansion loops, as well as
More details on how these config defined parameters accelerate feature engineering
in the documentation at:
https://www.freqtrade.io/en/stable/freqai-parameter-table/#feature-parameters
https://www.freqtrade.io/en/latest/freqai-parameter-table/#feature-parameters
https://www.freqtrade.io/en/stable/freqai-feature-engineering/#defining-the-features
https://www.freqtrade.io/en/latest/freqai-feature-engineering/#defining-the-features
:param df: strategy dataframe which will receive the features
:param period: period of the indicator - usage example:
@@ -657,9 +657,9 @@ Basic features. Make sure to remove the `{pair}` part from your features.
More details on how these config defined parameters accelerate feature engineering
in the documentation at:
https://www.freqtrade.io/en/stable/freqai-parameter-table/#feature-parameters
https://www.freqtrade.io/en/latest/freqai-parameter-table/#feature-parameters
https://www.freqtrade.io/en/stable/freqai-feature-engineering/#defining-the-features
https://www.freqtrade.io/en/latest/freqai-feature-engineering/#defining-the-features
:param df: strategy dataframe which will receive the features
dataframe["%-pct-change"] = dataframe["close"].pct_change()
@@ -690,7 +690,7 @@ Basic features. Make sure to remove the `{pair}` part from your features.
More details about feature engineering available:
https://www.freqtrade.io/en/stable/freqai-feature-engineering
https://www.freqtrade.io/en/latest/freqai-feature-engineering
:param df: strategy dataframe which will receive the features
usage example: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7
@@ -713,7 +713,7 @@ Targets now get their own, dedicated method.
More details about feature engineering available:
https://www.freqtrade.io/en/stable/freqai-feature-engineering
https://www.freqtrade.io/en/latest/freqai-feature-engineering
:param df: strategy dataframe which will receive the targets
usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
+3 -14
View File
@@ -6,12 +6,6 @@ To update your freqtrade installation, please use one of the below methods, corr
Breaking changes / changed behavior will be documented in the changelog that is posted alongside every release.
For the develop branch, please follow PR's to avoid being surprised by changes.
## Why update?
Keeping your bot updated not only ensures that you have the latest features and improvements, but is a requirement to keep your bot running smoothly.
Freqtrade is heavily dependent on the underlying exchange API's, which change pretty frequently if considered across exchanges.
To ensure ongoing compatibility, please make sure to update your bot regularly.
## Docker
!!! Note "Legacy installations using the `master` image"
@@ -44,12 +38,7 @@ pip install -e .
freqtrade install-ui
```
## Problems updating
### Problems updating
Update-problems usually come missing dependencies (you didn't follow the above instructions) - or from dependencies which fail to install.
We try to make sure that heavy dependencies have wheels available for major platforms, but sometimes this is not possible.
Please refer to the corresponding installation sections (common problem sections linked below).
[Common installation problems](installation.md#troubleshooting)
[Common installation problems - windows](installation.md#windows-installation-error)
Update-problems usually come missing dependencies (you didn't follow the above instructions) - or from updated dependencies, which fail to install (for example TA-lib).
Please refer to the corresponding installation sections (common problems linked below)
+1 -1
View File
@@ -416,6 +416,6 @@ Your original strategy will remain available in the `user_data/strategies_orig_u
!!! Warning "Conversion results"
Strategy updater will work on a "best effort" approach. Please do your due diligence and verify the results of the conversion.
We also recommend to run a python formatter (e.g. `ruff format`) to format results in a sane manner.
We also recommend to run a python formatter (e.g. `black`) to format results in a sane manner.
--8<-- "commands/strategy-updater.md"
+54
View File
@@ -0,0 +1,54 @@
# Windows installation
We **strongly** recommend that Windows users use [Docker](docker_quickstart.md) as this will work much easier and smoother (also more secure).
If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work.
Otherwise, please follow the instructions below.
All instructions assume that python 3.11+ is installed and available.
## Clone the git repository
First of all clone the repository by running:
``` powershell
git clone https://github.com/freqtrade/freqtrade.git
```
Now, choose your installation method, either automatically via script (recommended) or manually following the corresponding instructions.
## Install freqtrade automatically
### Run the installation script
The script will ask you a few questions to determine which parts should be installed.
```powershell
Set-ExecutionPolicy -ExecutionPolicy Bypass
cd freqtrade
. .\setup.ps1
```
## Install freqtrade manually
!!! Note "64bit Python version"
Please make sure to use 64bit Windows and 64bit Python to avoid problems with backtesting or hyperopt due to the memory constraints 32bit applications have under Windows.
32bit python versions are no longer supported under Windows.
!!! Hint
Using the [Anaconda Distribution](https://www.anaconda.com/distribution/) under Windows can greatly help with installation problems. Check out the [Anaconda installation section](installation.md#installation-with-conda) in the documentation for more information.
### Error during installation on Windows
``` bash
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
```
Unfortunately, many packages requiring compilation don't provide a pre-built wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use.
You can download the Visual C++ build tools from [here](https://visualstudio.microsoft.com/visual-cpp-build-tools/) and install "Desktop development with C++" in it's default configuration. Unfortunately, this is a heavy download / dependency so you might want to consider WSL2 or [docker compose](docker_quickstart.md) first.
![Windows installation](assets/windows_install.png)
---
+1 -1
View File
@@ -1,6 +1,6 @@
"""Freqtrade bot"""
__version__ = "2026.5-dev"
__version__ = "2025.12"
if "dev" in __version__:
from pathlib import Path
+1 -7
View File
@@ -103,13 +103,7 @@ ARGS_BACKTEST_SHOW = [
"backtest_breakdown",
]
ARGS_LIST_EXCHANGES = [
"print_one_column",
"list_exchanges_all",
"trading_mode",
"dex_exchanges",
"list_exchanges_futures_options",
]
ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all", "trading_mode", "dex_exchanges"]
ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column", "trading_mode"]
+4 -17
View File
@@ -2,7 +2,7 @@
Definition of cli arguments used in arguments.py
"""
from argparse import SUPPRESS, ArgumentTypeError
from argparse import ArgumentTypeError
from freqtrade import constants
from freqtrade.constants import (
@@ -215,7 +215,9 @@ AVAILABLE_CLI_OPTIONS = {
"--strategy-list",
help="Provide a space-separated list of strategies to backtest. "
"Please note that timeframe needs to be set either in config "
"or via command line. ",
"or via command line. When using this together with `--export trades`, "
"the strategy-name is injected into the filename "
"(so `backtest-data.json` becomes `backtest-data-SampleStrategy.json`",
nargs="+",
),
"backtest_notes": Arg(
@@ -238,14 +240,6 @@ AVAILABLE_CLI_OPTIONS = {
"exportfilename": Arg(
"--backtest-filename",
"--export-filename",
fthelp={
"freqtrade backtesting": (
"DEPRECATED: This option is deprecated for backtesting and will be removed "
"in a future release. "
"Using a custom filename for backtest results is no longer supported. "
"Use `--backtest-directory` to specify the directory."
),
},
help="Use this filename for backtest results."
"Example: `--backtest-filename=backtest_results_2020-09-27_16-20-48.json`. "
"Assumes either `user_data/backtest_results/` or `--export-directory` as base directory.",
@@ -394,13 +388,6 @@ AVAILABLE_CLI_OPTIONS = {
help="Print only DEX exchanges.",
action="store_true",
),
"list_exchanges_futures_options": Arg(
"--ccxt-show-futures-options-exchanges",
help=SUPPRESS,
# Show compatibility with ccxt for futures functionality
# Doesn't show in help as it's an internal/debug option.
action="store_true",
),
# List pairs / markets
"list_pairs_all": Arg(
"-a",
+1 -1
View File
@@ -223,7 +223,7 @@ def start_list_trades_data(args: dict[str, Any]) -> None:
end.strftime(DATETIME_PRINT_FORMAT),
str(length),
)
for pair, start, end, length in sorted(paircombs1, key=lambda x: x[0])
for pair, start, end, length in sorted(paircombs1, key=lambda x: (x[0]))
],
("Pair", "Type", "From", "To", "Trades"),
summary=title,
+37 -3
View File
@@ -8,10 +8,13 @@ logger = logging.getLogger(__name__)
def start_convert_db(args: dict[str, Any]) -> None:
from sqlalchemy import func, select
from sqlalchemy.orm import make_transient
from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.persistence import Trade, init_db
from freqtrade.persistence.db_migration import migrate_db
from freqtrade.persistence import Order, Trade, init_db
from freqtrade.persistence.migrations import set_sequence_ids
from freqtrade.persistence.pairlock import PairLock
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
@@ -19,4 +22,35 @@ def start_convert_db(args: dict[str, Any]) -> None:
session_target = Trade.session
init_db(config["db_url_from"])
logger.info("Starting db migration.")
migrate_db(session_target)
trade_count = 0
pairlock_count = 0
for trade in Trade.get_trades():
trade_count += 1
make_transient(trade)
for o in trade.orders:
make_transient(o)
session_target.add(trade)
session_target.commit()
for pairlock in PairLock.get_all_locks():
pairlock_count += 1
make_transient(pairlock)
session_target.add(pairlock)
session_target.commit()
# Update sequences
max_trade_id = session_target.scalar(select(func.max(Trade.id)))
max_order_id = session_target.scalar(select(func.max(Order.id)))
max_pairlock_id = session_target.scalar(select(func.max(PairLock.id)))
set_sequence_ids(
session_target.get_bind(),
trade_id=max_trade_id,
order_id=max_order_id,
pairlock_id=max_pairlock_id,
)
logger.info(f"Migrated {trade_count} Trades, and {pairlock_count} Pairlocks.")
+4 -15
View File
@@ -4,7 +4,7 @@ import sys
from typing import Any
from freqtrade.enums import RunMode
from freqtrade.exceptions import ConfigurationError, DependencyException, OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
logger = logging.getLogger(__name__)
@@ -38,15 +38,13 @@ def start_list_exchanges(args: dict[str, Any]) -> None:
else:
available_exchanges = [e for e in available_exchanges if e["valid"] is not False]
title = f"Exchanges available for Freqtrade ({len(available_exchanges)} exchanges):"
show_fut_reasons = args.get("list_exchanges_futures_options", False)
table = Table(title=title)
table.add_column("Exchange Name")
table.add_column("Class Name")
table.add_column("Markets")
table.add_column("Reason")
if show_fut_reasons:
table.add_column("Futures Reason")
trading_mode = args.get("trading_mode", None)
dex_only = args.get("dex_exchanges", False)
@@ -80,14 +78,12 @@ def start_list_exchanges(args: dict[str, Any]) -> None:
if exchange["dex"]:
trade_modes = Text("DEX: ") + trade_modes
trade_modes.stylize("bold", 0, 3)
futcol = [] if not show_fut_reasons else [exchange["comment_futures"]]
table.add_row(
name,
classname,
trade_modes,
exchange["comment"],
*futcol,
style=None if exchange["valid"] else "red",
)
# table.add_row(*[exchange[header] for header in headers])
@@ -166,14 +162,7 @@ def start_list_strategies(args: dict[str, Any]) -> None:
strategy_objs = sorted(strategy_objs, key=lambda x: x["name"])
for obj in strategy_objs:
if obj["class"]:
try:
obj["hyperoptable"] = detect_all_parameters(obj["class"])
except DependencyException as e:
logger.warning(
f"Cannot detect hyperoptable parameters for strategy {obj['name']}. Reason: {e}"
)
obj["hyperoptable"] = {}
obj["hyperoptable"] = detect_all_parameters(obj["class"])
else:
obj["hyperoptable"] = {}
@@ -393,7 +382,7 @@ def start_show_trades(args: dict[str, Any]) -> None:
tfilter = []
if config.get("trade_ids"):
tfilter.append(Trade.id.in_(int(tid) for tid in config["trade_ids"]))
tfilter.append(Trade.id.in_(config["trade_ids"]))
trades = Trade.get_trades(tfilter).all()
logger.info(f"Printing {len(trades)} Trades: ")
+1 -14
View File
@@ -236,10 +236,6 @@ CONF_SCHEMA = {
"type": "string",
"enum": BACKTEST_CACHE_AGE,
},
"skip_wallet_history_migration": {
"description": "Disable wallet history migration.",
"type": "boolean",
},
# Hyperopt
"hyperopt_path": {
"description": "Specify additional lookup path for Hyperopt Loss functions.",
@@ -756,8 +752,6 @@ CONF_SCHEMA = {
"jwt_secret_key": {
"description": "Secret key for JWT authentication.",
"type": "string",
"default": "somethingRandomSomethingRandom123",
"minLength": 32,
},
"CORS_origins": {
"description": "List of allowed CORS origins.",
@@ -770,14 +764,7 @@ CONF_SCHEMA = {
"enum": ["error", "info"],
},
},
"required": [
"enabled",
"listen_ip_address",
"listen_port",
"username",
"password",
"jwt_secret_key",
],
"required": ["enabled", "listen_ip_address", "listen_port", "username", "password"],
},
# end of RPC section
"db_url": {
@@ -92,7 +92,6 @@ def validate_config_consistency(conf: dict[str, Any], *, preliminary: bool = Fal
_validate_consumers(conf)
validate_migrated_strategy_settings(conf)
_validate_orderflow(conf)
_validate_demo_trading(conf)
# validate configuration before returning
logger.info("Validating configuration ...")
@@ -414,11 +413,6 @@ def _validate_orderflow(conf: dict[str, Any]) -> None:
)
def _validate_demo_trading(conf: dict[str, Any]) -> None:
if conf.get("exchange", {}).get("demo_trading", False) and conf.get("dry_run", False):
raise ConfigurationError("Demo trading cannot be used together with dry_run.")
def _strategy_settings(conf: dict[str, Any]) -> None:
process_deprecated_setting(conf, None, "use_sell_signal", None, "use_exit_signal")
process_deprecated_setting(conf, None, "sell_profit_only", None, "exit_profit_only")
+18 -25
View File
@@ -221,30 +221,30 @@ class Configuration:
config, argname="exportfilename", logstring="Storing backtest results to {} ..."
)
config["exportfilename"] = Path(config["exportfilename"])
if config.get("exportfilename"):
if Path(config["exportfilename"]).is_dir():
logger.warning(
"DEPRECATED: Using `--export-filename` with directories is deprecated, "
"use `--backtest-directory` instead."
)
if config.get("exportdirectory") is None:
# Fallback - assign export-directory directly.
config["exportdirectory"] = config["exportfilename"]
elif config.get("runmode") == RunMode.BACKTEST:
logger.warning(
"DEPRECATED: Using `--export-filename` has no impact when backtesting. "
"Please use `--notes` to annotate backtest results and "
"`--backtest-directory` to specify the output directory. "
)
if config.get("exportdirectory") and Path(config["exportdirectory"]).is_dir():
logger.warning(
"DEPRECATED: Using `--export-filename` with directories is deprecated, "
"use `--backtest-directory` instead."
)
if config.get("exportdirectory") is None:
# Fallback - assign export-directory directly.
config["exportdirectory"] = config["exportfilename"]
if not config.get("exportdirectory"):
config["exportdirectory"] = config["user_data_dir"] / "backtest_results"
config["exportfilename"] = config.get("exportfilename", None)
if not config.get("exportfilename"):
config["exportfilename"] = None
if config.get("exportfilename"):
# ensure exportfilename is a Path object
config["exportfilename"] = Path(config["exportfilename"])
config["exportdirectory"] = Path(config["exportdirectory"])
if self.args.get("show_sensitive"):
logger.warning(
"Sensitive information will be shown in the upcoming output. "
"Please make sure to never share this output without redacting "
"the information yourself."
)
def _process_optimize_options(self, config: Config) -> None:
# This will override the strategy configuration
self._args_to_config(
@@ -312,13 +312,6 @@ class Configuration:
self._process_datadir_options(config)
if self.args.get("show_sensitive"):
logger.warning(
"Sensitive information will be shown in the upcoming output. "
"Please make sure to never share this output without redacting "
"the information yourself."
)
self._args_to_config(
config,
argname="strategy_list",
@@ -410,7 +403,7 @@ class Configuration:
("include_inactive", "Detected --include-inactive-pairs: {}"),
("no_parallel_download", "Detected --no-parallel-download: {}"),
("download_trades", "Detected --dl-trades: {}"),
("convert_trades", "Detected --convert: {} - Converting trade data to OHLCV."),
("convert_trades", "Detected --convert: {} - Converting Trade data to OHCV {}"),
("dataformat_ohlcv", 'Using "{}" to store OHLCV data.'),
("dataformat_trades", 'Using "{}" to store trades data.'),
("show_timerange", "Detected --show-timerange"),
+1 -1
View File
@@ -30,7 +30,7 @@ def log_config_error_range(path: str, errmsg: str) -> str:
offsetlist = re.findall(r"(?<=Parse\serror\sat\soffset\s)\d+", errmsg)
if offsetlist:
offset = int(offsetlist[0])
text = Path(path).read_text(encoding="utf-8")
text = Path(path).read_text()
# Fetch an offset of 80 characters around the error line
subtext = text[offset - min(80, offset) : offset + 80]
segments = subtext.split("\n")
-4
View File
@@ -61,7 +61,6 @@ AVAILABLE_PAIRLISTS = [
"ProducerPairList",
"RemotePairList",
"MarketCapPairList",
"CrossMarketPairList",
"AgeFilter",
"DelistFilter",
"FullTradesFilter",
@@ -240,6 +239,3 @@ IntOrInf = float
EntryExecuteMode = Literal["initial", "pos_adjust", "replace"]
# Prefixes for low-priced coins like 1000PEPE/USDDT:USDT or KPEPE/USDC (hyperliquid)
PairPrefixes = ["1000", "1000000", "1M", "K"]
-1
View File
@@ -7,7 +7,6 @@ from .bt_fileutils import (
get_backtest_market_change,
get_backtest_result,
get_backtest_resultlist,
get_backtest_wallet_change,
get_latest_backtest_filename,
get_latest_hyperopt_file,
get_latest_hyperopt_filename,
+4 -27
View File
@@ -10,6 +10,7 @@ from io import BytesIO, StringIO
from pathlib import Path
from typing import Any, Literal
import numpy as np
import pandas as pd
from freqtrade.constants import LAST_BT_RESULT_FN
@@ -307,31 +308,10 @@ def get_backtest_market_change(filename: Path, include_ts: bool = True) -> pd.Da
else:
df = pd.read_feather(filename)
if include_ts:
df.loc[:, "__date_ts"] = df.loc[:, "date"].dt.as_unit("ms").astype("int64")
df.loc[:, "__date_ts"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000
return df
def get_backtest_wallet_change(filename: Path, strategy_name: str) -> pd.DataFrame | None:
"""
Read backtest wallet change file.
:param filename: Path to the backtest result zip file
:param strategy_name: Name of the strategy to load
:return: DataFrame with wallet change data
"""
if filename.suffix != ".zip":
return None
try:
data = load_file_from_zip(filename, f"{filename.stem}_{strategy_name}_wallet.feather")
df = pd.read_feather(BytesIO(data))
df.loc[:, "__date_ts"] = df.loc[:, "date"].dt.as_unit("ms").astype("int64")
return df
except ValueError:
pass
return None
def find_existing_backtest_stats(
dirname: Path | str, run_ids: dict[str, str], min_backtest_date: datetime | None = None
) -> dict[str, Any]:
@@ -523,16 +503,13 @@ def load_backtest_analysis_data(
return None
def trade_list_to_dataframe(
trades: list[Trade] | list[LocalTrade], *, minified: bool = True
) -> pd.DataFrame:
def trade_list_to_dataframe(trades: list[Trade] | list[LocalTrade]) -> pd.DataFrame:
"""
Convert list of Trade objects to pandas Dataframe
:param trades: List of trade objects
:param minified: Whether to use minified version of trade JSON
:return: Dataframe with BT_DATA_COLUMNS
"""
df = pd.DataFrame.from_records([t.to_json(minified) for t in trades], columns=BT_DATA_COLUMNS)
df = pd.DataFrame.from_records([t.to_json(True) for t in trades], columns=BT_DATA_COLUMNS)
if len(df) > 0:
df["close_date"] = pd.to_datetime(df["close_timestamp"], unit="ms", utc=True)
df["open_date"] = pd.to_datetime(df["open_timestamp"], unit="ms", utc=True)
@@ -1,4 +1,3 @@
from numpy import format_float_positional
from pandas import DataFrame, Series
@@ -12,10 +11,7 @@ def get_tick_size_over_time(candles: DataFrame) -> Series:
# count the number of significant digits for the open and close prices
for col in ["open", "high", "low", "close"]:
candles[f"{col}_count"] = (
candles[col]
.apply(format_float_positional, precision=14, unique=False, fractional=False, trim="-")
.str.extract(r"\.(\d*[1-9])")[0]
.str.len()
candles[col].round(14).apply("{:.15f}".format).str.extract(r"\.(\d*[1-9])")[0].str.len()
)
candles["max_count"] = candles[["open_count", "close_count", "high_count", "low_count"]].max(
axis=1
@@ -1,15 +1,9 @@
import logging
from datetime import datetime
import numpy as np
import pandas as pd
from freqtrade.constants import IntOrInf
from freqtrade.exchange import (
timeframe_to_prev_date,
timeframe_to_resample_freq,
)
from freqtrade.util import dt_from_ts
logger = logging.getLogger(__name__)
@@ -64,95 +58,3 @@ def evaluate_result_multi(
"""
df_final = analyze_trade_parallelism(trades, timeframe)
return df_final[df_final["open_trades"] > max_open_trades]
def balance_distribution_over_time(
trades: pd.DataFrame,
min_date: datetime,
max_date: datetime,
timeframe: str,
stake_currency: str,
start_balance: float,
pairlist: list[str],
) -> pd.DataFrame:
"""
Return a dataframe with stake_currency and the pairlist as columns
Each column will contain the amount of the currency at the given time
Columns added are:
- stake_currency: amount of stake currency
- <pair>: amount of base currency in the pair
- <pair>_leverage: leverage used for the pair at the time (NaN if no open trade)
- <pair>_is_short: 1 if the open trade is short, 0 if long (NaN if no open trade)
- <pair>_collateral: amount of stake currency used as collateral for open trades
:param trades: Trades Dataframe - can be loaded from backtest, or created
via trade_list_to_dataframe
:param timeframe: Frequency to use for the resulting dataframe
:param min_date: start date
:param max_date: End date (will be rounded down to timeframe)
:param stake_currency: The stake currency
:param start_balance: Starting balance in stake currency
:param pairlist: List of trading pairs to include in the dataframe
Can be obtained via trade_df["pair"].unique()
For pairs without trades, the column will be all zeros
:return: Dataframe with balance distribution over time
"""
min_date_res = timeframe_to_prev_date(timeframe, min_date)
max_date_res = timeframe_to_prev_date(timeframe, max_date)
index = pd.date_range(min_date_res, max_date_res, freq=timeframe_to_resample_freq(timeframe))
pairs_lev = [f"{pair}_leverage" for pair in pairlist]
pairs_is_short = [f"{pair}_is_short" for pair in pairlist]
pairs_collateral = [f"{pair}_collateral" for pair in pairlist]
pairs_lev += pairs_is_short
df = pd.DataFrame(
index=index, columns=[stake_currency] + pairlist + pairs_lev + pairs_collateral, dtype=float
)
# Initialize variables to starting values
df[stake_currency] = float(start_balance)
df[pairlist + pairs_collateral] = 0.0
df[pairs_lev] = np.nan
for trade in trades.sort_values(by=["open_date"]).itertuples():
pair = trade.pair
end_date = trade.close_date if trade.close_date is not pd.NaT else None
# Exclude open orders - these won't have order_filled_timestamp set.
df.loc[trade.open_date : end_date, f"{pair}_leverage"] = trade.leverage
df.loc[trade.open_date : end_date, f"{pair}_is_short"] = 1 if trade.is_short else 0
orders = [o for o in trade.orders if o["order_filled_timestamp"]]
current_position = 0
current_collateral = 0
for order in sorted(orders, key=lambda x: x["order_filled_timestamp"]):
filled_at = pd.Timestamp(dt_from_ts(order["order_filled_timestamp"]))
real_amount = order.get("filled", order["amount"])
stake = order["safe_price"] * real_amount
stake_no_lev = stake / trade.leverage
if order["ft_is_entry"]:
# Entry order: lock collateral and pay fee
# For both long and short: balance decreases by collateral + fee
fee_open = stake * trade.fee_open
current_position += real_amount
current_collateral += stake_no_lev
df.loc[filled_at:end_date, pair] += real_amount
df.loc[filled_at:end_date, f"{pair}_collateral"] += stake_no_lev
df.loc[filled_at:, stake_currency] -= stake_no_lev + fee_open
else:
# Exit order: release collateral and realize profit/loss
fee_close = stake * trade.fee_close
if trade.is_short:
# For SHORT
df.loc[filled_at:, stake_currency] += (
current_collateral * (1 + trade.leverage) - stake
) - fee_close
else:
# For LONG
df.loc[filled_at:, stake_currency] += (
stake - current_collateral * (trade.leverage - 1) - fee_close
)
df.loc[filled_at:end_date, pair] -= real_amount
df.loc[filled_at:end_date, f"{pair}_collateral"] -= stake_no_lev
current_position -= real_amount
current_collateral -= stake_no_lev
# Round to avoid floating point issues
df = df.round(14)
return df
+7 -11
View File
@@ -39,11 +39,7 @@ def ohlcv_to_dataframe(
df = DataFrame(ohlcv, columns=cols)
# Floor date to seconds to account for exchange imprecisions
from freqtrade.exchange import timeframe_to_floor_freq
resample_interval = timeframe_to_floor_freq(timeframe)
df["date"] = to_datetime(df["date"], unit="ms", utc=True).dt.floor(resample_interval)
df["date"] = to_datetime(df["date"], unit="ms", utc=True).dt.floor("s")
# Some exchanges return int values for Volume and even for OHLC.
# Convert them since TA-LIB indicators used in the strategy assume floats
@@ -63,14 +59,14 @@ def ohlcv_to_dataframe(
def clean_ohlcv_dataframe(
dataframe: DataFrame, timeframe: str, pair: str, *, fill_missing: bool, drop_incomplete: bool
data: DataFrame, timeframe: str, pair: str, *, fill_missing: bool, drop_incomplete: bool
) -> DataFrame:
"""
Cleanse a OHLCV dataframe by
* Grouping it by date (removes duplicate tics)
* dropping last candles if requested
* Filling up missing data (if requested)
:param dataframe: DataFrame containing candle (OHLCV) data.
:param data: DataFrame containing candle (OHLCV) data.
:param timeframe: timeframe (e.g. 5m). Used to fill up eventual missing data
:param pair: Pair this data is for (used to warn if fillup was necessary)
:param fill_missing: fill up missing candles with 0 candles
@@ -79,7 +75,7 @@ def clean_ohlcv_dataframe(
:return: DataFrame
"""
# group by index and aggregate results to eliminate duplicate ticks
dataframe = dataframe.groupby(by="date", as_index=False, sort=True).agg(
data = data.groupby(by="date", as_index=False, sort=True).agg(
{
"open": "first",
"high": "max",
@@ -90,13 +86,13 @@ def clean_ohlcv_dataframe(
)
# eliminate partial candle
if drop_incomplete:
dataframe.drop(dataframe.tail(1).index, inplace=True)
data.drop(data.tail(1).index, inplace=True)
logger.debug("Dropping last candle")
if fill_missing:
return ohlcv_fill_up_missing_data(dataframe, timeframe, pair)
return ohlcv_fill_up_missing_data(data, timeframe, pair)
else:
return dataframe
return data
def ohlcv_fill_up_missing_data(dataframe: DataFrame, timeframe: str, pair: str) -> DataFrame:
@@ -1,6 +1,6 @@
import logging
from pandas import DataFrame, read_feather
from pandas import DataFrame, read_feather, to_datetime
from pyarrow import dataset
from freqtrade.configuration import TimeRange
@@ -71,7 +71,7 @@ class FeatherDataHandler(IDataHandler):
"volume": "float",
}
)
pairdata["date"] = pairdata["date"].dt.as_unit("ms")
pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True)
return pairdata
except Exception as e:
logger.exception(
@@ -31,8 +31,8 @@ logger = logging.getLogger(__name__)
class IDataHandler(ABC):
_OHLCV_REGEX = r"^([\w-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)"
_TRADES_REGEX = r"^([\w-]+)\-(trades)?(?=\.)"
_OHLCV_REGEX = r"^([a-zA-Z_\d-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)"
_TRADES_REGEX = r"^([a-zA-Z_\d-]+)\-(trades)?(?=\.)"
def __init__(self, datadir: Path) -> None:
self._datadir = datadir
@@ -70,6 +70,28 @@ class IDataHandler(ABC):
if match and len(match.groups()) > 1
]
@classmethod
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> list[str]:
"""
Returns a list of all pairs with ohlcv data available in this datadir
for the specified timeframe
:param datadir: Directory to search for ohlcv files
:param timeframe: Timeframe to search pairs for
:param candle_type: Any of the enum CandleType (must match trading mode!)
:return: List of Pairs
"""
candle = ""
if candle_type != CandleType.SPOT:
datadir = datadir.joinpath("futures")
candle = f"-{candle_type}"
ext = cls._get_file_extension()
_tmp = [
re.search(r"^(\S+)(?=\-" + timeframe + candle + f".{ext})", p.name)
for p in datadir.glob(f"*{timeframe}{candle}.{ext}")
]
# Check if regex found something and only return these results
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
@abstractmethod
def ohlcv_store(
self, pair: str, timeframe: str, data: DataFrame, candle_type: CandleType
@@ -336,10 +358,11 @@ class IDataHandler(ABC):
def rebuild_pair_from_filename(pair: str) -> str:
"""
Rebuild pair name from filename
Replaces the first '_' with '/' and the second '_' (if present) with ':'.
e.g. BTC_USDT -> BTC/USDT, BTC_USDT_USDT -> BTC/USDT:USDT
Assumes a asset name of max. 7 length to also support BTC-PERP and BTC-PERP:USD names.
"""
return pair.replace("_", "/", 1).replace("_", ":", 1)
res = re.sub(r"^(([A-Za-z\d]{1,10})|^([A-Za-z\-]{1,6}))(_)", r"\g<1>/", pair, count=1)
res = re.sub("_", ":", res, count=1)
return res
def ohlcv_load(
self,
@@ -1,5 +1,6 @@
import logging
import numpy as np
from pandas import DataFrame, read_json, to_datetime
from freqtrade import misc
@@ -34,8 +35,8 @@ class JsonDataHandler(IDataHandler):
filename = self._pair_data_filename(self._datadir, pair, timeframe, candle_type)
self.create_dir_if_needed(filename)
_data = data.copy()
# Convert date to int (milliseconds)
_data["date"] = _data["date"].dt.as_unit("ms").astype("int64")
# Convert date to int
_data["date"] = _data["date"].astype(np.int64) // 1000 // 1000
# Reset index, select only appropriate columns and save as json
_data.reset_index(drop=True).loc[:, self._columns].to_json(
@@ -80,7 +81,7 @@ class JsonDataHandler(IDataHandler):
"volume": "float",
}
)
pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True).dt.as_unit("ms")
pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True)
return pairdata
def ohlcv_append(
@@ -104,9 +105,6 @@ class JsonDataHandler(IDataHandler):
:param trading_mode: Trading mode to use (used to determine the filename)
"""
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
# Convert StringDtype columns to object to avoid NaN serialization issues
for col in data.select_dtypes(include="string").columns:
data[col] = data[col].astype(object).where(data[col].notna(), other=None)
trades = data.values.tolist()
misc.file_dump_json(filename, trades, is_zip=self._use_zip)
@@ -1,6 +1,6 @@
import logging
from pandas import DataFrame, read_parquet
from pandas import DataFrame, read_parquet, to_datetime
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS
@@ -68,7 +68,7 @@ class ParquetDataHandler(IDataHandler):
"volume": "float",
}
)
pairdata["date"] = pairdata["date"].dt.as_unit("ms")
pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True)
return pairdata
except Exception as e:
logger.exception(
+29 -209
View File
@@ -140,7 +140,7 @@ def _calc_drawdown_series(
max_drawdown_df["drawdown_relative"] = (max_balance - cumulative_balance) / max_balance
else:
# NOTE: This is not completely accurate,
# but will be good enough if starting_balance is not available
# but might good enough if starting_balance is not available
max_drawdown_df["drawdown_relative"] = (
max_drawdown_df["high_value"] - max_drawdown_df["cumulative"]
) / max_drawdown_df["high_value"]
@@ -296,7 +296,7 @@ def calculate_cagr(days_passed: int, starting_balance: float, final_balance: flo
:param final_balance: Final balance to calculate CAGR against
:return: CAGR
"""
if (final_balance < 0) or (starting_balance <= 0) or (days_passed <= 0):
if final_balance < 0:
# With leveraged trades, final_balance can become negative.
return 0
return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1
@@ -333,77 +333,8 @@ def calculate_expectancy(trades: pd.DataFrame) -> tuple[float, float]:
return expectancy, expectancy_ratio
def _calculate_annualized_ratio(
expected_returns_mean: float,
denominator: float,
annualization_factor: int = 365,
) -> float:
"""
Helper function to calculate annualized ratios like Sharpe and Sortino.
:param expected_returns_mean: Mean of the returns (expected returns)
:param denominator: Denominator of the ratio (e.g. standard deviation for Sharpe)
:param annualization_factor: Factor to annualize the ratio (default is 365 for daily returns)
:return: Annualized ratio, or -100.0 if denominator is zero or NaN to indicate this is
not optimal.
"""
if denominator != 0 and not np.isnan(denominator):
return float(expected_returns_mean / denominator * np.sqrt(annualization_factor))
# Define high (negative) ratio to be clear that this is NOT optimal.
return -100.0
def _calculate_daily_returns_from_balance(
balance_history: pd.DataFrame,
date_col: str,
balance_col: str,
) -> pd.Series:
wallet = _prepare_balance_history(balance_history, date_col, balance_col)
if len(wallet) == 0:
return pd.DataFrame(columns=[date_col, balance_col])
# Sample balance to daily end-of-day values to normalize variable snapshot frequency.
daily_balance = (
wallet.set_index(date_col)[balance_col].resample("1D").last().dropna().rename(balance_col)
)
daily_balance = daily_balance.reset_index()
if len(daily_balance) < 2:
return pd.Series(dtype=float)
return daily_balance[balance_col].pct_change().dropna()
def _prepare_balance_history(
balance_history: pd.DataFrame,
date_col: str,
balance_col: str,
) -> pd.DataFrame:
"""
Prepare balance history for calculations by filtering out rows with
missing date or balance values.
"""
if (
len(balance_history) == 0
or date_col not in balance_history
or balance_col not in balance_history
):
return pd.DataFrame(columns=[date_col, balance_col])
wallet = balance_history.loc[:, [date_col, balance_col]].copy()
wallet = wallet.dropna(subset=[date_col, balance_col]).sort_values(date_col)
if len(wallet) == 0:
return pd.DataFrame(columns=[date_col, balance_col])
return wallet
def calculate_sortino(
trades: pd.DataFrame,
min_date: datetime | None,
max_date: datetime | None,
starting_balance: float,
trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float
) -> float:
"""
Calculate sortino
@@ -420,38 +351,18 @@ def calculate_sortino(
down_stdev = np.std(trades.loc[trades["profit_abs"] < 0, "profit_abs"] / starting_balance)
return _calculate_annualized_ratio(expected_returns_mean, down_stdev)
if down_stdev != 0 and not np.isnan(down_stdev):
sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365)
else:
# Define high (negative) sortino ratio to be clear that this is NOT optimal.
sortino_ratio = -100
def calculate_sortino_from_balance(
balance_history: pd.DataFrame,
date_col: str = "date",
balance_col: str = "total_quote",
) -> float:
"""
Calculate sortino ratio from historical balance snapshots.
:param balance_history: DataFrame containing at least date and balance columns
:param date_col: Column containing timestamps
:param balance_col: Column containing historical balance values
:return: sortino
"""
daily_returns = _calculate_daily_returns_from_balance(balance_history, date_col, balance_col)
if len(daily_returns) == 0:
return 0.0
expected_returns_mean = daily_returns.mean()
downside_returns = daily_returns[daily_returns < 0]
down_stdev = downside_returns.std(ddof=0)
return _calculate_annualized_ratio(expected_returns_mean, down_stdev)
# print(expected_returns_mean, down_stdev, sortino_ratio)
return sortino_ratio
def calculate_sharpe(
trades: pd.DataFrame,
min_date: datetime | None,
max_date: datetime | None,
starting_balance: float,
trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float
) -> float:
"""
Calculate sharpe
@@ -467,82 +378,26 @@ def calculate_sharpe(
expected_returns_mean = total_profit.sum() / days_period
up_stdev = np.std(total_profit)
return _calculate_annualized_ratio(expected_returns_mean, up_stdev)
if up_stdev != 0:
sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365)
else:
# Define high (negative) sharpe ratio to be clear that this is NOT optimal.
sharp_ratio = -100
def calculate_sharpe_from_balance(
balance_history: pd.DataFrame,
date_col: str = "date",
balance_col: str = "total_quote",
) -> float:
"""
Calculate sharpe ratio from historical balance snapshots.
:param balance_history: DataFrame containing at least date and balance columns
:param date_col: Column containing timestamps
:param balance_col: Column containing historical balance values
:return: sharpe
"""
daily_returns = _calculate_daily_returns_from_balance(balance_history, date_col, balance_col)
if len(daily_returns) == 0:
return 0.0
expected_returns_mean = daily_returns.mean()
up_stdev = daily_returns.std(ddof=0)
return _calculate_annualized_ratio(expected_returns_mean, up_stdev)
def calculate_max_drawdown_from_balance(
balance_history: pd.DataFrame,
date_col: str = "date",
balance_col: str = "total_quote",
relative: bool = False,
) -> DrawDownResult:
"""
Calculate max drawdown from historical balance snapshots.
:param balance_history: DataFrame containing at least date and balance columns
:param date_col: Column containing timestamps
:param balance_col: Column containing historical balance values
:param relative: If True, use relative drawdown for max calculation instead of absolute
:return: DrawDownResult object
:raise: ValueError if balance-history dataframe was found empty.
"""
wallet = _prepare_balance_history(
balance_history=balance_history,
date_col=date_col,
balance_col=balance_col,
)
if len(wallet) < 2:
raise ValueError("Balance-history dataframe empty.")
starting_balance = float(wallet[balance_col].iloc[0])
wallet.loc[:, "total_balance"] = wallet[balance_col].diff().fillna(0.0)
return calculate_max_drawdown(
wallet,
date_col=date_col,
value_col="total_balance",
starting_balance=starting_balance,
relative=relative,
)
# print(expected_returns_mean, up_stdev, sharp_ratio)
return sharp_ratio
def calculate_calmar(
trades: pd.DataFrame,
min_date: datetime | None,
max_date: datetime | None,
starting_balance: float,
trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float
) -> float:
"""
Calculate calmar from trades data.
Calculate calmar
:param trades: DataFrame containing trades (requires columns close_date and profit_abs)
:return: calmar
"""
if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date):
return 0.0
return 0
total_profit = trades["profit_abs"].sum() / starting_balance
days_period = max(1, (max_date - min_date).days)
@@ -558,51 +413,16 @@ def calculate_calmar(
)
max_drawdown = drawdown.relative_account_drawdown
except ValueError:
return 0.0
max_drawdown = 0
return _calculate_annualized_ratio(expected_returns_mean, max_drawdown)
if max_drawdown != 0:
calmar_ratio = expected_returns_mean / max_drawdown * math.sqrt(365)
else:
# Define high (negative) calmar ratio to be clear that this is NOT optimal.
calmar_ratio = -100
def calculate_calmar_from_balance(
balance_history: pd.DataFrame,
date_col: str = "date",
balance_col: str = "total_quote",
) -> float:
"""
Calculate calmar ratio from historical balance snapshots.
:param balance_history: DataFrame containing at least date and balance columns
:param date_col: Column containing timestamps
:param balance_col: Column containing historical balance values
:return: calmar
"""
wallet = _prepare_balance_history(
balance_history=balance_history,
date_col=date_col,
balance_col=balance_col,
)
if len(wallet) < 2:
return 0.0
starting_balance = float(wallet[balance_col].iloc[0])
final_balance = float(wallet[balance_col].iloc[-1])
days_period = max(1, (wallet[date_col].iloc[-1] - wallet[date_col].iloc[0]).days)
total_profit = (final_balance - starting_balance) / starting_balance
expected_returns_mean = total_profit / days_period * 100
try:
drawdown = calculate_max_drawdown_from_balance(
wallet,
date_col=date_col,
balance_col=balance_col,
)
max_drawdown = drawdown.relative_account_drawdown
except ValueError:
return 0.0
return _calculate_annualized_ratio(expected_returns_mean, max_drawdown)
# print(expected_returns_mean, max_drawdown, calmar_ratio)
return calmar_ratio
def calculate_sqn(trades: pd.DataFrame, starting_balance: float) -> float:
+5 -2
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class CandleType(StrEnum):
class CandleType(str, Enum):
"""Enum to distinguish candle types"""
SPOT = "spot"
@@ -14,6 +14,9 @@ class CandleType(StrEnum):
FUNDING_RATE = "funding_rate"
# BORROW_RATE = "borrow_rate" # * unimplemented
def __str__(self):
return f"{self.name.lower()}"
@staticmethod
def from_string(value: str) -> "CandleType":
if not value:
+5 -2
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class MarginMode(StrEnum):
class MarginMode(str, Enum):
"""
Enum to distinguish between
cross margin/futures margin_mode and
@@ -11,3 +11,6 @@ class MarginMode(StrEnum):
CROSS = "cross"
ISOLATED = "isolated"
NONE = ""
def __str__(self):
return f"{self.value.lower()}"
+2 -2
View File
@@ -1,6 +1,6 @@
from enum import StrEnum
from enum import Enum
class OrderTypeValues(StrEnum):
class OrderTypeValues(str, Enum):
limit = "limit"
market = "market"
+2 -2
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class PriceType(StrEnum):
class PriceType(str, Enum):
"""Enum to distinguish possible trigger prices for stoplosses"""
LAST = "last"
+9 -4
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class RPCMessageType(StrEnum):
class RPCMessageType(str, Enum):
STATUS = "status"
WARNING = "warning"
EXCEPTION = "exception"
@@ -25,16 +25,21 @@ class RPCMessageType(StrEnum):
NEW_CANDLE = "new_candle"
def __repr__(self):
# TODO: do we still need to overwrite __repr__? Impact needs to be looked at in detail
return self.value
def __str__(self):
return self.value
# Enum for parsing requests from ws consumers
class RPCRequestType(StrEnum):
class RPCRequestType(str, Enum):
SUBSCRIBE = "subscribe"
WHITELIST = "whitelist"
ANALYZED_DF = "analyzed_df"
def __str__(self):
return self.value
NO_ECHO_MESSAGES = (RPCMessageType.ANALYZED_DF, RPCMessageType.WHITELIST, RPCMessageType.NEW_CANDLE)
+2 -2
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class RunMode(StrEnum):
class RunMode(str, Enum):
"""
Bot running mode (backtest, hyperopt, ...)
can be "live", "dry-run", "backtest", "hyperopt".
+13 -4
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class SignalType(StrEnum):
class SignalType(Enum):
"""
Enum to distinguish between enter and exit signals
"""
@@ -11,8 +11,11 @@ class SignalType(StrEnum):
ENTER_SHORT = "enter_short"
EXIT_SHORT = "exit_short"
def __str__(self):
return f"{self.name.lower()}"
class SignalTagType(StrEnum):
class SignalTagType(Enum):
"""
Enum for signal columns
"""
@@ -20,7 +23,13 @@ class SignalTagType(StrEnum):
ENTER_TAG = "enter_tag"
EXIT_TAG = "exit_tag"
def __str__(self):
return f"{self.name.lower()}"
class SignalDirection(StrEnum):
class SignalDirection(str, Enum):
LONG = "long"
SHORT = "short"
def __str__(self):
return f"{self.name.lower()}"
+5 -2
View File
@@ -1,7 +1,7 @@
from enum import StrEnum
from enum import Enum
class TradingMode(StrEnum):
class TradingMode(str, Enum):
"""
Enum to distinguish between
spot, margin, futures or any other trading method
@@ -10,3 +10,6 @@ class TradingMode(StrEnum):
SPOT = "spot"
MARGIN = "margin"
FUTURES = "futures"
def __str__(self):
return f"{self.name.lower()}"
-2
View File
@@ -30,7 +30,6 @@ from freqtrade.exchange.exchange_utils import (
validate_exchange,
)
from freqtrade.exchange.exchange_utils_timeframe import (
timeframe_to_floor_freq,
timeframe_to_minutes,
timeframe_to_msecs,
timeframe_to_next_date,
@@ -44,7 +43,6 @@ from freqtrade.exchange.htx import Htx
from freqtrade.exchange.hyperliquid import Hyperliquid
from freqtrade.exchange.idex import Idex
from freqtrade.exchange.kraken import Kraken
from freqtrade.exchange.krakenfutures import Krakenfutures
from freqtrade.exchange.kucoin import Kucoin
from freqtrade.exchange.lbank import Lbank
from freqtrade.exchange.luno import Luno
+3 -15
View File
@@ -46,13 +46,8 @@ class Binance(Exchange):
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
"ws_enabled": True,
"has_delisting": True,
# Demo trading
# https://www.binance.com/en/support/faq/detail/9be58f73e5e14338809e3b705b9687dd
# Intentionally Disabled as it's a separate market - not a simulated live market.
"supports_demo_trading": False,
}
_ft_has_futures: FtHas = {
"ohlcv_candle_limit": 499,
"funding_fee_candle_limit": 1000,
"stoploss_order_types": {"limit": "stop", "market": "stop_market"},
"stoploss_blocks_assets": False, # Stoploss orders do not block assets
@@ -73,7 +68,6 @@ class Binance(Exchange):
"BFUSD": "USDT",
},
}
_can_use_data_download_fast = True
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
(TradingMode.SPOT, MarginMode.NONE),
@@ -187,8 +181,7 @@ class Binance(Exchange):
return DataFrame(columns=DEFAULT_DATAFRAME_COLUMNS)
if (
not self._can_use_data_download_fast
or self._config["exchange"].get("only_from_ccxt", False)
self._config["exchange"].get("only_from_ccxt", False)
or
# only download timeframes with significant improvements,
# otherwise fall back to rest API
@@ -412,10 +405,7 @@ class Binance(Exchange):
) -> tuple[str, list[list]]:
logger.info(f"Fetching trades for {pair} from Binance, {from_id=}, {since=}, {until=}")
if (
not self._config["exchange"].get("only_from_ccxt", False)
and self._can_use_data_download_fast
):
if not self._config["exchange"].get("only_from_ccxt", False):
if from_id is None or not since:
trades = await self._api_async.fetch_trades(
pair,
@@ -559,7 +549,7 @@ class Binance(Exchange):
class Binanceusdm(Binance):
"""Binance USDM Exchange
"""Binacne USDM Exchange
Same as Binance - only futures trading is supported (via ccxt).
Not actually necessary, binance should be preferred.
@@ -579,5 +569,3 @@ class Binanceus(Binance):
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
(TradingMode.SPOT, MarginMode.NONE),
]
# binance vision does not have data for binanceus
_can_use_data_download_fast = False
File diff suppressed because it is too large Load Diff
+31 -52
View File
@@ -4,10 +4,9 @@ from datetime import datetime, timedelta
import ccxt
from freqtrade.constants import BuySell
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, PriceType, TradingMode
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
from freqtrade.exceptions import (
DDosProtection,
InvalidOrderException,
OperationalException,
RetryableOrderError,
TemporaryError,
@@ -39,13 +38,6 @@ class Bitget(Exchange):
_ft_has_futures: FtHas = {
"funding_fee_candle_limit": 100,
"has_delisting": True,
"stop_price_param": "stopLossPrice",
"stop_price_prop": "stopLossPrice",
"stop_price_type_field": "triggerType",
"stop_price_type_value_mapping": {
PriceType.LAST: "fill_price",
PriceType.MARK: "mark_price",
},
}
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
@@ -102,36 +94,30 @@ class Bitget(Exchange):
return order
def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> CcxtOrder:
# old stoploss orders
paramsold = {"stop": True}
# new stoploss orders with stopLossPrice (used in futures starting 2026.4)
paramsnew = {"planType": "profit_loss"}
params_to_try = (
(paramsnew, paramsold) if self.trading_mode == TradingMode.FUTURES else (paramsold,)
)
for params2 in params_to_try:
for method in (
self._api.fetch_open_orders,
self._api.fetch_canceled_and_closed_orders,
):
try:
orders = method(pair, params=params2)
orders_f = [order for order in orders if order["id"] == order_id]
if orders_f:
order = orders_f[0]
self._log_exchange_response("get_stop_order_fallback", order)
return self._convert_stop_order(pair, order_id, order)
except (ccxt.OrderNotFound, ccxt.InvalidOrder):
pass
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
raise TemporaryError(
f"Could not get order due to {e.__class__.__name__}. Message: {e}"
) from e
except ccxt.BaseError as e:
raise OperationalException(e) from e
params2 = {
"stop": True,
}
for method in (
self._api.fetch_open_orders,
self._api.fetch_canceled_and_closed_orders,
):
try:
orders = method(pair, params=params2)
orders_f = [order for order in orders if order["id"] == order_id]
if orders_f:
order = orders_f[0]
self._log_exchange_response("get_stop_order_fallback", order)
return self._convert_stop_order(pair, order_id, order)
except (ccxt.OrderNotFound, ccxt.InvalidOrder):
pass
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
raise TemporaryError(
f"Could not get order due to {e.__class__.__name__}. Message: {e}"
) from e
except ccxt.BaseError as e:
raise OperationalException(e) from e
raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).")
@retrier(retries=API_RETRY_COUNT)
@@ -143,19 +129,6 @@ class Bitget(Exchange):
return self._fetch_stop_order_fallback(order_id, pair)
def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict:
cancel_params = params.copy() if params else {}
cancel_params["stop"] = True
if self.trading_mode != TradingMode.FUTURES:
return self.cancel_order(order_id, pair, cancel_params)
try:
return self.cancel_order(order_id, pair, {**cancel_params, "planType": "pos_loss"})
except (InvalidOrderException, IndexError):
# Keep compatibility with stoploss orders created by older versions.
return self.cancel_order(order_id, pair, cancel_params)
@retrier
def additional_exchange_init(self) -> None:
"""
@@ -177,6 +150,12 @@ class Bitget(Exchange):
except ccxt.BaseError as e:
raise OperationalException(e) from e
def _lev_prep(self, pair: str, leverage: float, side: BuySell, accept_fail: bool = False):
if self.trading_mode != TradingMode.SPOT:
# Explicitly setting margin_mode is not necessary as marginMode can be set per order.
# self.set_margin_mode(pair, self.margin_mode, accept_fail)
self._set_leverage(leverage, pair, accept_fail)
def _get_params(
self,
side: BuySell,
-3
View File
@@ -35,9 +35,6 @@ class Bybit(Exchange):
# TODO: Can be removed once bybit fully forces all accounts to unified mode.
"fetchOrder": False,
},
# Demo trading
# https://learn.bybit.com/en/bybit-guide/how-to-use-bybit-demo-trading
"supports_demo_trading": True,
}
_ft_has_futures: FtHas = {
"ohlcv_has_history": True,
+3 -3
View File
@@ -47,14 +47,14 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool:
f"{', '.join(available_exchanges())}"
)
valid, reason, _, _ = validate_exchange(exchange)
valid, reason, _ = validate_exchange(exchange)
if not valid:
if check_for_bad:
raise OperationalException(
f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}.'
f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}'
)
else:
logger.warning(f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}.')
logger.warning(f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}')
if MAP_EXCHANGE_CHILDCLASS.get(exchange, exchange) in SUPPORTED_EXCHANGES:
logger.info(
+28 -33
View File
@@ -36,12 +36,12 @@ API_RETRY_COUNT = 4
API_FETCH_ORDER_RETRY_COUNT = 5
BAD_EXCHANGES = {
"bitmex": "Various reasons",
"probit": "Requires additional, regular calls to `signIn()`",
"poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders",
"kucoinfutures": "Unsupported futures exchange",
"poloniexfutures": "Unsupported futures exchange",
"binancecoinm": "Unsupported futures exchange",
"bitmex": "Various reasons.",
"probit": "Requires additional, regular calls to `signIn()`.",
"poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders.",
"kucoinfutures": "Unsupported futures exchange.",
"poloniexfutures": "Unsupported futures exchange.",
"binancecoinm": "Unsupported futures exchange.",
}
MAP_EXCHANGE_CHILDCLASS = {
@@ -62,7 +62,6 @@ SUPPORTED_EXCHANGES = [
"htx",
"hyperliquid",
"kraken",
"krakenfutures",
"okx",
"myokx",
]
@@ -79,35 +78,31 @@ EXCHANGE_HAS_REQUIRED: dict[str, list[str]] = {
"fetchOHLCV": [],
}
EXCHANGE_HAS_OPTIONAL: dict[str, list[str]] = {
EXCHANGE_HAS_OPTIONAL = [
# Private
"fetchMyTrades": [], # Trades for order - fee detection
"createLimitOrder": [],
"createMarketOrder": [], # Either OR for orders
"fetchMyTrades", # Trades for order - fee detection
"createLimitOrder",
"createMarketOrder", # Either OR for orders
# 'setLeverage', # Margin/Futures trading
# 'setMarginMode', # Margin/Futures trading
# 'fetchFundingHistory', # Futures trading
# Public
"fetchOrderBook": [],
"fetchL2OrderBook": [],
"fetchTicker": [], # OR for pricing
"fetchTickers": [], # For volumepairlist?
"fetchTrades": [], # Downloading trades data
"fetchOrders": ["fetchOpenOrders", "fetchClosedOrders"], # , # Refinding balance...
"fetchOrderBook",
"fetchL2OrderBook",
"fetchTicker", # OR for pricing
"fetchTickers", # For volumepairlist?
"fetchTrades", # Downloading trades data
# 'fetchFundingRateHistory', # Futures trading
# 'fetchPositions', # Futures trading
# 'fetchLeverageTiers', # Futures initialization
# 'fetchMarketLeverageTiers', # Futures initialization
# 'fetchOpenOrders', 'fetchClosedOrders', # 'fetchOrders', # Refinding balance...
# "fetchPremiumIndexOHLCV", # Futures additional data
# "fetchMarkOHLCV", # Futures additional data
# "fetchIndexOHLCV", # Futures additional data
# ccxt.pro
"watchOHLCV": [],
}
EXCHANGE_HAS_OPTIONAL_FUTURES: dict[str, list[str]] = {
# private
"setLeverage": [], # Margin/Futures trading
"setMarginMode": [], # Margin/Futures trading
"fetchFundingHistory": [], # Futures trading
# Public
"fetchFundingRateHistory": [], # Futures trading
"fetchPositions": [], # Futures trading
"fetchLeverageTiers": ["fetchMarketLeverageTiers"], # Futures initialization
"fetchMarkOHLCV": [],
"fetchIndexOHLCV": [], # Futures additional data
"fetchPremiumIndexOHLCV": [],
}
"watchOHLCV",
]
def calculate_backoff(retrycount, max_retries):

Some files were not shown because too many files have changed in this diff Show More