name: Freqtrade CI
on:
push:
branches:
- stable
- develop
- ci/*
tags:
release:
types: [published]
pull_request:
schedule:
- cron: '0 3 * * 4'
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}"
cancel-in-progress: true
permissions: {}
jobs:
tests:
name: "Tests and Linting"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ "ubuntu-22.04", "ubuntu-24.04", "ubuntu-26.04", "ubuntu-24.04-arm", "macos-15" , "macos-26", "windows-2022", "windows-2025" ]
python-version: ["3.12", "3.13", "3.14"]
exclude:
- os: "ubuntu-22.04"
python-version: "3.13"
- os: "ubuntu-22.04"
python-version: "3.14"
- os: "ubuntu-24.04-arm"
python-version: "3.12"
- os: "ubuntu-26.04"
python-version: "3.12"
- os: "ubuntu-26.04"
python-version: "3.13"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7
with:
activate-environment: true
enable-cache: true
prune-cache: true
python-version: ${{ matrix.python-version }}
cache-dependency-glob: "requirements**.txt"
cache-suffix: "${{ matrix.python-version }}"
- name: Installation - macOS (Brew)
if: ${{ runner.os == 'macOS' }}
run: |
# brew update
# TODO: Should be the brew upgrade
brew install libomp
- name: Installation (python)
run: |
uv pip install -r requirements-dev.txt
uv pip install -e ft_client/
uv pip install -e .
- name: Check for version alignment
run: |
python build_helpers/freqtrade_client_version_align.py
- name: Tests
if: (!(runner.os == 'Linux' && matrix.python-version == '3.14' && matrix.os == 'ubuntu-24.04'))
run: |
pytest --random-order --durations 20 -n auto
- name: Tests with Coveralls
if: (runner.os == 'Linux' && matrix.python-version == '3.14' && matrix.os == 'ubuntu-24.04')
run: |
pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc
- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5
if: (runner.os == 'Linux' && matrix.python-version == '3.14' && matrix.os == 'ubuntu-24.04')
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
- name: Cleanup codecov dirty state files
if: (runner.os == 'Linux' && matrix.python-version == '3.14' && matrix.os == 'ubuntu-24.04')
run: |
# See https://github.com/codecov/codecov-action/issues/1851
rm -rf codecov codecov.SHA256SUM codecov.SHA256SUM.sig
- name: Run json schema extract
run: |
python build_helpers/extract_config_json_schema.py
- name: Run command docs partials extract
if: ${{ (matrix.python-version == '3.13') }}
run: |
python build_helpers/create_command_partials.py
- name: Check for repository changes - *nix
if: ${{ (runner.os != 'Windows') }}
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "Repository is dirty, changes detected:"
git status
git diff
exit 1
else
echo "Repository is clean, no changes detected."
fi
- name: Check for repository changes - Windows
if: ${{ runner.os == 'Windows' }}
run: |
if (git status --porcelain) {
Write-Host "Repository is dirty, changes detected:"
git status
git diff
exit 1
}
else {
Write-Host "Repository is clean, no changes detected."
}
- name: Backtesting (multi)
run: |
freqtrade create-userdir --userdir user_data
cp tests/testdata/config.tests.json user_data/config.json
freqtrade new-strategy -s AwesomeStrategy
freqtrade new-strategy -s AwesomeStrategyMin --template minimal
freqtrade new-strategy -s AwesomeStrategyAdv --template advanced
freqtrade backtesting --datadir tests/testdata --strategy-list AwesomeStrategy AwesomeStrategyMin AwesomeStrategyAdv -i 5m
- name: Hyperopt
run: |
cp tests/testdata/config.tests.json config.json
freqtrade create-userdir --userdir user_data
freqtrade hyperopt --datadir tests/testdata -e 6 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
- name: Run Ruff
run: |
ruff check --output-format=github
- name: Run Ruff format check
run: |
ruff format --check
- name: Mypy
if: ${{ matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-15' }}
run: |
mypy freqtrade scripts tests
- name: Run Pester tests (PowerShell)
if: ${{ runner.os == 'Windows' }}
shell: pwsh
run: |
$PSVersionTable
Install-PSResource -Name Pester -Version '[5.7.1]' -TrustRepository -Reinstall
$Error.clear()
Function Clear-Host { }
Invoke-Pester -Path "tests" -CI
if ($Error.Length -gt 0) {exit 1}
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708
if: ${{ failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) }}
with:
color: '#FF0000'
title: Freqtrade CI failed on ${{ matrix.os }} with Python ${{ matrix.python-version }}!
webhook: ${{ secrets.DISCORD_WEBHOOK }}
mypy-version-check:
name: "Mypy Version Check"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7
with:
activate-environment: true
python-version: "3.13"
enable-cache: false
- name: pre-commit dependencies
run: |
uv pip install $(grep -E "^pyyaml==" requirements-dev.txt)
python build_helpers/pre_commit_update.py
pre-commit:
name: "Pre-commit checks"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Set up Python 🐍
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: "3.14"
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd
docs-check:
name: "Documentation build"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Documentation syntax
run: |
./tests/test_docs.sh
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7
with:
activate-environment: true
python-version: "3.13"
enable-cache: false
- name: Documentation build
run: |
uv pip install -r docs/requirements-docs.txt
mkdocs build
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
color: '#FF0000'
title: Freqtrade doc test failed!
webhook: ${{ secrets.DISCORD_WEBHOOK }}
build-linux-online:
name: "Online / live tests"
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.14"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7
with:
activate-environment: true
enable-cache: true
prune-cache: true
python-version: "${{ matrix.python-version }}"
cache-dependency-glob: "requirements**.txt"
cache-suffix: "${{ matrix.python-version }}"
- name: Installation - *nix
run: |
uv pip install -r requirements-dev.txt
uv pip install -e ft_client/
uv pip install -e .
- name: Tests incl. ccxt compatibility tests
env:
CI_WEB_PROXY: http://140.238.213.2:13128
run: |
pytest --random-order --longrun --durations 20 -n auto
notify-complete:
name: "Notify CI Completion"
needs: [
build,
build-linux-online
]
runs-on: ubuntu-24.04
if: github.event_name != 'schedule' && github.repository == 'freqtrade/freqtrade'
steps:
- name: Discord notification
uses: sarisia/actions-status-discord@eb045afee445dc055c18d3d90bd0f244fd062708
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
with:
color: '#00FF00'
title: Test Completed!
webhook: ${{ secrets.DISCORD_WEBHOOK }}
build:
if: always()
name: "Build"
needs: [
tests,
docs-check,
mypy-version-check,
pre-commit,
]
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: ["3.13"]
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35
with:
jobs: ${{ toJSON(needs) }}
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Install uv and Python 🐍
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7
with:
activate-environment: true
python-version: "${{ matrix.python-version }}"
enable-cache: false
- name: Build distribution
run: |
uv pip install $(grep -E "^build==" requirements-dev.txt)
python -m build --sdist --wheel
- name: Upload artifacts 📦
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: freqtrade-build
path: |
dist
retention-days: 10
- name: Build Client distribution
run: |
python -m build --sdist --wheel ft_client
- name: Upload artifacts 📦
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: freqtrade-client-build
path: |
ft_client/dist
retention-days: 10
deploy-test-pypi:
name: "Publish Python 🐍 distribution 📦 to TestPyPI"
needs: [ build ]
runs-on: ubuntu-22.04
if: (github.event_name == 'release')
environment:
name: testpypi
url: https://test.pypi.org/p/freqtrade
permissions:
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Download artifact 📦
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
pattern: freqtrade*-build
path: dist
merge-multiple: true
- name: Publish to PyPI (Test)
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
with:
repository-url: https://test.pypi.org/legacy/
deploy-pypi:
name: "Publish Python 🐍 distribution 📦 to PyPI"
needs: [ build ]
runs-on: ubuntu-22.04
if: (github.event_name == 'release')
environment:
name: pypi
url: https://pypi.org/p/freqtrade
permissions:
id-token: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Download artifact 📦
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c
with:
pattern: freqtrade*-build
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33
docker-build:
name: "Docker Build and Deploy"
needs: [
build,
]
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
contents: read
secrets:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
packages-cleanup:
name: "Docker Package Cleanup"
uses: $/.github/workflows/packages-cleanup.yml
if: (github.event_name == 'push' || github.event_name == 'schedule') && github.repository == 'freqtrade/freqtrade'
permissions:
packages: write
with:
package_name: 'freqtrade'