> "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) 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
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.
## Architecture & Physics
TRIMA is implemented as a cascade of two Simple Moving Averages.
$$ TRIMA = SMA(SMA(Price, P_1), P_2) $$
Where $P_1$ and $P_2$ are roughly half the total period.
### The Weight Distribution
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
### 1. Period Splitting
$$ P_1 = \lfloor \frac{N}{2} \rfloor + 1 $$
$$ P_2 = \lceil \frac{N+1}{2} \rceil $$
### 2. The Cascade
$$ TRIMA = SMA(SMA(Price, P_1), P_2) $$
## Performance Profile
### Operation Count (Streaming Mode, Scalar)
TRIMA chains two SMA instances. Each SMA is O(1) with ~17 cycles (see SMA.md).
QuanTAlib's TRIMA uses cascaded SMA composition, achieving O(1) streaming updates by leveraging the O(1) nature of each internal SMA. The implementation demonstrates clean indicator composition:
### Composition Architecture
```csharp
[SkipLocalsInit]
publicsealedclassTrima:AbstractBase
{
privatereadonlySma_sma1;
privatereadonlySma_sma2;
publicTrima(intperiod)
{
intp1=(period+1)/2;
intp2=period/2+1;
_sma1=newSma(p1);
_sma2=newSma(p2);
}
}
```
TRIMA delegates all complexity to its internal SMA instances. Each SMA maintains its own O(1) running sum, so the cascade is also O(1).
### Key Optimizations
| Technique | Implementation | Benefit |
| :--- | :--- | :--- |
| **SMA delegation** | Two internal `Sma` instances | O(1) streaming via running sums |
Uses ArrayPool for the intermediate buffer to avoid heap allocation per batch.
### Bar Correction Propagation
The `isNew` flag propagates through both internal SMAs:
```csharp
// isNew=false triggers rollback in BOTH SMAs
TValuev1=_sma1.Update(input,isNew);// SMA1 rolls back its running sum
TValuev2=_sma2.Update(v1,isNew);// SMA2 rolls back based on corrected SMA1 output
```
This ensures consistent bar correction across the entire cascade.
### Common Pitfalls
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.