Versioned time series: every revision kept, any past state queryable

Forecasts get revised, measurements get corrected, prices get republished. The Timeseries Refinery keeps every version of every series, so you can always see a series exactly as it was known at any past date.

Successive revisions of a wind power forecast overlaid in The Timeseries Refinery
Successive revisions of a wind power forecast, overlaid. Every one of them stays queryable.

What is a versioned time series?

A versioned time series keeps every revision of its data instead of overwriting it. Each value is tracked along two timelines: the value date, when the value applies, and the insertion date, when it became known. This is also called a bitemporal time series, or point-in-time data, known as vintages in economic statistics.

With both timelines stored, any series can be read as of any past date. The answer is exactly what was known at that moment, not what is known today.

Diagram: each forecast run, identified by its insertion date, covers the next three days of value dates; a query as of October 3 at 09:00 returns the run inserted at 08:00
The two timelines. Each forecast run (insertion date, vertical) covers the next three days (value date, horizontal). A query as of 3 October at 09:00 returns the latest run known at that time.

Why it matters in energy and trading

In energy markets, almost every series that matters gets revised. Keeping only the latest values erases the information you need most when something has to be explained or evaluated.

How it works in The Timeseries Refinery

Versioning is not an option to enable or a schema to design: it is how the storage works. Every write that changes a series creates a new version, stamped with its insertion date, its author and optional metadata. A new version only rewrites the part of the series that changed; everything else is shared with the previous versions, so frequent updates stay compact. A write that changes nothing creates no version.

The latest version is what you get by default. Any earlier one is a single parameter away.

Version browser of The Timeseries Refinery: successive revisions of a forecast overlaid, and the evolution of one value date across revisions
The version browser: successive revisions of a forecast overlaid (top), and how the value for a single date evolved from one revision to the next (bottom).

What you can query

Everything below is available in the web interface, the Python API and the REST API. The examples use a wind power forecast in MW, published four times a day, each run covering the next three days. In Python, series come back as pandas objects, indexed in UTC:

>>> import pandas as pd
>>> from tshistory.api import timeseries
>>> tsa = timeseries('https://refinery.example.com/api')
>>> name = 'power.fr.wind.forecast'
>>> start = pd.Timestamp('2026-10-05 10:00', tz='UTC')
>>> end = pd.Timestamp('2026-10-05 13:00', tz='UTC')

>>> # the latest forecast for these four hours
>>> tsa.get(name, from_value_date=start, to_value_date=end)
2026-10-05 10:00:00+00:00    8683.0
2026-10-05 11:00:00+00:00    8661.0
2026-10-05 12:00:00+00:00    8607.0
2026-10-05 13:00:00+00:00    8576.0
Name: power.fr.wind.forecast, dtype: float64

>>> # the same hours, as they were known on 3 October at 09:00
>>> tsa.get(name, revision_date=pd.Timestamp('2026-10-03 09:00', tz='UTC'),
...         from_value_date=start, to_value_date=end)
2026-10-05 10:00:00+00:00    7805.0
2026-10-05 11:00:00+00:00    7667.0
2026-10-05 12:00:00+00:00    7533.0
2026-10-05 13:00:00+00:00    7412.0
Name: power.fr.wind.forecast, dtype: float64

>>> # the revisions of the series
>>> len(tsa.insertion_dates(name))
44
>>> tsa.insertion_dates(name)[-1]
Timestamp('2026-10-08 20:00:00+0000', tz='UTC')

>>> # how the forecast for one hour evolved, revision after revision
>>> hist = tsa.history(name, from_value_date=start, to_value_date=start)
>>> for idate, series in hist.items():
...     print(idate, series.iloc[0])
2026-10-02 14:00:00+00:00 8149.0
2026-10-02 20:00:00+00:00 9508.0
2026-10-03 02:00:00+00:00 8826.0
2026-10-03 08:00:00+00:00 7805.0
(5 more revisions)
2026-10-04 20:00:00+00:00 8380.0
2026-10-05 02:00:00+00:00 8607.0
2026-10-05 08:00:00+00:00 8683.0
Chart: the forecast for 5 October 10:00 across twelve revisions, peaking at 9,508 MW, dropping to 7,805 MW and ending at 8,683 MW
The same output as a chart: the forecast for one hour, revision after revision.

The forecast for 10:00 on 5 October moved between 7,805 and 9,508 MW across its revisions before settling. Without versioning, only the last value would remain.

Rebuild the forecast you actually had

For forecasts, the useful question is often: what did the forecast say, a given time ahead? The staircase queries answer it. The example below rebuilds a week day by day, each day taken from the forecast as it was known at 10:00 Paris time the day before, ahead of the day-ahead auction.

Heatmap of a day-ahead price forecast: one row per revision date, one column per delivery hour, forming a staircase as each run extends the horizon
A day-ahead price forecast in the heatmap view: one row per revision, one column per delivery hour. Each run pushes the horizon further, which draws the staircase.
>>> ts = tsa.block_staircase(
...     name,
...     from_value_date=pd.Timestamp('2026-10-01', tz='Europe/Paris'),
...     to_value_date=pd.Timestamp('2026-10-07', tz='Europe/Paris'),
...     revision_freq={'days': 1},
...     revision_time={'hour': 10},
...     revision_tz='Europe/Paris',
...     maturity_offset={'days': 1},
...     maturity_time={'hour': 0},
... )
>>> ts.tz_convert('Europe/Paris').head(3)
2026-10-01 00:00:00+02:00    10192.0
2026-10-01 01:00:00+02:00    10118.0
2026-10-01 02:00:00+02:00    10081.0
Name: power.fr.wind.forecast, dtype: float64

The simpler staircase call does the same with a fixed horizon, for instance the forecast known at least 24 hours before each value date:

>>> tsa.staircase(name, delta=pd.Timedelta(hours=24),
...               from_value_date=start, to_value_date=end)
2026-10-05 10:00:00+00:00    8592.0
2026-10-05 11:00:00+00:00    8630.0
2026-10-05 12:00:00+00:00    8520.0
2026-10-05 13:00:00+00:00    8440.0
Name: power.fr.wind.forecast, dtype: float64

Computed series are versioned too

A versioned store is only half the story if the calculations on top of it are not. In The Timeseries Refinery, formulas follow the same rule: when you ask for a computed series as of a past date, every input is read as of that date, all the way down to the primary sources. A residual load, a spread or a portfolio aggregate can be evaluated on the data as it was known on any day, without storing a single computed value.

Formula definitions are archived as well: every change is logged with the user or script that made it. Note that a past date applies to the data, not to the definition: a computed series is always evaluated with its current formula.

Old versions tab of a formula, listing each archived definition with its date and user
The archived definitions of a formula, with the date and author of each change.

Manual corrections stay visible

Analysts sometimes need to override bogus values from a provider. These manual edits are recorded as such, with their author, and can be told apart from upstream data at any time. Later provider updates do not erase these corrections; only when the provider changes a value that was corrected by hand does its new value take over. The whole sequence remains in the history.

Common questions

Does keeping every version cost a lot of storage?

Each new version only rewrites the part of the series that changed, and shares the rest with the previous versions. A write that changes nothing creates no version at all.

Is reading an old version different from reading the latest one?

It is the same call with one extra parameter, the revision date. There is no restore or rebuild step.

Can I import the history I already have?

Yes. Each write accepts an explicit insertion date, so an existing archive of forecasts can be loaded with its original dates. Versions must be written in chronological order, oldest first: a series only accepts revisions newer than its latest one.

Can a version be removed?

Yes. All revisions from a given insertion date onwards can be stripped from a series, for instance to undo a faulty import. This operation is irreversible, and it is not available on series that carry manual corrections.

Request a demo Read the API documentation

The Timeseries Refinery is an open-source platform for storing, computing and visualising versioned time series data — built for data-driven teams in energy, trading and industry. Its bitemporal storage keeps every version of every series: any series can be queried as it was known at any past date (as-of queries) — and computed series follow the same law: formulas evaluate against the versions known at that date. It provides a traceable formula engine, real-time dashboarding, an Excel client, and full Python and REST APIs. Learn more