Metadata-Version: 2.4
Name: lpma
Version: 0.1.0
Summary: LPMA: a zero-lag, non-ringing moving average built from linearly weighted IIR poles.
Project-URL: Homepage, https://github.com/SmoothingLab/lpma-python
Project-URL: Repository, https://github.com/SmoothingLab/lpma-python
Project-URL: Issues, https://github.com/SmoothingLab/lpma-python/issues
Author-email: Marcus John Hendry Don <marcus@smoothinglab.com>
License: MIT
License-File: LICENSE
Keywords: ema,filter,iir-filter,low-lag,moving-average,signal-processing,smoothing,technical-analysis,time-series,zero-lag
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.10
Requires-Dist: xpma>=0.1.0
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Provides-Extra: examples
Requires-Dist: matplotlib>=3.5; extra == 'examples'
Requires-Dist: numpy>=1.21; extra == 'examples'
Description-Content-Type: text/markdown

# lpma

`lpma` is a small Python library with one filter in it: **LPMA**, the Linear
Pole Moving Average. It is a zero-lag smoother built by cascading identical
IIR poles and combining their cumulative outputs with weights that fall on a
straight line against tap position. That linear weighting is what keeps the
filter free of ringing: its step response crosses the target once and
settles, unlike a naive Kalman-style blend of the same poles, which oscillates.

If you want a zero-lag moving average without the ringing that usually comes
with squeezing lag out of a filter, this is that filter.

## In plain English

- **Zero lag**: LPMA's weights are chosen so the filter's output has no
  steady-state lag on a trending input, the same target `XEPMA` (in the
  companion `xpma` package) aims at.
- **No ringing**: unlike a textbook Kalman-filter blend of the same poles
  (which typically shows ten or more oscillations settling to a step), LPMA
  shows exactly one overshoot and then a monotone settle. This is proved for
  every integer smoothness order and pole count, not just measured.
- **The mechanism**: each pole is an `IFEMA` (a fractional-order EMA cascade,
  from the `xpma` package), and the cumulative pole outputs are combined with
  the unique minimum-weight-energy weighting that satisfies the two zero-lag
  constraints. That minimum-weight-energy property, not a search for least
  overshoot, is what picks out the linear weights.

## Install

```bash
pip install lpma
```

The companion `xpma` package comes with it, as a declared dependency
(`xpma>=0.1.0`); there is nothing else to install.

## Quickstart

```python
from lpma import LPMA

prices = [10.0, 10.4, 10.2, 11.1, 12.3, 11.8, 12.5, 13.0, 12.7, 13.4]

smoother = LPMA(period=20, smoothness=2.0, num_poles=3)
smoothed = [smoother.get_next(p) for p in prices]

print(smoothed[-1])   # the current smoothed value
```

Like every filter in the `xpma` family, `LPMA` shares a tiny interface:
`get_next(x)` feeds one new sample and returns the updated value; `calc_next(x)`
asks "what would the output be for this next sample?" without changing
anything. `period` and `smoothness` accept fractional values; `num_poles` does
too (see below).

## Parameters

`LPMA(period, smoothness, num_poles)`, all three required:

- **`period`**: a real number greater than 1. Larger means more smoothing and
  more lag budget spent across the poles.
- **`smoothness`**: the order of the per-pole `IFEMA` cascade. `1` behaves like
  a single EMA per pole; higher removes more noise. Fractional values are
  supported (including `0 < smoothness < 1`, the `IFEMA` shelf regime).
- **`num_poles`**: how many poles to cascade, `>= 2`. `num_poles = 2` with
  `smoothness = 1` is identical to DEMA. Integer values (`2`, `3`, `4`, ...)
  are the standard, well-characterised construction. Fractional values are
  also accepted; see below.

## Fractional pole count

`num_poles` does not have to be a whole number. Writing `n = (m - 1) + f` with
`m = ceil(n)`, the first `m - 1` poles are the standard per-pole `IFEMA` and
the final pole is a shorter one carrying just the fractional share `f` of the
per-pole lag; weights are the closed-form, minimum-weight-energy solution
computed in exact rational arithmetic.

Fractional `num_poles` is most useful in `(2, 3)`, the coarsest integer gap in
the family (it interpolates between DEMA and `LPMA(period, smoothness, 3)`).
The reachable improvement in noise gain, overshoot and settling narrows
quickly for larger `n` (diminishing returns by `n ~ 8-12`): it is a
reachability tool for landing on a specific point between two integers, not a
new efficient-frontier point, and no fractional `n` dominates both integer
neighbours at once.

One caveat worth knowing before sweeping `num_poles` continuously: the family
is right-discontinuous at every integer `n >= 3` (the limit as `n` approaches
an integer from below does not reduce to the shorter integer configuration,
except at `n = 2`). The jump is small (about 2% in the affected metrics at
`n = 3`, growing slowly with `n`), but it means a continuous sweep across an
integer boundary meets a real, if minor, step.

## What is proved, and what is corroborated

This library is honest about its guarantees:

- **No ringing at every integer smoothness order (`s >= 1`), every pole count
  (`n >= 2`) and every period: proved.** The step response's error has exactly
  one sign change; this is a genuine discrete-time proof, not a continuous
  approximation or a numerical spot-check.
- **No ringing at fractional smoothness below 1 (`0 < s < 1`): proved up to
  one exactly-verified step.** The structural argument is complete and the
  remaining step is certified in exact arithmetic, one sign-count step short
  of the integer-order standard.
- **No ringing at fractional smoothness above 1 (non-integer `s > 1`):
  corroborated at scale, not fully proved.** The problem has been reduced to a
  single unimodality conjecture (verified with zero counterexamples across
  many thousands of configurations, plus a proved special case), but a general
  proof for every non-integer order above 1 remains open.
- **The linear weighting is proved to be the minimum-weight-energy
  (equivalently minimum-roughness, unique affine-in-position) zero-lag
  weighting.** It is not a minimum-overshoot or minimum-noise-gain optimum;
  weightings with less overshoot or less input-noise gain exist elsewhere in
  the zero-lag weight space, at the cost of larger, more oscillatory weights.
  No-ringing is a load-bearing, separately proved property, not a consequence
  of the weighting being "gentlest".
- **Fractional pole count**: no ringing checked exhaustively (no
  counterexamples found), not proved; see the caveat above for the one
  structural gap (non-nestedness) that is proved to exist.

## Documentation

- [docs/getting-started.md](docs/getting-started.md): install, your first
  smoothing, streaming vs the stateless probe.
- [docs/api.md](docs/api.md): the full parameter and behaviour reference.
- [docs/theory.md](docs/theory.md): why linear weights, why no ringing, and
  what is proved versus corroborated.
- [examples/](examples/): a runnable script.

## Paper

LPMA's construction, its transient-shape theorems and its design space are
written up in:

> Marcus John Hendry Don, *Zero lag without ringing: linearly weighted
> cascades of exponential poles* (2026). DOI:
> [10.5281/zenodo.21476430](https://doi.org/10.5281/zenodo.21476430).
> Reproduction archive:
> [10.5281/zenodo.21476438](https://doi.org/10.5281/zenodo.21476438).

The `xpma` foundations (the discounted-regression framing, the IFEMA cascade
LPMA's poles are built from) are written up in the companion paper:

> Marcus John Hendry Don, *Maximal monotone lag reduction in exponentially weighted
> smoothers: the two-rate discounted-regression family and its critical
> reductions* (2026). DOI:
> [10.5281/zenodo.21343537](https://doi.org/10.5281/zenodo.21343537).

## Licence

Released under the MIT Licence. See [LICENSE](LICENSE).
