Utilities¶
Volatility-adjusted log returns and adjusted log-price helpers.
tinycta.util
¶
Volatility adjustment and price normalization helpers (Polars expressions).
This module provides expression-level building blocks used to standardize log returns by an exponentially weighted volatility estimate and to integrate those standardized returns into adjusted log-price series. These are designed for use within Polars pipelines (e.g., DataFrame.with_columns) and operate column-wise.
Functions: - vol_adj: Standardize log returns using EWMA volatility and clip extremes. - adj_log_prices: Cumulative sum (integration) of standardized, clipped returns.
adj_log_prices(x, vola, clip, min_samples=1)
¶
Integrate clipped, volatility-adjusted log returns to adjusted log prices.
Uses vol_adj to standardize/clamp log returns and then integrates them
via cumulative sum. The resulting series behaves like a standardized price-
like process with roughly unit volatility.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Expr
|
Polars expression of the price series to transform. |
required |
vola
|
int
|
EWMA lookback (span-equivalent) used to estimate volatility. |
required |
clip
|
float
|
Symmetric clipping threshold applied after standardization. |
required |
min_samples
|
int
|
Minimum samples required by EWM to emit non-null values. |
1
|
Returns:
| Type | Description |
|---|---|
Expr
|
pl.Expr: Adjusted-log-price series obtained by cumulative sum of standardized returns. |
Example
import polars as pl from tinycta.util import adj_log_prices, vol_adj prices = pl.DataFrame({"A": [100.0, 102.0, 101.0, 104.0, 103.0, 106.0]}) out = prices.with_columns( ... vol_adj(pl.col("A"), vola=3, clip=4.2).alias("adj"), ... adj_log_prices(pl.col("A"), vola=3, clip=4.2).alias("level"), ... )
The result is the running total of the standardised returns, so each level is the previous one plus the current adjusted return:
float(out["level"][2]) == float(out["adj"][2]) True round(float(out["level"][3]) - float(out["level"][2]), 12) == round(float(out["adj"][3]), 12) True
cum_sum carries the leading nulls through, so the level series starts
where the adjusted returns do:
out["level"].null_count() 2
Source code in src/tinycta/util.py
vol_adj(x, vola, clip, min_samples=1)
¶
Compute clipped, volatility-adjusted log returns per column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Expr
|
Price series to transform. |
required |
vola
|
int
|
EWMA lookback (span-equivalent) for std. |
required |
clip
|
float
|
Symmetric clipping threshold applied after standardization. |
required |
min_samples
|
int
|
Minimum samples required by EWM to yield non-null values.
Note that |
1
|
Returns:
| Type | Description |
|---|---|
Expr
|
pl.Expr: Standardized and clipped log returns. |
Example
import polars as pl from tinycta.util import vol_adj prices = pl.DataFrame({"A": [100.0, 102.0, 101.0, 104.0, 103.0, 106.0]}) out = prices.with_columns(vol_adj(pl.col("A"), vola=3, clip=4.2).alias("adj"))
The first row has no log return and the second has no ewm_std (it is
undefined for a single observation), so the series starts on the third row:
out["adj"].null_count() 2
Standardised returns keep the sign of the underlying move:
[v > 0 for v in out["adj"][2:]][False, True, False, True]
clip bounds the output symmetrically, which is what keeps a single
volatility spike from dominating a downstream signal:
tight = prices.with_columns(vol_adj(pl.col("A"), vola=3, clip=1.0).alias("adj")) all(-1.0 <= v <= 1.0 for v in tight["adj"][2:]) True