Oscillator¶
Analytically scaled EWMA-difference oscillator for trend-following signals.
tinycta.osc
¶
Oscillator signal utilities built on Polars expressions.
This module provides a helper to compute an oscillator from price series using exponentially weighted moving averages (EWMA) and an analytical scaling factor. The functions are designed to be used inside Polars pipelines (e.g., with DataFrame.with_columns) and operate column-wise on numeric data.
osc(x, fast, slow, min_samples=1)
¶
Compute an analytically scaled EWMA-difference oscillator.
The oscillator is defined as (EMA_fast - EMA_slow) divided by the theoretical standard deviation of that difference under a unit-variance random walk: s = sqrt(1/(1-f²) - 2/(1-fg) + 1/(1-g²)) where f = 1 - 1/fast and g = 1 - 1/slow.
This gives consistent signal magnitudes regardless of the fast/slow parameter choice, without requiring a separate volatility lookback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Expr
|
Polars expression representing the price series to transform. |
required |
fast
|
int
|
Fast EWMA length (interpreted via |
required |
slow
|
int
|
Slow EWMA length (interpreted via |
required |
min_samples
|
int
|
Minimum number of observations required before EWMA means are emitted; controls warmup period (earlier rows are null until this threshold is met). |
1
|
Returns:
| Type | Description |
|---|---|
Expr
|
pl.Expr: A Polars expression representing the oscillator values. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
Example
prices = pl.DataFrame({"A": [1,2,3,4,5,6,7,8,9,10]}) df = prices.with_columns(osc(pl.col("A"), fast=2, slow=6).alias("osc_A"))
With min_samples=1 the first row is 0.0 (both EWMAs equal the first
observation), and the oscillator then rises as the fast mean pulls ahead
of the slow one on a trending series:
df["osc_A"].round(4).to_list() [0.0, 0.1117, 0.2836, 0.4985, 0.7389, 0.9898, 1.2398, 1.4809, 1.7086, 1.9202]
Note that the analytic scaling makes magnitudes comparable across
fast/slow choices only once the slower EWMA has warmed up; over a
span this short the slower pair is still in its transient.