quantlab.backend.ranged.RangedStore#

class quantlab.backend.ranged.RangedStore(path, owner, compute, *, guard=None, fill_values=None)[source]#

Bases: object

A store written by date range, the range recorded beside it.

Parameters:
pathstr or None

The Zarr store. None when the config names none: every method but store_range then refuses.

ownerstr

What the store is called in error and timer messages: a factor’s class name ("Momentum"), a risk model’s Class.part ("Use4RiskModel.regression").

computecallable

compute(start, end) returning the rows of the range, both inclusive, as an xr.Dataset on timestamp (and its other axes). It reads its own warm-up.

guardcallable, optional

guard(method) raises when the store may not be written (method is "build" or "extend"); it runs before anything is computed or written. A factor’s owner rule (ADR 0029).

fill_valuesmapping, optional

Per-variable fill values for cells a widening creates (see XrBackend.widen_and_append).

Attributes:
store_pathstr or None

As path.

ownerstr

As given.

Examples

>>> import numpy as np, pandas as pd, xarray as xr
>>> def compute(start, end):
...     times = pd.bdate_range(start, end)
...     return xr.Dataset(
...         {"x": (("timestamp", "symbol"), np.ones((len(times), 2)))},
...         coords={"timestamp": times, "symbol": ["a", "b"]},
...     )
>>> store = RangedStore("rows.zarr", "Rows", compute)
>>> store.build("2024-01-01", "2024-01-10")
>>> store.store_range()
('2024-01-01', '2024-01-10')
>>> store.check_covers("2024-01-02", "2024-01-11")
Traceback (most recent call last):
ValueError: Rows.read(): the store at rows.zarr covers 2024-01-01 to 2024-01-10, which does not contain 2024-01-02 to 2024-01-11. Extend it with extend(end) or rebuild it with build(start, end).
RANGE_SUFFIX = '.range.json'#

Appended to the store’s path to name the file recording its range.

append(rows, end)[source]#

Append rows, the bars after the recorded range up to end, and move its end.

The write half of extend, for rows computed elsewhere. Empty rows (no bar in the range) only move the end.

Raises:
ValueError

As extendable_range.

Examples

>>> store.append(compute("2024-01-08", "2024-01-10"), "2024-01-10")
>>> store.store_range()
('2024-01-01', '2024-01-10')
build(start, end)[source]#

Compute start to end and write it as the store, recording the range.

The store and its recorded range are replaced.

Parameters:
start, endstr, datetime.date or pd.Timestamp

The range the store covers.

Raises:
ValueError

If the store has no path, or the guard refuses the write.

Examples

>>> store.build("2024-01-01", "2024-01-10")
>>> store.store_range()
('2024-01-01', '2024-01-10')
check_covers(start, end)[source]#

Raise unless the recorded range contains start to end: a read’s refusals.

Parameters:
start, endstr, datetime.date or pd.Timestamp

The range a reader asks for. A date-only end includes every bar of that day.

Raises:
ValueError

If the store has no path or no recorded range, or the recorded range does not contain the request.

Examples

>>> store.check_covers("2024-01-02", "2024-01-09")   # inside: nothing happens
>>> store.check_covers("2024-01-02", "2024-01-11")
Traceback (most recent call last):
ValueError: Rows.read(): the store at rows.zarr covers 2024-01-01 to 2024-01-10, ...
extend(end)[source]#

Append the rows after the recorded range, up to end, and move its end.

The rows are computed from the first moment after the recorded end, so they are warmed from the inputs’ history; the store’s axes widen to fit.

Parameters:
endstr, datetime.date or pd.Timestamp

The new end of the store’s range.

Raises:
ValueError

As extendable_range.

Examples

>>> store.extend("2024-01-31")
>>> store.store_range()
('2024-01-01', '2024-01-31')
extendable_range(end)[source]#

Return the recorded range, refusing an extend to end.

Raises:
ValueError

If the store has no path, the guard refuses the write, the store has no recorded range, or the recorded range already reaches end.

Examples

>>> store.extendable_range("2024-01-31")
('2024-01-01', '2024-01-10')
>>> store.extendable_range("2024-01-05")
Traceback (most recent call last):
ValueError: Rows.extend(): the store at rows.zarr already covers 2024-01-01 to 2024-01-10; ...
property range_path#

The file recording the store’s range, <path>.range.json.

Examples

>>> RangedStore("rows.zarr", "Rows", compute).range_path
PosixPath('rows.zarr.range.json')
rewind(end)[source]#

Drop the bars after end and end the recorded range there: the inverse of extend.

The bars up to end stay as they are, and the symbol and variable axes keep every label, so the next extend computes the dropped bars again. The range is recorded first, then the store’s time axis shortened in place (XrBackend.shorten_axis): a rewind killed between the two leaves bars past the recorded end, which no read reaches and the next extend drops before it appends.

Parameters:
endstr, datetime.date or pd.Timestamp

The new end of the store’s range, inside it and before its end.

Raises:
ValueError

If the store has no path, the guard refuses the write, the store has no recorded range, end is not before the recorded end, or end is before the recorded start.

Examples

>>> store.build("2024-01-01", "2024-01-31")
>>> store.rewind("2024-01-15")
>>> store.store_range()
('2024-01-01', '2024-01-15')
>>> store.rewind("2024-01-15")
Traceback (most recent call last):
ValueError: Rows.rewind(): the store at rows.zarr covers 2024-01-01 to 2024-01-15; ...
store_range()[source]#

Return the (start, end) the store was built for; None when none is recorded.

Examples

>>> store.build("2024-01-01", "2024-01-10")
>>> store.store_range()
('2024-01-01', '2024-01-10')
write(rows, start, end)[source]#

Replace the store with rows, computed over start to end, and record the range.

The write half of build, for rows computed elsewhere (a chunk computed in a worker process).

Raises:
ValueError

If the store has no path, or the guard refuses the write.

Examples

>>> store.write(compute("2024-01-01", "2024-01-05"), "2024-01-01", "2024-01-05")
>>> store.store_range()
('2024-01-01', '2024-01-05')