Metadata-Version: 2.4
Name: precise
Version: 1.0.0rc1
Summary: Online (incremental) covariance and correlation estimation — the online complement to sklearn.covariance
Project-URL: Homepage, https://github.com/microprediction/precise
Project-URL: Documentation, https://microprediction.github.io/precise/
Project-URL: Repository, https://github.com/microprediction/precise
Author-email: Peter Cotton <peter.cotton@microprediction.com>
License: MIT
License-File: LICENSE
Keywords: correlation,covariance,incremental,ledoit-wolf,online,precision-matrix,shrinkage,streaming
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.9
Requires-Dist: numpy>=1.21
Provides-Extra: dev
Requires-Dist: black>=24.0; extra == 'dev'
Requires-Dist: build; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pandas>=1.3; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: scikit-learn>=1.1; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.0; extra == 'docs'
Requires-Dist: mkdocs>=1.5; extra == 'docs'
Provides-Extra: pandas
Requires-Dist: pandas>=1.3; extra == 'pandas'
Provides-Extra: research
Requires-Dist: pandas>=1.3; extra == 'research'
Requires-Dist: randomcov>=0.1; extra == 'research'
Provides-Extra: skaters
Requires-Dist: skaters>=0.1; extra == 'skaters'
Description-Content-Type: text/markdown

# precise

![ci](https://github.com/microprediction/precise/workflows/ci/badge.svg) ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg) ![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)

**Online (incremental) covariance and correlation estimation** — the streaming complement to
[`sklearn.covariance`](https://scikit-learn.org/stable/modules/covariance.html), whose estimators
are batch-only and have no `partial_fit`. Pure Python + numpy; no other required dependencies.

```bash
pip install precise
```

## Use

```python
from precise import EwaCovariance

est = EwaCovariance(r=0.05)
for y in stream:            # y is a 1d observation; pass a 2d array for a batch
    est.partial_fit(y)

est.covariance_             # (n, n) ndarray
est.correlation_            # unit-diagonal correlation
est.precision_             # inverse covariance
est.location_              # running mean
est.fit(X)                 # sklearn-style batch drop-in (X is 2d)
```

Every estimator is **truly online** — a constant amount of work per observation, no growing
buffers. State is a plain dict, so you can checkpoint mid-stream with `get_state()` / `set_state()`.

## Estimators

| Class | What it does |
|---|---|
| `EmpiricalCovariance` | running sample covariance (Welford) |
| `EwaCovariance` | exponentially weighted (recency-biased) |
| `AdaptiveEwaCovariance` | EWMA whose forgetting rate speeds up on regime change |
| `LedoitWolfCovariance` | online Ledoit-Wolf shrinkage towards a scaled identity |
| `OASCovariance` | online Oracle Approximating Shrinkage (often better-conditioned than LW) |
| `ShrunkCovariance` | fixed-intensity shrinkage to identity **or** a constant-correlation target |
| `PartialMomentsCovariance` | exponentially weighted partial-moment (semi-)covariance |
| `HuberCovariance` | online robust estimator that downweights outliers |
| `TylerCovariance` | recursive Tyler M-estimator — robust correlation/shape for elliptical data |
| `GeodesicEwaCovariance` | recency-weighted update along the affine-invariant SPD geodesic |
| `DCCCovariance` | dynamic conditional correlation — decouples volatility from correlation |
| `FactorCovariance` | online low-rank + diagonal (approximate factor model); O(d·k) per step |

```python
from precise import all_estimators, estimator_from_name
all_estimators()                          # the list of classes (a bake-off in one loop)
estimator_from_name("LedoitWolfCovariance")
```

## Keyed / dynamic universes (river-style)

In streaming/finance settings observations arrive as **dicts keyed by name**, and the set of names
can change over time. `keyed(...)` decorates *any* of the estimators above to consume keyed dicts
(river-style `update` / `learn_one`) and emit keyed output:

```python
from precise import keyed, EwaCovariance

d = keyed(EwaCovariance(r=0.05), dynamic=True)   # changing universe (DynamicUniverse)
d.update({"AAPL": 0.01, "MSFT": -0.02})
d.update({"MSFT": 0.00, "NVDA": 0.03})           # AAPL leaves, NVDA enters
d.covariance_                                     # dict-of-dicts over the live universe
d.to_frame()                                      # pandas DataFrame  (pip install precise[pandas])

k = keyed(EwaCovariance(r=0.05))                  # fixed universe, imputes missing keys (FixedUniverse)
```

`dynamic=False` (the default) gives a `FixedUniverse` (one wrapped estimator, missing keys imputed);
`dynamic=True` gives a `DynamicUniverse` (a wrapped estimator per live key-set). Both work with any
positional estimator — the adapter adds no covariance math of its own.

## Composing volatility × correlation

`H = D R D` is a composition, not a fixed algorithm. `ConditionalCovariance` lets you pick the
per-series **volatility** model and the **correlation** estimator independently — `DCCCovariance` is
just the EWMA/EWMA special case:

```python
from precise import ConditionalCovariance, EwaCovariance, LedoitWolfCovariance

est = ConditionalCovariance(vol=EwaCovariance(r=0.02),       # any estimator, used per series in 1-D
                            corr=LedoitWolfCovariance(r=0.05))  # correlation from any estimator
```

The volatility model can also be any univariate model from
[microprediction/skaters](https://github.com/microprediction/skaters) (Holt, Hosking, …) via
`from_skater` — precise doesn't depend on it; the adapter is duck-typed:

```python
import skaters
from precise import ConditionalCovariance, from_skater
est = ConditionalCovariance(vol=from_skater(skaters.holt), corr=EwaCovariance(r=0.05))
```

## Related

- **Generating** random covariance/correlation matrices to test against: [`randomcov`](https://github.com/microprediction/randomcov).
- **Portfolio construction** (Schur-complementary allocation, HRP) moved to [`schur`](https://github.com/microprediction/schur); for production use the [skfolio](https://skfolio.org/auto_examples/clustering/plot_6_schur.html) implementation is recommended.
- A [Robust Portfolio Literature Reading List](https://github.com/microprediction/precise/blob/main/LITERATURE.md) lives in this repo.
- Part of the [microprediction](https://github.com/microprediction/microprediction) project.

> Migrating from precise &lt; 1.0 (the functional "skater" API)? See [MIGRATING.md](https://github.com/microprediction/precise/blob/main/MIGRATING.md).

## Disclaimer

Not investment advice. Just code, subject to the MIT License.
