From 52dc3519e763023177bdffc7f02a1f98765a576a Mon Sep 17 00:00:00 2001 From: Luca Date: Mon, 4 May 2026 19:37:18 +0100 Subject: [PATCH] Release notes --- .github/copilot-instructions.md | 12 +++++ .github/workflows/build.yml | 3 -- .github/workflows/release.yml | 78 +++++++++++++++++++++++++++++++++ Makefile | 11 +++++ docs/release-notes.md | 68 ++++++++++++++++++++++++++++ mkdocs.yml | 1 + 6 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 docs/release-notes.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7ec3f14..3c4cde7 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -45,3 +45,15 @@ applyTo: '/**' * Strategy runtime markdown descriptions (read by `load_description()` at runtime) live inside the package at `quantflow/options/strategies/docs/` — they must be inside the package to be accessible when the library is installed * mkdocs documentation pages live in `docs/api/options/` — do not mix these two locations + +## Releasing + +The release procedure is fully driven by `make release` and the `release.yml` workflow: + +1. Bump `version` in `pyproject.toml` to the new release version. +2. Add a `## vX.Y.Z` section to `docs/release-notes.md` with the notes for the release. The header text is matched verbatim by the workflow's `awk` extractor, so it must be `## vX.Y.Z` exactly (no trailing dash, no title after the version). +3. Commit and merge to `main`. +4. From `main`, run `make release` — it reads the version from `pyproject.toml`, prompts for confirmation, then creates an annotated `vX.Y.Z` tag and pushes it. +5. The tag push triggers `.github/workflows/release.yml`, which runs lint and tests, publishes the package to PyPI (`make publish`), and posts the extracted `## vX.Y.Z` section as the GitHub Release body. + +Do not publish to PyPI manually or via the old `head_commit.message == 'release'` flow — the tag-triggered workflow is the only supported path. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2da905f..1ca8a86 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,9 +49,6 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} files: ./build/coverage.xml - - name: publish - if: ${{ matrix.python-version == '3.14' && github.event.head_commit.message == 'release' }} - run: make publish image: if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..052d708 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,78 @@ +name: release + +# Triggered by `v*` tags. Builds and publishes the package to PyPI, then +# extracts the matching section from docs/release-notes.md and publishes it +# as the GitHub Release body. Re-runs idempotently update an existing +# release rather than failing. + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + env: + PYTHON_ENV: ci + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + FMP_API_KEY: ${{ secrets.FMP_API_KEY }} + steps: + - uses: actions/checkout@v4 + + - name: install rops + uses: quantmind/rops/.github/actions/setup-rops@main + env: + GITHUB_TOKEN: ${{ secrets.TOKEN_DEPLOYMENT }} + + - name: install taplo + run: rops tools update taplo + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.14" + + - name: Install uv + run: pip install -U pip uv + + - name: Install dependencies + run: make install-dev + + - name: run lint + run: make lint-check + + - name: run tests + run: make tests + + - name: publish to PyPI + run: make publish + + - name: Extract release notes + run: | + TAG="${GITHUB_REF_NAME}" + awk -v tag="## ${TAG}" ' + $0 == tag { in_section = 1; next } + /^## / && in_section { exit } + in_section { print } + ' docs/release-notes.md > /tmp/notes.md + if [ ! -s /tmp/notes.md ]; then + echo "No section '## ${TAG}' found in docs/release-notes.md" >&2 + exit 1 + fi + echo "--- extracted notes for ${TAG} ---" + cat /tmp/notes.md + + - name: Publish GitHub Release + run: | + TAG="${GITHUB_REF_NAME}" + if gh release view "$TAG" >/dev/null 2>&1; then + gh release edit "$TAG" --notes-file /tmp/notes.md + else + gh release create "$TAG" --title "$TAG" --notes-file /tmp/notes.md + fi diff --git a/Makefile b/Makefile index 8e4dbd7..269b988 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,17 @@ publish: ## Release to pypi @uv build @uv publish --token $(PYPI_TOKEN) +.PHONY: release +release: ## Tag current version (from pyproject.toml) and push + $(eval VERSION := $(shell grep '^version' pyproject.toml | head -1 | sed 's/version = "\(.*\)"/\1/')) + @read -p "Tagging with v$(VERSION), are you sure? [Y/n] " ans; \ + ans=$${ans:-Y}; \ + if [ "$$ans" = "Y" ] || [ "$$ans" = "y" ]; then \ + git tag -a v$(VERSION) -m "v$(VERSION)" && git push origin v$(VERSION); \ + else \ + echo "Aborted."; \ + fi + .PHONY: tests tests: ## Unit tests @./dev/test diff --git a/docs/release-notes.md b/docs/release-notes.md new file mode 100644 index 0000000..d209014 --- /dev/null +++ b/docs/release-notes.md @@ -0,0 +1,68 @@ +# Release Notes + +This page is the source of truth for quantflow release notes. Each section +below maps to a tagged release on +[GitHub](https://github.com/quantmind/quantflow/releases). When a new tag is +pushed, the matching section is extracted by +`.github/workflows/release.yml` and published as the GitHub Release body. + +## v0.8.0 + +Volatility-surface calibration overhaul. This release adds a two-factor BNS +model, a double-Heston model (with optional jumps), Lewis and COS pricing +methods, and reworks the calibration package layout. Several module renames +and signature changes were made along the way: see **Breaking changes** below. + +### Breaking changes + +**Module renames.** + +- `quantflow.sp.weiner` is now `quantflow.sp.wiener` (typo fix). Update + imports. +- `quantflow.options.calibration` is now a package, not a single module. + Top-level imports keep working through the package `__init__.py` + re-exports. Code reaching into the old `quantflow.options.heston_calibration` + must switch to `quantflow.options.calibration.heston`. + +**`ModelOptionPrice` field rename.** (#47) + +- `ModelOptionPrice.moneyness` previously meant `log(K/F)`. It now means + standardised moneyness `log(K/F) / sqrt(ttm)`, and the raw log-strike is + exposed as a new field `log_strike`. Code reading `option.moneyness` and + expecting a log-strike must switch to `option.log_strike`. +- `get_intrinsic_value(moneyness=...)` argument renamed to `log_strike=...`. + +### New features + +- **`BNS2`**: two-factor Barndorff-Nielsen & Shephard stochastic-volatility + model with a single Brownian motion driving a convex combination of + independent Gamma-OU variances and per-factor leverage. New section in the + BNS calibration tutorial. (#54) +- **`DoubleHeston` and `DoubleHestonJ`**: two-factor Heston (with optional + log-price jumps) and matching `DoubleHestonCalibration` / + `DoubleHestonJCalibration`. (#46) +- **Lewis and COS option-pricing methods**: selectable via + `OptionPricingMethod`, alongside the existing Carr-Madan / FFT path. (#47) +- **CIR tutorial** with PDF comparison example. (#49) + +### Improvements and fixes + +- Heston calibration convergence fixes. (#45, #49) +- BNS calibration: dedicated `BNSCalibration` class extracted, characteristic + exponent derivation cleaned up, broader test coverage. (#50, #51) +- OU module reworked: clearer Gamma-OU API, stronger tests for moments and + the integrated Laplace transform. (#51) +- `pricing_method_comparison` example simplified; redundant time-comparison + code removed. (#48) + +### Documentation and assets + +- New logo set (favicon, lockup, marks, social banners) under + `docs/assets/logos/`. (#53) +- New `docs/mcp.md` page covering the MCP server. +- Bibliography rebuilt from BibTeX via `docs/bib2md.py`; glossary expanded; + mathjax tweaks for inline rendering. (#47, #49) +- Tutorial-writing instructions added at + `.github/instructions/tutorial.instructions.md`. + +[Full changelog](https://github.com/quantmind/quantflow/compare/v0.7.0...v0.8.0) diff --git a/mkdocs.yml b/mkdocs.yml index 085eb53..16ed196 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -131,6 +131,7 @@ nav: - Glossary: glossary.md - Contributing: contributing.md - Bibliography: bibliography.md + - Release Notes: release-notes.md markdown_extensions: - attr_list - tables