feat: Enhance volume indicators with ADOSC and SSF implementation and validation

This commit is contained in:
Miha Kralj
2025-12-20 15:08:07 -08:00
parent 5549c7329a
commit d21fea3c18
85 changed files with 5144 additions and 3954 deletions
+31 -103
View File
@@ -1,128 +1,56 @@
# TRIMA: Triangular Moving Average
## What It Does
> "The weighted blanket of moving averages. It doesn't care where the price is going right now; it cares where the price feels most comfortable."
The Triangular Moving Average (TRIMA) is a weighted moving average where the weights are assigned in a triangular pattern. The most recent data and the oldest data carry the least weight, while the data in the middle of the period carries the most weight. This creates a double-smoothing effect that produces a line much smoother than a Simple Moving Average (SMA) or Exponential Moving Average (EMA), making it ideal for identifying the primary trend without the distraction of short-term noise.
The Triangular Moving Average (TRIMA) places the majority of its weight on the middle of the data window, tapering off linearly towards the ends. This creates a triangular weight distribution (hence the name). It is mathematically equivalent to a double-smoothed SMA.
## Historical Context
While the concept of triangular weighting has roots in statistical signal processing, it was popularized in technical analysis as a way to solve the "whipsaw" problem of SMAs. By de-emphasizing the most recent data (which is often noisy), TRIMA focuses on the "consensus" of value over the period.
TRIMA has been a staple in cycle analysis. By double-smoothing the data, it effectively removes high-frequency noise, making it ideal for identifying dominant market cycles. However, this smoothness comes at the cost of significant lag.
## How It Works
## Architecture & Physics
### The Core Idea
TRIMA is implemented as a cascade of two Simple Moving Averages.
$$ TRIMA = SMA(SMA(Price, P_1), P_2) $$
TRIMA is mathematically equivalent to a "double SMA."
Where $P_1$ and $P_2$ are roughly half the total period.
- **SMA:** Average of $N$ prices.
- **TRIMA:** Average of an Average. Specifically, an SMA of period $X$ applied to an SMA of period $X$.
### The Weight Distribution
Because it averages an average, it is extremely smooth. However, this double smoothing comes at the cost of increased lag. It will turn significantly later than an EMA or SMA.
An SMA has a rectangular weight distribution (all weights equal). A WMA has a linear distribution (heaviest at the end). TRIMA has a triangular distribution (heaviest in the center).
### Mathematical Foundation
## Mathematical Foundation
The weights form a triangle. For a period of 5:
### 1. Period Splitting
- Weights: 1, 2, 3, 2, 1
- Sum of weights: $1+2+3+2+1 = 9$
$$ P_1 = \lfloor \frac{N}{2} \rfloor + 1 $$
$$ P_2 = \lceil \frac{N+1}{2} \rceil $$
Formula:
$$ TRIMA = \frac{\sum (Price_i \times Weight_i)}{\sum Weights} $$
### 2. The Cascade
Equivalent Calculation (Double SMA):
$$ TRIMA(N) \approx SMA(SMA(Price, \lceil N/2 \rceil), \lfloor N/2 \rfloor + 1) $$
### Implementation Details
Our implementation uses the Double SMA method for O(1) efficiency.
- **Complexity:** O(1) per update (two sliding window sums).
- **Stability:** Inherits the stability of SMA.
## Configuration
| Parameter | Default | Purpose | Adjustment Guidelines |
|-----------|---------|---------|----------------------|
| Period | 14 | Lookback window | Standard lookback. |
$$ TRIMA = SMA(SMA(Price, P_1), P_2) $$
## Performance Profile
| Operation | Complexity | Description |
|-----------|------------|-------------------|
| Streaming update | O(1) | Two sliding window sums |
| Bar correction | O(1) | Efficient state rollback |
| Batch processing | O(N) | Single pass through data |
| Memory footprint | O(period) | RingBuffers for the two internal SMAs |
### Zero-Allocation Design
## Interpretation
TRIMA relies on two internal `Sma` instances, which use pre-allocated `RingBuffer`s. The chaining of updates is done via value passing, ensuring no intermediate objects are created on the heap.
### Trading Signals
| Metric | Score | Notes |
| :--- | :--- | :--- |
| **Throughput** | High | 2 SMAs |
| **Complexity** | O(1) | Constant time update |
| **Accuracy** | 6/10 | Heavily smoothed, loses detail |
| **Timeliness** | 4/10 | Significant lag (Lag ≈ N/2 + N/2) |
| **Overshoot** | 9/10 | Very stable, minimal overshoot |
| **Smoothness** | 9/10 | Triangular weighting removes high freq noise |
#### Trend Identification
## Validation
- **Primary Trend:** TRIMA is excellent for visualizing the "major" trend. If TRIMA is rising, the long-term direction is up, regardless of short-term chops.
Validated against TA-Lib (`TA_TRIMA`) and Skender.Stock.Indicators.
### When It Works Best
### Common Pitfalls
- **Visual Clarity:** Traders often use TRIMA not for signals, but to declutter charts and see the underlying market structure.
### When It Struggles
- **Timing Entries:** Due to its significant lag, TRIMA is poor for timing entries or exits. It is a lagging indicator, not a leading one.
## Architecture Notes
This implementation makes specific trade-offs:
### Choice: Double SMA Composition
- **Implementation:** Composed of two `Sma` objects.
- **Rationale:** This is mathematically equivalent to the weighted sum method but allows us to reuse the O(1) optimization of the `Sma` class.
## References
- Merrill, Arthur A. "Filtered Waves." *Technical Analysis of Stocks & Commodities*.
## C# Usage
### Streaming Updates (Single Instance)
```csharp
using QuanTAlib;
var trima = new Trima(period: 14);
// Process each new bar
TValue result = trima.Update(new TValue(timestamp, closePrice));
Console.WriteLine($"TRIMA: {result.Value:F2}");
// Check if buffer is full
if (trima.IsHot)
{
// Indicator is fully initialized
}
```
### Batch Processing (Historical Data)
```csharp
// TSeries API
TSeries prices = ...;
TSeries trimaValues = Trima.Batch(prices, period: 14);
// Span API (High Performance)
double[] prices = new double[1000];
double[] output = new double[1000];
Trima.Calculate(prices.AsSpan(), output.AsSpan(), period: 14);
```
### Bar Correction (isNew Parameter)
```csharp
var trima = new Trima(14);
// New bar
trima.Update(new TValue(time, 100), isNew: true);
// Intra-bar update
trima.Update(new TValue(time, 101), isNew: false); // Replaces 100 with 101
1. **Lag**: TRIMA has more lag than SMA, EMA, or WMA. It is a lagging indicator, not a leading one.
2. **Signal Generation**: Due to its lag, TRIMA is poor for crossover signals. It is best used for visual trend identification or as a baseline for envelopes (e.g., TMA Bands).
3. **Even/Odd Periods**: The exact calculation of $P_1$ and $P_2$ differs slightly between implementations for even periods. QuanTAlib matches the standard definition used by TA-Lib.