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: repository-projects: read jobs: build-linux: runs-on: ${{ matrix.os }} strategy: matrix: os: [ "ubuntu-22.04", "ubuntu-24.04" ] python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install uv uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # v6.5.0 with: activate-environment: true enable-cache: true python-version: ${{ matrix.python-version }} cache-dependency-glob: "requirements**.txt" cache-suffix: "${{ matrix.python-version }}" prune-cache: false - 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 . - name: Check for version alignment run: | python build_helpers/freqtrade_client_version_align.py - name: Tests if: (!(runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04')) run: | pytest --random-order - name: Tests with Coveralls if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04') run: | pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc - name: Coveralls if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04') env: # Coveralls token. Not used as secret due to github not providing secrets to forked repositories COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu run: | # Allow failure for coveralls uv pip install coveralls coveralls || true - name: Run json schema extract # This should be kept before the repository check to ensure that the schema is up-to-date run: | python build_helpers/extract_config_json_schema.py - name: Run command docs partials extract # This should be kept before the repository check to ensure that the docs are up-to-date run: | python build_helpers/create_command_partials.py - name: Check for repository changes # TODO: python 3.13 slightly changed the output of argparse. if: (matrix.python-version != '3.13') 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: Backtesting (multi) run: | cp tests/testdata/config.tests.json config.json freqtrade create-userdir --userdir user_data freqtrade new-strategy -s AwesomeStrategy freqtrade new-strategy -s AwesomeStrategyMin --template minimal freqtrade backtesting --datadir tests/testdata --strategy-list AwesomeStrategy AwesomeStrategyMin -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: Sort imports (isort) run: | isort --check . - 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' run: | mypy freqtrade scripts tests - name: Discord notification 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: severity: error details: Freqtrade CI failed on ${{ matrix.os }} webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} build-macos: runs-on: ${{ matrix.os }} strategy: matrix: os: [ "macos-14", "macos-15" ] python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} check-latest: true - name: Install uv uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # v6.5.0 with: activate-environment: true enable-cache: true python-version: ${{ matrix.python-version }} cache-dependency-glob: "requirements**.txt" cache-suffix: "${{ matrix.python-version }}" prune-cache: false - name: Installation - macOS (Brew) run: | # brew update # TODO: Should be the brew upgrade # homebrew fails to update python due to unlinking failures # https://github.com/actions/runner-images/issues/6817 rm /usr/local/bin/2to3 || true rm /usr/local/bin/2to3-3.11 || true rm /usr/local/bin/2to3-3.12 || true rm /usr/local/bin/idle3 || true rm /usr/local/bin/idle3.11 || true rm /usr/local/bin/idle3.12 || true rm /usr/local/bin/pydoc3 || true rm /usr/local/bin/pydoc3.11 || true rm /usr/local/bin/pydoc3.12 || true rm /usr/local/bin/python3 || true rm /usr/local/bin/python3.11 || true rm /usr/local/bin/python3.12 || true rm /usr/local/bin/python3-config || true rm /usr/local/bin/python3.11-config || true rm /usr/local/bin/python3.12-config || true brew install libomp - name: Installation (python) run: | uv pip install wheel uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . - name: Tests run: | pytest --random-order - name: Check for repository changes 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: Backtesting run: | cp tests/testdata/config.tests.json config.json freqtrade create-userdir --userdir user_data freqtrade new-strategy -s AwesomeStrategyAdv --template advanced freqtrade backtesting --datadir tests/testdata --strategy AwesomeStrategyAdv - name: Hyperopt run: | cp tests/testdata/config.tests.json config.json freqtrade create-userdir --userdir user_data freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all - name: Sort imports (isort) run: | isort --check . - name: Run Ruff run: | ruff check --output-format=github - name: Run Ruff format check run: | ruff format --check - name: Mypy if: matrix.os == 'macos-15' run: | mypy freqtrade scripts - name: Discord notification 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: severity: info details: Test Succeeded! webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} build-windows: runs-on: ${{ matrix.os }} strategy: matrix: os: [ windows-latest ] python-version: ["3.11", "3.12", "3.13.6"] steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install uv uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # v6.5.0 with: activate-environment: true enable-cache: true python-version: ${{ matrix.python-version }} cache-dependency-glob: "requirements**.txt" cache-suffix: "${{ matrix.python-version }}" prune-cache: false - name: Installation run: | function uvpipFunction { uv pip $args } Set-Alias -name pip -value uvpipFunction python -m pip install --upgrade pip pip install -r requirements-dev.txt pip install -e . - name: Tests run: | pytest --random-order --durations 20 -n auto - name: Check for repository changes 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 run: | cp tests/testdata/config.tests.json config.json freqtrade create-userdir --userdir user_data freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy - name: Hyperopt run: | cp tests/testdata/config.tests.json config.json freqtrade create-userdir --userdir user_data freqtrade hyperopt --datadir tests/testdata -e 5 --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 run: | mypy freqtrade scripts tests - name: Run Pester tests (PowerShell) run: | $PSVersionTable 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} shell: powershell - name: Discord notification 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: severity: error details: Test Failed webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} mypy-version-check: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: pre-commit dependencies run: | pip install pyaml python build_helpers/pre_commit_update.py pre-commit: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-python@v5 with: python-version: "3.12" - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 docs-check: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Documentation syntax run: | ./tests/test_docs.sh - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Documentation build run: | pip install -r docs/requirements-docs.txt mkdocs build - name: Discord notification 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: severity: error details: Freqtrade doc test failed! webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} build-linux-online: # Run pytest with "live" checks runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install uv uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 # v6.5.0 with: activate-environment: true enable-cache: true python-version: "3.12" cache-dependency-glob: "requirements**.txt" cache-suffix: "3.12" prune-cache: false - 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 . - name: Tests incl. ccxt compatibility tests env: CI_WEB_PROXY: http://152.67.78.211:13128 run: | pytest --random-order --longrun --durations 20 -n auto # Notify only once - when CI completes (and after deploy) in case it's successful notify-complete: needs: [ build-linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit, build-linux-online ] 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: Check user permission id: check uses: prince-chrismc/check-actor-permissions-action@d504e74ba31658f4cdf4fcfeb509d4c09736d88e # v3.0.2 with: permission: "write" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Discord notification uses: rjstone/discord-webhook-notify@c2597273488aeda841dd1e891321952b51f7996f #v2.2.1 if: always() && steps.check.outputs.has-permission && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) with: severity: info details: Test Completed! webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} build: name: "Build" needs: [ build-linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit ] runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Build distribution run: | pip install -U build python -m build --sdist --wheel - name: Upload artifacts 📦 uses: actions/upload-artifact@v4 with: name: freqtrade-build path: | dist retention-days: 10 - name: Build Client distribution run: | pip install -U build python -m build --sdist --wheel ft_client - name: Upload artifacts 📦 uses: actions/upload-artifact@v4 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@v5 with: persist-credentials: false - name: Download artifact 📦 uses: actions/download-artifact@v5 with: pattern: freqtrade*-build path: dist merge-multiple: true - name: Publish to PyPI (Test) uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 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@v5 with: persist-credentials: false - name: Download artifact 📦 uses: actions/download-artifact@v5 with: pattern: freqtrade*-build path: dist merge-multiple: true - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 docker-build: name: "Docker Build and Deploy" needs: [ build-linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit ] 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: DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}