From 1271cd17d87b946bd75b5843163ffa8cc92c0b65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 18 Jul 2026 19:04:25 +0000 Subject: [PATCH] release: v0.13.1 --- docs/gpu-sweep-coverage-plan.md | 46 ++++++++++++--- pyproject.toml | 2 +- python/manifoldbt/plot/tearsheet.py | 7 ++- python/tests/test_plot_tearsheet.py | 91 +++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 python/tests/test_plot_tearsheet.py diff --git a/docs/gpu-sweep-coverage-plan.md b/docs/gpu-sweep-coverage-plan.md index 0f24ed6..13dd2ff 100644 --- a/docs/gpu-sweep-coverage-plan.md +++ b/docs/gpu-sweep-coverage-plan.md @@ -13,6 +13,30 @@ Log de mesures : `gpu_funding_bench.md`. Toute nouvelle phase y ajoute sa sectio --- +## État au 2026-07-17 : TOUTES les phases 0-5 shippées en 0.13.0 + +Phases **0, 1a, 1b, 2, 3, 4, 5 LIVRÉES** (voir chaque section + `gpu_funding_bench.md` +Parts 0-17). La 0.13.0 est publiée sur PyPI. Il ne reste que le backlog Phase 6. + +**Reste à faire (Phase 6), chaque cas se nomme quand il retombe sur CPU :** +- **Brackets multi-asset** : le kernel `sweep_t_multi` n'a pas de bracket (seul le + mono l'a, cf. Part 17). Raison nommée : « exit orders on a multi-asset universe + (the multi kernel has no bracket) ». +- **Frais per-venue** (2 exemples) : extension du fast path. +- **Cross-sectionnel** (1) : `sweep_t_multi`, restructuration en 2 passes. +- **MTF** (1) : grilles multiples, mapping d'index par timeframe. +- **Multi-asset + dérivées** (1) : l'exo/dérivée est globale, layout `[n_cols][n_sym][num_bars]`. + +**Branches en attente (hors périmètre GPU, à trier) :** +- `feat/import-dataframe` (1 commit) : import de barres depuis un DataFrame en + mémoire, sans round-trip CSV. Feature réelle, jamais mergée. +- `fix/python-test-suite` (2 commits) : branche pytest en CI + aligne les tests + golden/sweep. Vaut le coup (la CI ne teste pas pytest aujourd'hui), MAIS + rendrait la CI rouge sur le `test_sweep_returns_one_result_per_combo` golden + préexistant tant qu'il n'est pas réglé (plancher de sortie Pro). + +--- + ## Les trois couches qui bloquent une stratégie 1. **Ops transpileur manquants** : `Eq, Lag, Lead, RollingStd, RollingSum, @@ -55,14 +79,14 @@ Sans données, l'ordre ci-dessous est notre meilleure estimation. ## Phase 1 — Quick wins périmètre (≈½ journée) -### 1a. Funding multi-asset dans `sweep_t_multi` (~15 lignes) +### 1a. Funding multi-asset dans `sweep_t_multi` (~15 lignes, **FAIT** `bb4396c`) Le terme funding est déjà dans `sweep_t` (mono) : `capital += -position * close * rate` par barre, par symbole ici. Retirer le garde-fou `"multi-asset funding not yet on the GPU kernel"`. - **Validation** : colonne funding synthétique injectée, e2e multi bit-identique CPU==GPU (même harnais que le fix funding mono). -### 1b. Colonnes exogènes dans le sweep GPU (~30 lignes) +### 1b. Colonnes exogènes dans le sweep GPU (~30 lignes, **FAIT** `0a695e4`) `load_exo_columns` ASOF-joint déjà l'exo sur `master_timestamps` → un `Float64Array` de `num_bars`, indistinguable de `close` pour le kernel. Étendre la résolution des `col_names` du transpileur aux colonnes exo alignées, retirer @@ -179,13 +203,19 @@ plus intéressante du filtre), même sur CPU. --- -## Phase 4 — SL/TP sur le fast path GPU (gros ; décision APRÈS Phase 0) +## Phase 4 : SL/TP sur le fast path GPU (**FAIT 2026-07-17**) -`orders.is_empty()` est probablement le garde-fou le plus coûteux en usage réel -(hypothèse à confirmer par les données de la Phase 0). Résolution intra-bar -SL/TP = high/low + sémantique de priorité déjà fixée à la 0.12.0 côté lite CPU. -Design séparé — ne pas commencer avant d'avoir les données de fréquence et le -merge des phases précédentes. +**Livré** : brackets (stop-loss / take-profit / trailing) simulés dans le kernel +CUDA `sweep_t`, transcription 1:1 des phases B1/B0 de `sim_fast_lite_core_single` +(la référence CPU, elle-même tenue à `run()` par `backtest_orders.rs`). Gate +`allow_exits` sur `fast_path_blocker` (mono-asset seulement) ; OHL packés en un +buffer (arité cudarc). **5.3x vs CPU à 24k combos**, GPU==CPU exact. Le trou le +plus gros de l'audit exemples (5/18) est bouché → **exemples 7→10/18 sur GPU**. +Deux bugs livrés trouvés en route : signe de `max_drawdown` inversé sur tout +chemin lite, et ~360 lignes de bracket lite inatteignables. Détail : Part 17. + +Design d'origine (conservé) : résolution intra-bar SL/TP = high/low + sémantique +de priorité fixée à la 0.12.0 côté lite CPU. ## Phase 5 — Dispatch GPU/CPU (à faire À LA FIN) diff --git a/pyproject.toml b/pyproject.toml index 27fa4e0..0940b89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "manifoldbt" -version = "0.13.0" +version = "0.13.1" description = "Rust-powered backtesting engine for quantitative research" requires-python = ">=3.9" license = { file = "LICENSE" } diff --git a/python/manifoldbt/plot/tearsheet.py b/python/manifoldbt/plot/tearsheet.py index 18ec834..5128859 100644 --- a/python/manifoldbt/plot/tearsheet.py +++ b/python/manifoldbt/plot/tearsheet.py @@ -214,8 +214,11 @@ def tearsheet( # ── plotly.js include ───────────────────────────────────────── if plotlyjs == "inline": - import plotly.io as pio - plotly_js_tag = f"" + # get_plotlyjs lives in plotly.offline. plotly.io has never exposed it + # (checked on 5.24 and 6.9), so the old plotly.io lookup raised + # AttributeError on every inline report rather than embedding anything. + from plotly.offline import get_plotlyjs + plotly_js_tag = f"" else: plotly_js_tag = '' diff --git a/python/tests/test_plot_tearsheet.py b/python/tests/test_plot_tearsheet.py new file mode 100644 index 0000000..3e1a27c --- /dev/null +++ b/python/tests/test_plot_tearsheet.py @@ -0,0 +1,91 @@ +"""Tearsheet rendering, in particular the offline (``plotlyjs="inline"``) report. + +The inline path was written against ``plotly.io.get_plotlyjs``, which does not +exist and never has, so every offline report raised AttributeError. Nothing +exercised it, so nothing caught it. These tests render both modes for real. +""" +import os + +import pytest + +import manifoldbt as bt +from manifoldbt import run_with_parquet + +pytest.importorskip("plotly") + +tearsheet = pytest.importorskip("manifoldbt.plot.tearsheet").tearsheet + +# Roughly the size of the embedded plotly runtime; the point is to tell an +# actually-embedded bundle from a one-line CDN script tag, not to pin a version. +_RUNTIME_BYTES = 2_000_000 +_CDN_TAG = '