Average(Close, Length) | SimpleMovingAverage::new(length).unwrap() → .next(bar.close) | Create once in new(), call next() in on_bar() |
XAverage(Close, Length) | ExponentialMovingAverage::new(length).unwrap() → .next(bar.close) | Same streaming pattern |
RSI(Close, Length) | RelativeStrengthIndex::new(length).unwrap() → .next(bar.close) | Returns 0–100 range matching PL |
Stochastic(...) | SlowStochastic::new(k_period, d_period).unwrap() → .next(&data_item) | Accepts f64 but needs OHLC DataItem for correct stochastic formula; PL takes 11 params — map k_period and d_period |
ADX(Length) | Not in ta-rs; use yata::indicators::ADX or implement manually | ta-rs lacks ADX; yata crate covers it |
CCI(Length) | CommodityChannelIndex::new(length).unwrap() → .next(&data_item) | PL CCI takes only length (uses HLC internally); ta-rs requires a type implementing Close + High + Low |
AvgTrueRange(Length) | TrueRange::new() → .next(&data_item), feed each value into SimpleMovingAverage::new(length) | PL AvgTrueRange is a SIMPLE average of TrueRange. ta-rs AverageTrueRange is EMA-based — NOT equivalent; the smoothed (Wilder-style) PL match is SmoothedAverage(TrueRange, Len). Needs OHLC DataItem for correct true range |
BollingerBand(Close, Length, 2) | BollingerBands::new(length, 2.0).unwrap() → .next(bar.close) | Returns BollingerBandsOutput { average, upper, lower }. Note: ta-rs may use EMA internally — verify output matches PL's SMA-based Bollinger Bands |
Close Crosses Over MA | prev_close <= prev_ma && close > ma | No built-in crossover in ta-rs; store previous-bar values yourself |
Close Crosses Under MA | prev_close >= prev_ma && close < ma | Same pattern, reversed inequality |
Highest(Close, Length) | Maximum::new(length).unwrap() → .next(bar.close) | ta-rs Maximum indicator |
Lowest(Close, Length) | Minimum::new(length).unwrap() → .next(bar.close) | ta-rs Minimum indicator |
Momentum(Close, Length) | close - bars[bars.len() - 1 - length].close | No ta-rs built-in; compute directly from bar slice. Guard with bars.len() > length. |
TSI(Close, LongLen, ShortLen) | Manual: double-EMA of momentum / double-EMA of abs(momentum) | No ta-rs built-in; use two nested ExponentialMovingAverage pairs — one for EMA(EMA(mtm, long), short), one for EMA(EMA(abs(mtm), long), short), then 100 * ratio |
AverageFC(Close, Length) | SimpleMovingAverage::new(length).unwrap() → .next(bar.close) | FC = "fast calculation": AverageFC = SummationFC/Len (running-sum SMA), numerically identical to Average — use SimpleMovingAverage |
WAverage(Close, Length) | Manual: weighted sum Σ(close[i] * (length - i)) / Σ(1..=length) | No ta-rs built-in; iterate bars[len-length..len], weight newest bar highest |
AdaptiveMovAvg(Close, Length) | EfficiencyRatio::new(length) + manual AMA smoothing | Use ta-rs EfficiencyRatio; smoothing constant sc = (er * (fast_sc - slow_sc) + slow_sc)^2 with fast_sc = 2/(2+1), slow_sc = 2/(30+1), then AMA = prev + sc * (close - prev) (Kaufman formula — NOT er^2) |
MidPoint(Close, Length) | (Maximum::new(length).next(c) + Minimum::new(length).next(c)) / 2.0 | Combine ta-rs Maximum and Minimum |
MACD(Close, FastLen, SlowLen) | ta::indicators::MovingAverageConvergenceDivergence::new(fast, slow, signal).unwrap() → .next(bar.close) | Returns MACDOutput { macd, signal, histogram } |
KeltnerChannel(Close, Length, Factor) | KeltnerChannel::new(length, factor).unwrap() → .next(&data_item) | Returns KeltnerChannelOutput { average, upper, lower }; needs OHLC DataItem |
DMIPlus(Length) | Manual or yata; +DI = 100 × smoothed(+DM) / smoothed(TR) | Not in ta-rs; compute +DM = max(high - prev_high, 0) when > −DM, else 0 |
DMIMinus(Length) | Manual or yata; −DI = 100 × smoothed(−DM) / smoothed(TR) | Not in ta-rs; compute −DM = max(prev_low - low, 0) when > +DM, else 0 |
RateOfChange(Close, Length) | RateOfChange::new(length).unwrap() → .next(bar.close) | ta-rs RateOfChange; returns percentage change |
PercentR(Length) | Manual: 100 × (close − lowest) / (highest − lowest) | PL PercentR is POSITIVE 0..100 (= Williams %R + 100; thresholds 80/20). Use ta-rs Maximum/Minimum over length bars for highest-high/lowest-low |
MoneyFlow(Length) | Manual: MFI = 100 − 100/(1 + pos_flow/neg_flow) | Classify each bar's typical-price × volume as positive or negative, sum over length |
Parabolic(AFStep, AFMax) | Manual state machine tracking AF and EP | No crate built-in; maintain af, ep, sar, flip on new extreme; complex — ~40 lines |
Volatility(Length) | TrueRange::new() → .next(&data_item), feed each value into ExponentialMovingAverage::new(length) | PL Volatility is a smoothed average of TrueRange weighted toward the most recent bar — NOT a standard deviation of closes (that is StandardDev) |
UltimateOscillator(Fast, Mid, Slow) | Manual: weighted average of BP/TR ratios over three periods | BP = close − min(low, prev_close); sum BP and TR over 7/14/28, weight 4:2:1 |
ChaikinOsc(FastLen, SlowLen) | Manual: EMA(fast, ADL) − EMA(slow, ADL) | ADL = cum sum of ((close−low)−(high−close))/(high−low) × volume; apply two EMAs |
PriceOscillator(FastLen, SlowLen) | Manual: EMA(fast) − EMA(slow) | Two ExponentialMovingAverage instances; subtract slow from fast |
DirMovement(Length, oADX, oDIP, oDIM) | Manual or yata: computes ADX, +DI, −DI together | Multi-output; return a struct { adx, di_plus, di_minus } |
Extremes(Length, oHi, oHiBar, oLo, oLoBar) | Manual: scan bars[len-length..len] for max/min and their offsets | Multi-output; return { highest, highest_bar, lowest, lowest_bar } |
TrueRange | TrueRange::new() → .next(&data_item) | ta-rs TrueRange; needs OHLC DataItem |
StandardDev(Close, Length) | StandardDeviation::new(length).unwrap() → .next(bar.close) | ta-rs StandardDeviation |
TrueHigh | Manual: bar.high.max(prev_bar.close) | Max of current high and previous close; guard bars.len() > 1 |
TrueLow | Manual: bar.low.min(prev_bar.close) | Min of current low and previous close; guard bars.len() > 1 |
Range | Manual: bar.high - bar.low | Current bar's high minus low |
HighestBar(Close, Length) | Manual: index of max in bars[len-length..len] | Returns bars-ago offset (0 = current bar); scan with enumerate() + max_by() |
LowestBar(Close, Length) | Manual: index of min in bars[len-length..len] | Returns bars-ago offset; scan with enumerate() + min_by() |
NthHighest(N, Close, Length) | Manual: sort last length closes descending, take index N-1 | Collect into Vec, sort_by() descending, return [n-1] |
NthLowest(N, Close, Length) | Manual: sort last length closes ascending, take index N-1 | Collect into Vec, sort_by() ascending, return [n-1] |
NthHighestBar(N, Close, Length) | Manual: sort with indices, return bars-ago of Nth highest | Pair (value, offset), sort descending, return offset at N-1 |
NthLowestBar(N, Close, Length) | Manual: sort with indices, return bars-ago of Nth lowest | Pair (value, offset), sort ascending, return offset at N-1 |
SwingHigh(Occur, Price, Strength, Length) | Manual: bars[pivot] > all Strength bars on each side, scanning the last Length bars | Occurrence is the FIRST PL arg (Occur selects Nth most recent swing); returns −1 when none found; Length must exceed Strength; confirmed only after Strength bars pass |
SwingLow(Occur, Price, Strength, Length) | Manual: bars[pivot] < all Strength bars on each side, scanning the last Length bars | Same pivot logic, reversed comparison; −1 when none found |
SwingHighBar(Occur, Price, Strength, Length) | Manual: bars-ago offset of the detected swing high | Same detection as SwingHigh; return bars.len() - 1 - pivot_index |
SwingLowBar(Occur, Price, Strength, Length) | Manual: bars-ago offset of the detected swing low | Same detection as SwingLow; return offset |
Summation(Close, Length) | Manual: bars[len-length..len].iter().map(|b| b.close).sum::<f64>() | Rolling sum over last length bars |
Cum(Close) | Manual: self.cum_val += bar.close; | Cumulative sum from bar 1; store running total in struct field |
LinearRegValue(Close, Length, Offset) | Manual: least-squares fit over length bars, evaluate at Offset | Compute slope/intercept via Σxy, Σx²; project forward by Offset bars |
LinearRegAngle(Close, Length) | Manual: slope.atan().to_degrees() | Angle in degrees of the regression line slope |
LinearRegSlope(Close, Length) | Manual: (n*Σxy − Σx*Σy) / (n*Σx² − (Σx)²) | Standard least-squares slope formula over length bars |
Correlation(Close1, Close2, Length) | Manual: Pearson r over length bars | r = (nΣxy − ΣxΣy) / sqrt((nΣx²−(Σx)²)(nΣy²−(Σy)²)) |
RSquared(Close, Length) | Manual: correlation(close, 1..=length)² | R² of close vs bar index; square the Pearson r |
StdError(Close, Length) | Manual: std error of estimate around regression line | sqrt(Σ(close − predicted)² / (length − 2)) |
Median(Close, Length) | Manual: collect last length values, sort, pick middle | let mut v: Vec<f64> = ...; v.sort_by(|a,b| a.partial_cmp(b).unwrap()); v[length/2] |
ELDate(dt) | NaiveDate::from_ymd_opt(year, month, day) (chrono crate) | PL returns YYYMMDD integer (YYY = year − 1900); Rust uses chrono::NaiveDate |
MinutesToTime(mins) | Manual: let hhmm = (mins / 60) * 100 + mins % 60; | PL returns HHMM integer; Rust can also use NaiveTime::from_hms_opt(h, m, 0) |
TimeToMinutes(hhmm) | Manual: let mins = (hhmm / 100) * 60 + hhmm % 100; | Inverse of MinutesToTime; converts HHMM integer to total minutes |
AvgPrice | Manual: (bar.open + bar.high + bar.low + bar.close) / 4.0 | Average of OHLC |
MedianPrice | Manual: (bar.high + bar.low) / 2.0 | Midpoint of high and low |
TypicalPrice | Manual: (bar.high + bar.low + bar.close) / 3.0 | HLC average |
WeightedClose | Manual: (bar.high + bar.low + bar.close * 2.0) / 4.0 | Close-weighted HLC |
CountIF(Cond, Length) | Manual: bars[len-length..len].iter().filter(|b| cond(b)).count() | Count bars satisfying condition over last length bars |
MRO(Cond, Length, Instance) | Manual: scan backward from current bar for Instance-th true | Returns bars-ago offset; iterate (1..=length).rev(), decrement instance counter on match |
AccumDist | Manual: self.ad += ((close−low)−(high−close))/(high−low) * volume | Cumulative; guard division by zero when high == low |
IFF(Cond, TrueVal, FalseVal) | if cond { true_val } else { false_val } | Direct Rust if/else expression; returns a value |
TriAverage(Close, Length) | Manual: two SimpleMovingAverage instances of HALVED length (length + 2) / 2 (= ceil((Length+1)*0.5)) | Triangular MA = SMA of SMA with the halved length, not the full length applied twice; feed output of first into second |
FastK(StochLength) | Manual: (close - min) / (max - min) * 100 using Minimum/Maximum | Raw %K from default H/L/C; no ta-rs helper |
FastD(StochLength) | Manual: SimpleMovingAverage::new(3) applied to FastK values | Smoothed Fast %K |
SlowK(StochLength) | SlowStochastic::new(StochLength, 3).unwrap() → .next(&data_item) | Same as SlowStochastic output |
SlowD(StochLength) | Manual: SimpleMovingAverage::new(3) applied to SlowK values | Double-smoothed |
FastKCustom(H, L, C, StochLen) | Manual: (c - lowest_l) / (highest_h - lowest_l) * 100 | Custom prices; use Maximum/Minimum on custom series |
FastDCustom(H, L, C, StochLen) | Manual: SMA(3) of FastKCustom | Same pattern |
SlowKCustom(H, L, C, StochLen) | Manual: SMA(3) of FastKCustom | Same as FastDCustom |
SlowDCustom(H, L, C, StochLen) | Manual: SMA(3) of FastKCustom | Slow %D with custom prices |
StochasticExp(H, L, C, StochLen, S1, S2, ...) | Manual: EMA smoothing of FastKCustom | Use ExponentialMovingAverage instead of SMA for smoothing |
ADXR(Length) | Manual: (adx + adx_n_bars_ago) / 2.0 | Store ADX history; average current with N-bar-ago value |
ADXCustom(H, L, C, Length) | Manual or yata::indicators::ADX with custom prices | Same as ADX but with custom H/L/C inputs |
DMI(Length) | Manual or yata; same as ADX | Wrapper; same computation |
DMIPlusCustom(H, L, C, Length) | Manual: +DI using custom highs/lows | Compute +DM from custom price series |
DMIMinusCustom(H, L, C, Length) | Manual: −DI using custom highs/lows | Compute −DM from custom price series |
ParabolicCustom(AfStep, AfLimit) | Manual SAR state machine with custom limit | Same as Parabolic but cap AF at AfLimit |
TRIX(Close, Length) | Manual: ROC of triple ExponentialMovingAverage | Three nested EMA, then (ema3 - prev_ema3) / prev_ema3 * 100 |
MassIndex(SmoothLen, SumLen) | Manual: sum of EMA(H-L) / EMA(EMA(H-L)) over SumLen | Two nested EMAs, compute ratio, rolling sum |
EaseOfMovement | Manual: ((h + l) / 2 - (prev_h + prev_l) / 2) / (volume / (h - l)) | No crate built-in; uses current + previous bar |
SwingIndex | Manual: Wilder swing index formula using OHLC | Complex ~20-line formula with limit move |
AccumSwingIndex | Manual: self.accum_si += swing_index | Running cumulative sum of SwingIndex |
Detrend(Close, Length) | Manual: close - sma_value_offset | SMA offset by Length/2 bars |
PercentChange(Close, Length) | Manual: (close - bars[len-1-length].close) / bars[len-1-length].close * 100.0 | Simple percent change formula |
UlcerIndex(Close, Length) | Manual: (sum_of_squared_drawdown_pct / length).sqrt() | Track highest close in window, compute drawdown pct |
ParabolicSAR(AfStep, AfLimit, ...) | Manual SAR with direction/transition tracking | Extend SAR state machine to track position changes |
LinearReg(Close, Length, TgtBar, ...) | Manual: least-squares returning (value, slope, angle, intercept) | Full regression output as struct |
TrueRangeCustom(H, L, C) | Manual: (h - l).max((h - prev_c).abs()).max((l - prev_c).abs()) | Same formula as TrueRange with custom prices |
VolatilityStdDev(NumDays) | Manual: stdev of log returns × (252.0_f64).sqrt() | Annualized historical volatility |
StandardDevAnnual(Close, Length, DataType) | Manual: StandardDeviation::new(length).next(close) * (252.0_f64).sqrt() | Annualize the ta-rs StandardDeviation output |
HighestFC(Close, Length) | Maximum::new(length).unwrap() → .next(bar.close) | Same as Highest; ta-rs Maximum |
LowestFC(Close, Length) | Minimum::new(length).unwrap() → .next(bar.close) | Same as Lowest; ta-rs Minimum |
PivotHighVS(Inst, Price, LStr, RStr, Len) | Manual: scan for bar higher than LStr bars left and RStr bars right | Asymmetric left/right strength; return price or -1.0 |
PivotLowVS(Inst, Price, LStr, RStr, Len) | Manual: scan for bar lower than LStr bars left and RStr bars right | Same pattern for lows |
PivotHighVSBar(Inst, Price, LStr, RStr, Len) | Manual: bars-ago offset of pivot high | Same detection, return bars.len() - 1 - pivot_index |
PivotLowVSBar(Inst, Price, LStr, RStr, Len) | Manual: bars-ago offset of pivot low | Same detection, return offset |
Divergence(P1, P2, Str, Len, HiLo) | Manual: compare pivot highs/lows of price vs indicator | Return 1 if divergence found between two series |
TimeSeriesForecast(Close, Length) | Manual: linear_reg_value + slope | Uses same slope/intercept as LinearRegValue |
SummationFC(Close, Length) | Manual: bars[len-length..len].iter().map(|b| b.close).sum::<f64>() | Same as Summation; fast calc variant |
OpenD(N) | Manual: aggregate bars into daily periods, return daily_bars[daily_bars.len()-1-N].open | Requires daily bar aggregation from intraday data |
HighD(N) | Manual: daily_bars[..].high | Aggregate highs per day |
LowD(N) | Manual: daily_bars[..].low | Aggregate lows per day |
CloseD(N) | Manual: daily_bars[..].close | Last close per day |
OpenW(N) | Manual: aggregate to weekly | Same pattern, weekly periods |
HighW(N) | Manual: aggregate to weekly | Weekly high |
LowW(N) | Manual: aggregate to weekly | Weekly low |
CloseW(N) | Manual: aggregate to weekly | Weekly close |
OpenM(N) | Manual: aggregate to monthly | Monthly open |
HighM(N) | Manual: aggregate to monthly | Monthly high |
LowM(N) | Manual: aggregate to monthly | Monthly low |
CloseM(N) | Manual: aggregate to monthly | Monthly close |
OpenY(N) | Manual: aggregate to yearly | Yearly open |
HighY(N) | Manual: aggregate to yearly | Yearly high |
LowY(N) | Manual: aggregate to yearly | Yearly low |
CloseY(N) | Manual: aggregate to yearly | Yearly close |
LRO(Cond, Length, N) | Manual: scan from length bars ago forward, find Nth true | Least recent occurrence; returns bars-ago offset |
SummationIf(Cond, Price, Length) | Manual: sum price over last length bars where cond is true | Conditional rolling sum |
IFFString(Cond, TrueStr, FalseStr) | if cond { true_str.to_string() } else { false_str.to_string() } | Rust if expression returning String |
OBV | Manual: self.obv += if close > prev_close { vol } else if close < prev_close { -vol } else { 0.0 } | On Balance Volume; running total |
VolumeROC(Length) | RateOfChange::new(length).unwrap() → .next(bar.volume) | ta-rs RateOfChange on volume |
VolumeOsc(ShortLen, LongLen) | Manual: SMA(volume, short) - SMA(volume, long) | Two SimpleMovingAverage on volume, subtract |
PriceVolTrend | Manual: self.pvt += (close - prev_close) / prev_close * volume | Cumulative price-volume trend |
LWAccDis | Manual: self.lwad += (close - open) / (high - low) * volume | Larry Williams A/D; running total |
Fisher(Price) | Manual: normalize to −0.999..0.999, then 0.5 * ((1.0 + norm) / (1.0 - norm)).ln() | Fisher transformation |
FisherINV(Price) | Manual: ((2.0 * price).exp() - 1.0) / ((2.0 * price).exp() + 1.0) | Inverse Fisher |
C_Doji(Pct) | Manual: (close - open).abs() <= (high - low) * pct / 100.0 | Doji detection |
C_Hammer_HangingMan(Len, Factor, ...) | Manual: check body/shadow ratios | Lower shadow ≥ 2× body |
C_BullEng_BearEng(Len, ...) | Manual: current body engulfs previous | Compare current and previous OHLC |
C_BullHar_BearHar(Len, ...) | Manual: current body inside previous | Opposite of engulfing |
C_MornDoji_EveDoji(Len, Pct, ...) | Manual: 3-bar pattern with middle doji | Check 3 consecutive bars |
C_MornStar_EveStar(Len, ...) | Manual: 3-bar reversal | Down-small-up or up-small-down |
C_PierceLine_DkCloud(Len, ...) | Manual: 2-bar piercing pattern | Gap + close past midpoint |
C_ShootingStar(Len, Factor) | Manual: small body, long upper shadow | Upper shadow ≥ 2× body |
C_3WhSolds_3BlkCrows(Len, Factor, ...) | Manual: 3 consecutive trend bars | Three ascending or descending closes |
| Statistical extended | | |
AvgDeviation(Close, N) | Manual: mean absolute deviation over window | MAD |
Variance(Close, N) | Manual: sum((x - mean)^2) / N | Population variance |
Kurtosis(Close, N) | Manual: 4th moment calculation | Excess kurtosis |
Skew(Close, N) | Manual: 3rd moment calculation | Skewness |
PercentRank(ValToRank, Price, N) | Manual: count values ≤ target / N | Percent rank |
Covariance(P1, P2, N) | Manual: sum((P1-mean1)*(P2-mean2)) / N | Covariance |
Quartile(Close, N, Q) | Manual: sort window, pick percentile | Quartile value |
TrimMean(Close, N, Pct) | Manual: sort, trim edges, average | Trimmed mean |
Mode(Close, N, Type) | Manual: frequency count over window | Modal value |
HarmonicMean(Close, N) | Manual: N / sum(1/x) | Harmonic mean |
| Moving averages extended | | |
SmoothedAverage(Close, N) | Manual: Wilder smoothing prev*(N-1)/N + val/N | Same as RMA |
| Miscellaneous | | |
BarAnnualization | Manual: compute from bar frequency | Bars-per-year factor |
LastBarOnChart | bar_index == data.len() - 1 | True on last bar |
| Custom functions | | |
StochRSI(Close, N, M) | Manual: compute RSI, then (rsi - lowest(rsi, M)) / (highest(rsi, M) - lowest(rsi, M)) | Stochastic RSI |
supertrend(N, Mult) | Manual: ATR bands + direction flip logic | Supertrend |
NVI(Start) | Manual: accumulate on volume-down bars | Negative Volume Index |
PVI(Start) | Manual: accumulate on volume-up bars | Positive Volume Index |
Coppo(N1, N2, N3) | Manual: WMA of two ROC periods | Coppock Curve |
LWTI(Close, P, N) | Manual: (sma(diff, N) / sma(range, N)) * 50 + 50 | Larry Williams TI |
TVI(Close, Vol, Tick) | Manual: cumulative directional volume | Trade Volume Index |
SharpeRatio(Period, Rate, Calc, Cap) | Manual: (avg_return - rf) / std_return | Portfolio Sharpe |
WRSI(N, Close) | Manual: Wilder smoothing RSI (same formula as standard RSI) | Wilder RSI |
NewMA(Close, N) | Manual: Heikin-Ashi + triple EMA hybrid | Hybrid MA |