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.
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.
- Weather, load, wind and solar forecasts are republished several times a day. To evaluate a forecast provider or a model, you need each forecast as it stood when it was issued, not the latest one.
- A backtest that feeds yesterday's decisions with today's data looks better than reality. Reading every input as of the decision time removes this look-ahead bias by construction.
- Grid operators and data providers correct published data after the fact. The original and corrected values both remain available, together with the date the correction arrived.
- When an auditor, a regulator or a manager asks why a position was taken on a given day, you can replay the exact data that was available at that time.
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.
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
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.
>>> 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.
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.