Skip to main content

Rust-implemented time-series utilities exposed to Python via PyO3.

Project description

rust_timeseries — Python bindings for duration models and tests

rust_timeseries is a Python-first package for

  • modelling event-time durations with ACD-type models,
  • calling a Rust-implemented maximum-likelihood optimizer,
  • extracting fitted parameters and optimizer diagnostics, and
  • running the Escanciano–Lobato heteroskedasticity proxy test.

All heavy computation runs in Rust (see lib.rs, duration, and statistical_tests), but the public surface is a small, typed Python API defined by the compiled _rust_timeseries extension and the stub files

  • duration_models.pyi
  • statistical_tests.pyi.

This document describes only what is actually exposed through those bindings.


1. Installation

From source, using maturin (development workflow):

maturin develop --release

Once the wheel is installed into your environment you can import:

import rust_timeseries as rts

and typically:

from rust_timeseries import duration_models, statistical_tests

A future PyPI release will allow installation via

pip install rust_timeseries

without changing the Python API.


2. Python modules

The Python-visible layout mirrors the bindings defined in lib.rs:

rust_timeseries/
    duration_models.py      # ACD models and optimization results
    statistical_tests.py    # Escanciano–Lobato test
    _rust_timeseries.*      # compiled extension (internal)

Rust-only modules such as duration, inference, optimization, and utils are implementation details and are not part of the public Python API.

Users should import only from

  • rust_timeseries.duration_models
  • rust_timeseries.statistical_tests.

3. Duration models (rust_timeseries.duration_models)

The duration_models module is the main entry point for ACD(p, q)-type duration processes. The public classes implemented in lib.rs and described in duration_models.pyi are:

  • ACD – duration model used from Python,
  • ACDOptimOutcome – optimization diagnostics,
  • ACDFittedParams – fitted model-space parameters.

3.1 Constructing an ACD model

ACD instances are configured once and reused across fits. The constructor exposes the same arguments that the PyO3 wrapper forwards into Rust:

from rust_timeseries.duration_models import ACD

acd = ACD(
    data_length=len(durations),  # in-sample length
    p=1,                         # AR order of ψ_t (optional)
    q=1,                         # MA order of durations (optional)
    init=None,
    init_fixed=None,
    init_psi_lags=None,
    init_durations_lags=None,
    tol_grad=None,
    tol_cost=None,
    max_iter=None,
    line_searcher=None,
    lbfgs_mem=None,
    psi_guards=None,
)

Key arguments (see duration_models.pyi for full details):

  • data_length: int
    Length of the in-sample duration series. This determines internal buffer sizes and is checked whenever you call fit, forecast, or standard_errors.
  • p: int | None, q: int | None
    Model orders. At least one of them must be positive.
  • init, init_fixed, init_psi_lags, init_durations_lags
    Initialization policy and associated values, forwarded unchanged to Rust-side initialization logic.
  • tol_grad, tol_cost, max_iter, line_searcher, lbfgs_mem
    Optimizer tolerances and configuration.
  • psi_guards: tuple[float, float] | None
    Optional lower/upper bounds on the conditional mean process ψ_t, mapped into Rust as PsiGuards.

The default constructor uses an exponential innovation distribution. Two alternative constructors are available:

  • ACD.wacd(...) – Weibull-innovation ACD(p, q) with shape parameter k.
  • ACD.gacd(...) – generalized-gamma-innovation ACD(p, q) with shape parameters p_shape and d_shape.

Both wacd and gacd accept the same configuration arguments as ACD (including data_length, p, q, and optimizer settings), plus their respective shape parameters.


3.2 Fitting a model (ACD.fit)

Model fitting is performed by calling fit on an ACD instance. The method is backed by the Rust function ACDModel::fit and stores results on the Python object; it does not return the fitted parameters directly.

Minimal example:

import numpy as np
from rust_timeseries.duration_models import ACD

durations = np.asarray(durations, dtype=float)

acd = ACD(data_length=len(durations), p=1, q=1)

# Initial parameter guess in the unconstrained parameterization.
theta0 = np.asarray(theta0, dtype=float)

acd.fit(
    durations=durations,
    theta0=theta0,
    unit="seconds",        # or other supported unit strings
    t0=None,               # optional index offset
    diurnal_adjusted=False # whether durations are already adjusted
)

Signature (from the bindings):

def fit(
    self,
    durations,
    theta0,
    unit: str | None = "seconds",
    t0: int | None = None,
    diurnal_adjusted: bool | None = False,
) -> None: ...

On success, the fitted model state is cached inside the Rust ACDModel. You can then access:

  • acd.results – an ACDOptimOutcome instance with optimizer diagnostics,
  • acd.fitted_params – an ACDFittedParams instance with model parameters.

Both are defined as @property-style getters in lib.rs.

If you call results or fitted_params before any successful fit, the bindings raise a Python exception originating from the Rust ACDError::ModelNotFitted variant.


3.3 Forecasting (ACD.forecast and ACD.forecast_result)

Forecasts are produced via ACD.forecast, which delegates to the Rust ACDModel::forecast and stores the forecast path internally:

horizon = 20

psi_h = acd.forecast(
    durations=durations,
    horizon=horizon,
    unit="seconds",
    t0=None,
    diurnal_adjusted=False,
)

Signature:

def forecast(
    self,
    durations,
    horizon: int,
    unit: str | None = "seconds",
    t0: int | None = None,
    diurnal_adjusted: bool | None = False,
) -> float: ...

The return value is the final forecasted ψ at the requested horizon, while the full path is cached and exposed through

  • acd.forecast_result – a 1-D list of float corresponding to the most recent forecast call.

If forecast has not yet been called, forecast_result returns an empty list.


3.4 Standard errors (ACD.standard_errors)

The standard_errors method computes model parameter standard errors for a given duration series, optionally using HAC corrections. It forwards options directly into the Rust-side HAC implementation via extract_hac_options and ACDModel::standard_errors.

Example:

se = acd.standard_errors(
    durations=durations,
    unit="seconds",
    t0=None,
    diurnal_adjusted=False,
    robust=True,
    kernel="bartlett",
    bandwidth=None,
    center=False,
    small_sample_correction=True,
)

Signature:

def standard_errors(
    self,
    durations,
    unit: str | None = "seconds",
    t0: int | None = None,
    diurnal_adjusted: bool | None = False,
    robust: bool | None = False,
    kernel: str | None = "bartlett",
    bandwidth: int | None = None,
    center: bool | None = False,
    small_sample_correction: bool | None = True,
) -> list[float]: ...

Return value:

  • A Python list[float] with standard errors corresponding to the fitted parameter vector. When robust=False, these are based on the model-based information matrix; when robust=True, a HAC estimator is used.

3.5 Optimization diagnostics (ACDOptimOutcome)

The ACD.results property returns an ACDOptimOutcome object, which is a thin wrapper around the Rust OptimOutcome type. The PyO3 bindings defined in lib.rs expose the following attributes:

  • theta_hat: list[float]
    Final parameter vector in the unconstrained parameterization.
  • value: float
    Objective value at the solution (e.g. maximized log-likelihood or its negation, depending on configuration).
  • converged: bool
    Whether the optimizer terminated with a successful status.
  • status: str
    Human-readable termination reason.
  • iterations: int
    Number of iterations taken.
  • grad_norm: float | None
    Norm of the gradient at the solution, when available.
  • fn_evals: list[tuple[str, int]]
    Evaluation counters keyed by function name.

A typical access pattern:

outcome = acd.results

print("Converged:", outcome.converged, "-", outcome.status)
print("Objective value:", outcome.value)
print("Iterations:", outcome.iterations)
print("Gradient norm:", outcome.grad_norm)
print("Function evals:", outcome.fn_evals)

3.6 Fitted parameters (ACDFittedParams)

The ACD.fitted_params property returns an ACDFittedParams instance that wraps the Rust ACDParams struct. The bindings in lib.rs expose the following read-only properties:

  • omega: float
    Baseline level parameter.
  • slack: float
    Positive slack term used to enforce strict stationarity.
  • alpha: list[float]
    Vector of ARCH-type coefficients (size p).
  • beta: list[float]
    Vector of GARCH-type coefficients (size q).
  • psi_lags: list[float]
    Initialization lags for ψ_t used at the start of the sample.

Example:

params = acd.fitted_params

print("omega:", params.omega)
print("slack:", params.slack)
print("alpha:", params.alpha)
print("beta:", params.beta)
print("psi_lags:", params.psi_lags)

These values are guaranteed (by the Rust-side constructors) to satisfy the model invariants documented in ACDParams (positivity, stationarity, shape constraints).


4. Escanciano–Lobato test (rust_timeseries.statistical_tests)

The statistical_tests module currently exposes a single class, EscancianoLobato, which implements the Escanciano–Lobato heteroskedasticity proxy test from the Rust type ELOutcome.

4.1 Constructing EscancianoLobato

The constructor validates inputs, converts them into a contiguous float64 slice, runs the test in Rust, and stores the result:

import numpy as np
from rust_timeseries.statistical_tests import EscancianoLobato

residuals = np.asarray(residuals, dtype=float)

el = EscancianoLobato(
    residuals,  # 1-D array-like of float64, no NaNs, length >= 1
    q=2.4,      # positive float proxy order (optional)
    d=None,     # positive integer max lag; default floor(n**0.2)
)

Signature (from statistical_tests.pyi and lib.rs):

class EscancianoLobato:
    def __init__(
        self,
        data,
        q: float | None = 2.4,
        d: int | None = None,
    ) -> None: ...

Validation rules enforced in the bindings:

  • data must be non-empty and contain no NaNs,
  • q must be positive when provided,
  • d must be positive when provided.

If any of these fail, a ValueError is raised from the PyO3 binding.

4.2 Accessing test results

EscancianoLobato wraps a Rust ELOutcome and exposes three read-only properties:

  • statistic: float
    The test statistic.
  • pvalue: float
    Asymptotic p-value under the null.
  • p_tilde: int
    Data-driven lag choice that maximizes the penalized statistic.

Example:

print("EL statistic:", el.statistic)
print("EL p-value:", el.pvalue)
print("Selected lag p~:", el.p_tilde)

5. Design notes

  • Python-first
    The core of the codebase lives in Rust, but the bindings are designed so that quantitative researchers can stay in Python. Inputs are standard array-like objects (numpy.ndarray, pandas.Series, Python lists), and outputs are plain Python scalars and lists.

  • Tight coupling to Rust invariants
    The PyO3 layer performs only shape checks, basic validation, and error mapping. All model invariants (positivity, stationarity, etc.) are enforced in the Rust types (ACDModel, ACDParams, ELOutcome).

  • Explicit error reporting
    Whenever a Rust error is raised (for example via the ACDError type), it is surfaced as a Python exception with a clear string message. Fitted-state- dependent getters (results, fitted_params) fail loudly if you call them before fit.

  • Minimal, inspectable objects
    The Python wrappers (ACD, ACDOptimOutcome, ACDFittedParams, EscancianoLobato) are intentionally small and read-only. They are easy to log, serialize, or convert to dictionaries for downstream analysis.


6. Status and extension points

The current Python API is intentionally narrow and corresponds exactly to the bindings implemented in lib.rs and documented in duration_models.pyi and statistical_tests.pyi. Natural extensions, to be added in Rust and then surfaced through the same pattern, include:

  • additional duration families that reuse the same ACD interface,
  • further goodness-of-fit and residual tests under statistical_tests,
  • higher-level helpers for multi-asset or panel duration data.

When extending the library, keep the following principles:

  • add functionality first in the Rust core,
  • expose it through a PyO3 wrapper (#[pyclass] or #[pymethods]),
  • update the .pyi stubs to match,
  • and only then document it here.

This keeps the README, the stubs, and the compiled extension in sync.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rust_timeseries-1.0.0.tar.gz (1.2 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rust_timeseries-1.0.0-cp313-cp313-win_amd64.whl (431.2 kB view details)

Uploaded CPython 3.13Windows x86-64

rust_timeseries-1.0.0-cp313-cp313-manylinux_2_34_x86_64.whl (605.5 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.34+ x86-64

rust_timeseries-1.0.0-cp313-cp313-macosx_11_0_arm64.whl (522.6 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

rust_timeseries-1.0.0-cp312-cp312-win_amd64.whl (431.6 kB view details)

Uploaded CPython 3.12Windows x86-64

rust_timeseries-1.0.0-cp312-cp312-manylinux_2_34_x86_64.whl (606.1 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

rust_timeseries-1.0.0-cp312-cp312-macosx_11_0_arm64.whl (522.8 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

rust_timeseries-1.0.0-cp311-cp311-win_amd64.whl (427.3 kB view details)

Uploaded CPython 3.11Windows x86-64

rust_timeseries-1.0.0-cp311-cp311-manylinux_2_34_x86_64.whl (603.4 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.34+ x86-64

rust_timeseries-1.0.0-cp311-cp311-macosx_11_0_arm64.whl (526.6 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

File details

Details for the file rust_timeseries-1.0.0.tar.gz.

File metadata

  • Download URL: rust_timeseries-1.0.0.tar.gz
  • Upload date:
  • Size: 1.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for rust_timeseries-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2f8b10152d50a49fcd7c281a8db58b416ec293d73eecc0279a46cc6184f7271d
MD5 e55d2e800d534a04efe769e51aa95d3a
BLAKE2b-256 1226c8ea529bb25963d25679cf4b733f0e039e87f22d9e660643074294bbec8b

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 534742a81ad452f7ee2a48ea950f4988bbc38826a2781d2b3a011b774784e921
MD5 0c185edf22ec3bfd3661127cb2f2cbcb
BLAKE2b-256 e67afa5bed65eeebc9723c61cfc2bed59c16d49708618df4c324d792fe931d7a

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp313-cp313-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 66db7d693fba6c231ea10805d103313798f131372881e2e004f0a44b4ece463c
MD5 091341d82296966228e7adf8042b6d00
BLAKE2b-256 c520710daaff58c84a189caed153f945e6edf293f35c2bf93b54a0180a7c0079

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 326cb1e627f282b58345dcdc59e30c9b01fa859d5efdfd0ffe577b06bd76a60f
MD5 83cecbe5536a2deb812be57bb3ee7816
BLAKE2b-256 e51832bf91a09d99b0ef0c0a55f7b48c6310bc8d47488c837d27fd1934b9d3d8

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 618b9750967661fcdd2b4d8622d99d043a047731c39b24487266eee2cf5555d2
MD5 493264565879401537b17d72f9f199f1
BLAKE2b-256 566ef5a99bb045b5768623e889e7c50176869131444dcf72c56af2357c67dc32

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 6486b54c2638707a50104b4eb3611c340e2d7ecfb133b5e9be85916885ff23ba
MD5 2960acfce5d94a8fcd1c423fe759ec89
BLAKE2b-256 3a2f6a912b880b33d76bb4d56a7d6e22635ab6c0bf735e2eab65ffd6055a6059

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 36076501f7240ff289d5442dedb16ce8b094421796af84a41fb44ff3940ddcec
MD5 407f1c902689b3c43de89b9b5978de9a
BLAKE2b-256 161ffae76990d5870c5f5252d517533920ea05a3b3cab1ece8519ddc828e98e1

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 b74a896d9aaec942cfec9f48ab6514be019bc19a638926dd7062f9cf34956bbd
MD5 f5ec87ca2e4637b7c01597efc9ead086
BLAKE2b-256 4df72ff962f1ae9898983f24322a679fb380692bec621bd07b103b7d351b7b10

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp311-cp311-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 0935b24b76d21ad403b8839cb7179a111d3a8d94855b0b851bc2776d63f3bffb
MD5 ad30b6f9c077eea570a6bcb87c1b9101
BLAKE2b-256 29d89ee12ed5cfbe1ed845fc71de08edbdcc042c6b10127ade1d82109ab734b9

See more details on using hashes here.

File details

Details for the file rust_timeseries-1.0.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rust_timeseries-1.0.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e2abfc1194bac6cc913d318a5e6157fe75dc6838b1b90d1dc6140dae6a133010
MD5 6cfea140df1a3ef44e967fc2287ddc04
BLAKE2b-256 39f19368b421169320cf2093986e5f1ae90ea2218098453cf4b475bce0e0c925

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page