Skip to content

Commit badd468

Browse files
quantbaiclaude
andcommitted
release: v0.3.0
[NUMERICAL] ts_covariance unified to ddof=0, fixing cov/(std*std)==corr identity. Documentation restructured: OPERATORS.md as reference, rationale in CLAUDE.md. Fixed incorrect signatures (trade_when, scale, bucket) and README example. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c650efc commit badd468

4 files changed

Lines changed: 27 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ Numerical changes are marked with [NUMERICAL].
77

88
## [Unreleased]
99

10+
## [0.3.0] - 2026-03-24
11+
12+
### Fixed
13+
- [NUMERICAL] `ts_covariance`: unified to ddof=0 (population), consistent with all other
14+
variance/std operators. Fixes the broken identity `cov/(std_x*std_y) == corr` which
15+
previously had ~5-20% error due to mixed ddof. Cross-validated against numpy (diff < 1e-15).
16+
17+
### Changed
18+
- OPERATORS.md rewritten as pure operator reference manual (signatures, behavior, edge cases)
19+
- Design rationale moved to CLAUDE.md Section 4.1 (developer-facing)
20+
- Fixed incorrect signatures in docs: `trade_when`, `scale`, `bucket`
21+
- Fixed README example code to use only columns present in sample data
22+
1023
## [0.2.0] - 2026-03-23
1124

1225
### Added
@@ -24,7 +37,7 @@ Numerical changes are marked with [NUMERICAL].
2437

2538
### Fixed
2639
- [NUMERICAL] `ts_product`: silently returned null for negative inputs; now correctly handles negative values via sign-magnitude decomposition
27-
- [NUMERICAL] `ts_covariance`: used ddof=0 (population) inconsistent with `ts_corr` (ddof=1); aligned to ddof=1 (sample)
40+
- [NUMERICAL] `ts_covariance`: added explicit ddof parameter (was using Polars default)
2841
- [NUMERICAL] `divide()`: no zero-denominator protection; now returns null where abs(divisor) < 1e-10
2942
- [NUMERICAL] `inverse()`: no zero protection; now returns null where abs(x) < 1e-10
3043
- `ts_regression` rettype=7 (MSE): implicit Inf-to-null on window=2; now has explicit guard

OPERATORS.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Elvers Operator Reference
22

3-
79 operators. All accept and return `Factor`.
3+
72 operators. All accept and return `Factor`.
44

55
---
66

@@ -50,9 +50,9 @@ Negation: `-x`.
5050

5151
Remaps group labels to consecutive integers `0..(n-1)` per timestamp. Dense rank (ties get same value).
5252

53-
### `bucket(x, n)`
53+
### `bucket(x, buckets=None, range_params=None)`
5454

55-
Assigns values to `n` equal-frequency buckets by cross-sectional rank. Returns integer labels. Null in, null out.
55+
Assigns values to bucket indices based on edge boundaries. Provide either `buckets` (list of edges) or `range_params` (start, end, step). Returns integer labels. Null in, null out.
5656

5757
---
5858

@@ -213,9 +213,9 @@ Rolling OLS regression.
213213

214214
Zero guard on `sum(x^2) < 1e-10` and `SST < 1e-10`.
215215

216-
### `trade_when(cond, x, exit_value=-1.79e308)`
216+
### `trade_when(trigger, alpha, exit_cond)`
217217

218-
Hold `x` when `cond` is true, forward-fill otherwise. Sentinel-based exit.
218+
Hold `alpha` when `trigger > 0`, null when `exit_cond > 0`, forward-fill otherwise. All three parameters are Factors.
219219

220220
---
221221

@@ -241,9 +241,9 @@ Cross-sectional mean, broadcast to all symbols.
241241

242242
Cross-sectional median, broadcast to all symbols.
243243

244-
### `scale(x, target=1, longscale=1, shortscale=1)`
244+
### `scale(x, target=1.0, longscale=0.0, shortscale=0.0)`
245245

246-
Scale so `sum(abs(x)) = target`. Separate long/short via `longscale`/`shortscale`. Returns 0 if sum < 1e-10.
246+
Scale so `sum(abs(x)) = target`. When `longscale`/`shortscale` are non-zero, scale long and short legs separately. Returns 0 if sum < 1e-10.
247247

248248
### `normalize(x, use_std=False, limit=0.0)`
249249

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ pip install elvers
3939
## Usage
4040

4141
```python
42-
from elvers import load, ts_rank, ts_regression, zscore, signal, group_neutralize
42+
from elvers import load, ts_rank, ts_regression, zscore, signal
4343

44-
panel = load("ohlcv.parquet") # or load() for built-in sample data
44+
panel = load() # built-in sample data (crypto 1d OHLCV)
4545
close, volume = panel["close"], panel["volume"]
4646

4747
momentum = ts_rank(close, 20)
4848
vol_adj = zscore(momentum) / zscore(ts_rank(volume, 20))
4949
beta_resid = ts_regression(close, volume, window=60, rettype=0)
50-
alpha = signal(group_neutralize(vol_adj, panel["sector"]))
50+
alpha = signal(vol_adj)
5151
```
5252

5353
Sub-daily data is supported via the `interval` parameter:
@@ -58,7 +58,7 @@ panel = load("hourly.parquet", interval="1h")
5858

5959
## Operators
6060

61-
70+ operators. All accept and return `Factor`.
61+
72 operators. All accept and return `Factor`.
6262

6363
**Time-Series** -- rolling window per symbol:
6464

elvers/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
"""
2-
Elvers - Polars-native factor expression engine.
3-
4-
High-performance, strictly-typed multi-factor alpha research
5-
built on Polars for lightning-fast factor computation.
2+
Elvers - Polars-native factor computation engine.
63
74
Author: quantbai
85
"""
96

10-
__version__ = "0.2.0"
7+
__version__ = "0.3.0"
118
__author__ = "quantbai"
129

1310
from .core import Factor

0 commit comments

Comments
 (0)