publish.py records GITHUB_RUN_STARTED_AT, a variable Actions never sets,
so every published result had an empty run_started_at. The publish job
now asks `gh run view` for the run's startedAt, with actions: read to be
allowed to, and passes it through `date` so that only a date can reach
$GITHUB_ENV. A failed lookup leaves the field empty, as before.
plotly 7.0.0 landed on PyPI and removed the mapbox trace types that
vectorbt 0.28.4's template still references, so 'import vectorbt' died
before a single bar ran. plotly was the one transitive dependency the
lock did not cover; it does now, on 6.9.0, the last 6.x line. The
version printout includes it so the results stay comparable.
render() wrote the size of the first documented point and stopped, so a
run with three bracket_sl_tp timings published one measured line for
three rows. A divergence can grow with the number of bars, so each point
now gets its own line, as a list item, and the cause follows once.
test_report.py renders hand-built payloads and imports no engine.
Seven badges on one wrapped line buried the only one meant to be clicked. Discord
now sits alone above the rest, in the large style, and the second line keeps four
informational ones: PyPI, Python, Rust, benchmarks. Dropped the licence badge,
which repeated the LICENSE link already in the footer.
The pyversions badge was rendering "missing" in red, and it was right to. Every
published wheel carries NO `Programming Language :: Python :: 3.x` classifier:
crates/bt-python/pyproject.toml, the file maturin actually builds from, has no
`classifiers` field at all. The public pyproject.toml has one, but it does not
build anything, so its list never reached PyPI. `requires-python = ">=3.9"` is a
separate field and does not populate classifiers.
Replaced with a static "python 3.9+" badge, since PyPI cannot answer the question
for 0.18.0 whatever we ask it. The engine-side fix travels separately; once a
wheel ships with classifiers, PyPI's sidebar will list the versions too and this
can go back to being dynamic.
The performance table was still quoting run #11 (manifoldbt 0.17.3), while the
site publishes run #13 (0.18.0) from the same workflow. Same shape, different
numbers, and the headline claim disagreed with the site by a few percent:
10M bars with the metric suite is 329 ms against 102.38 s, a factor of 311,
not 317 ms / 97.46 s / 308. Every row now comes from run 32469701489, the one
data/benchmark.json is synced from.
Added the stop-loss/take-profit bracket row, where raptorbt is level with us
(x1.0). It belongs next to the five-asset row for the same reason that one is
there: deciding which of the stop and the target triggers first is a sequential
intra-bar walk in both engines, so there is nothing left to vectorise. Publishing
only the workloads we win looks better and reads worse.
Qualified the backtrader figure. It is measured on a developer machine with a
different script, not on the CI runner, so quoting it beside a CI table implied a
comparability it does not have. It also no longer appears in the headline bullet:
the site makes no numeric backtrader claim, and the two engines are not doing the
same work.
Documented the two additions that were reachable but undocumented:
- tf("1h").apply(expr) evaluates on that timeframe's own grid, so periods count
in ITS bars. The note warns about sma(tf("1h").close, 20), which looks
equivalent and is not: it counts simulation bars over a step-held series, so
on a 1m run it is a 20-minute smoothing of an hourly staircase.
- choice(name, {branch: expr}) makes a selector a grid axis.
Every snippet in these two sections was executed verbatim against the 0.19.0rc1
wheel before committing, including the warning, which was checked to actually
differ rather than asserted.
Header badges: PyPI version and Python versions (both live), Rust core, CUDA,
benchmarks-in-CI linking the workflow, and the licence.
PyPI accepte un televersement bien avant que son index le serve. Mesure sur la
publication de 0.18.0: le televersement s'est termine a 01:48:18 et ce workflow
a demande la version a 01:48:50, trente-deux secondes plus tard, pour se faire
repondre "from versions: ..., 0.17.3".
Ce n'est pas un hasard de timing. Le workflow est declenche par
`release: published`, et cette release est creee juste apres le televersement:
la course est donc GARANTIE a chaque publication. Elle n'avait jamais pu se voir
parce que ce chemin n'avait jamais tourne sur une vraie release, seulement sur
des declenchements manuels ou la version existait depuis longtemps.
Les deux jobs attendent desormais que l'index serve la version, jusqu'a dix
minutes, contre soixante de budget. `--no-cache-dir` parce que pip met en cache
la reponse de l'index, y compris celle qui ne connait pas encore la version: sans
cela les tentatives suivantes reliraient la meme reponse perimee.
Logique verifiee sur quatre cas avant de pousser (disponible tout de suite,
disponible apres trois echecs, jamais disponible donc echec franc, et pas de
version imposee), plutot que de la decouvrir a la prochaine release.
Source-available rather than fully OSI open-source. Use, modify and
self-host remain free. Reselling the software or running it as a paid
hosted service is now explicitly disallowed by the Commons Clause.
- Cross-exchange backtesting (Pro)
- Dict universe format (provider-based symbol resolution)
- Exogenous data support (register_exo + exo() expressions)
- Provider-based data layout (binance/1h/TICKER.arrow)
- Preload fix for provider layout
- Exo column resampling for multi-resolution
- Pro gate for cross-exchange (clean exit)
- ATR/ADX rolling SMA fix
- Precise mode hybrid fills
- Add ingest() function for downloading bar data from providers
- Add manifoldbt CLI entry point (manifoldbt ingest ...)
- Supported providers: binance, hyperliquid (free), databento, massive (Pro)
- Update type stubs for py_ingest