Changelog¶
What changed in each release of py-beacon. The newest release is first.
The format follows Keep a Changelog and the version numbers follow Semantic Versioning. Before 1.0, a breaking change raises the middle number, as in 0.1 to 0.2.
Unreleased¶
0.2.0 - 2026-09-26¶
Choose where the engine's data comes from: named stores, synthetic data you can extend to today, CSV and Excel import, and read-only Postgres. Also documentation at pybeacon.dev, and fixes to index levels and backtests around splits and delistings.
Added¶
- Documentation at https://pybeacon.dev: a guide to each part of py-beacon (data, universes, methodology, expressions, backtests, funds, derivatives, the optimiser, risk, attribution, charts, reports and the server), and a complete Python reference. Every example in it runs.
- The engine can start without any data and load a store later. Loading runs as a job with progress, and the event socket announces
data.loadedwhen the new data is being served. - Named data stores. Register a data folder under a name you choose, see every store at
GET /data/stores, and switch which one the engine serves. The engine remembers the active store and serves it again on the next start. - Generate synthetic data from the engine:
POST /data/syntheticcreates a new store with the size, dates and seed you choose (end date defaulting to today), and serves it when it is ready. It runs as a job with progress, and gives exactly the same data aspython -m beacon.syntheticwith the same settings. python -m beacon.synthetic --progressprints a line at each stage, for a program running it.- Extend a generated store to today without changing its history:
python -m beacon.synthetic --extend PATH, orbeacon.synthetic.extend. Prices, exchange rates, listings, dividends, splits and features carry on from where the store stops, and the rows already there are never changed. The same store extended to the same date always gives the same data. Stores generated from this version on keep the settings this needs beside the data. - Import your own data from CSV files or an Excel workbook:
POST /data/importchecks every row and saves a new store, or refuses with one finding per problem naming its sheet, row and column.GET /data/import/templatedownloads a blank template, andbeacon.data.importing.load_filesdoes the same from Python. Dates are read as YYYY-MM-DD only, so a date cannot be read as the wrong day. - The
serverextra now includesexcel, for Excel import. - Refresh a store from its own source:
POST /data/stores/{id}/refreshextends synthetic data to today, reads a folder or database again, and saves what changes. If the store is being served, the engine serves the refreshed data. Each store inGET /data/storessays what a refresh would do. - A folder store can choose to refresh from Yahoo Finance instead (
refresh_from), which downloads new prices and saves them into the folder. It is never the default. - A data store can be a Postgres database. Give it tables or views named
market,reference, and optionallyfx,corporate_actionsandfeatures, with the import template's columns; views over your own tables work. The engine only reads, over a read-only connection, and checks every row as an import does. The password is never stored: name an environment variable that holds it. Needs the newpostgresextra.
Changed¶
- A backtest's return metrics start from its initial capital:
get_returns()has one return per simulated day, the first from the capital to the first close. The tracking difference, tracking error, volatility and drawdown now include the cost of the opening trades, which they used to leave out, so a run with costs no longer shows a positive tracking difference for that reason. - In a backtest result from the engine,
returns,drawdownandannual_returnsstart from the initial capital too, so they agree with the metrics.returnsnow has one value per day oflevel, the first from the capital to the first close.levelis unchanged. POST /data/coverage/{dataset}/syncis deprecated. It now refreshes the store being served from that store's own source, and no longer downloads from Yahoo Finance unless the store chose it. Its body is ignored, and its job is arefresh:{store_id}job. UsePOST /data/stores/{id}/refresh.- A request that needs data when none is loaded now answers 409
NO_DATA_LOADED, saying what could not be done. It answered 500CONFIGURATION_ERRORbefore. /healthsays which store is being served and whether one is loading./healthand the data events carrydata_version, a token that changes whenever the data being served changes. A client compares it to tell whether what it cached is still current.
Fixed¶
IndexFund.calculate_navkeeps the fund's trading cost when it extends a run to a later date. It used to re-run at zero cost.- A split, reverse split or stock dividend no longer moves an index level or a backtest's NAV. On the ex-date the units or shares held change by the ratio, as the price does. Before, a split between rebalances cut the level and NAV by the split, including in synthetic data, which splits every year.
- A constituent delisted the session before a rebalance no longer takes its weight out of the index level. It leaves first, as on any other day.
SelectionResult.excluded_bynames the right step when prices go stale. With stale names dropped, it credited each exclusion to the step before it, and a stale name to the last rule.- A damaged data file no longer stops the engine with a crash. It is refused with a message naming the file, and a damaged store found at startup is skipped with a warning.
0.1.1 - 2026-09-25¶
Added¶
DataFetcher.fx_routesays how a currency pair is converted: from a stored rate, its inverse, or a cross through USD.
Fixed¶
- A currency with no stored rate of its own is now converted using the inverse of the reverse pair, or a cross through USD. Before, an index in pounds holding US shares refused because only GBP to USD was stored.
- The quickstart in the README and on the docs home page runs again.
0.1.0 - 2026-09-24¶
The first release.
Added¶
- Build an index from rules and a weighting scheme: equal weight, market cap or free-float market cap, with optional weight caps.
- Calculate the index level on a real exchange calendar, as price, total return or net total return.
- Adjust the index for dividends, special dividends and delistings.
- Write selection rules as expressions, such as
data.market.market_cap > 1e9, including rules on company fundamentals and other features. - Backtest a portfolio that trades to the index weights, with transaction costs, drift thresholds and a benchmark.
- Read results with tracking error, attribution, risk, concentration and drift, and draw them as charts.
- Hold names in several currencies. Prices are converted with FX rates.
- Load data from files, generate a realistic synthetic dataset, or download prices with yfinance. Data is kept in a local store that reports its coverage and age.
- Three settings, shown on
/health: whether a missing FX rate carries forward, when a stale price drops a name, and how long a free float carries forward (90 days by default). - A local API server for the Beacon desktop app, covering data, indices, universes, backtests, the optimiser, risk, derivatives and PDF reports. Long tasks run as jobs with live progress.
- This changelog, served at
GET /changelogso the app can show what changed. - Optional extras keep the core install small:
data,excel,pdf,optimise,plotandserver.
Notes¶
- An index or backtest never uses a price, rate or free float dated after the day it is working on.
- Requires Python 3.11 or later.