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.