Files
QuanTAlib/lib/numerics/normalize/Normalize.md
Miha Kralj 4ab3a7fb53 doc headers
2026-02-27 07:48:12 -08:00

236 lines
7.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NORMALIZE: Min-Max Normalization
| Property | Value |
| ---------------- | -------------------------------- |
| **Category** | Numeric |
| **Inputs** | Source (close) |
| **Parameters** | `period` (default 14) |
| **Outputs** | Single series (Normalize) |
| **Output range** | Varies (see docs) |
| **Warmup** | `period` bars |
### TL;DR
- The Normalize transformer applies min-max scaling to map any value series into the bounded range [0, 1] based on the observed minimum and maximum w...
- Parameterized by `period` (default 14).
- Output range: Varies (see docs).
- Requires `period` bars of warmup before first valid output (IsHot = true).
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
> "Normalization is the art of making apples and oranges comparable—by insisting that everything lives on the same scale from 0 to 1."
The Normalize transformer applies min-max scaling to map any value series into the bounded range [0, 1] based on the observed minimum and maximum within a rolling lookback window. This technique is fundamental for feature scaling, creating bounded oscillators, and comparing series with different magnitudes.
## Mathematical Foundation
### Core Formula
$$
\text{Norm}_t = \frac{x_t - \min_{[t-n+1, t]}}{\max_{[t-n+1, t]} - \min_{[t-n+1, t]}}
$$
where:
- $x_t$ is the input value at time $t$
- $n$ is the lookback period
- $\min_{[t-n+1, t]}$ is the minimum value in the window
- $\max_{[t-n+1, t]}$ is the maximum value in the window
### Edge Case: Flat Range
When $\max = \min$ (all values identical):
$$
\text{Norm}_t = 0.5
$$
This neutral value is returned since the "position" within a zero-width range is undefined.
### Key Properties
| Property | Value | Description |
|:---------|:------|:------------|
| **Range** | $[0, 1]$ | Guaranteed bounded output |
| **Min maps to** | 0 | Lowest value in window → 0 |
| **Max maps to** | 1 | Highest value in window → 1 |
| **Linear** | Yes | Preserves relative distances within window |
| **Invertible** | Yes* | If you know min/max |
*Given the min and max used, original value = Norm × (max - min) + min
## Financial Applications
### Oscillator Construction
Convert any price-based measure to oscillator form:
$$
\text{NormalizedRSI} = \text{Normalize}(\text{RSI}, 100)
$$
### Cross-Asset Comparison
Compare instruments with different price scales:
$$
\text{RelativeStrength} = \text{Normalize}(\text{Price}_A, n) - \text{Normalize}(\text{Price}_B, n)
$$
### Machine Learning Features
Prepare inputs for models requiring bounded features:
$$
\text{Feature}_i = \text{Normalize}(x_i, \text{lookback})
$$
### Dynamic Range Detection
Identify where price sits within recent range:
$$
\text{Position} = \text{Normalize}(\text{Close}, 20)
$$
Values near 1.0 indicate price at recent highs; near 0.0 at recent lows.
## Parameter Guide
### Period Selection
| Period | Behavior | Use Case |
|:-------|:---------|:---------|
| 5-10 | Highly responsive | Short-term oscillators |
| 14-20 | Standard | General normalization |
| 50-100 | Smooth | Position within broader context |
| 200+ | Very stable | Long-term percentile-like behavior |
### Period Effects
- **Shorter periods**: More volatile output, quicker adaptation to new ranges
- **Longer periods**: Smoother output, but slower to adapt; may stay near extremes longer
## Implementation Details
### Rolling Window Approach
The implementation maintains a ring buffer of size $n$ and recalculates min/max on each update. This provides O(n) complexity per update but ensures correctness with the rolling window semantics.
### Streaming Characteristics
| Metric | Value |
|:-------|:------|
| **Warmup Period** | $n$ (period) |
| **Memory** | O(n) for ring buffer |
| **Complexity** | O(n) per update |
### Precision Considerations
| Scenario | Handling |
|:---------|:---------|
| **Zero range** | Returns 0.5 |
| **Very small range** | Full precision maintained |
| **NaN/Infinity input** | Last valid value substituted |
## Performance Profile
### Operation Count (Streaming Mode)
| Operation | Count | Notes |
|:----------|:-----:|:------|
| Buffer add | 1 | O(1) ring buffer |
| Min scan | n | Linear scan of window |
| Max scan | n | Combined with min scan |
| SUB | 2 | value - min, max - min |
| DIV | 1 | Final division |
| **Total** | O(n) | Dominated by min/max scan |
### Quality Metrics
| Metric | Score | Notes |
|:-------|:-----:|:------|
| **Accuracy** | 10/10 | Exact min-max scaling |
| **Boundedness** | 10/10 | Guaranteed [0, 1] output |
| **Adaptability** | 8/10 | Adapts to rolling window |
| **Timeliness** | 7/10 | Requires warmup period |
## Usage Examples
### Basic Usage
```csharp
// Create Normalize with 14-period lookback
var norm = new Normalize(14);
// Feed price data
var price = new TValue(DateTime.UtcNow, 105.0);
var normalized = norm.Update(price); // Value in [0, 1]
```
### Creating Oscillator from Any Series
```csharp
var rsi = new Rsi(14);
var normRsi = new Normalize(rsi, 100); // Chain: RSI → Normalize
// RSI output (0-100) gets normalized to [0, 1] over 100 periods
foreach (var bar in data)
{
rsi.Update(new TValue(bar.Time, bar.Close));
// normRsi automatically updates via event
}
```
### Comparing Multiple Assets
```csharp
var normA = new Normalize(50);
var normB = new Normalize(50);
// Compare where each asset sits in its own range
var posA = normA.Update(new TValue(now, priceA));
var posB = normB.Update(new TValue(now, priceB));
var relativeStrength = posA.Value - posB.Value; // [-1, 1]
```
### Span API for Batch Processing
```csharp
double[] prices = GetHistoricalPrices();
double[] normalized = new double[prices.Length];
Normalize.Calculate(prices, normalized, period: 20);
```
## Common Pitfalls
1. **Lookback Dependency**: Output depends heavily on what's in the lookback window. Unusual spikes or crashes in the window can distort normalization for the entire period duration.
2. **Not Truly Bounded During Warmup**: Before the warmup period completes, the window is partial, which may produce less meaningful normalization.
3. **Flat Market Handling**: When a series has no variation over the period, output becomes 0.5. This may need special handling if your strategy interprets 0.5 differently.
4. **Window Lag**: When price breaks out of a long-established range, the old min/max remains in the window until it ages out, causing the normalized value to stay pinned at 0 or 1.
5. **Memory Requirements**: Each instance requires O(period) memory for the ring buffer. For many indicators with long periods, this can add up.
6. **Non-Stationarity**: Min-max normalization assumes the range is representative. In trending markets, the normalization may consistently return values near 0 or 1.
## Validation
| Test | Status |
|:-----|:------:|
| **Output in [0, 1]** | ✅ |
| **Max value → 1** | ✅ |
| **Min value → 0** | ✅ |
| **Flat range → 0.5** | ✅ |
| **Linear mapping** | ✅ |
| **Rolling window correctness** | ✅ |
| **Streaming = Batch** | ✅ |
## References
- Aksoy, S., & Haralick, R. M. (2001). "Feature normalization and likelihood-based similarity measures for image retrieval." *Pattern Recognition Letters*.
- Patro, S., & Sahu, K. K. (2015). "Normalization: A preprocessing stage." *IARJSET*.
- Géron, A. (2019). *Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow*. O'Reilly Media.