diff --git a/_sidebar.md b/_sidebar.md
index 837f0797..a489628e 100644
--- a/_sidebar.md
+++ b/_sidebar.md
@@ -57,7 +57,7 @@
* [GDEMA - Generalized DEMA](/lib/trends_IIR/gdema/Gdema.md)
* [HEMA - Hull Exponential MA](/lib/trends_IIR/hema/Hema.md)
* [HOLT - Holt Exponential Smoothing](/lib/trends_IIR/holt/Holt.md)
- * [HTIT - Ehlers Hilbert Transform Instant Trendline (also known as HT_TRENDLINE)](/lib/trends_IIR/htit/Htit.md)
+ * [HT_TRENDLINE - Ehlers Hilbert Transform Instant Trendline](/lib/trends_IIR/ht_trendline/HtTrendline.md)
* [HWMA - Holt-Winters MA](/lib/trends_IIR/hwma/Hwma.md)
* [JMA - Jurik MA](/lib/trends_IIR/jma/Jma.md)
* [KAMA - Kaufman Adaptive MA](/lib/trends_IIR/kama/Kama.md)
@@ -138,7 +138,7 @@
* [DEM - DeMarker Oscillator](/lib/oscillators/dem/Dem.md)
* [DOSC - Derivative Oscillator](/lib/oscillators/dosc/Dosc.md)
* [DPO - Detrended Price Oscillator](/lib/oscillators/dpo/Dpo.md)
- * [DYMOI - Dynamic Momentum Index](/lib/oscillators/dymoi/Dymoi.md)
+ * [DYMI - Dynamic Momentum Index](/lib/oscillators/dymi/dymi.md)
* [ER - Efficiency Ratio](/lib/oscillators/er/Er.md)
* [ERI - Elder Ray Index](/lib/oscillators/eri/Eri.md)
* [FI - Force Index](/lib/oscillators/fi/Fi.md)
@@ -210,7 +210,7 @@
* [MOM - Momentum](/lib/momentum/mom/Mom.md)
* [PMO - Price Momentum Oscillator](/lib/momentum/pmo/Pmo.md)
* [PPO - Percentage Price Oscillator](/lib/momentum/ppo/Ppo.md)
- * [PRS - Price Relative Strength](/lib/momentum/prs/Prs.md)
+ * [RS - Price Relative Strength](/lib/momentum/rs/Rs.md)
* [ROC - Rate of Change](/lib/momentum/roc/Roc.md)
* [ROCP - Rate of Change Percentage](/lib/momentum/rocp/Rocp.md)
* [ROCR - Rate of Change Ratio](/lib/momentum/rocr/Rocr.md)
@@ -249,7 +249,7 @@
* [YZV - Yang-Zhang Volatility](/lib/volatility/yzv/Yzv.md)
* [Volume](/lib/volume/_index.md)
- * [ADL - Accumulation/Distribution Line](/lib/volume/adl/Adl.md)
+ * [AD - Accumulation/Distribution Line](/lib/volume/ad/Ad.md)
* [ADOSC - Chaikin A/D Oscillator](/lib/volume/adosc/Adosc.md)
* [AOBV - Archer On-Balance Volume](/lib/volume/aobv/Aobv.md)
* [CMF - Chaikin Money Flow](/lib/volume/cmf/Cmf.md)
@@ -284,14 +284,14 @@
* [APZ - Adaptive Price Zone](/lib/channels/apz/apz.md)
* [ATRBANDS - ATR Bands](/lib/channels/atrbands/Atrbands.md)
* [BBANDS - Bollinger Bands](/lib/channels/bbands/Bbands.md)
- * [DCHANNEL - Donchian Channels](/lib/channels/dchannel/Dchannel.md)
+ * [DC - Donchian Channels](/lib/channels/dc/dc.md)
* [DECAYCHANNEL - Decay Min-Max Channel](/lib/channels/decaychannel/decaychannel.md)
* [FCB - Fractal Chaos Bands](/lib/channels/fcb/Fcb.md)
* [JBANDS - Jurik Volatility Bands](/lib/channels/jbands/Jbands.md)
- * [KCHANNEL - Keltner Channel](/lib/channels/kchannel/Kchannel.md)
+ * [KC - Keltner Channel](/lib/channels/kc/Kc.md)
* [MAENV - Moving Average Envelope](/lib/channels/maenv/Maenv.md)
* [MMCHANNEL - Min-Max Channel](/lib/channels/mmchannel/Mmchannel.md)
- * [PCHANNEL - Price Channel](/lib/channels/pchannel/Pchannel.md)
+ * [PC - Price Channel](/lib/channels/pc/pc.md)
* [REGCHANNEL - Regression Channels](/lib/channels/regchannel/Regchannel.md)
* [SDCHANNEL - Standard Deviation Channel](/lib/channels/sdchannel/Sdchannel.md)
* [STARCHANNEL - Stoller Average Range Channel](/lib/channels/starchannel/Starchannel.md)
@@ -328,7 +328,7 @@
* [PIVOTEXT - Extended Traditional Pivots](/lib/reversals/pivotext/Pivotext.md)
* [PIVOTFIB - Fibonacci Pivot Points](/lib/reversals/pivotfib/Pivotfib.md)
* [PIVOTWOOD - Woodie's Pivot Points](/lib/reversals/pivotwood/Pivotwood.md)
- * [PSAR - Parabolic Stop And Reverse](/lib/reversals/psar/Psar.md)
+ * [SAR - Parabolic Stop And Reverse](/lib/reversals/sar/Sar.md)
* [SAREXT - Parabolic SAR Extended](/lib/reversals/sarext/Sarext.md)
* [SWINGS - Swing High/Low Detection](/lib/reversals/swings/Swings.md)
* [TTM_SCALPER - TTM Scalper Alert](/lib/reversals/ttm_scalper/TtmScalper.md)
@@ -342,7 +342,7 @@
* [BETA - Beta Coefficient](/lib/statistics/beta/Beta.md)
* [CMA - Cumulative MA](/lib/statistics/cma/Cma.md)
* [COINTEGRATION - Cointegration](/lib/statistics/cointegration/Cointegration.md)
- * [CORRELATION - Correlation](/lib/statistics/correlation/Correlation.md)
+ * [CORREL - Correlation](/lib/statistics/correl/Correl.md)
* [COVARIANCE - Covariance](/lib/statistics/covariance/Covariance.md)
* [ENTROPY - Shannon Entropy](/lib/statistics/entropy/Entropy.md)
* [GEOMEAN - Geometric Mean](/lib/statistics/geomean/Geomean.md)
diff --git a/docs/indicators.md b/docs/indicators.md
index 8787f9c7..55835dec 100644
--- a/docs/indicators.md
+++ b/docs/indicators.md
@@ -90,7 +90,7 @@ Infinite Impulse Response filters. Output depends on current input and past outp
| [**GDEMA**](../lib/trends_IIR/gdema/Gdema.md) | Generalized DEMA | Configurable volume factor for tunable lag/smoothness |
| [**HEMA**](../lib/trends_IIR/hema/Hema.md) | Hull Exponential MA | Hull concept with EMA |
| [**HOLT**](../lib/trends_IIR/holt/Holt.md) | Holt Exponential Smoothing | Double exponential smoothing (level + trend) |
-| [**HTIT**](../lib/trends_IIR/htit/Htit.md) | Ehlers Hilbert Instantaneous Trend (also known as HT_TRENDLINE) | Dominant cycle extraction |
+| [**HT_TRENDLINE**](../lib/trends_IIR/ht_trendline/HtTrendline.md) | Ehlers Hilbert Instantaneous Trend | Dominant cycle extraction |
| [**HWMA**](../lib/trends_IIR/hwma/Hwma.md) | Holt-Winters MA | Triple exponential smoothing (IIR) |
| [**JMA**](../lib/trends_IIR/jma/Jma.md) | Jurik MA | Adaptive, low-lag, proprietary algorithm |
| [**KAMA**](../lib/trends_IIR/kama/Kama.md) | Kaufman Adaptive MA | Efficiency ratio adaptation |
@@ -181,7 +181,7 @@ Bounded indicators that oscillate around a centerline or between fixed extremes.
| [**DEM**](../lib/oscillators/dem/Dem.md) | DeMarker Oscillator | Bounded 0-1 comparing sequential highs/lows |
| [**DOSC**](../lib/oscillators/dosc/Dosc.md) | Derivative Oscillator | Double-smoothed RSI minus signal line |
| [**DPO**](../lib/oscillators/dpo/Dpo.md) | Detrended Price Oscillator | Displaced SMA trend removal |
-| [**DYMOI**](../lib/oscillators/dymoi/Dymoi.md) | Dynamic Momentum Index | Volatility-adaptive RSI period |
+| [**DYMI**](../lib/oscillators/dymi/Dymi.md) | Dynamic Momentum Index | Volatility-adaptive RSI period |
| [**ER**](../lib/oscillators/er/Er.md) | Efficiency Ratio | Net movement / total path length |
| [**ERI**](../lib/oscillators/eri/Eri.md) | Elder Ray Index | Bull/bear power relative to EMA |
| [**FI**](../lib/oscillators/fi/Fi.md) | Force Index | Price change × volume buying/selling power |
@@ -263,7 +263,7 @@ Rate of change and velocity measurements. First derivatives of price.
| [**MOM**](../lib/momentum/mom/Mom.md) | Momentum | Raw price difference over N periods |
| [**PMO**](../lib/momentum/pmo/Pmo.md) | Price Momentum Oscillator | Double-smoothed ROC |
| [**PPO**](../lib/momentum/ppo/Ppo.md) | Percentage Price Oscillator | Percentage EMA difference |
-| [**PRS**](../lib/momentum/prs/Prs.md) | Price Relative Strength | Dual-input ratio comparison |
+| [**RS**](../lib/momentum/rs/Rs.md) | Price Relative Strength | Dual-input ratio comparison |
| [**ROC**](../lib/momentum/roc/Roc.md) | Rate of Change | Absolute price change over N periods |
| [**ROCP**](../lib/momentum/rocp/Rocp.md) | Rate of Change Percentage | Percentage price change over N periods |
| [**ROCR**](../lib/momentum/rocr/Rocr.md) | Rate of Change Ratio | Price ratio over N periods |
@@ -312,8 +312,8 @@ Price-volume relationships and accumulation/distribution measurements.
| Indicator | Full Name | Notes |
| :-------- | :-------- | :---- |
-| [**ADL**](../lib/volume/adl/Adl.md) | Accumulation/Distribution Line | Volume-weighted close position |
-| [**ADOSC**](../lib/volume/adosc/Adosc.md) | Chaikin A/D Oscillator | ADL momentum (fast EMA - slow EMA) |
+| [**AD**](../lib/volume/ad/Ad.md) | Accumulation/Distribution Line | Volume-weighted close position |
+| [**ADOSC**](../lib/volume/adosc/Adosc.md) | Chaikin A/D Oscillator | AD momentum (fast EMA - slow EMA) |
| [**AOBV**](../lib/volume/aobv/Aobv.md) | Archer On-Balance Volume | OBV with signal line |
| [**CMF**](../lib/volume/cmf/Cmf.md) | Chaikin Money Flow | Volume-weighted close position over period |
| [**EFI**](../lib/volume/efi/Efi.md) | Elder's Force Index | Price change × volume |
@@ -352,14 +352,14 @@ Price envelope and boundary indicators for breakout and mean-reversion strategie
| [**APZ**](../lib/channels/apz/apz.md) | Adaptive Price Zone | EMA-based volatility zone |
| [**ATRBANDS**](../lib/channels/atrbands/Atrbands.md) | ATR Bands | ATR-based envelope around price |
| [**BBANDS**](../lib/channels/bbands/Bbands.md) | Bollinger Bands | SMA ± StdDev bands |
-| [**DCHANNEL**](../lib/channels/dchannel/Dchannel.md) | Donchian Channels | Highest-high / lowest-low breakout bands |
+| [**DC**](../lib/channels/dc/dc.md) | Donchian Channels | Highest-high / lowest-low breakout bands |
| [**DECAYCHANNEL**](../lib/channels/decaychannel/decaychannel.md) | Decay Min-Max Channel | Exponential decay toward midpoint |
| [**FCB**](../lib/channels/fcb/fcb.md) | Fractal Chaos Bands | Williams fractal-based support/resistance |
| [**JBANDS**](../lib/channels/jbands/Jbands.md) | Jurik Adaptive Bands | Snap-to-extreme, decay-to-price volatility bands |
-| [**KCHANNEL**](../lib/channels/kchannel/kchannel.md) | Keltner Channel | EMA with ATR bands; smoother than Bollinger |
+| [**KC**](../lib/channels/kc/kc.md) | Keltner Channel | EMA with ATR bands; smoother than Bollinger |
| [**MAENV**](../lib/channels/maenv/maenv.md) | Moving Average Envelope | Fixed percentage bands around selectable MA |
| [**MMCHANNEL**](../lib/channels/mmchannel/mmchannel.md) | Min-Max Channel | Rolling highest high / lowest low |
-| [**PCHANNEL**](../lib/channels/pchannel/pchannel.md) | Price Channel | Highest high / lowest low with midline |
+| [**PC**](../lib/channels/pc/pc.md) | Price Channel | Highest high / lowest low with midline |
| [**REGCHANNEL**](../lib/channels/regchannel/regchannel.md) | Regression Channel | Linear regression with StdDev bands |
| [**SDCHANNEL**](../lib/channels/sdchannel/sdchannel.md) | Standard Deviation Channel | MA with standard deviation bands |
| [**STARCHANNEL**](../lib/channels/starchannel/starchannel.md) | Stoller Average Range Channel | SMA with ATR bands |
@@ -381,7 +381,7 @@ Mathematical and statistical computations on price series.
| [**BETA**](../lib/statistics/beta/Beta.md) | Beta Coefficient | Systematic risk measure |
| [**CMA**](../lib/statistics/cma/Cma.md) | Cumulative Moving Average | Expanding window average |
| [**COINTEGRATION**](../lib/statistics/cointegration/Cointegration.md) | Cointegration | Engle-Granger two-step with ADF test |
-| [**CORRELATION**](../lib/statistics/correlation/Correlation.md) | Pearson Correlation | Linear relationship [-1, +1] |
+| [**CORREL**](../lib/statistics/correl/Correl.md) | Pearson Correlation | Linear relationship [-1, +1] |
| [**COVARIANCE**](../lib/statistics/covariance/Covariance.md) | Covariance | Joint variability measure |
| [**ENTROPY**](../lib/statistics/entropy/Entropy.md) | Shannon Entropy | Information content via histogram binning |
| [**GEOMEAN**](../lib/statistics/geomean/Geomean.md) | Geometric Mean | Rolling geometric mean via log-sum |
@@ -543,7 +543,7 @@ Reversal indicators identify potential turning points, compute adaptive stop lev
| [**PIVOTEXT**](../lib/reversals/pivotext/Pivotext.md) | Extended Traditional Pivots | Extended pivots: 11 levels (PP, R1-R5, S1-S5); classic formula with R4/R5/S4/S5 |
| [**PIVOTFIB**](../lib/reversals/pivotfib/Pivotfib.md) | Fibonacci Pivot Points | Fibonacci pivots: 7 levels (PP, R1-R3, S1-S3); ratios 0.382/0.618/1.000 applied to range |
| [**PIVOTWOOD**](../lib/reversals/pivotwood/Pivotwood.md) | Woodie's Pivot Points | Close-weighted pivots: 7 levels (PP, R1-R3, S1-S3); PP = (H+L+2C)/4 biased toward close |
-| [**PSAR**](../lib/reversals/psar/Psar.md) | Parabolic Stop And Reverse | Accelerating trailing stop; SAR dots flip on reversal; Welles Wilder (1978) |
+| [**SAR**](../lib/reversals/sar/Sar.md) | Parabolic Stop And Reverse | Accelerating trailing stop; SAR dots flip on reversal; Welles Wilder (1978) |
| [**SAREXT**](../lib/reversals/sarext/Sarext.md) | Parabolic SAR Extended | Asymmetric AF PSAR; sign-encoded output |
| [**SWINGS**](../lib/reversals/swings/Swings.md) | Swing High/Low Detection | Configurable-lookback pattern detector; dual SwingHigh/SwingLow with persistent levels |
| [**TTM_SCALPER**](../lib/reversals/ttm_scalper/TtmScalper.md) | TTM Scalper Alert | 3-bar pivot high/low detection for scalping entries; John Carter |
diff --git a/docs/pinescript.md b/docs/pinescript.md
index aaf158ee..68d733e6 100644
--- a/docs/pinescript.md
+++ b/docs/pinescript.md
@@ -6,7 +6,7 @@
You are here because you want an indicator on your TradingView chart. Maybe someone on Crypto Twitter posted a screenshot with colored lines and you thought "I need that." Maybe you googled "best RSI Pine Script" at 2 AM. Maybe you clicked a link by accident. All valid paths to enlightenment.
-Here is the good news: QuanTAlib provides **Pine Script v6 source code** for every one of its 396 indicators. Each script is self-contained, tested against the C# reference implementation, and ready to paste into TradingView's Pine Editor. No dependencies. No imports. No subscription to someone's Discord.
+Here is the good news: QuanTAlib provides **Pine Script v6 source code** for every one of its 393 indicators. Each script is self-contained, tested against the C# reference implementation, and ready to paste into TradingView's Pine Editor. No dependencies. No imports. No subscription to someone's Discord.
Here is the less-good news: the scripts contain actual mathematics. You do not have to read it. But it is there, silently judging.
@@ -116,7 +116,7 @@ IIR stands for Infinite Impulse Response. These use recursive feedback: the prev
| GDEMA | Generalized DEMA | [gdema.pine](../lib/trends_IIR/gdema/gdema.pine) |
| HEMA | Hull Exponential MA | [hema.pine](../lib/trends_IIR/hema/hema.pine) |
| HOLT | Holt Exponential Smoothing | [holt.pine](../lib/trends_IIR/holt/holt.pine) |
-| HTIT | Ehlers Hilbert Transform Instant Trendline | [htit.pine](../lib/trends_IIR/htit/htit.pine) |
+| HT_TRENDLINE | Ehlers Hilbert Transform Instant Trendline | [ht_trendline.pine](../lib/trends_IIR/ht_trendline/ht_trendline.pine) |
| HWMA | Holt-Winters MA | [hwma.pine](../lib/trends_IIR/hwma/hwma.pine) |
| JMA | Jurik MA | [jma.pine](../lib/trends_IIR/jma/jma.pine) |
| KAMA | Kaufman Adaptive MA | [kama.pine](../lib/trends_IIR/kama/kama.pine) |
@@ -211,7 +211,7 @@ Numbers that bounce between limits. Overbought, oversold, divergence. You know t
| DEM | DeMarker Oscillator | [dem.pine](../lib/oscillators/dem/dem.pine) |
| DOSC | Derivative Oscillator | [dosc.pine](../lib/oscillators/dosc/dosc.pine) |
| DPO | Detrended Price Oscillator | [dpo.pine](../lib/oscillators/dpo/dpo.pine) |
-| DYMOI | Dynamic Momentum Index | [dymoi.pine](../lib/oscillators/dymoi/dymoi.pine) |
+| DYMI | Dynamic Momentum Index | [dymi.pine](../lib/oscillators/dymi/dymi.pine) |
| ER | Efficiency Ratio | [er.pine](../lib/oscillators/er/er.pine) |
| ERI | Elder Ray Index | [eri.pine](../lib/oscillators/eri/eri.pine) |
| FI | Force Index | [fi.pine](../lib/oscillators/fi/fi.pine) |
@@ -295,7 +295,7 @@ How fast price is moving. Direction matters here.
| MOM | Momentum | [mom.pine](../lib/momentum/mom/mom.pine) |
| PMO | Price Momentum Oscillator | [pmo.pine](../lib/momentum/pmo/pmo.pine) |
| PPO | Percentage Price Oscillator | [ppo.pine](../lib/momentum/ppo/ppo.pine) |
-| PRS | Price Relative Strength | [prs.pine](../lib/momentum/prs/prs.pine) |
+| RS | Price Relative Strength | [rs.pine](../lib/momentum/rs/rs.pine) |
| ROC | Rate of Change | [roc.pine](../lib/momentum/roc/roc.pine) |
| ROCP | Rate of Change Percentage | [rocp.pine](../lib/momentum/rocp/rocp.pine) |
| ROCR | Rate of Change Ratio | [rocr.pine](../lib/momentum/rocr/rocr.pine) |
@@ -348,7 +348,7 @@ What the crowd is doing with their money.
| Indicator | What It Does | Pine Script |
| :--- | :--- | :--- |
-| ADL | Accumulation/Distribution Line | [adl.pine](../lib/volume/adl/adl.pine) |
+| AD | Accumulation/Distribution Line | [ad.pine](../lib/volume/ad/ad.pine) |
| ADOSC | Chaikin A/D Oscillator | [adosc.pine](../lib/volume/adosc/adosc.pine) |
| AOBV | Archer On-Balance Volume | [aobv.pine](../lib/volume/aobv/aobv.pine) |
| CMF | Chaikin Money Flow | [cmf.pine](../lib/volume/cmf/cmf.pine) |
@@ -390,14 +390,14 @@ Upper band, lower band, sometimes a middle. Price bounces between them. In theor
| APZ | Adaptive Price Zone | [apz.pine](../lib/channels/apz/apz.pine) |
| ATRBANDS | ATR Bands | [atrbands.pine](../lib/channels/atrbands/atrbands.pine) |
| BBANDS | Bollinger Bands | [bbands.pine](../lib/channels/bbands/bbands.pine) |
-| DCHANNEL | Donchian Channels | [dchannel.pine](../lib/channels/dchannel/dchannel.pine) |
+| DC | Donchian Channels | [dc.pine](../lib/channels/dc/dc.pine) |
| DECAYCHANNEL | Decay Min-Max Channel | [decaychannel.pine](../lib/channels/decaychannel/decaychannel.pine) |
| FCB | Fractal Chaos Bands | [fcb.pine](../lib/channels/fcb/fcb.pine) |
| JBANDS | Jurik Volatility Bands | [Jbands.pine](../lib/channels/jbands/Jbands.pine) |
-| KCHANNEL | Keltner Channel | [kchannel.pine](../lib/channels/kchannel/kchannel.pine) |
+| KC | Keltner Channel | [kc.pine](../lib/channels/kc/kc.pine) |
| MAENV | Moving Average Envelope | [maenv.pine](../lib/channels/maenv/maenv.pine) |
| MMCHANNEL | Min-Max Channel | [mmchannel.pine](../lib/channels/mmchannel/mmchannel.pine) |
-| PCHANNEL | Price Channel | [pchannel.pine](../lib/channels/pchannel/pchannel.pine) |
+| PC | Price Channel | [pc.pine](../lib/channels/pc/pc.pine) |
| REGCHANNEL | Regression Channels | [regchannel.pine](../lib/channels/regchannel/regchannel.pine) |
| SDCHANNEL | Standard Deviation Channel | [sdchannel.pine](../lib/channels/sdchannel/sdchannel.pine) |
| STARCHANNEL | Stoller Average Range Channel | [starchannel.pine](../lib/channels/starchannel/starchannel.pine) |
@@ -447,7 +447,7 @@ Where price might turn around. Pivots, stops, and fractal patterns.
| PIVOTEXT | Extended Traditional Pivots | [pivotext.pine](../lib/reversals/pivotext/pivotext.pine) |
| PIVOTFIB | Fibonacci Pivot Points | [pivotfib.pine](../lib/reversals/pivotfib/pivotfib.pine) |
| PIVOTWOOD | Woodie's Pivot Points | [pivotwood.pine](../lib/reversals/pivotwood/pivotwood.pine) |
-| PSAR | Parabolic Stop And Reverse | [psar.pine](../lib/reversals/psar/psar.pine) |
+| SAR | Parabolic Stop And Reverse | [sar.pine](../lib/reversals/sar/sar.pine) |
| SAREXT | Parabolic SAR Extended | [sarext.pine](../lib/reversals/sarext/sarext.pine) |
| SWINGS | Swing High/Low Detection | [swings.pine](../lib/reversals/swings/swings.pine) |
| TTM_SCALPER | TTM Scalper Alert | [ttmscalper.pine](../lib/reversals/ttm_scalper/ttmscalper.pine) |
@@ -474,7 +474,7 @@ Quantitative measures of price behavior. Correlation, regression, distribution a
| BETA | Beta Coefficient | [beta.pine](../lib/statistics/beta/beta.pine) |
| CMA | Cumulative MA | [cma.pine](../lib/statistics/cma/cma.pine) |
| COINTEGRATION | Cointegration | [cointegration.pine](../lib/statistics/cointegration/cointegration.pine) |
-| CORRELATION | Correlation | [correlation.pine](../lib/statistics/correlation/correlation.pine) |
+| CORREL | Correlation | [correl.pine](../lib/statistics/correl/correl.pine) |
| COVARIANCE | Covariance | [covariance.pine](../lib/statistics/covariance/covariance.pine) |
| ENTROPY | Shannon Entropy | [entropy.pine](../lib/statistics/entropy/entropy.pine) |
| GEOMEAN | Geometric Mean | [geomean.pine](../lib/statistics/geomean/geomean.pine) |
@@ -677,7 +677,7 @@ You do not have to read any of this. But if you ever wonder why your backtest re
## The Full Catalog
-**[All 396 indicators with descriptions →](../lib/_index.md)**
+**[All 393 indicators with descriptions →](../lib/_index.md)**
Every indicator in that list has a `.pine` file. Every `.pine` file works on TradingView. Every implementation matches the C# reference engine.
diff --git a/docs/python.md b/docs/python.md
index 004be0f2..1761c40c 100644
--- a/docs/python.md
+++ b/docs/python.md
@@ -58,10 +58,10 @@ If you know the indicator name, call it. If you do not, here is the map:
| **Momentum** | `momentum` | Speed of price changes | `roc`, `mom`, `macd`, `tsi`, `vel` |
| **Volatility** | `volatility` | Price variability | `atr`, `bbw`, `stddev`, `hv`, `tr` |
| **Volume** | `volume` | Trading activity | `obv`, `vwma`, `mfi`, `cmf`, `adl` |
-| **Statistics** | `statistics` | Statistical measures | `zscore`, `correlation`, `entropy` |
-| **Channels** | `channels` | Price boundaries | `bbands`, `kchannel`, `dchannel` |
+| **Statistics** | `statistics` | Statistical measures | `zscore`, `correl`, `entropy` |
+| **Channels** | `channels` | Price boundaries | `bbands`, `kc`, `dc` |
| **Cycles** | `cycles` | Cycle analysis | `ht_dcperiod`, `ht_sine`, `cg`, `dsp` |
-| **Reversals** | `reversals` | Pattern detection | `psar`, `pivot`, `fractals`, `swings` |
+| **Reversals** | `reversals` | Pattern detection | `sar`, `pivot`, `fractals`, `swings` |
| **Errors** | `errors` | Error metrics, loss functions | `rmse`, `mae`, `mape`, `smape` |
| **Numerics** | `numerics` | Mathematical transforms | `fft`, `normalize`, `sigmoid`, `slope` |
diff --git a/docs/validation.md b/docs/validation.md
index 3483a1f4..a96f35a7 100644
--- a/docs/validation.md
+++ b/docs/validation.md
@@ -55,7 +55,7 @@ Next parity targets (highest impact):
| **Absolute Price Oscillator** | [Apo](../lib/oscillators/apo/Apo.md) | [✔️](../lib/oscillators/apo/Apo.md#validation) | [✔️](../lib/oscillators/apo/Apo.md#validation) | - | [✔️](../lib/oscillators/apo/Apo.md#validation) | ✔️ |
| **Acceleration Bands** | [AccBands](../lib/channels/accbands/accbands.md) | [✔️](../lib/channels/accbands/accbands.md#validation) | - | - | - | ❔ |
| **Acceleration Oscillator** | [Ac](../lib/oscillators/ac/Ac.md) | - | - | - | - | - |
-| **Accumulation/Distribution Line** | [Adl](../lib/volume/adl/adl.md) | [✔️](../lib/volume/adl/adl.md#validation) | [✔️](../lib/volume/adl/adl.md#validation) | [✔️](../lib/volume/adl/adl.md#validation) | [✔️](../lib/volume/adl/adl.md#validation) | ❔ |
+| **Accumulation/Distribution Line** | [Ad](../lib/volume/ad/ad.md) | [✔️](../lib/volume/ad/ad.md#validation) | [✔️](../lib/volume/ad/ad.md#validation) | [✔️](../lib/volume/ad/ad.md#validation) | [✔️](../lib/volume/ad/ad.md#validation) | ❔ |
| **Accumulation/Distribution Oscillator** | [Adosc](../lib/volume/adosc/adosc.md) | [✔️](../lib/volume/adosc/adosc.md#validation) | [✔️](../lib/volume/adosc/adosc.md#validation) | [✔️](../lib/volume/adosc/adosc.md#validation) | [✔️](../lib/volume/adosc/adosc.md#validation) | ❔ |
| **Accumulation Swing Index** | [Asi](../lib/momentum/asi/Asi.md) | - | - | - | - | - |
| **Adaptive Price Zone** | [Apz](../lib/channels/apz/apz.md) | - | - | - | - | - |
@@ -125,7 +125,7 @@ Next parity targets (highest impact):
| **Convolution Moving Average** | [Conv](../lib/trends_FIR/conv/Conv.md) | [✔️](../lib/trends_FIR/conv/Conv.md#validation) | [✔️](../lib/trends_FIR/conv/Conv.md#validation) | [✔️](../lib/trends_FIR/conv/Conv.md#validation) | [✔️](../lib/trends_FIR/conv/Conv.md#validation) | - |
| **Coppock Curve** | [Coppock](../lib/oscillators/coppock/Coppock.md) | - | - | - | - | ❔ |
| **Coral Trend Filter** | [Coral](../lib/trends_IIR/coral/Coral.md) | - | - | - | - | - |
-| **Correlation** | [Correlation](../lib/statistics/correlation/Correlation.md) | [✔️](../lib/statistics/correlation/Correlation.md#validation) | - | [✔️](../lib/statistics/correlation/Correlation.md#validation) | - | - |
+| **Correlation** | [Correl](../lib/statistics/correl/Correl.md) | [✔️](../lib/statistics/correl/Correl.md#validation) | - | [✔️](../lib/statistics/correl/Correl.md#validation) | - | - |
| **Correlation Trend Indicator** | [Cti](../lib/oscillators/cti/Cti.md) | - | - | - | - | ⚠️ |
| **Cumulative Moving Average** | [Cma](../lib/statistics/cma/Cma.md) | - | - | - | - | - |
| **Decay Min-Max Channel** | [Decaychannel](../lib/channels/decaychannel/decaychannel.md) | - | - | - | - | - |
@@ -139,10 +139,10 @@ Next parity targets (highest impact):
| **Directional Movement Index** | [Dx](../lib/dynamics/dx/Dx.md) | [✔️](../lib/dynamics/dx/Dx.md#validation) | [✔️](../lib/dynamics/dx/Dx.md#validation) | [✔️](../lib/dynamics/dx/Dx.md#validation) | [✔️](../lib/dynamics/dx/Dx.md#validation) | - |
| **Directional Movement Index (Jurik)** | [Dmx](../lib/dynamics/dmx/Dmx.md) | - | - | - | - | - |
| **Dirty Data Detection** | Dirty | - | - | - | - | - |
-| **Donchian Channels** | [Dchannel](../lib/channels/dchannel/Dchannel.md) | - | - | [✔️](../lib/channels/dchannel/Dchannel.md#validation) | [⚠️](../lib/channels/dchannel/Dchannel.md#validation "structural test only; Ooples Donchian uses different channel width") | ❔ |
+| **Donchian Channels** | [Dc](../lib/channels/dc/dc.md) | - | - | [✔️](../lib/channels/dc/dc.md#validation) | [⚠️](../lib/channels/dc/dc.md#validation "structural test only; Ooples Donchian uses different channel width") | ❔ |
| **Double Exponential Moving Average** | [Dema](../lib/trends_IIR/dema/Dema.md) | [✔️](../lib/trends_IIR/dema/Dema.md#validation) | [✔️](../lib/trends_IIR/dema/Dema.md#validation) | [✔️](../lib/trends_IIR/dema/Dema.md#validation) | [✔️](../lib/trends_IIR/dema/Dema.md#validation) | ⚠️ |
| **Double Weighted Moving Average** | [Dwma](../lib/trends_FIR/dwma/Dwma.md) | [✔️](../lib/trends_FIR/dwma/Dwma.md#validation) | [✔️](../lib/trends_FIR/dwma/Dwma.md#validation) | [✔️](../lib/trends_FIR/dwma/Dwma.md#validation) | - | - |
-| **Dynamic Momentum Index** | [Dymoi](../lib/oscillators/dymoi/Dymoi.md) | - | - | - | [⚠️](../lib/oscillators/dymoi/Dymoi.md#validation "structural test only; Ooples uses different dynamic period logic") | - |
+| **Dynamic Momentum Index** | [Dymi](../lib/oscillators/dymi/Dymi.md) | - | - | - | [⚠️](../lib/oscillators/dymi/Dymi.md#validation "structural test only; Ooples uses different dynamic period logic") | - |
| **Ease of Movement** | [Eom](../lib/volume/eom/Eom.md) | - | [✔️](../lib/volume/eom/Eom.md#validation) | - | - | ⚠️ |
| **Efficiency Ratio** | [Er](../lib/oscillators/er/Er.md) | - | - | - | - | ⚠️ |
| **Ehlers Autocorrelation Periodogram** | [Eacp](../lib/cycles/eacp/eacp.md) | - | - | - | - | - |
@@ -190,7 +190,7 @@ Next parity targets (highest impact):
| **Highest value** | [Highest](../lib/numerics/highest/Highest.md) | [✔️](../lib/numerics/highest/Highest.md#validation) | [✔️](../lib/numerics/highest/Highest.md#validation) | - | - | - |
| **Ehlers Hilbert Transform Dominant Cycle Period** | [HtDcPeriod](../lib/cycles/ht_dcperiod/HtDcperiod.md) | [✔️](../lib/cycles/ht_dcperiod/HtDcperiod.md#validation) | - | - | - | - |
| **Ehlers Hilbert Transform Dominant Cycle Phase** | [HtDcPhase](../lib/cycles/ht_dcphase/HtDcphase.md) | [✔️](../lib/cycles/ht_dcphase/HtDcphase.md#validation) | - | - | - | - |
-| **Ehlers Hilbert Transform Instantaneous Trend** | [Htit](../lib/trends_IIR/htit/Htit.md) | [✔️](../lib/trends_IIR/htit/Htit.md#validation) | - | [✔️](../lib/trends_IIR/htit/Htit.md#validation) | [✔️](../lib/trends_IIR/htit/Htit.md#validation) | ❔ |
+| **Ehlers Hilbert Transform Instantaneous Trend** | [HtTrendline](../lib/trends_IIR/ht_trendline/HtTrendline.md) | [✔️](../lib/trends_IIR/ht_trendline/HtTrendline.md#validation) | - | [✔️](../lib/trends_IIR/ht_trendline/HtTrendline.md#validation) | [✔️](../lib/trends_IIR/ht_trendline/HtTrendline.md#validation) | ❔ |
| **Ehlers Hilbert Transform Phasor Components** | [HtPhasor](../lib/cycles/ht_phasor/HtPhasor.md) | [✔️](../lib/cycles/ht_phasor/HtPhasor.md#validation) | - | - | - | - |
| **Ehlers Hilbert Transform SineWave** | [HtSine](../lib/cycles/ht_sine/HtSine.md) | [✔️](../lib/cycles/ht_sine/HtSine.md#validation) | - | - | - | - |
| **Ehlers Hilbert Transform Trend vs Cycle Mode** | [Ht_trendmode](../lib/dynamics/ht_trendmode/HtTrendmode.md) | [✔️](../lib/dynamics/ht_trendmode/HtTrendmode.md#validation) | - | - | - | - |
@@ -217,7 +217,7 @@ Next parity targets (highest impact):
| **Kalman Filter** | [Kalman](../lib/filters/kalman/Kalman.md) | - | - | - | - | - |
| **Kaufman Adaptive Moving Average** | [Kama](../lib/trends_IIR/kama/Kama.md) | [✔️](../lib/trends_IIR/kama/Kama.md#validation) | [✔️](../lib/trends_IIR/kama/Kama.md#validation) | [✔️](../lib/trends_IIR/kama/Kama.md#validation) | [✔️](../lib/trends_IIR/kama/Kama.md#validation) | ❔ |
| **KDJ Indicator** | [Kdj](../lib/oscillators/kdj/Kdj.md) | - | - | [⚠️](../lib/oscillators/kdj/Kdj.md#validation "structural test via GetStoch; KDJ uses RMA smoothing vs SMA") | - | ❔ |
-| **Keltner Channel** | [Kchannel](../lib/channels/kchannel/kchannel.md) | - | - | [✔️](../lib/channels/kchannel/kchannel.md#validation) | [⚠️](../lib/channels/kchannel/kchannel.md#validation "structural test only; Ooples Keltner uses different ATR multiplier") | ❔ |
+| **Keltner Channel** | [Kc](../lib/channels/kc/kc.md) | - | - | [✔️](../lib/channels/kc/kc.md#validation) | [⚠️](../lib/channels/kc/kc.md#validation "structural test only; Ooples Keltner uses different ATR multiplier") | ❔ |
| **Kendall Rank Correlation** | [Kendall](../lib/statistics/kendall/Kendall.md) | - | - | - | - | - |
| **Klinger Volume Oscillator** | [Kvo](../lib/volume/kvo/Kvo.md) | - | [✔️](../lib/volume/kvo/Kvo.md#validation) | [✔️](../lib/volume/kvo/Kvo.md#validation) | [⚠️](../lib/volume/kvo/Kvo.md#validation "structural test only; Ooples KVO uses different volume weighting") | ❔ |
| **Know Sure Thing** | [Kst](../lib/oscillators/kst/Kst.md) | - | - | - | - | ❔ |
@@ -270,7 +270,7 @@ Next parity targets (highest impact):
| **Open-Close Average** | [Midbody](../lib/core/midbody/Midbody.md) | - | - | [✔️](../lib/core/midbody/Midbody.md#validation) | - | - |
| **One Euro Filter** | [OneEuro](../lib/filters/oneeuro/OneEuro.md) | - | - | - | - | - |
| **On Balance Volume** | [Obv](../lib/volume/obv/Obv.md) | [⚠️](../lib/volume/obv/Obv.md#validation) | [✔️](../lib/volume/obv/Obv.md#validation) | [✔️](../lib/volume/obv/Obv.md#validation) | [⚠️](../lib/volume/obv/Obv.md#validation) | ✔️ |
-| **Parabolic SAR** | [Psar](../lib/reversals/psar/Psar.md) | [✔️](../lib/reversals/psar/Psar.md#validation) | - | [✔️](../lib/reversals/psar/Psar.md#validation) | [⚠️](../lib/reversals/psar/Psar.md#validation "minor SAR initialization differences prevent numeric match") | ❔ |
+| **Parabolic SAR** | [Sar](../lib/reversals/sar/Sar.md) | [✔️](../lib/reversals/sar/Sar.md#validation) | - | [✔️](../lib/reversals/sar/Sar.md#validation) | [⚠️](../lib/reversals/sar/Sar.md#validation "minor SAR initialization differences prevent numeric match") | ❔ |
| **Pascal Weighted Moving Average** | [Pwma](../lib/trends_FIR/pwma/Pwma.md) | - | - | - | [✔️](../lib/trends_FIR/pwma/Pwma.md#validation) | ❔ |
| **Percentage Change** | [Change](../lib/numerics/change/Change.md) | - | [✔️](../lib/numerics/change/Change.md#validation) | - | - | - |
| **Percentage Price Oscillator** | [Ppo](../lib/momentum/ppo/Ppo.md) | [✔️](../lib/momentum/ppo/Ppo.md#validation) | [✔️](../lib/momentum/ppo/Ppo.md#validation) | - | [✔️](../lib/momentum/ppo/Ppo.md#validation) | ❔ |
@@ -284,9 +284,9 @@ Next parity targets (highest impact):
| **Pivot Points (Fibonacci)** | [Pivotfib](../lib/reversals/pivotfib/Pivotfib.md) | - | - | - | - | - |
| **Positive Volume Index** | [Pvi](../lib/volume/pvi/Pvi.md) | - | [✔️](../lib/volume/pvi/Pvi.md#validation) | - | - | ⚠️ |
| **Pretty Good Oscillator** | [Pgo](../lib/oscillators/pgo/Pgo.md) | - | - | - | [⚠️](../lib/oscillators/pgo/Pgo.md#validation "structural test only; Ooples PGO uses different ATR normalization") | ❔ |
-| **Price Channel** | [Pchannel](../lib/channels/pchannel/pchannel.md) | - | - | [✔️](../lib/channels/pchannel/pchannel.md#validation) | - | - |
+| **Price Channel** | [Pc](../lib/channels/pc/pc.md) | - | - | [✔️](../lib/channels/pc/pc.md#validation) | - | - |
| **Price Momentum Oscillator** | [Pmo](../lib/momentum/pmo/Pmo.md) | - | - | [✔️](../lib/momentum/pmo/Pmo.md#validation) | [✔️](../lib/momentum/pmo/Pmo.md#validation) | - |
-| **Price Relative Strength** | [Prs](../lib/momentum/prs/Prs.md) | - | - | [✔️](../lib/momentum/prs/Prs.md#validation) | - | - |
+| **Price Relative Strength** | [Rs](../lib/momentum/rs/Rs.md) | - | - | [✔️](../lib/momentum/rs/Rs.md#validation) | - | - |
| **Price Volume Divergence** | [Pvd](../lib/volume/pvd/Pvd.md) | - | - | - | - | - |
| **Price Volume Rank** | [Pvr](../lib/volume/pvr/Pvr.md) | - | - | - | - | ⚠️ |
| **Price Volume Trend** | [Pvt](../lib/volume/pvt/Pvt.md) | - | - | - | [✔️](../lib/volume/pvt/Pvt.md#validation) | ⚠️ |
diff --git a/index.html b/index.html
index 6b68ba62..35e7652e 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
- QuanTAlib Docs - Dark Theme
+ QuanTAlib Documentation
@@ -42,17 +42,14 @@
.sidebar-nav li {
margin: 0;
+ font-weight: 400;
+ font-size: 14px !important;
}
.sidebar-nav ul {
padding-left: 0;
}
- .sidebar-nav li {
- font-weight: 400;
- font-size: 14px !important;
- }
-
.sidebar-nav li.sidebar-l1 > a,
.sidebar-nav li.sidebar-l1 > p > a,
.sidebar-nav li.sidebar-l1 > p {
diff --git a/lib/_index.md b/lib/_index.md
index 66e6b9cd..133cdb5d 100644
--- a/lib/_index.md
+++ b/lib/_index.md
@@ -8,7 +8,7 @@
| [ACCEL](numerics/accel/Accel.md) | Acceleration | Numerics |
| [ACF](statistics/acf/Acf.md) | Autocorrelation Function | Statistics |
| [ADF](statistics/adf/Adf.md) | Augmented Dickey-Fuller Test | Statistics |
-| [ADL](volume/adl/Adl.md) | Accumulation/Distribution Line | Volume |
+| [AD](volume/ad/Ad.md) | Accumulation/Distribution Line | Volume |
| [ADOSC](volume/adosc/Adosc.md) | Chaikin A/D Oscillator | Volume |
| [ADR](volatility/adr/Adr.md) | Average Daily Range | Volatility |
| [ADX](dynamics/adx/Adx.md) | Average Directional Index | Dynamics |
@@ -75,7 +75,7 @@
| [CONV](trends_FIR/conv/Conv.md) | Convolution MA | Trends (FIR) |
| [COPPOCK](oscillators/coppock/Coppock.md) | Coppock Curve | Oscillators |
| [CORAL](trends_IIR/coral/Coral.md) | Coral Trend Filter | Trends (IIR) |
-| [CORRELATION](statistics/correlation/Correlation.md) | Correlation | Statistics |
+| [CORREL](statistics/correl/Correl.md) | Correlation | Statistics |
| [COVARIANCE](statistics/covariance/Covariance.md) | Covariance | Statistics |
| [CRMA](trends_FIR/crma/Crma.md) | Cubic Regression MA | Trends (FIR) |
| [CRSI](oscillators/crsi/Crsi.md) | Connors RSI | Oscillators |
@@ -83,7 +83,7 @@
| [CV](volatility/cv/Cv.md) | Coefficient of Variation | Volatility |
| [CVI](volatility/cvi/Cvi.md) | Chaikin Volatility | Volatility |
| [CWT](numerics/cwt/Cwt.md) | Continuous Wavelet Transform | Numerics |
-| [DCHANNEL](channels/dchannel/Dchannel.md) | Donchian Channels | Channels |
+| [DC](channels/dc/dc.md) | Donchian Channels | Channels |
| [DECAYCHANNEL](channels/decaychannel/decaychannel.md) | Decay Min-Max Channel | Channels |
| [DECO](oscillators/deco/Deco.md) | Ehlers Decycler Oscillator | Oscillators |
| [DEM](oscillators/dem/Dem.md) | DeMarker Oscillator | Oscillators |
@@ -92,7 +92,7 @@
| [DMX](dynamics/dmx/Dmx.md) | Jurik Directional Movement Index | Dynamics |
| [DOSC](oscillators/dosc/Dosc.md) | Derivative Oscillator | Oscillators |
| [DPO](oscillators/dpo/Dpo.md) | Detrended Price Oscillator | Oscillators |
-| [DYMOI](oscillators/dymoi/Dymoi.md) | Dynamic Momentum Index | Oscillators |
+| [DYMI](oscillators/dymi/Dymi.md) | Dynamic Momentum Index | Oscillators |
| [DSMA](trends_IIR/dsma/Dsma.md) | Deviation-Scaled MA | Trends (IIR) |
| [DSP](cycles/dsp/Dsp.md) | Ehlers Detrended Synthetic Price | Cycles |
| [DWMA](trends_FIR/dwma/Dwma.md) | Double Weighted MA | Trends (FIR) |
@@ -145,7 +145,7 @@
| [HOMOD](cycles/homod/Homod.md) | Ehlers Homodyne Discriminator | Cycles |
| [HP](filters/hp/Hp.md) | Hodrick-Prescott | Filters |
| [HPF](filters/hpf/Hpf.md) | Ehlers Highpass Filter | Filters |
-| [HTIT](trends_IIR/htit/Htit.md) | Ehlers Hilbert Transform Instantaneous Trend (aka HT_TRENDLINE) | Trends (IIR) |
+| [HT_TRENDLINE](trends_IIR/ht_trendline/HtTrendline.md) | Ehlers Hilbert Transform Instantaneous Trend | Trends (IIR) |
| [HT_DCPERIOD](cycles/ht_dcperiod/Htdcperiod.md) | Ehlers HT Dominant Cycle Period | Cycles |
| [HT_DCPHASE](cycles/ht_dcphase/Htdcphase.md) | Ehlers HT Dominant Cycle Phase | Cycles |
| [HT_PHASOR](cycles/ht_phasor/Htphasor.md) | Ehlers HT Phasor Components | Cycles |
@@ -172,7 +172,7 @@
| [KAISER](trends_FIR/kaiser/Kaiser.md) | Kaiser Window MA | Trends (FIR) |
| [KALMAN](filters/kalman/Kalman.md) | Kalman Filter | Filters |
| [KAMA](trends_IIR/kama/Kama.md) | Kaufman Adaptive MA | Trends (IIR) |
-| [KCHANNEL](channels/kchannel/kchannel.md) | Keltner Channel | Channels |
+| [KC](channels/kc/kc.md) | Keltner Channel | Channels |
| [KDJ](oscillators/kdj/Kdj.md) | KDJ Indicator | Oscillators |
| [KENDALL](statistics/kendall/Kendall.md) | Kendall Rank Correlation | Statistics |
| [KRI](oscillators/kri/Kri.md) | Kairi Relative Index | Oscillators |
@@ -238,7 +238,7 @@
| [ONEEURO](filters/oneeuro/OneEuro.md) | One Euro Filter | Filters |
| [PACF](statistics/pacf/Pacf.md) | Partial Autocorrelation Function | Statistics |
| [PARZEN](trends_FIR/parzen/Parzen.md) | Parzen Window MA | Trends (FIR) |
-| [PCHANNEL](channels/pchannel/Pchannel.md) | Price Channel | Channels |
+| [PC](channels/pc/pc.md) | Price Channel | Channels |
| [PERCENTILE](statistics/percentile/Percentile.md) | Percentile | Statistics |
| [PFE](dynamics/pfe/Pfe.md) | Polarized Fractal Efficiency | Dynamics |
| [PGO](oscillators/pgo/Pgo.md) | Pretty Good Oscillator | Oscillators |
@@ -253,8 +253,8 @@
| [POISSONDIST](numerics/poissondist/Poissondist.md) | Poisson Distribution | Numerics |
| [POLYFIT](statistics/polyfit/Polyfit.md) | Polynomial Fitting | Statistics |
| [PPO](momentum/ppo/Ppo.md) | Percentage Price Oscillator | Momentum |
-| [PRS](momentum/prs/Prs.md) | Price Relative Strength | Momentum |
-| [PSAR](reversals/psar/Psar.md) | Parabolic Stop And Reverse | Reversals |
+| [RS](momentum/rs/Rs.md) | Price Relative Strength | Momentum |
+| [SAR](reversals/sar/Sar.md) | Parabolic Stop And Reverse | Reversals |
| [PSL](oscillators/psl/Psl.md) | Psychological Line | Oscillators |
| [PSEUDOHUBER](errors/pseudohuber/Pseudohuber.md) | Pseudo-Huber Loss | Errors |
| [PVD](volume/pvd/Pvd.md) | Price Volume Divergence | Volume |
diff --git a/lib/channels/_index.md b/lib/channels/_index.md
index 052652ed..b0784441 100644
--- a/lib/channels/_index.md
+++ b/lib/channels/_index.md
@@ -12,14 +12,14 @@ Channels define dynamic support and resistance. Upper band shows where price ten
| [APZ](apz/Apz.md) | Adaptive Price Zone | Double-smoothed EMA volatility channel by Lee Leibfarth. Adapts to recent volatility. |
| [ATRBANDS](atrbands/Atrbands.md) | ATR Bands | ATR-based volatility bands around moving average. |
| [BBANDS](bbands/Bbands.md) | Bollinger Bands | Standard deviation bands around SMA. Classic volatility channel. |
-| [DCHANNEL](dchannel/Dchannel.md) | Donchian Channels | Highest high and lowest low over N periods. Turtle trading foundation. |
+| [DC](dc/dc.md) | Donchian Channels | Highest high and lowest low over N periods. Turtle trading foundation. |
| [DECAYCHANNEL](decaychannel/DecayChannel.md) | Decay Min-Max Channel | Exponentially decaying min-max channel. Half-life decay toward midpoint. |
| [FCB](fcb/Fcb.md) | Fractal Chaos Bands | Tracks fractal highs and lows. Identifies chaos-based support/resistance. |
| [JBANDS](jbands/Jbands.md) | Jurik Adaptive Envelope Bands | JMA's internal adaptive envelopes. Snap to extremes, decay toward price. |
-| [KCHANNEL](kchannel/Kchannel.md) | Keltner Channel | EMA with ATR bands. Smoother than Bollinger. |
+| [KC](kc/Kc.md) | Keltner Channel | EMA with ATR bands. Smoother than Bollinger. |
| [MAENV](maenv/Maenv.md) | Moving Average Envelope | Fixed percentage bands around moving average. Simple but effective. |
| [MMCHANNEL](mmchannel/MmChannel.md) | Min-Max Channel | Rolling highest high / lowest low using O(1) monotonic deques. |
-| [PCHANNEL](pchannel/Pchannel.md) | Price Channel | Highest high and lowest low. Identical to Donchian Channels. |
+| [PC](pc/pc.md) | Price Channel | Highest high and lowest low. Identical to Donchian Channels. |
| [REGCHANNEL](regchannel/RegChannel.md) | Linear Regression Channel | Linear regression line with standard deviation bands. |
| [SDCHANNEL](sdchannel/SdChannel.md) | Standard Deviation Channel | Moving average with standard deviation bands. |
| [STARCHANNEL](starchannel/StarChannel.md) | Stoller Average Range Channel | SMA with ATR bands. Similar to Keltner but uses SMA instead of EMA. |
diff --git a/lib/channels/accbands/accbands.md b/lib/channels/accbands/accbands.md
index df0cd2e7..618eb31a 100644
--- a/lib/channels/accbands/accbands.md
+++ b/lib/channels/accbands/accbands.md
@@ -13,7 +13,7 @@
| **PineScript** | [accbands.pine](accbands.pine) |
- Acceleration Bands construct a volatility envelope using the intra-bar high-low range rather than close-to-close standard deviation, creating chann...
-- **Similar:** [BBands](../bbands/bbands.md), [KChannel](../kchannel/kchannel.md) | **Complementary:** ADX for trend strength | **Trading note:** Wider than Bollinger Bands; effective for breakout trading using high-low range volatility.
+- **Similar:** [BBands](../bbands/bbands.md), [KC](../kc/kc.md) | **Complementary:** ADX for trend strength | **Trading note:** Wider than Bollinger Bands; effective for breakout trading using high-low range volatility.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Acceleration Bands construct a volatility envelope using the intra-bar high-low range rather than close-to-close standard deviation, creating channels that accommodate the full price excursion of the underlying asset. Each bar's contribution to band width is normalized by price level ($w = (H-L)/(H+L)$), making the bands scale-invariant across instruments. Three independent Simple Moving Averages of the adjusted high, adjusted low, and close prices form the upper, lower, and middle bands respectively. Headley's original breakout rule declares a trend when price closes outside the bands for two consecutive bars.
diff --git a/lib/channels/apz/apz.md b/lib/channels/apz/apz.md
index a9b679d4..ca942f85 100644
--- a/lib/channels/apz/apz.md
+++ b/lib/channels/apz/apz.md
@@ -13,7 +13,7 @@
| **PineScript** | [apz.pine](apz.pine) |
- APZ constructs a volatility-adaptive envelope using double-smoothed exponential moving averages with an aggressive smoothing factor derived from $\...
-- **Similar:** [BBands](../bbands/bbands.md), [KChannel](../kchannel/kchannel.md) | **Complementary:** RSI for overbought/oversold confirmation | **Trading note:** EMA-based deviation adapts faster than standard deviation bands; responsive to recent volatility changes.
+- **Similar:** [BBands](../bbands/bbands.md), [KC](../kc/kc.md) | **Complementary:** RSI for overbought/oversold confirmation | **Trading note:** EMA-based deviation adapts faster than standard deviation bands; responsive to recent volatility changes.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
APZ constructs a volatility-adaptive envelope using double-smoothed exponential moving averages with an aggressive smoothing factor derived from $\sqrt{\text{period}}$, making it significantly faster than standard EMA-based channels. The center line is a double-EMA of price; the band width is a double-EMA of the high-low range, scaled by a multiplier. Designed specifically for mean-reversion trading in non-trending markets, APZ identifies overbought/oversold extremes where price is likely to reverse rather than continue. A closing price outside the zone signals an immediate overshoot, not a breakout.
diff --git a/lib/channels/atrbands/atrbands.md b/lib/channels/atrbands/atrbands.md
index 5101651a..0d13c33c 100644
--- a/lib/channels/atrbands/atrbands.md
+++ b/lib/channels/atrbands/atrbands.md
@@ -13,7 +13,7 @@
| **PineScript** | [atrbands.pine](atrbands.pine) |
- ATR Bands create a volatility-adaptive envelope by projecting Wilder's Average True Range above and below a central Simple Moving Average.
-- **Similar:** [KChannel](../kchannel/kchannel.md), [STBands](../stbands/stbands.md) | **Complementary:** ADX to distinguish trend vs range | **Trading note:** Volatility-normalized symmetric bands using ATR; adapts to true volatility including gaps.
+- **Similar:** [KC](../kc/kc.md), [STBands](../stbands/stbands.md) | **Complementary:** ADX to distinguish trend vs range | **Trading note:** Volatility-normalized symmetric bands using ATR; adapts to true volatility including gaps.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
ATR Bands create a volatility-adaptive envelope by projecting Wilder's Average True Range above and below a central Simple Moving Average. Unlike fixed-percentage envelopes or standard-deviation bands, ATR Bands use True Range to measure volatility, making them robust for assets with gaps, pre-market moves, and 24/7 trading where the "hidden" volatility between bars is significant. The True Range captures the maximum of intra-bar range, gap-up distance, and gap-down distance, ensuring that overnight gaps contribute fully to band width even when the current bar's open-to-close range is narrow.
diff --git a/lib/channels/bbands/bbands.md b/lib/channels/bbands/bbands.md
index da29056b..9d327409 100644
--- a/lib/channels/bbands/bbands.md
+++ b/lib/channels/bbands/bbands.md
@@ -13,7 +13,7 @@
| **PineScript** | [bbands.pine](bbands.pine) |
- Bollinger Bands construct a volatility-adaptive envelope around a Simple Moving Average using population standard deviation as the width measure.
-- **Similar:** [KChannel](../kchannel/kchannel.md), [SDChannel](../sdchannel/sdchannel.md), [ABERR](../aberr/aberr.md) | **Complementary:** %B and BandWidth for squeeze detection; RSI for momentum confirmation | **Trading note:** Most widely used band indicator; 2s captures ~95% of normally-distributed data but markets exhibit fat tails.
+- **Similar:** [KC](../kc/kc.md), [SDChannel](../sdchannel/sdchannel.md), [ABERR](../aberr/aberr.md) | **Complementary:** %B and BandWidth for squeeze detection; RSI for momentum confirmation | **Trading note:** Most widely used band indicator; 2s captures ~95% of normally-distributed data but markets exhibit fat tails.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Bollinger Bands construct a volatility-adaptive envelope around a Simple Moving Average using population standard deviation as the width measure. The bands expand during high-volatility periods and contract during consolidation, dynamically adapting to changing market conditions. Under Gaussian assumptions, $\pm 2\sigma$ contains approximately 95.4% of price action, but financial returns exhibit fat tails and volatility clustering, so the bands function more as a volatility-normalized reference frame than a strict probability envelope. The derived metrics %B (price position as a fraction of band width) and BandWidth (normalized band spread) extend the raw bands into a complete analytical toolkit.
diff --git a/lib/channels/pchannel/Pchannel.Quantower.cs b/lib/channels/dc/Dc.Quantower.cs
similarity index 85%
rename from lib/channels/pchannel/Pchannel.Quantower.cs
rename to lib/channels/dc/Dc.Quantower.cs
index 92764354..90a42527 100644
--- a/lib/channels/pchannel/Pchannel.Quantower.cs
+++ b/lib/channels/dc/Dc.Quantower.cs
@@ -5,11 +5,11 @@ using static QuanTAlib.IndicatorExtensions;
namespace QuanTAlib;
///
-/// Pchannel: Price Channel - Quantower Indicator Adapter
+/// Dc: Donchian Channels - Quantower Indicator Adapter
/// Upper = rolling highest high; Lower = rolling lowest low; Middle = (Upper + Lower) / 2.
/// Uses streaming O(1) deques with bar-correction support.
///
-public sealed class PchannelIndicator : Indicator, IWatchlistIndicator
+public sealed class DcIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 10, minimum: 1, maximum: 500, increment: 1, decimalPlaces: 0)]
public int Period { get; set; } = 20;
@@ -17,14 +17,14 @@ public sealed class PchannelIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show Cold Values", sortIndex: 100)]
public bool ShowColdValues { get; set; } = true;
- private Pchannel? _indicator;
+ private Dc? _indicator;
public int MinHistoryDepths => Period;
- public override string ShortName => $"Pchannel({Period})";
+ public override string ShortName => $"Dc({Period})";
- public PchannelIndicator()
+ public DcIndicator()
{
- Name = "Pchannel - Price Channel";
+ Name = "Dc - Donchian Channels";
Description = "Price channel using rolling highest high / lowest low with midpoint average";
SeparateWindow = false;
OnBackGround = true;
@@ -32,7 +32,7 @@ public sealed class PchannelIndicator : Indicator, IWatchlistIndicator
protected override void OnInit()
{
- _indicator = new Pchannel(Period);
+ _indicator = new Dc(Period);
AddLineSeries(new LineSeries("Middle", Color.DodgerBlue, 2, LineStyle.Solid));
AddLineSeries(new LineSeries("Upper", Color.FromArgb(255, 180, 180), 1, LineStyle.Dash));
diff --git a/lib/channels/dchannel/Dchannel.cs b/lib/channels/dc/Dc.cs
similarity index 96%
rename from lib/channels/dchannel/Dchannel.cs
rename to lib/channels/dc/Dc.cs
index 1ddad134..9e735f10 100644
--- a/lib/channels/dchannel/Dchannel.cs
+++ b/lib/channels/dc/Dc.cs
@@ -5,13 +5,13 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
///
-/// DCHANNEL: Donchian Channels
+/// DC: Donchian Channels
/// Upper = rolling highest high; Lower = rolling lowest low; Middle = (Upper + Lower) / 2.
/// Streaming path uses monotonic deques for O(1) amortized updates; corrections (isNew=false)
/// rebuild deques without allocations.
///
[SkipLocalsInit]
-public sealed class Dchannel : ITValuePublisher
+public sealed class Dc : ITValuePublisher
{
private readonly int _period;
private readonly double[] _hBuf;
@@ -39,7 +39,7 @@ public sealed class Dchannel : ITValuePublisher
public event TValuePublishedHandler? Pub;
- public Dchannel(int period)
+ public Dc(int period)
{
if (period <= 0)
{
@@ -56,12 +56,12 @@ public sealed class Dchannel : ITValuePublisher
_state = new State(double.NaN, double.NaN, false);
_p_state = _state;
- Name = $"Dchannel({period})";
+ Name = $"Dc({period})";
WarmupPeriod = period;
_barHandler = HandleBar;
}
- public Dchannel(TBarSeries source, int period) : this(period)
+ public Dc(TBarSeries source, int period) : this(period)
{
Prime(source);
source.Pub += _barHandler;
@@ -318,9 +318,9 @@ public sealed class Dchannel : ITValuePublisher
return (new TSeries(tMiddle, vMiddle), new TSeries(tUpper, vUpper), new TSeries(tLower, vLower));
}
- public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Dchannel Indicator) Calculate(TBarSeries source, int period)
+ public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Dc Indicator) Calculate(TBarSeries source, int period)
{
- var indicator = new Dchannel(source, period);
+ var indicator = new Dc(source, period);
var results = indicator.Update(source);
return (results, indicator);
}
diff --git a/lib/channels/dchannel/dchannel.md b/lib/channels/dc/dc.md
similarity index 92%
rename from lib/channels/dchannel/dchannel.md
rename to lib/channels/dc/dc.md
index 84f77b09..301721c9 100644
--- a/lib/channels/dchannel/dchannel.md
+++ b/lib/channels/dc/dc.md
@@ -1,4 +1,4 @@
-# DCHANNEL: Donchian Channels
+# DC: Donchian Channels
> *The highest high and lowest low over a window — Donchian's simplicity captures breakout potential in two lines.*
@@ -10,10 +10,10 @@
| **Outputs** | Multiple series (Upper, Lower) |
| **Output range** | Tracks input |
| **Warmup** | `period` bars |
-| **PineScript** | [dchannel.pine](dchannel.pine) |
+| **PineScript** | [dc.pine](dc.pine) |
-- Donchian Channels track the highest high and lowest low over a fixed lookback period, defining the absolute price boundaries within which an asset ...
-- **Similar:** [PChannel](../pchannel/pchannel.md), [UChannel](../uchannel/uchannel.md) | **Complementary:** Volume on breakouts; ATR for position sizing | **Trading note:** Pure price-based highest high/lowest low; used in the original Turtle Trading system.
+- Donchian Channels track the highest high and lowest low over a fixed lookback period, defining the absolute price boundaries within which an asset has traded.
+- **Similar:** [PC](../pc/pc.md), [UChannel](../uchannel/uchannel.md) | **Complementary:** Volume on breakouts; ATR for position sizing | **Trading note:** Pure price-based highest high/lowest low; used in the original Turtle Trading system.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Donchian Channels track the highest high and lowest low over a fixed lookback period, defining the absolute price boundaries within which an asset has traded. Unlike volatility-based bands that compute statistical dispersion, Donchian Channels represent actual historical extremes — the literal "price box." The implementation uses monotonic deques for $O(1)$ amortized sliding-window max/min, ensuring that computing a 500-period channel costs no more than a 20-period one. The midpoint of the upper and lower bands serves as a simple trend bias indicator.
@@ -75,7 +75,7 @@ The bands stay flat until either a new extreme occurs or the old extreme exits t
### Operation Count (Streaming Mode)
-DCHANNEL uses two monotonic deques for $O(1)$ amortized sliding-window max/min:
+DC uses two monotonic deques for $O(1)$ amortized sliding-window max/min:
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
diff --git a/lib/channels/dchannel/dchannel.pine b/lib/channels/dc/dc.pine
similarity index 90%
rename from lib/channels/dchannel/dchannel.pine
rename to lib/channels/dc/dc.pine
index 2da080b0..07eac9be 100644
--- a/lib/channels/dchannel/dchannel.pine
+++ b/lib/channels/dc/dc.pine
@@ -1,7 +1,7 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Donchian Channels (DCHANNEL)", "DCHANNEL", overlay=true)
+indicator("Donchian Channels (DC)", "DC", overlay=true)
//@function Calculates the Donchian Channel (DC) efficiently using monotonic deques
//@param hi Source series for the highest high calculation (usually high)
@@ -9,7 +9,7 @@ indicator("Donchian Channels (DCHANNEL)", "DCHANNEL", overlay=true)
//@param p Lookback period (p > 0)
//@returns Tuple containing [basis, upper_band, lower_band]
//@optimized Uses monotonic deque for O(1) amortized complexity per bar
-dchannel(series float hi, series float lo, simple int p) =>
+dc(series float hi, series float lo, simple int p) =>
if p <= 0
runtime.error("Period must be > 0")
var float[] hbuf = array.new_float(p, na)
@@ -41,7 +41,7 @@ i_high = input.source(high, "High Source")
i_low = input.source(low, "Low Source")
// Calculation
-[basis, upper, lower] = dchannel(i_high, i_low, i_period)
+[basis, upper, lower] = dc(i_high, i_low, i_period)
// Plot
plot(basis, "Basis", color=color.yellow, linewidth=2)
diff --git a/lib/channels/pchannel/tests/Pchannel.Quantower.Tests.cs b/lib/channels/dc/tests/Dc.Quantower.Tests.cs
similarity index 86%
rename from lib/channels/pchannel/tests/Pchannel.Quantower.Tests.cs
rename to lib/channels/dc/tests/Dc.Quantower.Tests.cs
index 15822cb1..38411282 100644
--- a/lib/channels/pchannel/tests/Pchannel.Quantower.Tests.cs
+++ b/lib/channels/dc/tests/Dc.Quantower.Tests.cs
@@ -3,16 +3,16 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class PchannelIndicatorTests
+public class DcIndicatorTests
{
[Fact]
public void Constructor_SetsDefaults()
{
- var ind = new PchannelIndicator();
+ var ind = new DcIndicator();
Assert.Equal(20, ind.Period);
Assert.True(ind.ShowColdValues);
- Assert.Equal("Pchannel - Price Channel", ind.Name);
+ Assert.Equal("Dc - Donchian Channels", ind.Name);
Assert.False(ind.SeparateWindow);
Assert.True(ind.OnBackGround);
}
@@ -20,21 +20,21 @@ public class PchannelIndicatorTests
[Fact]
public void MinHistoryDepths_EqualsPeriod()
{
- var ind = new PchannelIndicator { Period = 15 };
+ var ind = new DcIndicator { Period = 15 };
Assert.Equal(15, ind.MinHistoryDepths);
}
[Fact]
public void ShortName_ReflectsParameters()
{
- var ind = new PchannelIndicator { Period = 12 };
+ var ind = new DcIndicator { Period = 12 };
Assert.Contains("12", ind.ShortName, StringComparison.Ordinal);
}
[Fact]
public void Initialize_AddsThreeLineSeries()
{
- var ind = new PchannelIndicator { Period = 14 };
+ var ind = new DcIndicator { Period = 14 };
ind.Initialize();
Assert.Equal(3, ind.LinesSeries.Count);
@@ -46,7 +46,7 @@ public class PchannelIndicatorTests
[Fact]
public void ProcessUpdate_Historical_ComputesValues()
{
- var ind = new PchannelIndicator { Period = 3 };
+ var ind = new DcIndicator { Period = 3 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -63,7 +63,7 @@ public class PchannelIndicatorTests
[Fact]
public void ProcessUpdate_NewBar_Appends()
{
- var ind = new PchannelIndicator { Period = 3 };
+ var ind = new DcIndicator { Period = 3 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -79,7 +79,7 @@ public class PchannelIndicatorTests
[Fact]
public void ProcessUpdate_NewTick_DoesNotThrow()
{
- var ind = new PchannelIndicator { Period = 5 };
+ var ind = new DcIndicator { Period = 5 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -94,7 +94,7 @@ public class PchannelIndicatorTests
[Fact]
public void MultipleUpdates_ProducesFiniteSeries()
{
- var ind = new PchannelIndicator { Period = 5 };
+ var ind = new DcIndicator { Period = 5 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -119,7 +119,7 @@ public class PchannelIndicatorTests
[Fact]
public void Bands_Order_Correct()
{
- var ind = new PchannelIndicator { Period = 3 };
+ var ind = new DcIndicator { Period = 3 };
ind.Initialize();
var now = DateTime.UtcNow;
diff --git a/lib/channels/dchannel/tests/Dchannel.Tests.cs b/lib/channels/dc/tests/Dc.Tests.cs
similarity index 75%
rename from lib/channels/dchannel/tests/Dchannel.Tests.cs
rename to lib/channels/dc/tests/Dc.Tests.cs
index dc917590..04d01a56 100644
--- a/lib/channels/dchannel/tests/Dchannel.Tests.cs
+++ b/lib/channels/dc/tests/Dc.Tests.cs
@@ -4,23 +4,23 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class DchannelTests
+public class DcTests
{
[Fact]
- public void Dchannel_Constructor_ValidatesInput()
+ public void Dc_Constructor_ValidatesInput()
{
- Assert.Throws(() => new Dchannel(0));
- Assert.Throws(() => new Dchannel(-5));
+ Assert.Throws(() => new Dc(0));
+ Assert.Throws(() => new Dc(-5));
- var d = new Dchannel(10);
+ var d = new Dc(10);
Assert.Equal(10, d.WarmupPeriod);
- Assert.Contains("Dchannel", d.Name, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("Dc", d.Name, StringComparison.OrdinalIgnoreCase);
}
[Fact]
- public void Dchannel_InitialState_Defaults()
+ public void Dc_InitialState_Defaults()
{
- var d = new Dchannel(5);
+ var d = new Dc(5);
Assert.Equal(0, d.Last.Value);
Assert.Equal(0, d.Upper.Value);
@@ -29,9 +29,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_CalculatesBands()
+ public void Dc_CalculatesBands()
{
- var d = new Dchannel(3);
+ var d = new Dc(3);
d.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 105, 1000));
d.Update(new TBar(DateTime.UtcNow, 105, 115, 95, 110, 1000));
@@ -45,9 +45,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_SlidingWindow_Updates()
+ public void Dc_SlidingWindow_Updates()
{
- var d = new Dchannel(2);
+ var d = new Dc(2);
d.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 100, 1000));
d.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 101, 1000));
@@ -64,9 +64,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_IsHot_TurnsTrueAfterWarmup()
+ public void Dc_IsHot_TurnsTrueAfterWarmup()
{
- var d = new Dchannel(4);
+ var d = new Dc(4);
for (int i = 0; i < 3; i++)
{
@@ -79,9 +79,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_IsNewFalse_RebuildsState()
+ public void Dc_IsNewFalse_RebuildsState()
{
- var d = new Dchannel(3);
+ var d = new Dc(3);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 7);
TBar remembered = default;
@@ -109,9 +109,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_NaN_UsesLastValid()
+ public void Dc_NaN_UsesLastValid()
{
- var d = new Dchannel(3);
+ var d = new Dc(3);
d.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 105, 1000));
d.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 106, 1000));
@@ -126,9 +126,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_Reset_Clears()
+ public void Dc_Reset_Clears()
{
- var d = new Dchannel(3);
+ var d = new Dc(3);
d.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 100, 1000));
d.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 101, 1000));
@@ -144,9 +144,9 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_BatchVsStreaming_Match()
+ public void Dc_BatchVsStreaming_Match()
{
- var dStream = new Dchannel(10);
+ var dStream = new Dc(10);
var gbm = new GBM(startPrice: 100, mu: 0.02, sigma: 0.1, seed: 42);
var series = new TBarSeries();
@@ -161,7 +161,7 @@ public class DchannelTests
double expectedUp = dStream.Upper.Value;
double expectedLo = dStream.Lower.Value;
- var (midBatch, upBatch, loBatch) = Dchannel.Batch(series, 10);
+ var (midBatch, upBatch, loBatch) = Dc.Batch(series, 10);
Assert.Equal(expectedMid, midBatch.Last.Value, 1e-10);
Assert.Equal(expectedUp, upBatch.Last.Value, 1e-10);
@@ -169,7 +169,7 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_SpanBatch_Validates()
+ public void Dc_SpanBatch_Validates()
{
double[] high = [110, 115, 120];
double[] low = [90, 95, 100];
@@ -180,14 +180,14 @@ public class DchannelTests
double[] highShort = [110, 115];
double[] smallOut = new double[1];
- Assert.Throws(() => Dchannel.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 0));
- Assert.Throws(() => Dchannel.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), -1));
- Assert.Throws(() => Dchannel.Batch(highShort.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
- Assert.Throws(() => Dchannel.Batch(high.AsSpan(), low.AsSpan(), smallOut.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
+ Assert.Throws(() => Dc.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 0));
+ Assert.Throws(() => Dc.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), -1));
+ Assert.Throws(() => Dc.Batch(highShort.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
+ Assert.Throws(() => Dc.Batch(high.AsSpan(), low.AsSpan(), smallOut.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
}
[Fact]
- public void Dchannel_SpanBatch_ComputesCorrectly()
+ public void Dc_SpanBatch_ComputesCorrectly()
{
double[] high = [110, 115, 120, 125];
double[] low = [90, 95, 100, 105];
@@ -195,7 +195,7 @@ public class DchannelTests
double[] upper = new double[4];
double[] lower = new double[4];
- Dchannel.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 3);
+ Dc.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 3);
// Period=3: index 2 is first valid (indices 0,1,2)
// H=[110,115,120], L=[90,95,100] → Upper=120, Lower=90, Middle=105
@@ -210,14 +210,14 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_Calculate_ReturnsIndicatorAndResults()
+ public void Dc_Calculate_ReturnsIndicatorAndResults()
{
var series = new TBarSeries();
series.Add(DateTime.UtcNow, 100, 110, 90, 100, 1000);
series.Add(DateTime.UtcNow, 105, 115, 95, 105, 1000);
series.Add(DateTime.UtcNow, 110, 120, 100, 110, 1000);
- var ((mid, up, lo), ind) = Dchannel.Calculate(series, 2);
+ var ((mid, up, lo), ind) = Dc.Calculate(series, 2);
Assert.True(ind.IsHot);
// Period=2: last 2 bars H=[115,120], L=[95,100] → Upper=120, Lower=95, Middle=107.5
@@ -233,10 +233,10 @@ public class DchannelTests
}
[Fact]
- public void Dchannel_Event_Publishes()
+ public void Dc_Event_Publishes()
{
var src = new TBarSeries();
- var d = new Dchannel(src, 2);
+ var d = new Dc(src, 2);
bool fired = false;
d.Pub += (object? sender, in TValueEventArgs args) => fired = true;
diff --git a/lib/channels/dchannel/tests/Dchannel.Validation.Tests.cs b/lib/channels/dc/tests/Dc.Validation.Tests.cs
similarity index 82%
rename from lib/channels/dchannel/tests/Dchannel.Validation.Tests.cs
rename to lib/channels/dc/tests/Dc.Validation.Tests.cs
index 759ba61a..caffb43b 100644
--- a/lib/channels/dchannel/tests/Dchannel.Validation.Tests.cs
+++ b/lib/channels/dc/tests/Dc.Validation.Tests.cs
@@ -6,13 +6,13 @@ using OoplesFinance.StockIndicators.Models;
namespace QuanTAlib.Tests;
-public sealed class DchannelValidationTests : IDisposable
+public sealed class DcValidationTests : IDisposable
{
private readonly ValidationTestData _testData;
private readonly ITestOutputHelper _output;
private bool _disposed;
- public DchannelValidationTests(ITestOutputHelper output)
+ public DcValidationTests(ITestOutputHelper output)
{
_output = output;
_testData = new ValidationTestData();
@@ -44,7 +44,7 @@ public sealed class DchannelValidationTests : IDisposable
series.Add(new TBar(t0.AddMinutes(1), 0, 14, 10, 12, 100));
series.Add(new TBar(t0.AddMinutes(2), 0, 16, 12, 14, 100));
- var ind = new Dchannel(3);
+ var ind = new Dc(3);
var (mid, up, lo) = ind.Update(series);
Assert.Equal(16.0, up.Last.Value, 1e-10);
@@ -52,7 +52,7 @@ public sealed class DchannelValidationTests : IDisposable
Assert.Equal(12.0, mid.Last.Value, 1e-10);
Assert.True(ind.IsHot);
- _output.WriteLine("Dchannel manual period-3 calculation validated");
+ _output.WriteLine("Dc manual period-3 calculation validated");
}
[Fact]
@@ -63,18 +63,18 @@ public sealed class DchannelValidationTests : IDisposable
foreach (int period in periods)
{
// Batch (instance)
- var inst = new Dchannel(period);
+ var inst = new Dc(period);
var (bMid, bUp, bLo) = inst.Update(_testData.Bars);
// Static batch
- var (sMid, sUp, sLo) = Dchannel.Batch(_testData.Bars, period);
+ var (sMid, sUp, sLo) = Dc.Batch(_testData.Bars, period);
ValidationHelper.VerifySeriesEqual(bMid, sMid);
ValidationHelper.VerifySeriesEqual(bUp, sUp);
ValidationHelper.VerifySeriesEqual(bLo, sLo);
// Streaming
- var streaming = new Dchannel(period);
+ var streaming = new Dc(period);
var sMidStream = new TSeries();
var sUpStream = new TSeries();
var sLoStream = new TSeries();
@@ -96,7 +96,7 @@ public sealed class DchannelValidationTests : IDisposable
double[] spanMid = new double[high.Length];
double[] spanUp = new double[high.Length];
double[] spanLo = new double[high.Length];
- Dchannel.Batch(high.AsSpan(), low.AsSpan(),
+ Dc.Batch(high.AsSpan(), low.AsSpan(),
spanMid.AsSpan(), spanUp.AsSpan(), spanLo.AsSpan(), period);
for (int i = 0; i < high.Length; i++)
@@ -107,7 +107,7 @@ public sealed class DchannelValidationTests : IDisposable
}
}
- _output.WriteLine("Dchannel mode consistency validated (batch/stream/span)");
+ _output.WriteLine("Dc mode consistency validated (batch/stream/span)");
}
[Fact]
@@ -115,7 +115,7 @@ public sealed class DchannelValidationTests : IDisposable
{
const int period = 20;
var pub = new TBarSeries();
- var evtInd = new Dchannel(pub, period);
+ var evtInd = new Dc(pub, period);
var evtMid = new TSeries();
var evtUp = new TSeries();
var evtLo = new TSeries();
@@ -128,20 +128,20 @@ public sealed class DchannelValidationTests : IDisposable
evtLo.Add(evtInd.Lower);
}
- var (bMid, bUp, bLo) = Dchannel.Batch(_testData.Bars, period);
+ var (bMid, bUp, bLo) = Dc.Batch(_testData.Bars, period);
ValidationHelper.VerifySeriesEqual(bMid, evtMid);
ValidationHelper.VerifySeriesEqual(bUp, evtUp);
ValidationHelper.VerifySeriesEqual(bLo, evtLo);
- _output.WriteLine("Dchannel eventing mode validated");
+ _output.WriteLine("Dc eventing mode validated");
}
[Fact]
public void Validate_Calculate_ReturnsHotIndicator()
{
const int period = 15;
- var ((mid, up, lo), ind) = Dchannel.Calculate(_testData.Bars, period);
+ var ((mid, up, lo), ind) = Dc.Calculate(_testData.Bars, period);
Assert.True(ind.IsHot);
Assert.Equal(period, ind.WarmupPeriod);
@@ -154,7 +154,7 @@ public sealed class DchannelValidationTests : IDisposable
ind.Update(next);
Assert.True(ind.IsHot);
- _output.WriteLine("Dchannel Calculate validated");
+ _output.WriteLine("Dc Calculate validated");
}
[Fact]
@@ -162,9 +162,9 @@ public sealed class DchannelValidationTests : IDisposable
{
const int period = 25;
- var (bMid, bUp, bLo) = Dchannel.Batch(_testData.Bars, period);
+ var (bMid, bUp, bLo) = Dc.Batch(_testData.Bars, period);
- var primed = new Dchannel(period);
+ var primed = new Dc(period);
var subset = new TBarSeries();
for (int i = 0; i < 200; i++)
{
@@ -182,13 +182,13 @@ public sealed class DchannelValidationTests : IDisposable
Assert.Equal(bUp.Last.Value, primed.Upper.Value, 1e-9);
Assert.Equal(bLo.Last.Value, primed.Lower.Value, 1e-9);
- _output.WriteLine("Dchannel Prime validated against batch");
+ _output.WriteLine("Dc Prime validated against batch");
}
[Fact]
public void Validate_LargeDataset_FiniteOutputs()
{
- var (mid, up, lo) = Dchannel.Batch(_testData.Bars, 50);
+ var (mid, up, lo) = Dc.Batch(_testData.Bars, 50);
ValidationHelper.VerifyAllFinite(mid, startIndex: 0);
ValidationHelper.VerifyAllFinite(up, startIndex: 0);
@@ -199,20 +199,20 @@ public sealed class DchannelValidationTests : IDisposable
Assert.True(up[i].Value >= lo[i].Value, $"Upper >= Lower at {i}");
}
- _output.WriteLine("Dchannel large dataset validated");
+ _output.WriteLine("Dc large dataset validated");
}
[Fact]
public void Validate_Skender_Batch_UpperBand()
{
// Convention difference: Skender Donchian uses prior N bars [i-N, i-1] (excludes current bar)
- // QuanTAlib Dchannel uses inclusive N bars [i-N+1, i] (includes current bar).
+ // QuanTAlib Dc uses inclusive N bars [i-N+1, i] (includes current bar).
// Therefore: QuanTAlib[i] should match Skender[i+1] for converged values.
int[] periods = { 5, 10, 20, 50, 100 };
foreach (var period in periods)
{
- var (_, qUp, _) = Dchannel.Batch(_testData.Bars, period);
+ var (_, qUp, _) = Dc.Batch(_testData.Bars, period);
var sResult = _testData.SkenderQuotes.GetDonchian(period).ToList();
int count = Math.Min(qUp.Count, sResult.Count);
@@ -231,7 +231,7 @@ public sealed class DchannelValidationTests : IDisposable
$"Period={period}, Mismatch at q[{i}] vs s[{i + 1}]: QuanTAlib={qValue:G17}, Skender={sValue.Value:G17}");
}
}
- _output.WriteLine("Dchannel upper band validated against Skender GetDonchian (offset +1)");
+ _output.WriteLine("Dc upper band validated against Skender GetDonchian (offset +1)");
}
[Fact]
@@ -242,7 +242,7 @@ public sealed class DchannelValidationTests : IDisposable
foreach (var period in periods)
{
- var (_, _, qLo) = Dchannel.Batch(_testData.Bars, period);
+ var (_, _, qLo) = Dc.Batch(_testData.Bars, period);
var sResult = _testData.SkenderQuotes.GetDonchian(period).ToList();
int count = Math.Min(qLo.Count, sResult.Count);
@@ -261,7 +261,7 @@ public sealed class DchannelValidationTests : IDisposable
$"Period={period}, Mismatch at q[{i}] vs s[{i + 1}]: QuanTAlib={qValue:G17}, Skender={sValue.Value:G17}");
}
}
- _output.WriteLine("Dchannel lower band validated against Skender GetDonchian (offset +1)");
+ _output.WriteLine("Dc lower band validated against Skender GetDonchian (offset +1)");
}
[Fact]
@@ -272,7 +272,7 @@ public sealed class DchannelValidationTests : IDisposable
foreach (var period in periods)
{
- var (qMid, _, _) = Dchannel.Batch(_testData.Bars, period);
+ var (qMid, _, _) = Dc.Batch(_testData.Bars, period);
var sResult = _testData.SkenderQuotes.GetDonchian(period).ToList();
int count = Math.Min(qMid.Count, sResult.Count);
@@ -291,7 +291,7 @@ public sealed class DchannelValidationTests : IDisposable
$"Period={period}, Mismatch at q[{i}] vs s[{i + 1}]: QuanTAlib={qValue:G17}, Skender={sValue.Value:G17}");
}
}
- _output.WriteLine("Dchannel centerline validated against Skender GetDonchian (offset +1)");
+ _output.WriteLine("Dc centerline validated against Skender GetDonchian (offset +1)");
}
[Fact]
@@ -302,12 +302,12 @@ public sealed class DchannelValidationTests : IDisposable
foreach (var period in periods)
{
- var dchannel = new Dchannel(period);
+ var dc = new Dc(period);
var qUpResults = new TSeries();
foreach (var bar in _testData.Bars)
{
- dchannel.Update(bar);
- qUpResults.Add(dchannel.Upper);
+ dc.Update(bar);
+ qUpResults.Add(dc.Upper);
}
var sResult = _testData.SkenderQuotes.GetDonchian(period).ToList();
@@ -328,11 +328,11 @@ public sealed class DchannelValidationTests : IDisposable
$"Period={period}, Mismatch at q[{i}] vs s[{i + 1}]: QuanTAlib={qValue:G17}, Skender={sValue.Value:G17}");
}
}
- _output.WriteLine("Dchannel streaming upper band validated against Skender GetDonchian (offset +1)");
+ _output.WriteLine("Dc streaming upper band validated against Skender GetDonchian (offset +1)");
}
[Fact]
- public void Dchannel_MatchesOoples_Structural()
+ public void Dc_MatchesOoples_Structural()
{
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.15, seed: 42);
var bars = gbm.Fetch(500, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
diff --git a/lib/channels/decaychannel/decaychannel.md b/lib/channels/decaychannel/decaychannel.md
index 0e9a3aca..067746e0 100644
--- a/lib/channels/decaychannel/decaychannel.md
+++ b/lib/channels/decaychannel/decaychannel.md
@@ -13,7 +13,7 @@
| **PineScript** | [decaychannel.pine](decaychannel.pine) |
- Decay Channel combines the absolute price boundaries of Donchian Channels with exponential decay toward the midpoint, creating an envelope that exp...
-- **Similar:** [DChannel](../dchannel/dchannel.md), [PChannel](../pchannel/pchannel.md) | **Complementary:** Trend indicators like ADX | **Trading note:** Bands decay toward price when no new extremes form, reducing lag compared to traditional channels.
+- **Similar:** [DC](../dc/dc.md), [PC](../pc/pc.md) | **Complementary:** Trend indicators like ADX | **Trading note:** Bands decay toward price when no new extremes form, reducing lag compared to traditional channels.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Decay Channel combines the absolute price boundaries of Donchian Channels with exponential decay toward the midpoint, creating an envelope that expands instantly on new volatility but contracts smoothly during consolidation. While Donchian Channels hold their width until an extreme exits the lookback window, Decay Channel allows the bands to "forget" old extremes over time using a half-life model. The period parameter serves as the half-life: after that many bars without a new extreme, the band has decayed 50% of the distance back toward center. The decayed values are always clamped within Donchian bounds, ensuring they never extrapolate beyond actual price history.
diff --git a/lib/channels/fcb/fcb.md b/lib/channels/fcb/fcb.md
index 3b0414d1..f61f2b3f 100644
--- a/lib/channels/fcb/fcb.md
+++ b/lib/channels/fcb/fcb.md
@@ -13,7 +13,7 @@
| **PineScript** | [fcb.pine](fcb.pine) |
- Fractal Chaos Bands filter raw price action through Bill Williams' fractal detection logic, tracking the highest confirmed fractal high and lowest ...
-- **Similar:** [DChannel](../dchannel/dchannel.md), [PChannel](../pchannel/pchannel.md) | **Complementary:** Williams Fractals for additional confirmation | **Trading note:** Based on Bill Williams' fractal theory; bands update only on fractal pivots, creating a staircase pattern.
+- **Similar:** [DC](../dc/dc.md), [PC](../pc/pc.md) | **Complementary:** Williams Fractals for additional confirmation | **Trading note:** Based on Bill Williams' fractal theory; bands update only on fractal pivots, creating a staircase pattern.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Fractal Chaos Bands filter raw price action through Bill Williams' fractal detection logic, tracking the highest confirmed fractal high and lowest confirmed fractal low over a lookback period. Unlike Donchian Channels which use every bar's high and low, FCB uses only structurally significant turning points — bars where the middle element of a 3-bar pattern is a local extremum. The result is a "cleaner" channel that ignores transient spikes and focuses on confirmed support and resistance levels. The bands tend to remain flat during trends and step discretely when new structural pivots form, making them useful for identifying genuine breakouts versus noise.
diff --git a/lib/channels/jbands/jbands.md b/lib/channels/jbands/jbands.md
index 098e152c..398475ad 100644
--- a/lib/channels/jbands/jbands.md
+++ b/lib/channels/jbands/jbands.md
@@ -13,7 +13,7 @@
| **PineScript** | [Jbands.pine](Jbands.pine) |
- JBANDS expose the internal adaptive envelope mechanism of the Jurik Moving Average (JMA), producing asymmetric bands that snap instantly to new pri...
-- **Similar:** [BBands](../bbands/bbands.md), [KChannel](../kchannel/kchannel.md) | **Complementary:** Momentum oscillators for reversal timing | **Trading note:** Uses JMA (Jurik Moving Average) as center line for smoother, lower-lag bands compared to standard Bollinger.
+- **Similar:** [BBands](../bbands/bbands.md), [KC](../kc/kc.md) | **Complementary:** Momentum oscillators for reversal timing | **Trading note:** Uses JMA (Jurik Moving Average) as center line for smoother, lower-lag bands compared to standard Bollinger.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
JBANDS expose the internal adaptive envelope mechanism of the Jurik Moving Average (JMA), producing asymmetric bands that snap instantly to new price extremes and decay exponentially during consolidation. Unlike standard volatility bands (Bollinger, Keltner) which maintain symmetric width around a center line, JBANDS feature "snap-and-decay" hysteresis: expansion is instantaneous (plasticity), contraction is gradual (elasticity). The decay rate is dynamically modulated by a two-stage volatility estimator — a 10-bar SMA feeding a 128-bar trimmed mean — making the bands tight during quiet markets and expansive during trends. The center line is the full JMA: a 2-pole IIR filter with phase control and adaptive alpha.
diff --git a/lib/channels/kchannel/Kchannel.Quantower.cs b/lib/channels/kc/Kc.Quantower.cs
similarity index 85%
rename from lib/channels/kchannel/Kchannel.Quantower.cs
rename to lib/channels/kc/Kc.Quantower.cs
index 50ea1bc3..d753f0c7 100644
--- a/lib/channels/kchannel/Kchannel.Quantower.cs
+++ b/lib/channels/kc/Kc.Quantower.cs
@@ -5,14 +5,14 @@ using static QuanTAlib.IndicatorExtensions;
namespace QuanTAlib;
///
-/// Kchannel: Keltner Channel - Quantower Indicator Adapter
+/// Kc: Keltner Channel - Quantower Indicator Adapter
/// A volatility-based envelope using EMA as the middle line and ATR for band width.
/// Middle = EMA(close, period) with warmup compensation
/// Upper = Middle + (multiplier × ATR)
/// Lower = Middle - (multiplier × ATR)
/// ATR uses RMA (Wilder's smoothing) with warmup compensation.
///
-public sealed class KchannelIndicator : Indicator, IWatchlistIndicator
+public sealed class KcIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 10, minimum: 1, maximum: 500, increment: 1, decimalPlaces: 0)]
public int Period { get; set; } = 20;
@@ -23,14 +23,14 @@ public sealed class KchannelIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show Cold Values", sortIndex: 100)]
public bool ShowColdValues { get; set; } = true;
- private Kchannel? _indicator;
+ private Kc? _indicator;
public int MinHistoryDepths => Period * 2;
- public override string ShortName => $"Kchannel({Period},{Multiplier})";
+ public override string ShortName => $"Kc({Period},{Multiplier})";
- public KchannelIndicator()
+ public KcIndicator()
{
- Name = "Kchannel - Keltner Channel";
+ Name = "Kc - Keltner Channel";
Description = "EMA-based channel with ATR-derived band width";
SeparateWindow = false;
OnBackGround = true;
@@ -38,7 +38,7 @@ public sealed class KchannelIndicator : Indicator, IWatchlistIndicator
protected override void OnInit()
{
- _indicator = new Kchannel(Period, Multiplier);
+ _indicator = new Kc(Period, Multiplier);
AddLineSeries(new LineSeries("Middle", Color.DodgerBlue, 2, LineStyle.Solid));
AddLineSeries(new LineSeries("Upper", Color.FromArgb(255, 180, 180), 1, LineStyle.Dash));
diff --git a/lib/channels/kchannel/Kchannel.cs b/lib/channels/kc/Kc.cs
similarity index 96%
rename from lib/channels/kchannel/Kchannel.cs
rename to lib/channels/kc/Kc.cs
index d576a7ac..3caa74a4 100644
--- a/lib/channels/kchannel/Kchannel.cs
+++ b/lib/channels/kc/Kc.cs
@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
///
-/// KCHANNEL: Keltner Channel
+/// KC: Keltner Channel
/// A volatility-based envelope using EMA as the middle line and ATR for band width.
/// Middle = EMA(source, period) with warmup compensation
/// Upper = Middle + (multiplier × ATR)
@@ -13,7 +13,7 @@ namespace QuanTAlib;
/// ATR uses RMA (Wilder's smoothing) with warmup compensation.
///
[SkipLocalsInit]
-public sealed class Kchannel : ITValuePublisher
+public sealed class Kc : ITValuePublisher
{
private readonly int _period;
private readonly double _multiplier;
@@ -49,7 +49,7 @@ public sealed class Kchannel : ITValuePublisher
public event TValuePublishedHandler? Pub;
- public Kchannel(int period = 20, double multiplier = 2.0)
+ public Kc(int period = 20, double multiplier = 2.0)
{
if (period < 1)
{
@@ -68,13 +68,13 @@ public sealed class Kchannel : ITValuePublisher
WarmupPeriod = period * 2;
- Name = $"Kchannel({period},{multiplier})";
+ Name = $"Kc({period},{multiplier})";
_barHandler = HandleBar;
Reset();
}
- public Kchannel(TBarSeries source, int period = 20, double multiplier = 2.0) : this(period, multiplier)
+ public Kc(TBarSeries source, int period = 20, double multiplier = 2.0) : this(period, multiplier)
{
Prime(source);
source.Pub += _barHandler;
@@ -403,9 +403,9 @@ public sealed class Kchannel : ITValuePublisher
return (new TSeries(tMiddle, vMiddle), new TSeries(tUpper, vUpper), new TSeries(tLower, vLower));
}
- public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Kchannel Indicator) Calculate(TBarSeries source, int period = 20, double multiplier = 2.0)
+ public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Kc Indicator) Calculate(TBarSeries source, int period = 20, double multiplier = 2.0)
{
- var indicator = new Kchannel(source, period, multiplier);
+ var indicator = new Kc(source, period, multiplier);
var results = indicator.Update(source);
return (results, indicator);
}
diff --git a/lib/channels/kchannel/kchannel.md b/lib/channels/kc/kc.md
similarity index 93%
rename from lib/channels/kchannel/kchannel.md
rename to lib/channels/kc/kc.md
index e399450a..84002180 100644
--- a/lib/channels/kchannel/kchannel.md
+++ b/lib/channels/kc/kc.md
@@ -1,4 +1,4 @@
-# KCHANNEL: Keltner Channel
+# KC: Keltner Channel
> *Keltner wraps an EMA in ATR-scaled bands — a volatility envelope that responds to both trend and range.*
@@ -10,10 +10,10 @@
| **Outputs** | Multiple series (Upper, Lower) |
| **Output range** | Tracks input |
| **Warmup** | `period * 2` bars |
-| **PineScript** | [kchannel.pine](kchannel.pine) |
+| **PineScript** | [kc.pine](kc.pine) |
- Keltner Channel constructs a volatility-adaptive envelope by projecting Average True Range above and below an Exponential Moving Average center line.
-- **Similar:** [BBands](../bbands/bbands.md), [APZ](../apz/apz.md) | **Complementary:** Bollinger Band squeeze (BBands inside KChannel signals compression); MACD for trend direction | **Trading note:** ATR-based width adapts to true volatility including gaps; Chester Keltner's 1960 original used typical price and average range.
+- **Similar:** [BBands](../bbands/bbands.md), [APZ](../apz/apz.md) | **Complementary:** Bollinger Band squeeze (BBands inside KC signals compression); MACD for trend direction | **Trading note:** ATR-based width adapts to true volatility including gaps; Chester Keltner's 1960 original used typical price and average range.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Keltner Channel constructs a volatility-adaptive envelope by projecting Average True Range above and below an Exponential Moving Average center line. The channel differs from ATR Bands solely in the center line: Keltner uses EMA (faster, more responsive) while ATR Bands use SMA (more stable, more lag). The EMA center combined with ATR width creates a channel that both tracks trend and adapts to volatility, making it one of the most widely used channel indicators for trend-following and mean-reversion strategies. The implementation uses EMA with warmup compensation for accurate early values and Wilder's smoothing (RMA) for ATR.
@@ -93,7 +93,7 @@ $O(1)$ per bar: one EMA update, one True Range computation, one RMA update, and
### Operation Count (Streaming Mode)
-KCHANNEL combines an EMA with warmup compensation (center), True Range computation, and Wilder's RMA with warmup compensation (ATR):
+KC combines an EMA with warmup compensation (center), True Range computation, and Wilder's RMA with warmup compensation (ATR):
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
diff --git a/lib/channels/kchannel/kchannel.pine b/lib/channels/kc/kc.pine
similarity index 90%
rename from lib/channels/kchannel/kchannel.pine
rename to lib/channels/kc/kc.pine
index 96262305..613686de 100644
--- a/lib/channels/kchannel/kchannel.pine
+++ b/lib/channels/kc/kc.pine
@@ -1,7 +1,7 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Keltner Channel (KCHANNEL)", "KCHANNEL", overlay=true)
+indicator("Keltner Channel (KC)", "KC", overlay=true)
//@function Calculates Keltner Channel using EMA and ATR
//@param source Series to calculate middle line from
@@ -9,7 +9,7 @@ indicator("Keltner Channel (KCHANNEL)", "KCHANNEL", overlay=true)
//@param mult ATR multiplier for band width
//@returns tuple with [middle, upper, lower] band values
//@optimized Uses EMA with warmup and ATR with compensator, O(1) complexity per bar
-kchannel(series float source, simple int length, simple float mult) =>
+kc(series float source, simple int length, simple float mult) =>
if length <= 0 or mult <= 0.0
runtime.error("Length and multiplier must be greater than 0")
var float alpha = 2.0 / (length + 1)
@@ -48,7 +48,7 @@ i_length = input.int(20, "Length", minval=1)
i_mult = input.float(2.0, "ATR Multiplier", minval=0.001)
// Calculation
-[middle, upper, lower] = kchannel(i_source, i_length, i_mult)
+[middle, upper, lower] = kc(i_source, i_length, i_mult)
// Plot
plot(middle, "Middle", color=color.yellow, linewidth=2)
diff --git a/lib/channels/kchannel/tests/Kchannel.Quantower.Tests.cs b/lib/channels/kc/tests/Kc.Quantower.Tests.cs
similarity index 86%
rename from lib/channels/kchannel/tests/Kchannel.Quantower.Tests.cs
rename to lib/channels/kc/tests/Kc.Quantower.Tests.cs
index 0b6ffeec..1807e3a5 100644
--- a/lib/channels/kchannel/tests/Kchannel.Quantower.Tests.cs
+++ b/lib/channels/kc/tests/Kc.Quantower.Tests.cs
@@ -3,17 +3,17 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class KchannelIndicatorTests
+public class KcIndicatorTests
{
[Fact]
public void Constructor_SetsDefaults()
{
- var ind = new KchannelIndicator();
+ var ind = new KcIndicator();
Assert.Equal(20, ind.Period);
Assert.Equal(2.0, ind.Multiplier);
Assert.True(ind.ShowColdValues);
- Assert.Equal("Kchannel - Keltner Channel", ind.Name);
+ Assert.Equal("Kc - Keltner Channel", ind.Name);
Assert.False(ind.SeparateWindow);
Assert.True(ind.OnBackGround);
}
@@ -21,14 +21,14 @@ public class KchannelIndicatorTests
[Fact]
public void MinHistoryDepths_EqualsPeriodTimesTwo()
{
- var ind = new KchannelIndicator { Period = 15 };
+ var ind = new KcIndicator { Period = 15 };
Assert.Equal(30, ind.MinHistoryDepths); // Period * 2
}
[Fact]
public void ShortName_ReflectsParameters()
{
- var ind = new KchannelIndicator { Period = 12, Multiplier = 1.5 };
+ var ind = new KcIndicator { Period = 12, Multiplier = 1.5 };
Assert.Contains("12", ind.ShortName, StringComparison.Ordinal);
Assert.Contains("1.5", ind.ShortName, StringComparison.Ordinal);
}
@@ -36,7 +36,7 @@ public class KchannelIndicatorTests
[Fact]
public void Initialize_AddsThreeLineSeries()
{
- var ind = new KchannelIndicator { Period = 14, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 14, Multiplier = 2.0 };
ind.Initialize();
Assert.Equal(3, ind.LinesSeries.Count);
@@ -48,7 +48,7 @@ public class KchannelIndicatorTests
[Fact]
public void ProcessUpdate_Historical_ComputesValues()
{
- var ind = new KchannelIndicator { Period = 3, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 3, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -65,7 +65,7 @@ public class KchannelIndicatorTests
[Fact]
public void ProcessUpdate_NewBar_Appends()
{
- var ind = new KchannelIndicator { Period = 3, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 3, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -81,7 +81,7 @@ public class KchannelIndicatorTests
[Fact]
public void ProcessUpdate_NewTick_DoesNotThrow()
{
- var ind = new KchannelIndicator { Period = 5, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 5, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -96,7 +96,7 @@ public class KchannelIndicatorTests
[Fact]
public void MultipleUpdates_ProducesFiniteSeries()
{
- var ind = new KchannelIndicator { Period = 5, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 5, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -121,7 +121,7 @@ public class KchannelIndicatorTests
[Fact]
public void Bands_Order_Correct()
{
- var ind = new KchannelIndicator { Period = 5, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 5, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -144,7 +144,7 @@ public class KchannelIndicatorTests
[Fact]
public void Bands_Expand_WithVolatility()
{
- var ind = new KchannelIndicator { Period = 5, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 5, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -173,7 +173,7 @@ public class KchannelIndicatorTests
[Fact]
public void FirstBar_AllBandsEqualClose()
{
- var ind = new KchannelIndicator { Period = 10, Multiplier = 2.0 };
+ var ind = new KcIndicator { Period = 10, Multiplier = 2.0 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -193,8 +193,8 @@ public class KchannelIndicatorTests
[Fact]
public void Multiplier_AffectsBandWidth()
{
- var ind1 = new KchannelIndicator { Period = 10, Multiplier = 1.0 };
- var ind2 = new KchannelIndicator { Period = 10, Multiplier = 2.0 };
+ var ind1 = new KcIndicator { Period = 10, Multiplier = 1.0 };
+ var ind2 = new KcIndicator { Period = 10, Multiplier = 2.0 };
ind1.Initialize();
ind2.Initialize();
diff --git a/lib/channels/kchannel/tests/Kchannel.Tests.cs b/lib/channels/kc/tests/Kc.Tests.cs
similarity index 76%
rename from lib/channels/kchannel/tests/Kchannel.Tests.cs
rename to lib/channels/kc/tests/Kc.Tests.cs
index 597185b2..ca2b8dfa 100644
--- a/lib/channels/kchannel/tests/Kchannel.Tests.cs
+++ b/lib/channels/kc/tests/Kc.Tests.cs
@@ -4,25 +4,25 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class KchannelTests
+public class KcTests
{
[Fact]
- public void Kchannel_Constructor_ValidatesInput()
+ public void Kc_Constructor_ValidatesInput()
{
- Assert.Throws(() => new Kchannel(0));
- Assert.Throws(() => new Kchannel(-5));
- Assert.Throws(() => new Kchannel(10, 0.0));
- Assert.Throws(() => new Kchannel(10, -1.0));
+ Assert.Throws(() => new Kc(0));
+ Assert.Throws(() => new Kc(-5));
+ Assert.Throws(() => new Kc(10, 0.0));
+ Assert.Throws(() => new Kc(10, -1.0));
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
Assert.Equal(20, k.WarmupPeriod); // period * 2
- Assert.Contains("Kchannel", k.Name, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("Kc", k.Name, StringComparison.OrdinalIgnoreCase);
}
[Fact]
- public void Kchannel_InitialState_Defaults()
+ public void Kc_InitialState_Defaults()
{
- var k = new Kchannel(5);
+ var k = new Kc(5);
Assert.Equal(0, k.Last.Value);
Assert.Equal(0, k.Upper.Value);
@@ -31,9 +31,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_FirstBar_AllBandsEqualClose()
+ public void Kc_FirstBar_AllBandsEqualClose()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
var result = k.Update(new TBar(DateTime.UtcNow, 100, 105, 95, 102, 1000));
@@ -44,9 +44,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_SecondBar_BandsExpand()
+ public void Kc_SecondBar_BandsExpand()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
k.Update(new TBar(DateTime.UtcNow, 100, 105, 95, 100, 1000));
@@ -59,11 +59,11 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_BandWidth_ProportionalToATR()
+ public void Kc_BandWidth_ProportionalToATR()
{
- var k1 = new Kchannel(10, 1.0);
- var k2 = new Kchannel(10, 2.0);
- var k3 = new Kchannel(10, 3.0);
+ var k1 = new Kc(10, 1.0);
+ var k2 = new Kc(10, 2.0);
+ var k3 = new Kc(10, 3.0);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.2, seed: 42);
@@ -85,9 +85,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_BandOrder_Correct()
+ public void Kc_BandOrder_Correct()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.15, seed: 42);
for (int i = 0; i < 50; i++)
@@ -105,9 +105,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_MiddleIsEMA()
+ public void Kc_MiddleIsEMA()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 42);
for (int i = 0; i < 50; i++)
@@ -121,9 +121,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_BandSymmetry()
+ public void Kc_BandSymmetry()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 42);
for (int i = 0; i < 50; i++)
@@ -139,9 +139,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_IsHot_TurnsTrueAfterWarmup()
+ public void Kc_IsHot_TurnsTrueAfterWarmup()
{
- var k = new Kchannel(5);
+ var k = new Kc(5);
// WarmupPeriod = 5 * 2 = 10
for (int i = 0; i < 9; i++)
@@ -155,9 +155,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_IsNewFalse_RebuildsState()
+ public void Kc_IsNewFalse_RebuildsState()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 7);
TBar remembered = default;
@@ -187,9 +187,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_NaN_UsesLastValid()
+ public void Kc_NaN_UsesLastValid()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
k.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 105, 1000));
k.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 106, 1000));
@@ -204,9 +204,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_Reset_Clears()
+ public void Kc_Reset_Clears()
{
- var k = new Kchannel(10, 2.0);
+ var k = new Kc(10, 2.0);
k.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 100, 1000));
k.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 101, 1000));
k.Update(new TBar(DateTime.UtcNow, 102, 112, 92, 102, 1000));
@@ -223,9 +223,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_BatchVsStreaming_Match()
+ public void Kc_BatchVsStreaming_Match()
{
- var kStream = new Kchannel(20, 1.5);
+ var kStream = new Kc(20, 1.5);
var gbm = new GBM(startPrice: 100, mu: 0.02, sigma: 0.15, seed: 42);
var series = new TBarSeries();
@@ -240,7 +240,7 @@ public class KchannelTests
double expectedUp = kStream.Upper.Value;
double expectedLo = kStream.Lower.Value;
- var (midBatch, upBatch, loBatch) = Kchannel.Batch(series, 20, 1.5);
+ var (midBatch, upBatch, loBatch) = Kc.Batch(series, 20, 1.5);
Assert.Equal(expectedMid, midBatch.Last.Value, 1e-10);
Assert.Equal(expectedUp, upBatch.Last.Value, 1e-10);
@@ -248,7 +248,7 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_SpanBatch_Validates()
+ public void Kc_SpanBatch_Validates()
{
double[] high = [110, 115, 120];
double[] low = [90, 95, 100];
@@ -260,15 +260,15 @@ public class KchannelTests
double[] highShort = [110, 115];
double[] smallOut = new double[1];
- Assert.Throws(() => Kchannel.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 0));
- Assert.Throws(() => Kchannel.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), -1));
- Assert.Throws(() => Kchannel.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 10, 0.0));
- Assert.Throws(() => Kchannel.Batch(highShort.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
- Assert.Throws(() => Kchannel.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), smallOut.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
+ Assert.Throws(() => Kc.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 0));
+ Assert.Throws(() => Kc.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), -1));
+ Assert.Throws(() => Kc.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 10, 0.0));
+ Assert.Throws(() => Kc.Batch(highShort.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
+ Assert.Throws(() => Kc.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), smallOut.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
}
[Fact]
- public void Kchannel_SpanBatch_ComputesCorrectly()
+ public void Kc_SpanBatch_ComputesCorrectly()
{
double[] high = [105, 110, 115, 112, 118];
double[] low = [95, 100, 105, 102, 108];
@@ -277,7 +277,7 @@ public class KchannelTests
double[] upper = new double[5];
double[] lower = new double[5];
- Kchannel.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 3);
+ Kc.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 3);
// First bar: all equal close
Assert.Equal(100.0, middle[0], 1e-10);
@@ -293,14 +293,14 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_Calculate_ReturnsIndicatorAndResults()
+ public void Kc_Calculate_ReturnsIndicatorAndResults()
{
var series = new TBarSeries();
series.Add(DateTime.UtcNow, 100, 110, 90, 100, 1000);
series.Add(DateTime.UtcNow, 105, 115, 95, 105, 1000);
series.Add(DateTime.UtcNow, 102, 112, 92, 102, 1000);
- var ((mid, up, lo), ind) = Kchannel.Calculate(series, 2);
+ var ((mid, up, lo), ind) = Kc.Calculate(series, 2);
Assert.True(double.IsFinite(mid.Last.Value));
Assert.True(double.IsFinite(up.Last.Value));
@@ -314,10 +314,10 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_Event_Publishes()
+ public void Kc_Event_Publishes()
{
var src = new TBarSeries();
- var k = new Kchannel(src, 2);
+ var k = new Kc(src, 2);
bool fired = false;
k.Pub += (object? sender, in TValueEventArgs args) => fired = true;
@@ -326,10 +326,10 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_HighVolatility_WiderBands()
+ public void Kc_HighVolatility_WiderBands()
{
- var kLow = new Kchannel(20, 2.0);
- var kHigh = new Kchannel(20, 2.0);
+ var kLow = new Kc(20, 2.0);
+ var kHigh = new Kc(20, 2.0);
// Low volatility data
for (int i = 0; i < 50; i++)
@@ -350,10 +350,10 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_ShorterPeriod_FasterResponse()
+ public void Kc_ShorterPeriod_FasterResponse()
{
- var kShort = new Kchannel(5, 2.0);
- var kLong = new Kchannel(20, 2.0);
+ var kShort = new Kc(5, 2.0);
+ var kLong = new Kc(20, 2.0);
// Initial stable period
for (int i = 0; i < 30; i++)
@@ -382,9 +382,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_TrueRange_IncludesGaps()
+ public void Kc_TrueRange_IncludesGaps()
{
- var k = new Kchannel(3, 2.0);
+ var k = new Kc(3, 2.0);
// Bar 1: normal range
k.Update(new TBar(DateTime.UtcNow, 100, 105, 95, 100, 1000));
@@ -404,10 +404,10 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_WarmupCompensation_ReducesStartupBias()
+ public void Kc_WarmupCompensation_ReducesStartupBias()
{
// Warmup compensation should make early values more accurate
- var k = new Kchannel(20, 2.0);
+ var k = new Kc(20, 2.0);
// Create bars with consistent volatility
for (int i = 0; i < 100; i++)
@@ -426,9 +426,9 @@ public class KchannelTests
}
[Fact]
- public void Kchannel_LongSeriesStability()
+ public void Kc_LongSeriesStability()
{
- var k = new Kchannel(20, 2.0);
+ var k = new Kc(20, 2.0);
var gbm = new GBM(startPrice: 100, mu: 0.001, sigma: 0.02, seed: 123);
for (int i = 0; i < 10000; i++)
diff --git a/lib/channels/kchannel/tests/Kchannel.Validation.Tests.cs b/lib/channels/kc/tests/Kc.Validation.Tests.cs
similarity index 84%
rename from lib/channels/kchannel/tests/Kchannel.Validation.Tests.cs
rename to lib/channels/kc/tests/Kc.Validation.Tests.cs
index c72eb8ce..94119cb4 100644
--- a/lib/channels/kchannel/tests/Kchannel.Validation.Tests.cs
+++ b/lib/channels/kc/tests/Kc.Validation.Tests.cs
@@ -6,13 +6,13 @@ using OoplesFinance.StockIndicators.Models;
namespace QuanTAlib.Tests;
-public sealed class KchannelValidationTests : IDisposable
+public sealed class KcValidationTests : IDisposable
{
private readonly ValidationTestData _testData;
private readonly ITestOutputHelper _output;
private bool _disposed;
- public KchannelValidationTests(ITestOutputHelper output)
+ public KcValidationTests(ITestOutputHelper output)
{
_output = output;
_testData = new ValidationTestData();
@@ -49,7 +49,7 @@ public sealed class KchannelValidationTests : IDisposable
// Bar 2: close=105, high=112, low=100 (range=12, prevClose=102, TR=max(12,10,2)=12)
series.Add(new TBar(t0.AddMinutes(2), 105, 112, 100, 105, 100));
- var ind = new Kchannel(10, 2.0);
+ var ind = new Kc(10, 2.0);
var (mid, up, lo) = ind.Update(series);
// First bar: all equal close
@@ -72,7 +72,7 @@ public sealed class KchannelValidationTests : IDisposable
Assert.Equal(upperDist, lowerDist, 1e-10);
}
- _output.WriteLine("Kchannel manual calculation validated");
+ _output.WriteLine("Kc manual calculation validated");
}
[Fact]
@@ -86,18 +86,18 @@ public sealed class KchannelValidationTests : IDisposable
foreach (double multiplier in multipliers)
{
// Batch (instance)
- var inst = new Kchannel(period, multiplier);
+ var inst = new Kc(period, multiplier);
var (bMid, bUp, bLo) = inst.Update(_testData.Bars);
// Static batch
- var (sMid, sUp, sLo) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (sMid, sUp, sLo) = Kc.Batch(_testData.Bars, period, multiplier);
ValidationHelper.VerifySeriesEqual(bMid, sMid);
ValidationHelper.VerifySeriesEqual(bUp, sUp);
ValidationHelper.VerifySeriesEqual(bLo, sLo);
// Streaming
- var streaming = new Kchannel(period, multiplier);
+ var streaming = new Kc(period, multiplier);
var sMidStream = new TSeries();
var sUpStream = new TSeries();
var sLoStream = new TSeries();
@@ -120,7 +120,7 @@ public sealed class KchannelValidationTests : IDisposable
double[] spanMid = new double[high.Length];
double[] spanUp = new double[high.Length];
double[] spanLo = new double[high.Length];
- Kchannel.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(),
+ Kc.Batch(high.AsSpan(), low.AsSpan(), close.AsSpan(),
spanMid.AsSpan(), spanUp.AsSpan(), spanLo.AsSpan(), period, multiplier);
for (int i = 0; i < high.Length; i++)
@@ -132,7 +132,7 @@ public sealed class KchannelValidationTests : IDisposable
}
}
- _output.WriteLine("Kchannel mode consistency validated (batch/stream/span)");
+ _output.WriteLine("Kc mode consistency validated (batch/stream/span)");
}
[Fact]
@@ -142,7 +142,7 @@ public sealed class KchannelValidationTests : IDisposable
const double multiplier = 2.0;
var pub = new TBarSeries();
- var evtInd = new Kchannel(pub, period, multiplier);
+ var evtInd = new Kc(pub, period, multiplier);
var evtMid = new TSeries();
var evtUp = new TSeries();
var evtLo = new TSeries();
@@ -155,13 +155,13 @@ public sealed class KchannelValidationTests : IDisposable
evtLo.Add(evtInd.Lower);
}
- var (bMid, bUp, bLo) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (bMid, bUp, bLo) = Kc.Batch(_testData.Bars, period, multiplier);
ValidationHelper.VerifySeriesEqual(bMid, evtMid);
ValidationHelper.VerifySeriesEqual(bUp, evtUp);
ValidationHelper.VerifySeriesEqual(bLo, evtLo);
- _output.WriteLine("Kchannel eventing mode validated");
+ _output.WriteLine("Kc eventing mode validated");
}
[Fact]
@@ -170,7 +170,7 @@ public sealed class KchannelValidationTests : IDisposable
const int period = 15;
const double multiplier = 2.5;
- var ((mid, up, lo), ind) = Kchannel.Calculate(_testData.Bars, period, multiplier);
+ var ((mid, up, lo), ind) = Kc.Calculate(_testData.Bars, period, multiplier);
Assert.True(ind.IsHot);
Assert.Equal(period * 2, ind.WarmupPeriod);
@@ -183,7 +183,7 @@ public sealed class KchannelValidationTests : IDisposable
ind.Update(next);
Assert.True(ind.IsHot);
- _output.WriteLine("Kchannel Calculate validated");
+ _output.WriteLine("Kc Calculate validated");
}
[Fact]
@@ -192,9 +192,9 @@ public sealed class KchannelValidationTests : IDisposable
const int period = 25;
const double multiplier = 1.5;
- var (bMid, bUp, bLo) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (bMid, bUp, bLo) = Kc.Batch(_testData.Bars, period, multiplier);
- var primed = new Kchannel(period, multiplier);
+ var primed = new Kc(period, multiplier);
var subset = new TBarSeries();
for (int i = 0; i < 200; i++)
{
@@ -212,13 +212,13 @@ public sealed class KchannelValidationTests : IDisposable
Assert.Equal(bUp.Last.Value, primed.Upper.Value, 1e-9);
Assert.Equal(bLo.Last.Value, primed.Lower.Value, 1e-9);
- _output.WriteLine("Kchannel Prime validated against batch");
+ _output.WriteLine("Kc Prime validated against batch");
}
[Fact]
public void Validate_LargeDataset_FiniteOutputs()
{
- var (mid, up, lo) = Kchannel.Batch(_testData.Bars, 50, 2.0);
+ var (mid, up, lo) = Kc.Batch(_testData.Bars, 50, 2.0);
ValidationHelper.VerifyAllFinite(mid, startIndex: 0);
ValidationHelper.VerifyAllFinite(up, startIndex: 0);
@@ -230,13 +230,13 @@ public sealed class KchannelValidationTests : IDisposable
Assert.True(up[i].Value > lo[i].Value, $"Upper > Lower at {i}");
}
- _output.WriteLine("Kchannel large dataset validated");
+ _output.WriteLine("Kc large dataset validated");
}
[Fact]
public void Validate_BandSymmetry_AllBars()
{
- var ind = new Kchannel(20, 2.0);
+ var ind = new Kc(20, 2.0);
var (mid, up, lo) = ind.Update(_testData.Bars);
for (int i = 0; i < mid.Count; i++)
@@ -246,7 +246,7 @@ public sealed class KchannelValidationTests : IDisposable
Assert.Equal(upperWidth, lowerWidth, 1e-10);
}
- _output.WriteLine("Kchannel band symmetry validated for all bars");
+ _output.WriteLine("Kc band symmetry validated for all bars");
}
[Fact]
@@ -257,7 +257,7 @@ public sealed class KchannelValidationTests : IDisposable
for (int i = 0; i < multipliers.Length; i++)
{
- var ind = new Kchannel(20, multipliers[i]);
+ var ind = new Kc(20, multipliers[i]);
foreach (var bar in _testData.Bars)
{
ind.Update(bar);
@@ -273,7 +273,7 @@ public sealed class KchannelValidationTests : IDisposable
Assert.Equal(expected, widths[i], 1e-9);
}
- _output.WriteLine("Kchannel multiplier scaling validated");
+ _output.WriteLine("Kc multiplier scaling validated");
}
[Fact]
@@ -284,7 +284,7 @@ public sealed class KchannelValidationTests : IDisposable
for (int i = 0; i < periods.Length; i++)
{
- var ind = new Kchannel(periods[i], 2.0);
+ var ind = new Kc(periods[i], 2.0);
foreach (var bar in _testData.Bars)
{
ind.Update(bar);
@@ -298,7 +298,7 @@ public sealed class KchannelValidationTests : IDisposable
Assert.True(double.IsFinite(m));
}
- _output.WriteLine("Kchannel period effect validated");
+ _output.WriteLine("Kc period effect validated");
}
[Fact]
@@ -315,7 +315,7 @@ public sealed class KchannelValidationTests : IDisposable
// Bar 2: gap down (prev close=115, new high=100)
series.Add(new TBar(t0.AddMinutes(2), 95, 100, 90, 95, 100));
- var ind = new Kchannel(3, 2.0);
+ var ind = new Kc(3, 2.0);
var (mid, up, lo) = ind.Update(series);
// Bands should expand due to gaps
@@ -325,7 +325,7 @@ public sealed class KchannelValidationTests : IDisposable
Assert.True(width > 0, $"Band width > 0 at bar {i}");
}
- _output.WriteLine("Kchannel ATR true range validated with gaps");
+ _output.WriteLine("Kc ATR true range validated with gaps");
}
[Fact]
@@ -340,7 +340,7 @@ public sealed class KchannelValidationTests : IDisposable
series.Add(new TBar(t0.AddMinutes(i), 100, 105, 95, 100, 100));
}
- var ind = new Kchannel(20, 2.0);
+ var ind = new Kc(20, 2.0);
var (mid, _, _) = ind.Update(series);
// After warmup, middle should be very close to constant price
@@ -349,13 +349,13 @@ public sealed class KchannelValidationTests : IDisposable
Assert.InRange(mid[i].Value, 99.9, 100.1);
}
- _output.WriteLine("Kchannel warmup compensation validated");
+ _output.WriteLine("Kc warmup compensation validated");
}
[Fact]
public void Validate_StateRestoration_Iterative()
{
- var ind = new Kchannel(15, 2.5);
+ var ind = new Kc(15, 2.5);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 42);
// Build up state
@@ -382,7 +382,7 @@ public sealed class KchannelValidationTests : IDisposable
Assert.True(double.IsFinite(ind.Upper.Value));
Assert.True(double.IsFinite(ind.Lower.Value));
- _output.WriteLine("Kchannel state restoration validated");
+ _output.WriteLine("Kc state restoration validated");
}
[Fact]
@@ -398,7 +398,7 @@ public sealed class KchannelValidationTests : IDisposable
foreach (var period in periods)
{
- var (qMiddle, _, _) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (qMiddle, _, _) = Kc.Batch(_testData.Bars, period, multiplier);
// Skender: atrPeriods = period to match QuanTAlib's single-period design
var sResult = _testData.SkenderQuotes
@@ -408,7 +408,7 @@ public sealed class KchannelValidationTests : IDisposable
// Compare middle band (EMA of close) using ValidationHelper
ValidationHelper.VerifyData(qMiddle, sResult, s => s.Centerline);
}
- _output.WriteLine("Kchannel middle band validated against Skender for all periods");
+ _output.WriteLine("Kc middle band validated against Skender for all periods");
}
[Fact]
@@ -419,7 +419,7 @@ public sealed class KchannelValidationTests : IDisposable
foreach (var period in periods)
{
- var (_, up, _) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (_, up, _) = Kc.Batch(_testData.Bars, period, multiplier);
var sResult = _testData.SkenderQuotes
.GetKeltner(period, multiplier, period)
@@ -427,7 +427,7 @@ public sealed class KchannelValidationTests : IDisposable
ValidationHelper.VerifyData(up, sResult, s => s.UpperBand);
}
- _output.WriteLine("Kchannel upper band validated against Skender for all periods");
+ _output.WriteLine("Kc upper band validated against Skender for all periods");
}
[Fact]
@@ -438,7 +438,7 @@ public sealed class KchannelValidationTests : IDisposable
foreach (var period in periods)
{
- var (_, _, lo) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (_, _, lo) = Kc.Batch(_testData.Bars, period, multiplier);
var sResult = _testData.SkenderQuotes
.GetKeltner(period, multiplier, period)
@@ -446,7 +446,7 @@ public sealed class KchannelValidationTests : IDisposable
ValidationHelper.VerifyData(lo, sResult, s => s.LowerBand);
}
- _output.WriteLine("Kchannel lower band validated against Skender for all periods");
+ _output.WriteLine("Kc lower band validated against Skender for all periods");
}
[Fact]
@@ -460,7 +460,7 @@ public sealed class KchannelValidationTests : IDisposable
.GetKeltner(period, multiplier, period)
.ToList();
- var (ourMid, ourUp, ourLo) = Kchannel.Batch(_testData.Bars, period, multiplier);
+ var (ourMid, ourUp, ourLo) = Kc.Batch(_testData.Bars, period, multiplier);
int warmup = period * 2;
for (int i = warmup; i < ourMid.Count && i < sResult.Count; i++)
@@ -475,7 +475,7 @@ public sealed class KchannelValidationTests : IDisposable
}
}
- _output.WriteLine($"Kchannel vs Skender band structure validated");
+ _output.WriteLine($"Kc vs Skender band structure validated");
}
[Fact]
@@ -486,7 +486,7 @@ public sealed class KchannelValidationTests : IDisposable
foreach (int period in periods)
{
- var (mid, up, lo) = Kchannel.Batch(_testData.Bars, period, 2.0);
+ var (mid, up, lo) = Kc.Batch(_testData.Bars, period, 2.0);
// Band width should be exactly 2x ATR (multiplier * ATR)
for (int i = 1; i < mid.Count; i++)
@@ -501,7 +501,7 @@ public sealed class KchannelValidationTests : IDisposable
}
}
- _output.WriteLine("Kchannel band width consistency validated");
+ _output.WriteLine("Kc band width consistency validated");
}
[Fact]
@@ -519,7 +519,7 @@ public sealed class KchannelValidationTests : IDisposable
// Bar 2: TR = max(105-95, |105-100|, |95-100|) = max(10, 5, 5) = 10
series.Add(new TBar(t0.AddMinutes(2), 100, 105, 95, 100, 100));
- var ind = new Kchannel(3, 1.0); // multiplier=1 so width = 2*ATR
+ var ind = new Kc(3, 1.0); // multiplier=1 so width = 2*ATR
var (mid, up, lo) = ind.Update(series);
// All outputs should be finite
@@ -537,11 +537,11 @@ public sealed class KchannelValidationTests : IDisposable
Assert.True(width > 0, $"Band width > 0 at bar {i}");
}
- _output.WriteLine("Kchannel ATR calculation validated");
+ _output.WriteLine("Kc ATR calculation validated");
}
[Fact]
- public void Kchannel_MatchesOoples_Structural()
+ public void Kc_MatchesOoples_Structural()
{
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.15, seed: 42);
var bars = gbm.Fetch(500, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
diff --git a/lib/channels/mmchannel/mmchannel.md b/lib/channels/mmchannel/mmchannel.md
index cf5683f1..acde1665 100644
--- a/lib/channels/mmchannel/mmchannel.md
+++ b/lib/channels/mmchannel/mmchannel.md
@@ -13,7 +13,7 @@
| **PineScript** | [mmchannel.pine](mmchannel.pine) |
- Min-Max Channel tracks the highest high and lowest low over a lookback period, creating a pure price envelope without any midpoint calculation.
-- **Similar:** [DChannel](../dchannel/dchannel.md), [PChannel](../pchannel/pchannel.md) | **Complementary:** ATR for volatility context | **Trading note:** Min/Max channel; breakout system based on highest high and lowest low.
+- **Similar:** [DC](../dc/dc.md), [PC](../pc/pc.md) | **Complementary:** ATR for volatility context | **Trading note:** Min/Max channel; breakout system based on highest high and lowest low.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Min-Max Channel tracks the highest high and lowest low over a lookback period, creating a pure price envelope without any midpoint calculation. Unlike Donchian Channels which include a middle band, MMCHANNEL delivers only the raw extremes. The implementation uses monotonic deques for O(1) amortized updates: each element enters the deque once and leaves at most once, so total work over $N$ bars is $O(N)$ regardless of period length.
diff --git a/lib/channels/mmchannel/tests/Mmchannel.Validation.Tests.cs b/lib/channels/mmchannel/tests/Mmchannel.Validation.Tests.cs
index 5ef5bfa8..4d10a3f7 100644
--- a/lib/channels/mmchannel/tests/Mmchannel.Validation.Tests.cs
+++ b/lib/channels/mmchannel/tests/Mmchannel.Validation.Tests.cs
@@ -187,21 +187,21 @@ public sealed class MmchannelValidationTests : IDisposable
}
[Fact]
- public void Validate_AgainstDchannel_Bands()
+ public void Validate_AgainstDc_Bands()
{
- // Mmchannel upper/lower should exactly match Dchannel upper/lower
+ // Mmchannel upper/lower should exactly match Dc upper/lower
int[] periods = { 10, 20, 50 };
foreach (int period in periods)
{
- var (_, dcUp, dcLo) = Dchannel.Batch(_testData.Bars, period);
+ var (_, dcUp, dcLo) = Dc.Batch(_testData.Bars, period);
var (mmUp, mmLo) = Mmchannel.Batch(_testData.Bars, period);
ValidationHelper.VerifySeriesEqual(dcUp, mmUp);
ValidationHelper.VerifySeriesEqual(dcLo, mmLo);
}
- _output.WriteLine("Mmchannel matches Dchannel upper/lower bands");
+ _output.WriteLine("Mmchannel matches Dc upper/lower bands");
}
[Fact]
@@ -209,7 +209,7 @@ public sealed class MmchannelValidationTests : IDisposable
{
// Note: Skender's Donchian uses lookbackPeriods+1 for the window size (includes current bar differently)
// This test validates that we get finite, reasonable results, but exact match is not expected
- // due to this convention difference. The exact match is validated via Dchannel comparison above.
+ // due to this convention difference. The exact match is validated via Dc comparison above.
const int period = 20;
var skenderResult = _testData.SkenderQuotes
diff --git a/lib/channels/dchannel/Dchannel.Quantower.cs b/lib/channels/pc/Pc.Quantower.cs
similarity index 84%
rename from lib/channels/dchannel/Dchannel.Quantower.cs
rename to lib/channels/pc/Pc.Quantower.cs
index 03a6f700..ff95915d 100644
--- a/lib/channels/dchannel/Dchannel.Quantower.cs
+++ b/lib/channels/pc/Pc.Quantower.cs
@@ -5,11 +5,11 @@ using static QuanTAlib.IndicatorExtensions;
namespace QuanTAlib;
///
-/// Dchannel: Donchian Channels - Quantower Indicator Adapter
+/// Pc: Price Channel - Quantower Indicator Adapter
/// Upper = rolling highest high; Lower = rolling lowest low; Middle = (Upper + Lower) / 2.
/// Uses streaming O(1) deques with bar-correction support.
///
-public sealed class DchannelIndicator : Indicator, IWatchlistIndicator
+public sealed class PcIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 10, minimum: 1, maximum: 500, increment: 1, decimalPlaces: 0)]
public int Period { get; set; } = 20;
@@ -17,14 +17,14 @@ public sealed class DchannelIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show Cold Values", sortIndex: 100)]
public bool ShowColdValues { get; set; } = true;
- private Dchannel? _indicator;
+ private Pc? _indicator;
public int MinHistoryDepths => Period;
- public override string ShortName => $"Dchannel({Period})";
+ public override string ShortName => $"Pc({Period})";
- public DchannelIndicator()
+ public PcIndicator()
{
- Name = "Dchannel - Donchian Channels";
+ Name = "Pc - Price Channel";
Description = "Price channel using rolling highest high / lowest low with midpoint average";
SeparateWindow = false;
OnBackGround = true;
@@ -32,7 +32,7 @@ public sealed class DchannelIndicator : Indicator, IWatchlistIndicator
protected override void OnInit()
{
- _indicator = new Dchannel(Period);
+ _indicator = new Pc(Period);
AddLineSeries(new LineSeries("Middle", Color.DodgerBlue, 2, LineStyle.Solid));
AddLineSeries(new LineSeries("Upper", Color.FromArgb(255, 180, 180), 1, LineStyle.Dash));
diff --git a/lib/channels/pchannel/Pchannel.cs b/lib/channels/pc/Pc.cs
similarity index 96%
rename from lib/channels/pchannel/Pchannel.cs
rename to lib/channels/pc/Pc.cs
index 9911679b..3e16f83c 100644
--- a/lib/channels/pchannel/Pchannel.cs
+++ b/lib/channels/pc/Pc.cs
@@ -5,14 +5,14 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
///
-/// PCHANNEL: Price Channel
+/// PC: Price Channel
/// Upper = rolling highest high; Lower = rolling lowest low; Middle = (Upper + Lower) / 2.
-/// Functionally equivalent to Donchian Channels (DCHANNEL).
+/// Functionally equivalent to Donchian Channels (DC).
/// Streaming path uses monotonic deques for O(1) amortized updates; corrections (isNew=false)
/// rebuild deques without allocations.
///
[SkipLocalsInit]
-public sealed class Pchannel : ITValuePublisher
+public sealed class Pc : ITValuePublisher
{
private readonly int _period;
private readonly double[] _hBuf;
@@ -48,7 +48,7 @@ public sealed class Pchannel : ITValuePublisher
public event TValuePublishedHandler? Pub;
- public Pchannel(int period)
+ public Pc(int period)
{
if (period <= 0)
{
@@ -69,12 +69,12 @@ public sealed class Pchannel : ITValuePublisher
_state = new State(double.NaN, double.NaN, false);
_p_state = _state;
- Name = $"Pchannel({period})";
+ Name = $"Pc({period})";
WarmupPeriod = period;
_barHandler = HandleBar;
}
- public Pchannel(TBarSeries source, int period) : this(period)
+ public Pc(TBarSeries source, int period) : this(period)
{
Prime(source);
source.Pub += _barHandler;
@@ -425,9 +425,9 @@ public sealed class Pchannel : ITValuePublisher
return (new TSeries(tMiddle, vMiddle), new TSeries(tUpper, vUpper), new TSeries(tLower, vLower));
}
- public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Pchannel Indicator) Calculate(TBarSeries source, int period)
+ public static ((TSeries Middle, TSeries Upper, TSeries Lower) Results, Pc Indicator) Calculate(TBarSeries source, int period)
{
- var indicator = new Pchannel(source, period);
+ var indicator = new Pc(source, period);
var results = indicator.Update(source);
return (results, indicator);
}
diff --git a/lib/channels/pchannel/pchannel.md b/lib/channels/pc/pc.md
similarity index 88%
rename from lib/channels/pchannel/pchannel.md
rename to lib/channels/pc/pc.md
index a2da63b7..8e14007f 100644
--- a/lib/channels/pchannel/pchannel.md
+++ b/lib/channels/pc/pc.md
@@ -1,4 +1,4 @@
-# PCHANNEL: Price Channel
+# PC: Price Channel
> *Price channels frame the trading range by its own high-low extremes, defining the field of play.*
@@ -10,10 +10,10 @@
| **Outputs** | Multiple series (Upper, Lower) |
| **Output range** | Tracks input |
| **Warmup** | `period` bars |
-| **PineScript** | [pchannel.pine](pchannel.pine) |
+| **PineScript** | [pc.pine](pc.pine) |
-- Price Channel tracks the highest high and lowest low over a lookback period with a midpoint average, creating a three-line price envelope that defi...
-- **Similar:** [DChannel](../dchannel/dchannel.md), [MMChannel](../mmchannel/mmchannel.md) | **Complementary:** Volume confirmation on breakouts | **Trading note:** Price channel based on percentage offset from midpoint.
+- Price Channel tracks the highest high and lowest low over a lookback period with a midpoint average, creating a three-line price envelope that defines where the market has been.
+- **Similar:** [DC](../dc/dc.md), [MMChannel](../mmchannel/mmchannel.md) | **Complementary:** Volume confirmation on breakouts | **Trading note:** Price channel based on percentage offset from midpoint.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Price Channel tracks the highest high and lowest low over a lookback period with a midpoint average, creating a three-line price envelope that defines where the market has been. Functionally identical to Donchian Channels, the indicator uses actual price extremes rather than volatility estimates, producing bands that represent real support and resistance levels. This implementation uses monotonic deques for O(1) amortized updates instead of the naive O(n) rescan that most platforms use internally.
@@ -56,7 +56,7 @@ $$
M_t = \frac{U_t + L_t}{2}
$$
-This represents the equilibrium price of the lookback window. Unlike MMCHANNEL which omits the midpoint, PCHANNEL always emits all three lines.
+This represents the equilibrium price of the lookback window. Unlike MMCHANNEL which omits the midpoint, PC always emits all three lines.
### 4. Monotonic Deque Mechanism
@@ -93,7 +93,7 @@ Streaming: $O(1)$ amortized per bar. Each element enters and exits each deque at
### Operation Count (Streaming Mode)
-PCHANNEL uses two monotonic deques for $O(1)$ amortized sliding-window max/min plus a midpoint:
+PC uses two monotonic deques for $O(1)$ amortized sliding-window max/min plus a midpoint:
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
@@ -105,7 +105,7 @@ PCHANNEL uses two monotonic deques for $O(1)$ amortized sliding-window max/min p
| MUL (× 0.5 for middle) | 1 | 3 | 3 |
| **Total (amortized)** | **~6** | — | **~8 cycles** |
-Identical to DCHANNEL in cost. Each element enters and exits each deque exactly once over the full series, yielding $O(N)$ total work across $N$ bars regardless of period.
+Identical to DC in cost. Each element enters and exits each deque exactly once over the full series, yielding $O(N)$ total work across $N$ bars regardless of period.
### Batch Mode (SIMD Analysis)
diff --git a/lib/channels/pchannel/pchannel.pine b/lib/channels/pc/pc.pine
similarity index 94%
rename from lib/channels/pchannel/pchannel.pine
rename to lib/channels/pc/pc.pine
index 3c04be73..50683a4b 100644
--- a/lib/channels/pchannel/pchannel.pine
+++ b/lib/channels/pc/pc.pine
@@ -1,13 +1,13 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Price Channel (PCHANNEL)", "PCHANNEL", overlay=true)
+indicator("Price Channel (PC)", "PC", overlay=true)
//@function Calculates Price Channel
//@param length_param Lookback period for determining the highest high and lowest low
//@returns tuple [upperChannel, middleChannel, lowerChannel]
//@optimized Uses monotonic deque for O(1) amortized complexity per bar
-pchannel(simple int length_param) =>
+pc(simple int length_param) =>
if length_param <= 0
runtime.error("Length must be greater than 0")
var deque_hi = array.new_int(0)
@@ -55,7 +55,7 @@ pchannel(simple int length_param) =>
i_length = input.int(20, "Length", minval=1)
// Calculation
-[upperCh, middleCh, lowerCh] = pchannel(i_length)
+[upperCh, middleCh, lowerCh] = pc(i_length)
// Plot
plot(middleCh, "Middle Channel", color=color.yellow, linewidth=2)
diff --git a/lib/channels/dchannel/tests/Dchannel.Quantower.Tests.cs b/lib/channels/pc/tests/Pc.Quantower.Tests.cs
similarity index 86%
rename from lib/channels/dchannel/tests/Dchannel.Quantower.Tests.cs
rename to lib/channels/pc/tests/Pc.Quantower.Tests.cs
index db6b5945..f941f17e 100644
--- a/lib/channels/dchannel/tests/Dchannel.Quantower.Tests.cs
+++ b/lib/channels/pc/tests/Pc.Quantower.Tests.cs
@@ -3,16 +3,16 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class DchannelIndicatorTests
+public class PcIndicatorTests
{
[Fact]
public void Constructor_SetsDefaults()
{
- var ind = new DchannelIndicator();
+ var ind = new PcIndicator();
Assert.Equal(20, ind.Period);
Assert.True(ind.ShowColdValues);
- Assert.Equal("Dchannel - Donchian Channels", ind.Name);
+ Assert.Equal("Pc - Price Channel", ind.Name);
Assert.False(ind.SeparateWindow);
Assert.True(ind.OnBackGround);
}
@@ -20,21 +20,21 @@ public class DchannelIndicatorTests
[Fact]
public void MinHistoryDepths_EqualsPeriod()
{
- var ind = new DchannelIndicator { Period = 15 };
+ var ind = new PcIndicator { Period = 15 };
Assert.Equal(15, ind.MinHistoryDepths);
}
[Fact]
public void ShortName_ReflectsParameters()
{
- var ind = new DchannelIndicator { Period = 12 };
+ var ind = new PcIndicator { Period = 12 };
Assert.Contains("12", ind.ShortName, StringComparison.Ordinal);
}
[Fact]
public void Initialize_AddsThreeLineSeries()
{
- var ind = new DchannelIndicator { Period = 14 };
+ var ind = new PcIndicator { Period = 14 };
ind.Initialize();
Assert.Equal(3, ind.LinesSeries.Count);
@@ -46,7 +46,7 @@ public class DchannelIndicatorTests
[Fact]
public void ProcessUpdate_Historical_ComputesValues()
{
- var ind = new DchannelIndicator { Period = 3 };
+ var ind = new PcIndicator { Period = 3 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -63,7 +63,7 @@ public class DchannelIndicatorTests
[Fact]
public void ProcessUpdate_NewBar_Appends()
{
- var ind = new DchannelIndicator { Period = 3 };
+ var ind = new PcIndicator { Period = 3 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -79,7 +79,7 @@ public class DchannelIndicatorTests
[Fact]
public void ProcessUpdate_NewTick_DoesNotThrow()
{
- var ind = new DchannelIndicator { Period = 5 };
+ var ind = new PcIndicator { Period = 5 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -94,7 +94,7 @@ public class DchannelIndicatorTests
[Fact]
public void MultipleUpdates_ProducesFiniteSeries()
{
- var ind = new DchannelIndicator { Period = 5 };
+ var ind = new PcIndicator { Period = 5 };
ind.Initialize();
var now = DateTime.UtcNow;
@@ -119,7 +119,7 @@ public class DchannelIndicatorTests
[Fact]
public void Bands_Order_Correct()
{
- var ind = new DchannelIndicator { Period = 3 };
+ var ind = new PcIndicator { Period = 3 };
ind.Initialize();
var now = DateTime.UtcNow;
diff --git a/lib/channels/pchannel/tests/Pchannel.Tests.cs b/lib/channels/pc/tests/Pc.Tests.cs
similarity index 76%
rename from lib/channels/pchannel/tests/Pchannel.Tests.cs
rename to lib/channels/pc/tests/Pc.Tests.cs
index 3d794238..835c4356 100644
--- a/lib/channels/pchannel/tests/Pchannel.Tests.cs
+++ b/lib/channels/pc/tests/Pc.Tests.cs
@@ -4,23 +4,23 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class PchannelTests
+public class PcTests
{
[Fact]
- public void Pchannel_Constructor_ValidatesInput()
+ public void Pc_Constructor_ValidatesInput()
{
- Assert.Throws(() => new Pchannel(0));
- Assert.Throws(() => new Pchannel(-5));
+ Assert.Throws(() => new Pc(0));
+ Assert.Throws(() => new Pc(-5));
- var pc = new Pchannel(10);
+ var pc = new Pc(10);
Assert.Equal(10, pc.WarmupPeriod);
- Assert.Contains("Pchannel", pc.Name, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("Pc", pc.Name, StringComparison.OrdinalIgnoreCase);
}
[Fact]
- public void Pchannel_InitialState_Defaults()
+ public void Pc_InitialState_Defaults()
{
- var pc = new Pchannel(5);
+ var pc = new Pc(5);
Assert.Equal(0, pc.Last.Value);
Assert.Equal(0, pc.Upper.Value);
@@ -29,9 +29,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_CalculatesBands()
+ public void Pc_CalculatesBands()
{
- var pc = new Pchannel(3);
+ var pc = new Pc(3);
pc.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 105, 1000));
pc.Update(new TBar(DateTime.UtcNow, 105, 115, 95, 110, 1000));
@@ -45,9 +45,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_SlidingWindow_Updates()
+ public void Pc_SlidingWindow_Updates()
{
- var pc = new Pchannel(2);
+ var pc = new Pc(2);
pc.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 100, 1000));
pc.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 101, 1000));
@@ -65,9 +65,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_IsHot_TurnsTrueAfterWarmup()
+ public void Pc_IsHot_TurnsTrueAfterWarmup()
{
- var pc = new Pchannel(4);
+ var pc = new Pc(4);
for (int i = 0; i < 3; i++)
{
@@ -80,9 +80,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_IsNewFalse_RebuildsState()
+ public void Pc_IsNewFalse_RebuildsState()
{
- var pc = new Pchannel(3);
+ var pc = new Pc(3);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 7);
TBar remembered = default;
@@ -110,9 +110,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_NaN_UsesLastValid()
+ public void Pc_NaN_UsesLastValid()
{
- var pc = new Pchannel(3);
+ var pc = new Pc(3);
pc.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 105, 1000));
pc.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 106, 1000));
@@ -127,9 +127,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_Reset_Clears()
+ public void Pc_Reset_Clears()
{
- var pc = new Pchannel(3);
+ var pc = new Pc(3);
pc.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 100, 1000));
pc.Update(new TBar(DateTime.UtcNow, 101, 111, 91, 101, 1000));
@@ -145,9 +145,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_BatchVsStreaming_Match()
+ public void Pc_BatchVsStreaming_Match()
{
- var pcStream = new Pchannel(10);
+ var pcStream = new Pc(10);
var gbm = new GBM(startPrice: 100, mu: 0.02, sigma: 0.1, seed: 42);
var series = new TBarSeries();
@@ -162,7 +162,7 @@ public class PchannelTests
double expectedUp = pcStream.Upper.Value;
double expectedLo = pcStream.Lower.Value;
- var (midBatch, upBatch, loBatch) = Pchannel.Batch(series, 10);
+ var (midBatch, upBatch, loBatch) = Pc.Batch(series, 10);
Assert.Equal(expectedMid, midBatch.Last.Value, 1e-10);
Assert.Equal(expectedUp, upBatch.Last.Value, 1e-10);
@@ -170,7 +170,7 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_SpanBatch_Validates()
+ public void Pc_SpanBatch_Validates()
{
double[] high = [110, 115, 120];
double[] low = [90, 95, 100];
@@ -181,14 +181,14 @@ public class PchannelTests
double[] highShort = [110, 115];
double[] smallOut = new double[1];
- Assert.Throws(() => Pchannel.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 0));
- Assert.Throws(() => Pchannel.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), -1));
- Assert.Throws(() => Pchannel.Batch(highShort.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
- Assert.Throws(() => Pchannel.Batch(high.AsSpan(), low.AsSpan(), smallOut.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
+ Assert.Throws(() => Pc.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 0));
+ Assert.Throws(() => Pc.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), -1));
+ Assert.Throws(() => Pc.Batch(highShort.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
+ Assert.Throws(() => Pc.Batch(high.AsSpan(), low.AsSpan(), smallOut.AsSpan(), upper.AsSpan(), lower.AsSpan(), 2));
}
[Fact]
- public void Pchannel_SpanBatch_ComputesCorrectly()
+ public void Pc_SpanBatch_ComputesCorrectly()
{
double[] high = [110, 115, 120, 125];
double[] low = [90, 95, 100, 105];
@@ -196,7 +196,7 @@ public class PchannelTests
double[] upper = new double[4];
double[] lower = new double[4];
- Pchannel.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 3);
+ Pc.Batch(high.AsSpan(), low.AsSpan(), middle.AsSpan(), upper.AsSpan(), lower.AsSpan(), 3);
// Period=3: index 2 is first valid (indices 0,1,2)
// H=[110,115,120], L=[90,95,100] → Upper=120, Lower=90, Middle=105
@@ -211,14 +211,14 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_Calculate_ReturnsIndicatorAndResults()
+ public void Pc_Calculate_ReturnsIndicatorAndResults()
{
var series = new TBarSeries();
series.Add(DateTime.UtcNow, 100, 110, 90, 100, 1000);
series.Add(DateTime.UtcNow, 105, 115, 95, 105, 1000);
series.Add(DateTime.UtcNow, 110, 120, 100, 110, 1000);
- var ((mid, up, lo), ind) = Pchannel.Calculate(series, 2);
+ var ((mid, up, lo), ind) = Pc.Calculate(series, 2);
Assert.True(ind.IsHot);
// Period=2: last 2 bars H=[115,120], L=[95,100] → Upper=120, Lower=95, Middle=107.5
@@ -234,10 +234,10 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_Event_Publishes()
+ public void Pc_Event_Publishes()
{
var src = new TBarSeries();
- var pc = new Pchannel(src, 2);
+ var pc = new Pc(src, 2);
bool fired = false;
pc.Pub += (object? sender, in TValueEventArgs args) => fired = true;
@@ -246,9 +246,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_MiddleIsMidpoint()
+ public void Pc_MiddleIsMidpoint()
{
- var pc = new Pchannel(3);
+ var pc = new Pc(3);
pc.Update(new TBar(DateTime.UtcNow, 100, 110, 90, 105, 1000));
pc.Update(new TBar(DateTime.UtcNow, 105, 115, 95, 110, 1000));
pc.Update(new TBar(DateTime.UtcNow, 110, 120, 100, 115, 1000));
@@ -258,9 +258,9 @@ public class PchannelTests
}
[Fact]
- public void Pchannel_UpperGreaterOrEqualLower()
+ public void Pc_UpperGreaterOrEqualLower()
{
- var pc = new Pchannel(5);
+ var pc = new Pc(5);
var gbm = new GBM(startPrice: 100, mu: 0.02, sigma: 0.2, seed: 123);
for (int i = 0; i < 100; i++)
diff --git a/lib/channels/pchannel/tests/Pchannel.Validation.Tests.cs b/lib/channels/pc/tests/Pc.Validation.Tests.cs
similarity index 78%
rename from lib/channels/pchannel/tests/Pchannel.Validation.Tests.cs
rename to lib/channels/pc/tests/Pc.Validation.Tests.cs
index fe8a6b96..cfafa0d0 100644
--- a/lib/channels/pchannel/tests/Pchannel.Validation.Tests.cs
+++ b/lib/channels/pc/tests/Pc.Validation.Tests.cs
@@ -3,13 +3,13 @@ using Xunit.Abstractions;
namespace QuanTAlib.Tests;
-public sealed class PchannelValidationTests : IDisposable
+public sealed class PcValidationTests : IDisposable
{
private readonly ValidationTestData _testData;
private readonly ITestOutputHelper _output;
private bool _disposed;
- public PchannelValidationTests(ITestOutputHelper output)
+ public PcValidationTests(ITestOutputHelper output)
{
_output = output;
_testData = new ValidationTestData();
@@ -40,7 +40,7 @@ public sealed class PchannelValidationTests : IDisposable
series.Add(new TBar(t0.AddMinutes(1), 0, 14, 10, 12, 100));
series.Add(new TBar(t0.AddMinutes(2), 0, 16, 12, 14, 100));
- var ind = new Pchannel(3);
+ var ind = new Pc(3);
var (mid, up, lo) = ind.Update(series);
Assert.Equal(16.0, up.Last.Value, 1e-10);
@@ -48,7 +48,7 @@ public sealed class PchannelValidationTests : IDisposable
Assert.Equal(12.0, mid.Last.Value, 1e-10);
Assert.True(ind.IsHot);
- _output.WriteLine("Pchannel manual period-3 calculation validated");
+ _output.WriteLine("Pc manual period-3 calculation validated");
}
[Fact]
@@ -58,16 +58,16 @@ public sealed class PchannelValidationTests : IDisposable
foreach (int period in periods)
{
- var inst = new Pchannel(period);
+ var inst = new Pc(period);
var (bMid, bUp, bLo) = inst.Update(_testData.Bars);
- var (sMid, sUp, sLo) = Pchannel.Batch(_testData.Bars, period);
+ var (sMid, sUp, sLo) = Pc.Batch(_testData.Bars, period);
ValidationHelper.VerifySeriesEqual(bMid, sMid);
ValidationHelper.VerifySeriesEqual(bUp, sUp);
ValidationHelper.VerifySeriesEqual(bLo, sLo);
- var streaming = new Pchannel(period);
+ var streaming = new Pc(period);
var sMidStream = new TSeries();
var sUpStream = new TSeries();
var sLoStream = new TSeries();
@@ -88,7 +88,7 @@ public sealed class PchannelValidationTests : IDisposable
double[] spanMid = new double[high.Length];
double[] spanUp = new double[high.Length];
double[] spanLo = new double[high.Length];
- Pchannel.Batch(high.AsSpan(), low.AsSpan(),
+ Pc.Batch(high.AsSpan(), low.AsSpan(),
spanMid.AsSpan(), spanUp.AsSpan(), spanLo.AsSpan(), period);
for (int i = 0; i < high.Length; i++)
@@ -99,7 +99,7 @@ public sealed class PchannelValidationTests : IDisposable
}
}
- _output.WriteLine("Pchannel mode consistency validated (batch/stream/span)");
+ _output.WriteLine("Pc mode consistency validated (batch/stream/span)");
}
[Fact]
@@ -107,7 +107,7 @@ public sealed class PchannelValidationTests : IDisposable
{
const int period = 20;
var pub = new TBarSeries();
- var evtInd = new Pchannel(pub, period);
+ var evtInd = new Pc(pub, period);
var evtMid = new TSeries();
var evtUp = new TSeries();
var evtLo = new TSeries();
@@ -120,39 +120,39 @@ public sealed class PchannelValidationTests : IDisposable
evtLo.Add(evtInd.Lower);
}
- var (bMid, bUp, bLo) = Pchannel.Batch(_testData.Bars, period);
+ var (bMid, bUp, bLo) = Pc.Batch(_testData.Bars, period);
ValidationHelper.VerifySeriesEqual(bMid, evtMid);
ValidationHelper.VerifySeriesEqual(bUp, evtUp);
ValidationHelper.VerifySeriesEqual(bLo, evtLo);
- _output.WriteLine("Pchannel eventing mode validated");
+ _output.WriteLine("Pc eventing mode validated");
}
[Fact]
- public void Validate_AgainstDchannel_ExactMatch()
+ public void Validate_AgainstDc_ExactMatch()
{
- // Pchannel should produce identical results to Dchannel
+ // Pc should produce identical results to Dc
int[] periods = { 10, 20, 50 };
foreach (int period in periods)
{
- var (dcMid, dcUp, dcLo) = Dchannel.Batch(_testData.Bars, period);
- var (pcMid, pcUp, pcLo) = Pchannel.Batch(_testData.Bars, period);
+ var (dcMid, dcUp, dcLo) = Dc.Batch(_testData.Bars, period);
+ var (pcMid, pcUp, pcLo) = Pc.Batch(_testData.Bars, period);
ValidationHelper.VerifySeriesEqual(dcMid, pcMid);
ValidationHelper.VerifySeriesEqual(dcUp, pcUp);
ValidationHelper.VerifySeriesEqual(dcLo, pcLo);
}
- _output.WriteLine("Pchannel matches Dchannel exactly");
+ _output.WriteLine("Pc matches Dc exactly");
}
[Fact]
public void Validate_Calculate_ReturnsHotIndicator()
{
const int period = 15;
- var ((mid, up, lo), ind) = Pchannel.Calculate(_testData.Bars, period);
+ var ((mid, up, lo), ind) = Pc.Calculate(_testData.Bars, period);
Assert.True(ind.IsHot);
Assert.Equal(period, ind.WarmupPeriod);
@@ -164,7 +164,7 @@ public sealed class PchannelValidationTests : IDisposable
ind.Update(next);
Assert.True(ind.IsHot);
- _output.WriteLine("Pchannel Calculate validated");
+ _output.WriteLine("Pc Calculate validated");
}
[Fact]
@@ -172,9 +172,9 @@ public sealed class PchannelValidationTests : IDisposable
{
const int period = 25;
- var (bMid, bUp, bLo) = Pchannel.Batch(_testData.Bars, period);
+ var (bMid, bUp, bLo) = Pc.Batch(_testData.Bars, period);
- var primed = new Pchannel(period);
+ var primed = new Pc(period);
var subset = new TBarSeries();
for (int i = 0; i < 200; i++)
{
@@ -192,13 +192,13 @@ public sealed class PchannelValidationTests : IDisposable
Assert.Equal(bUp.Last.Value, primed.Upper.Value, 1e-9);
Assert.Equal(bLo.Last.Value, primed.Lower.Value, 1e-9);
- _output.WriteLine("Pchannel Prime validated against batch");
+ _output.WriteLine("Pc Prime validated against batch");
}
[Fact]
public void Validate_LargeDataset_FiniteOutputs()
{
- var (mid, up, lo) = Pchannel.Batch(_testData.Bars, 50);
+ var (mid, up, lo) = Pc.Batch(_testData.Bars, 50);
ValidationHelper.VerifyAllFinite(mid, startIndex: 0);
ValidationHelper.VerifyAllFinite(up, startIndex: 0);
@@ -209,13 +209,13 @@ public sealed class PchannelValidationTests : IDisposable
Assert.True(up[i].Value >= lo[i].Value, $"Upper >= Lower at {i}");
}
- _output.WriteLine("Pchannel large dataset validated");
+ _output.WriteLine("Pc large dataset validated");
}
[Fact]
public void Validate_StateRestoration_Iterative()
{
- var ind = new Pchannel(15);
+ var ind = new Pc(15);
var gbm = new GBM(startPrice: 100, mu: 0.01, sigma: 0.1, seed: 42);
for (int i = 0; i < 50; i++)
@@ -242,6 +242,6 @@ public sealed class PchannelValidationTests : IDisposable
Assert.Equal(savedUp, ind.Upper.Value, 1e-10);
Assert.Equal(savedLo, ind.Lower.Value, 1e-10);
- _output.WriteLine("Pchannel state restoration validated");
+ _output.WriteLine("Pc state restoration validated");
}
}
diff --git a/lib/channels/starchannel/starchannel.md b/lib/channels/starchannel/starchannel.md
index 006246f0..b465c5ce 100644
--- a/lib/channels/starchannel/starchannel.md
+++ b/lib/channels/starchannel/starchannel.md
@@ -13,7 +13,7 @@
| **PineScript** | [starchannel.pine](starchannel.pine) |
- Stoller Average Range Channel creates a volatility-adaptive price envelope using Average True Range (ATR) to determine band width around a simple m...
-- **Similar:** [KChannel](../kchannel/kchannel.md), [ATRBands](../atrbands/atrbands.md) | **Complementary:** ADX for trend confirmation | **Trading note:** Stoller Average Range Channel; ATR-based bands around a moving average.
+- **Similar:** [KC](../kc/kc.md), [ATRBands](../atrbands/atrbands.md) | **Complementary:** ADX for trend confirmation | **Trading note:** Stoller Average Range Channel; ATR-based bands around a moving average.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Stoller Average Range Channel creates a volatility-adaptive price envelope using Average True Range (ATR) to determine band width around a simple moving average centerline. The bands automatically expand during volatile periods and contract during calmer markets. The implementation uses a circular buffer for the SMA running sum and Wilder's RMA with a warmup compensator for ATR, achieving O(1) streaming updates per bar.
diff --git a/lib/channels/starchannel/tests/Starchannel.Validation.Tests.cs b/lib/channels/starchannel/tests/Starchannel.Validation.Tests.cs
index c523554e..7861b036 100644
--- a/lib/channels/starchannel/tests/Starchannel.Validation.Tests.cs
+++ b/lib/channels/starchannel/tests/Starchannel.Validation.Tests.cs
@@ -501,16 +501,16 @@ public sealed class StarchannelValidationTests : IDisposable
}
[Fact]
- public void Validate_KchannelComparison_Structure()
+ public void Validate_KcComparison_Structure()
{
- // Compare structural properties with Kchannel (EMA vs SMA middle)
+ // Compare structural properties with Kc (EMA vs SMA middle)
// Both use ATR for bands, so band calculation should be similar
const int period = 20;
const double multiplier = 2.0;
var star = new Starchannel(period, multiplier);
- var kelt = new Kchannel(period, multiplier);
+ var kelt = new Kc(period, multiplier);
foreach (var bar in _testData.Bars)
{
@@ -543,18 +543,18 @@ public sealed class StarchannelValidationTests : IDisposable
double keltLowerDist = kelt.Last.Value - kelt.Lower.Value;
Assert.Equal(keltUpperDist, keltLowerDist, 1e-10);
- _output.WriteLine("Starchannel vs Kchannel structure validated");
+ _output.WriteLine("Starchannel vs Kc structure validated");
}
[Fact]
- public void Validate_Starchannel_DifferentFromKchannel()
+ public void Validate_Starchannel_DifferentFromKc()
{
- // Starchannel (SMA) should differ from Kchannel (EMA) in the middle line
+ // Starchannel (SMA) should differ from Kc (EMA) in the middle line
const int period = 20;
const double multiplier = 2.0;
var star = new Starchannel(period, multiplier);
- var kelt = new Kchannel(period, multiplier);
+ var kelt = new Kc(period, multiplier);
foreach (var bar in _testData.Bars)
{
@@ -569,7 +569,7 @@ public sealed class StarchannelValidationTests : IDisposable
// Just verify they're both finite and reasonable
Assert.True(double.IsFinite(diff));
- _output.WriteLine($"Starchannel vs Kchannel middle difference: {diff:F6}");
+ _output.WriteLine($"Starchannel vs Kc middle difference: {diff:F6}");
}
// ═══════════════════════════════════════════════════════════════
diff --git a/lib/channels/stbands/stbands.md b/lib/channels/stbands/stbands.md
index 8fd7007b..8f0ac7d2 100644
--- a/lib/channels/stbands/stbands.md
+++ b/lib/channels/stbands/stbands.md
@@ -13,7 +13,7 @@
| **PineScript** | [stbands.pine](stbands.pine) |
- Super Trend Bands provide ATR-based dynamic support and resistance levels with asymmetric ratchet logic: the upper band only tightens downward duri...
-- **Similar:** [BBands](../bbands/bbands.md), [KChannel](../kchannel/kchannel.md) | **Complementary:** Volume for breakout validation | **Trading note:** Stoller bands use ATR multiplier instead of standard deviation.
+- **Similar:** [BBands](../bbands/bbands.md), [KC](../kc/kc.md) | **Complementary:** Volume for breakout validation | **Trading note:** Stoller bands use ATR multiplier instead of standard deviation.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Super Trend Bands provide ATR-based dynamic support and resistance levels with asymmetric ratchet logic: the upper band only tightens downward during downtrends, and the lower band only tightens upward during uptrends. This creates natural trailing stop-loss levels that respect market momentum. A trend direction signal ($+1$ or $-1$) flips when price breaches the opposite band. The ATR is computed as a simple moving average of True Range via a ring buffer with running sum, providing O(1) streaming updates.
diff --git a/lib/channels/uchannel/uchannel.md b/lib/channels/uchannel/uchannel.md
index bf5fd245..1ec13e31 100644
--- a/lib/channels/uchannel/uchannel.md
+++ b/lib/channels/uchannel/uchannel.md
@@ -13,7 +13,7 @@
| **PineScript** | [uchannel.pine](uchannel.pine) |
- Ehlers Ultimate Channel applies the Ultrasmooth Filter (USF) twice: once to the close price for the centerline and once to True Range for band widt...
-- **Similar:** [DChannel](../dchannel/dchannel.md), [PChannel](../pchannel/pchannel.md) | **Complementary:** Volume for breakout confirmation | **Trading note:** Upper/lower channel based on recent price extremes.
+- **Similar:** [DC](../dc/dc.md), [PC](../pc/pc.md) | **Complementary:** Volume for breakout confirmation | **Trading note:** Upper/lower channel based on recent price extremes.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Ehlers Ultimate Channel applies the Ultrasmooth Filter (USF) twice: once to the close price for the centerline and once to True Range for band width, creating a channel where both the trend estimate and the volatility measure share the same low-lag, zero-overshoot filter characteristics. Unlike UBANDS which uses RMS of price residuals, UCHANNEL uses Smoothed True Range (STR) for band width, making it responsive to gap-inclusive volatility. Separate period parameters allow independent tuning of centerline smoothness and band-width responsiveness.
diff --git a/lib/dynamics/ghla/Ghla.md b/lib/dynamics/ghla/Ghla.md
index 50e842ab..10e4c618 100644
--- a/lib/dynamics/ghla/Ghla.md
+++ b/lib/dynamics/ghla/Ghla.md
@@ -13,7 +13,7 @@
| **PineScript** | [ghla.pine](ghla.pine) |
- The Gann High-Low Activator (GHLA) is a trend-following stop/reversal indicator that alternates between the Simple Moving Average of Highs and the ...
-- **Similar:** [Super](../super/Super.md), [PSAR](../../reversals/psar/Psar.md) | **Complementary:** ATR for stop placement | **Trading note:** Gann High-Low Activator; trend-following stop based on prior period's median.
+- **Similar:** [Super](../super/Super.md), [SAR](../../reversals/sar/Sar.md) | **Complementary:** ATR for stop placement | **Trading note:** Gann High-Low Activator; trend-following stop based on prior period's median.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Gann High-Low Activator (GHLA) is a trend-following stop/reversal indicator that alternates between the Simple Moving Average of Highs and the Simple Moving Average of Lows based on a three-state crossover rule. Developed by Robert Krausz and published in *Technical Analysis of Stocks & Commodities* (February 1998), the indicator produces a single trailing line: SMA(Low) during uptrends (acting as dynamic support) and SMA(High) during downtrends (acting as dynamic resistance). The flip between states occurs only when price closes decisively beyond the opposing SMA, creating a hysteresis zone that filters minor whipsaws. With a default period of 3 bars, GHLA responds aggressively to trend changes while requiring just $O(N)$ additions and one comparison per bar.
diff --git a/lib/dynamics/super/Super.md b/lib/dynamics/super/Super.md
index 2bc06c91..1c2e225d 100644
--- a/lib/dynamics/super/Super.md
+++ b/lib/dynamics/super/Super.md
@@ -13,7 +13,7 @@
| **PineScript** | [super.pine](super.pine) |
- SuperTrend is a trend-following overlay that uses ATR-scaled bands around the HL2 midpoint, switching between upper and lower bands based on close ...
-- **Similar:** [PSAR](../../reversals/psar/Psar.md), [Ghla](../ghla/Ghla.md) | **Complementary:** ADX for trend strength | **Trading note:** SuperTrend; ATR-based trailing stop that flips direction. Popular in crypto and forex.
+- **Similar:** [SAR](../../reversals/sar/Sar.md), [Ghla](../ghla/Ghla.md) | **Complementary:** ADX for trend strength | **Trading note:** SuperTrend; ATR-based trailing stop that flips direction. Popular in crypto and forex.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
SuperTrend is a trend-following overlay that uses ATR-scaled bands around the HL2 midpoint, switching between upper and lower bands based on close price breakouts. A ratchet mechanism prevents the active band from moving against the trend, creating a step-like trailing stop that adapts to volatility. The indicator is a two-state machine (bullish/bearish) with O(1) per-bar updates and zero allocations in the hot path.
diff --git a/lib/errors/rsquared/Rsquared.md b/lib/errors/rsquared/Rsquared.md
index bc12ba56..c7626e1d 100644
--- a/lib/errors/rsquared/Rsquared.md
+++ b/lib/errors/rsquared/Rsquared.md
@@ -13,7 +13,7 @@
| **PineScript** | [rsquared.pine](rsquared.pine) |
- The Coefficient of Determination (R²) measures the proportion of variance in the actual values that is predictable from the predicted values.
-- **Similar:** [RSE](../rse/Rse.md), [Correlation](../../statistics/correlation/Correlation.md) | **Trading note:** R-squared (coefficient of determination); 1.0 = perfect fit, 0 = no better than mean.
+- **Similar:** [RSE](../rse/Rse.md), [Correl](../../statistics/correl/Correl.md) | **Trading note:** R-squared (coefficient of determination); 1.0 = perfect fit, 0 = no better than mean.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Coefficient of Determination (R²) measures the proportion of variance in the actual values that is predictable from the predicted values. R² ranges from negative infinity to 1, where 1 indicates perfect predictions.
diff --git a/lib/momentum/_index.md b/lib/momentum/_index.md
index 5dfa8ad9..bdeb25b9 100644
--- a/lib/momentum/_index.md
+++ b/lib/momentum/_index.md
@@ -16,7 +16,7 @@ Momentum indicators measure the velocity and acceleration of price changes. Unli
| [MOM](mom/Mom.md) | Momentum | Raw price change over specified period. |
| [PMO](pmo/Pmo.md) | Price Momentum Oscillator | Double-smoothed ROC oscillator. |
| [PPO](ppo/Ppo.md) | Percentage Price Oscillator | MACD expressed as percentage for cross-instrument comparison. |
-| [PRS](prs/Prs.md) | Price Relative Strength | Performance ratio between two assets. |
+| [RS](rs/Rs.md) | Price Relative Strength | Performance ratio between two assets. |
| [ROC](roc/Roc.md) | Rate of Change | Absolute price change over N periods. |
| [ROCP](rocp/Rocp.md) | Rate of Change Percentage | Percentage price change over N periods. |
| [ROCR](rocr/Rocr.md) | Rate of Change Ratio | Price ratio over N periods. |
diff --git a/lib/momentum/prs/Prs.Quantower.cs b/lib/momentum/rs/Rs.Quantower.cs
similarity index 78%
rename from lib/momentum/prs/Prs.Quantower.cs
rename to lib/momentum/rs/Rs.Quantower.cs
index 72b1950f..6955343f 100644
--- a/lib/momentum/prs/Prs.Quantower.cs
+++ b/lib/momentum/rs/Rs.Quantower.cs
@@ -5,20 +5,20 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
///
-/// Quantower adapter for PRS (Price Relative Strength) indicator.
+/// Quantower adapter for RS (Price Relative Strength) indicator.
/// Compares two price sources from the same symbol to identify relative performance.
///
///
/// This adapter compares two different price sources from the same symbol (e.g., Close vs Open,
/// Close vs Volume, High vs Low). For cross-symbol relative strength analysis, use the core
-/// PRS class directly with data from multiple symbols.
+/// RS class directly with data from multiple symbols.
///
/// The output is the ratio of Base/Comparison with optional EMA smoothing.
/// Values above 1.0 indicate the base source is higher than comparison.
/// Rising values indicate base is outperforming, falling values indicate underperformance.
///
[SkipLocalsInit]
-public sealed class PrsIndicator : Indicator, IWatchlistIndicator
+public sealed class RsIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Smooth Period", sortIndex: 0, minimum: 1, maximum: 10000)]
public int SmoothPeriod { get; set; } = 1;
@@ -32,7 +32,7 @@ public sealed class PrsIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
- private Prs _prs = null!;
+ private Rs _rs = null!;
private readonly LineSeries _series;
private string _sourceName = null!;
private Func _priceSelector = null!;
@@ -42,16 +42,16 @@ public sealed class PrsIndicator : Indicator, IWatchlistIndicator
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName => SmoothPeriod == 1
- ? $"PRS:{_sourceName}/{Source2}"
- : $"PRS({SmoothPeriod}):{_sourceName}/{Source2}";
+ ? $"RS:{_sourceName}/{Source2}"
+ : $"RS({SmoothPeriod}):{_sourceName}/{Source2}";
- public PrsIndicator()
+ public RsIndicator()
{
OnBackGround = true;
SeparateWindow = true;
- Name = "PRS - Price Relative Strength";
+ Name = "RS - Price Relative Strength";
Description = "Compares relative performance between two price sources. Ratio > 1 means base is higher than comparison.";
- _series = new LineSeries(name: "PRS", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
+ _series = new LineSeries(name: "RS", color: IndicatorExtensions.Momentum, width: 2, style: LineStyle.Solid);
AddLineSeries(_series);
}
@@ -60,7 +60,7 @@ public sealed class PrsIndicator : Indicator, IWatchlistIndicator
_priceSelector = Source.GetPriceSelector();
_priceSelector2 = Source2.GetPriceSelector();
_sourceName = Source.ToString();
- _prs = new Prs(SmoothPeriod);
+ _rs = new Rs(SmoothPeriod);
base.OnInit();
}
@@ -77,7 +77,7 @@ public sealed class PrsIndicator : Indicator, IWatchlistIndicator
var tvalBase = new TValue(item.TimeLeft.Ticks, baseValue);
var tvalComp = new TValue(item.TimeLeft.Ticks, compValue);
- double value = _prs.Update(tvalBase, tvalComp, isNew).Value;
- _series.SetValue(value, _prs.IsHot, ShowColdValues);
+ double value = _rs.Update(tvalBase, tvalComp, isNew).Value;
+ _series.SetValue(value, _rs.IsHot, ShowColdValues);
}
}
diff --git a/lib/momentum/prs/Prs.cs b/lib/momentum/rs/Rs.cs
similarity index 86%
rename from lib/momentum/prs/Prs.cs
rename to lib/momentum/rs/Rs.cs
index fe3d60bc..4b3b4a97 100644
--- a/lib/momentum/prs/Prs.cs
+++ b/lib/momentum/rs/Rs.cs
@@ -1,4 +1,4 @@
-// PRS: Price Relative Strength
+// RS: Price Relative Strength
// Compares the performance of one asset to another by calculating the ratio
// and optionally applying EMA smoothing for trend identification.
@@ -8,15 +8,15 @@ using static System.Math;
namespace QuanTAlib;
///
-/// PRS: Price Relative Strength
+/// RS: Price Relative Strength
///
///
/// Measures relative performance between two assets by calculating their price ratio.
-/// A rising PRS indicates the base asset is outperforming the comparison asset.
-/// A falling PRS indicates underperformance. Optional EMA smoothing reduces noise.
+/// A rising RS indicates the base asset is outperforming the comparison asset.
+/// A falling RS indicates underperformance. Optional EMA smoothing reduces noise.
///
/// Key characteristics:
-/// - Ratio-based: PRS = Base / Comparison
+/// - Ratio-based: RS = Base / Comparison
/// - Trend indicator: Rising = outperformance, Falling = underperformance
/// - Smoothing: Optional EMA with bias compensation for warmup
/// - Division by zero: Returns NaN when comparison is zero
@@ -28,14 +28,14 @@ namespace QuanTAlib;
///
///
/// Interpretation:
-/// - PRS > 1.0: Base asset is worth more per unit
-/// - PRS increasing: Base outperforming comparison
-/// - PRS decreasing: Base underperforming comparison
+/// - RS > 1.0: Base asset is worth more per unit
+/// - RS increasing: Base outperforming comparison
+/// - RS decreasing: Base underperforming comparison
/// - Use with baseline (1.0 or initial ratio) for normalized view
///
-/// Detailed documentation
+/// Detailed documentation
[SkipLocalsInit]
-public sealed class Prs : AbstractBase
+public sealed class Rs : AbstractBase
{
private const double Epsilon = 1e-10;
private readonly int _smoothPeriod;
@@ -75,7 +75,7 @@ public sealed class Prs : AbstractBase
/// Creates a new Price Relative Strength indicator.
///
/// Smoothing period for EMA (1 = no smoothing)
- public Prs(int smoothPeriod = 1)
+ public Rs(int smoothPeriod = 1)
{
if (smoothPeriod < 1)
{
@@ -87,12 +87,12 @@ public sealed class Prs : AbstractBase
_isWarmup = true;
_e = 1.0;
- Name = smoothPeriod == 1 ? "Prs" : $"Prs({smoothPeriod})";
+ Name = smoothPeriod == 1 ? "Rs" : $"Rs({smoothPeriod})";
WarmupPeriod = smoothPeriod;
}
///
- /// Updates the PRS indicator with new values from both series.
+ /// Updates the RS indicator with new values from both series.
///
/// Base asset price
/// Comparison asset price
@@ -152,16 +152,16 @@ public sealed class Prs : AbstractBase
return Update(new TValue(now, baseValue), new TValue(now, compValue), isNew);
}
/// Not supported for bi-input indicator. Use Update(baseValue, compValue) instead.
- /// PRS requires paired base/comparison inputs; single-input updates are invalid.
+ /// RS requires paired base/comparison inputs; single-input updates are invalid.
public override TValue Update(TValue input, bool isNew = true)
{
- throw new NotSupportedException("PRS requires two inputs (base and comparison). Use Update(baseValue, compValue).");
+ throw new NotSupportedException("RS requires two inputs (base and comparison). Use Update(baseValue, compValue).");
}
/// Not supported for bi-input indicator. Use Calculate(baseSeries, compSeries, period) instead.
- /// PRS requires paired base/comparison series; single-series updates are invalid.
+ /// RS requires paired base/comparison series; single-series updates are invalid.
public override TSeries Update(TSeries source)
{
- throw new NotSupportedException("PRS requires two inputs. Use Batch(baseSeries, compSeries, period).");
+ throw new NotSupportedException("RS requires two inputs. Use Batch(baseSeries, compSeries, period).");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -246,7 +246,7 @@ public sealed class Prs : AbstractBase
}
public override void Prime(ReadOnlySpan source, TimeSpan? step = null)
{
- throw new NotSupportedException("PRS requires two inputs. Use Prime(baseSource, compSource).");
+ throw new NotSupportedException("RS requires two inputs. Use Prime(baseSource, compSource).");
}
///
@@ -290,7 +290,7 @@ public sealed class Prs : AbstractBase
}
///
- /// Calculates PRS for two time series.
+ /// Calculates RS for two time series.
///
public static TSeries Batch(TSeries baseSeries, TSeries compSeries, int smoothPeriod = 1)
{
@@ -299,7 +299,7 @@ public sealed class Prs : AbstractBase
throw new ArgumentException("Series must have the same length", nameof(compSeries));
}
- var indicator = new Prs(smoothPeriod);
+ var indicator = new Rs(smoothPeriod);
var result = new TSeries(baseSeries.Count);
var times = baseSeries.Times;
@@ -340,7 +340,7 @@ public sealed class Prs : AbstractBase
throw new ArgumentException("Smoothing period must be >= 1", nameof(smoothPeriod));
}
- var indicator = new Prs(smoothPeriod);
+ var indicator = new Rs(smoothPeriod);
for (int i = 0; i < baseSeries.Length; i++)
{
@@ -349,9 +349,9 @@ public sealed class Prs : AbstractBase
}
}
- public static (TSeries Results, Prs Indicator) Calculate(TSeries baseSeries, TSeries compSeries, int smoothPeriod = 1)
+ public static (TSeries Results, Rs Indicator) Calculate(TSeries baseSeries, TSeries compSeries, int smoothPeriod = 1)
{
- var indicator = new Prs(smoothPeriod);
+ var indicator = new Rs(smoothPeriod);
TSeries results = Batch(baseSeries, compSeries, smoothPeriod);
return (results, indicator);
}
diff --git a/lib/momentum/prs/Prs.md b/lib/momentum/rs/rs.md
similarity index 77%
rename from lib/momentum/prs/Prs.md
rename to lib/momentum/rs/rs.md
index 912dbf6e..19887e7b 100644
--- a/lib/momentum/prs/Prs.md
+++ b/lib/momentum/rs/rs.md
@@ -1,4 +1,4 @@
-# PRS: Price Relative Strength
+# RS: Price Relative Strength
> *Price relative strength ratios two instruments, revealing which one leads and which one lags in the race.*
@@ -7,10 +7,10 @@
| **Category** | Momentum |
| **Inputs** | Source (close) |
| **Parameters** | `smoothPeriod` (default 1) |
-| **Outputs** | Single series (PRS) |
+| **Outputs** | Single series (RS) |
| **Output range** | Varies (see docs) |
| **Warmup** | `smoothPeriod` bars |
-| **PineScript** | [prs.pine](prs.pine) |
+| **PineScript** | [rs.pine](rs.pine) |
- **Category:** Momentum **Also known as:** Relative Strength Comparison, Price Ratio, Performance Ratio
- **Similar:** [ROC](../roc/Roc.md), [Bias](../bias/Bias.md) | **Complementary:** Relative strength vs benchmark | **Trading note:** Price Relative Strength; ratio or spread between two series. Sector rotation tool.
@@ -19,13 +19,13 @@
**Category:** Momentum
**Also known as:** Relative Strength Comparison, Price Ratio, Performance Ratio
-[Pine Script Implementation of PRS](https://github.com/mihakralj/pinescript/blob/main/indicators/momentum/prs.pine)
+[Pine Script Implementation of RS](https://github.com/mihakralj/pinescript/blob/main/indicators/momentum/rs.pine)
## Overview
-Price Relative Strength (PRS) measures the performance of one asset relative to another by calculating the ratio between their prices. This indicator helps identify which asset is outperforming and is fundamental for sector rotation, pairs trading, and relative performance analysis.
+Price Relative Strength (RS) measures the performance of one asset relative to another by calculating the ratio between their prices. This indicator helps identify which asset is outperforming and is fundamental for sector rotation, pairs trading, and relative performance analysis.
-Unlike the Relative Strength Index (RSI) which measures momentum within a single asset, PRS compares two different price series. A rising PRS indicates the base asset is outperforming the comparison asset; a falling PRS indicates underperformance.
+Unlike the Relative Strength Index (RSI) which measures momentum within a single asset, RS compares two different price series. A rising RS indicates the base asset is outperforming the comparison asset; a falling RS indicates underperformance.
## Core Concepts
@@ -46,7 +46,7 @@ Unlike the Relative Strength Index (RSI) which measures momentum within a single
## Calculation
**Simplified Explanation:**
-PRS divides the base asset's price by the comparison asset's price, then optionally smooths the result with an EMA.
+RS divides the base asset's price by the comparison asset's price, then optionally smooths the result with an EMA.
**Technical Formula:**
@@ -68,9 +68,9 @@ Smoothed = compensation × EMA_biased
## Interpretation Details
**Trend Analysis:**
-- **PRS rising:** Base asset outperforming comparison (bullish for base)
-- **PRS falling:** Base asset underperforming comparison (bearish for base)
-- **PRS flat:** Both assets moving in tandem
+- **RS rising:** Base asset outperforming comparison (bullish for base)
+- **RS falling:** Base asset underperforming comparison (bearish for base)
+- **RS flat:** Both assets moving in tandem
**Common Comparisons:**
- Stock vs. sector ETF (stock relative to its sector)
@@ -80,15 +80,15 @@ Smoothed = compensation × EMA_biased
- Commodity vs. currency (inflation dynamics)
**Trading Signals:**
-- **PRS crossover above prior high:** Breakout in relative strength
-- **PRS crossover below prior low:** Breakdown in relative strength
-- **Divergence:** Price makes new high, PRS does not = warning
+- **RS crossover above prior high:** Breakout in relative strength
+- **RS crossover below prior low:** Breakdown in relative strength
+- **Divergence:** Price makes new high, RS does not = warning
## Performance Profile
### Operation Count (Streaming Mode)
-PRS with smoothing is three scalar operations: one division for the raw ratio, one FMA for the EMA update, and one divide for the bias compensation factor. Without smoothing (smoothPeriod = 1), the bias step is skipped entirely.
+RS with smoothing is three scalar operations: one division for the raw ratio, one FMA for the EMA update, and one divide for the bias compensation factor. Without smoothing (smoothPeriod = 1), the bias step is skipped entirely.
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
diff --git a/lib/momentum/prs/prs.pine b/lib/momentum/rs/rs.pine
similarity index 89%
rename from lib/momentum/prs/prs.pine
rename to lib/momentum/rs/rs.pine
index 1c98028a..203c8065 100644
--- a/lib/momentum/prs/prs.pine
+++ b/lib/momentum/rs/rs.pine
@@ -1,14 +1,14 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Price Relative Strength (PRS)", "PRS", overlay=false)
+indicator("Price Relative Strength (RS)", "RS", overlay=false)
//@function Calculates Price Relative Strength comparing two assets
//@param base Base asset price series
//@param comp Compare asset price series
//@param smooth_len Smoothing period for ratio
//@returns Tuple containing raw ratio and smoothed ratio
-prs(series float base, series float comp, simple int smooth_len=1)=>
+rs(series float base, series float comp, simple int smooth_len=1)=>
if smooth_len<=0
runtime.error("Smoothing length must be greater than 0")
float ratio = na
@@ -44,8 +44,8 @@ i_log = input.bool(false, "Logarithmic Scale")
// Get comparison data
float comp_close = request.security(i_comp, timeframe.period, close, barmerge.gaps_off, barmerge.lookahead_off)
-// Calculate PRS
-[raw_ratio, smooth_ratio] = prs(i_base, comp_close, i_smooth)
+// Calculate RS
+[raw_ratio, smooth_ratio] = rs(i_base, comp_close, i_smooth)
// Apply optional normalization
if i_norm
diff --git a/lib/momentum/prs/tests/Prs.Quantower.Tests.cs b/lib/momentum/rs/tests/Rs.Quantower.Tests.cs
similarity index 75%
rename from lib/momentum/prs/tests/Prs.Quantower.Tests.cs
rename to lib/momentum/rs/tests/Rs.Quantower.Tests.cs
index 25d4375f..dbb99fa9 100644
--- a/lib/momentum/prs/tests/Prs.Quantower.Tests.cs
+++ b/lib/momentum/rs/tests/Rs.Quantower.Tests.cs
@@ -2,54 +2,54 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib.Tests;
-public class PrsIndicatorTests
+public class RsIndicatorTests
{
[Fact]
- public void PrsIndicator_Constructor_SetsDefaults()
+ public void RsIndicator_Constructor_SetsDefaults()
{
- var indicator = new PrsIndicator();
+ var indicator = new RsIndicator();
Assert.Equal(1, indicator.SmoothPeriod);
Assert.Equal(SourceType.Close, indicator.Source);
Assert.Equal(SourceType.Open, indicator.Source2);
Assert.True(indicator.ShowColdValues);
- Assert.Equal("PRS - Price Relative Strength", indicator.Name);
+ Assert.Equal("RS - Price Relative Strength", indicator.Name);
Assert.True(indicator.SeparateWindow);
Assert.True(indicator.OnBackGround);
}
[Fact]
- public void PrsIndicator_MinHistoryDepths_EqualsOne()
+ public void RsIndicator_MinHistoryDepths_EqualsOne()
{
- var indicator = new PrsIndicator();
+ var indicator = new RsIndicator();
- Assert.Equal(1, PrsIndicator.MinHistoryDepths);
+ Assert.Equal(1, RsIndicator.MinHistoryDepths);
Assert.Equal(1, ((IWatchlistIndicator)indicator).MinHistoryDepths);
}
[Fact]
- public void PrsIndicator_ShortName_NoSmoothing_ContainsSourceTypes()
+ public void RsIndicator_ShortName_NoSmoothing_ContainsSourceTypes()
{
- var indicator = new PrsIndicator { SmoothPeriod = 1 };
+ var indicator = new RsIndicator { SmoothPeriod = 1 };
- Assert.Contains("PRS", indicator.ShortName, StringComparison.Ordinal);
+ Assert.Contains("RS", indicator.ShortName, StringComparison.Ordinal);
Assert.DoesNotContain("(", indicator.ShortName, StringComparison.Ordinal);
}
[Fact]
- public void PrsIndicator_ShortName_WithSmoothing_IncludesPeriod()
+ public void RsIndicator_ShortName_WithSmoothing_IncludesPeriod()
{
- var indicator = new PrsIndicator { SmoothPeriod = 14 };
+ var indicator = new RsIndicator { SmoothPeriod = 14 };
indicator.Initialize();
- Assert.Contains("PRS", indicator.ShortName, StringComparison.Ordinal);
+ Assert.Contains("RS", indicator.ShortName, StringComparison.Ordinal);
Assert.Contains("14", indicator.ShortName, StringComparison.Ordinal);
}
[Fact]
- public void PrsIndicator_Initialize_CreatesPrsInstance()
+ public void RsIndicator_Initialize_CreatesRsInstance()
{
- var indicator = new PrsIndicator { SmoothPeriod = 10 };
+ var indicator = new RsIndicator { SmoothPeriod = 10 };
// Initialize should not throw
indicator.Initialize();
@@ -59,9 +59,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
+ public void RsIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
{
- var indicator = new PrsIndicator { SmoothPeriod = 1 };
+ var indicator = new RsIndicator { SmoothPeriod = 1 };
indicator.Initialize();
// Add historical data
@@ -77,9 +77,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_ProcessUpdate_NewBar_ComputesValue()
+ public void RsIndicator_ProcessUpdate_NewBar_ComputesValue()
{
- var indicator = new PrsIndicator { SmoothPeriod = 5 };
+ var indicator = new RsIndicator { SmoothPeriod = 5 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -93,9 +93,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_ProcessUpdate_NewTick_ProcessesWithoutError()
+ public void RsIndicator_ProcessUpdate_NewTick_ProcessesWithoutError()
{
- var indicator = new PrsIndicator { SmoothPeriod = 1 };
+ var indicator = new RsIndicator { SmoothPeriod = 1 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -114,9 +114,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_MultipleUpdates_ProducesSequence()
+ public void RsIndicator_MultipleUpdates_ProducesSequence()
{
- var indicator = new PrsIndicator { SmoothPeriod = 3 };
+ var indicator = new RsIndicator { SmoothPeriod = 3 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -133,10 +133,10 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_CloseVsOpen_ReturnsPositiveRatio()
+ public void RsIndicator_CloseVsOpen_ReturnsPositiveRatio()
{
// Test with Close vs Open - Close should be higher than Open in uptrend
- var indicator = new PrsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = SourceType.Open };
+ var indicator = new RsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = SourceType.Open };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -154,10 +154,10 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_OpenVsClose_ReturnsInverseRatio()
+ public void RsIndicator_OpenVsClose_ReturnsInverseRatio()
{
// Reverse the sources - Open vs Close
- var indicator = new PrsIndicator { SmoothPeriod = 1, Source = SourceType.Open, Source2 = SourceType.Close };
+ var indicator = new RsIndicator { SmoothPeriod = 1, Source = SourceType.Open, Source2 = SourceType.Close };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -174,13 +174,13 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_DifferentSourceTypes_Work()
+ public void RsIndicator_DifferentSourceTypes_Work()
{
var sources = new[] { SourceType.Open, SourceType.High, SourceType.Low, SourceType.Close, SourceType.HL2, SourceType.HLC3 };
foreach (var source in sources)
{
- var indicator = new PrsIndicator { SmoothPeriod = 1, Source = source, Source2 = SourceType.Close };
+ var indicator = new RsIndicator { SmoothPeriod = 1, Source = source, Source2 = SourceType.Close };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -193,13 +193,13 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_DifferentSource2Types_Work()
+ public void RsIndicator_DifferentSource2Types_Work()
{
var source2Types = new[] { SourceType.Open, SourceType.High, SourceType.Low, SourceType.HL2 };
foreach (var source2 in source2Types)
{
- var indicator = new PrsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = source2 };
+ var indicator = new RsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = source2 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -211,9 +211,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_SmoothPeriod_CanBeChanged()
+ public void RsIndicator_SmoothPeriod_CanBeChanged()
{
- var indicator = new PrsIndicator { SmoothPeriod = 50 };
+ var indicator = new RsIndicator { SmoothPeriod = 50 };
Assert.Equal(50, indicator.SmoothPeriod);
@@ -222,9 +222,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_Source2_CanBeChanged()
+ public void RsIndicator_Source2_CanBeChanged()
{
- var indicator = new PrsIndicator { Source2 = SourceType.High };
+ var indicator = new RsIndicator { Source2 = SourceType.High };
Assert.Equal(SourceType.High, indicator.Source2);
@@ -233,9 +233,9 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_ReInitialize_ResetsState()
+ public void RsIndicator_ReInitialize_ResetsState()
{
- var indicator = new PrsIndicator { SmoothPeriod = 5 };
+ var indicator = new RsIndicator { SmoothPeriod = 5 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -248,7 +248,7 @@ public class PrsIndicatorTests
Assert.Equal(10, indicator.LinesSeries[0].Count);
// Re-initialize with new instance
- var indicator2 = new PrsIndicator { SmoothPeriod = 5 };
+ var indicator2 = new RsIndicator { SmoothPeriod = 5 };
indicator2.Initialize();
indicator2.HistoricalData.AddBar(now.AddMinutes(100), 200, 210, 190, 205);
indicator2.ProcessUpdate(new UpdateArgs(UpdateReason.HistoricalBar));
@@ -257,10 +257,10 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_HighVsLow_AlwaysGreaterThanOne()
+ public void RsIndicator_HighVsLow_AlwaysGreaterThanOne()
{
// High is always > Low, so ratio should always be > 1
- var indicator = new PrsIndicator { SmoothPeriod = 1, Source = SourceType.High, Source2 = SourceType.Low };
+ var indicator = new RsIndicator { SmoothPeriod = 1, Source = SourceType.High, Source2 = SourceType.Low };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -286,19 +286,19 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_Description_IsSet()
+ public void RsIndicator_Description_IsSet()
{
- var indicator = new PrsIndicator();
+ var indicator = new RsIndicator();
Assert.Contains("relative", indicator.Description, StringComparison.OrdinalIgnoreCase);
Assert.Contains("performance", indicator.Description, StringComparison.OrdinalIgnoreCase);
}
[Fact]
- public void PrsIndicator_SameSourceAndSource2_ReturnsOne()
+ public void RsIndicator_SameSourceAndSource2_ReturnsOne()
{
// When comparing a source to itself, ratio should be 1.0
- var indicator = new PrsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = SourceType.Close };
+ var indicator = new RsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = SourceType.Close };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -320,11 +320,11 @@ public class PrsIndicatorTests
}
[Fact]
- public void PrsIndicator_SmoothingReducesVariance()
+ public void RsIndicator_SmoothingReducesVariance()
{
// Compare unsmoothed vs smoothed - smoothed should have less variance
- var unsmoothed = new PrsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = SourceType.Open };
- var smoothed = new PrsIndicator { SmoothPeriod = 10, Source = SourceType.Close, Source2 = SourceType.Open };
+ var unsmoothed = new RsIndicator { SmoothPeriod = 1, Source = SourceType.Close, Source2 = SourceType.Open };
+ var smoothed = new RsIndicator { SmoothPeriod = 10, Source = SourceType.Close, Source2 = SourceType.Open };
unsmoothed.Initialize();
smoothed.Initialize();
diff --git a/lib/momentum/prs/tests/Prs.Tests.cs b/lib/momentum/rs/tests/Rs.Tests.cs
similarity index 69%
rename from lib/momentum/prs/tests/Prs.Tests.cs
rename to lib/momentum/rs/tests/Rs.Tests.cs
index a48c6379..0c0564ad 100644
--- a/lib/momentum/prs/tests/Prs.Tests.cs
+++ b/lib/momentum/rs/tests/Rs.Tests.cs
@@ -2,7 +2,7 @@ using Xunit;
namespace QuanTAlib.Tests;
-public class PrsTests
+public class RsTests
{
private const double Epsilon = 1e-10;
@@ -10,78 +10,78 @@ public class PrsTests
[Fact]
public void Constructor_DefaultParameters()
{
- var prs = new Prs();
- Assert.Equal("Prs", prs.Name);
- Assert.Equal(1, prs.SmoothPeriod);
+ var rs = new Rs();
+ Assert.Equal("Rs", rs.Name);
+ Assert.Equal(1, rs.SmoothPeriod);
}
[Fact]
public void Constructor_CustomSmoothPeriod()
{
- var prs = new Prs(10);
- Assert.Equal("Prs(10)", prs.Name);
- Assert.Equal(10, prs.SmoothPeriod);
+ var rs = new Rs(10);
+ Assert.Equal("Rs(10)", rs.Name);
+ Assert.Equal(10, rs.SmoothPeriod);
}
[Fact]
public void Constructor_ZeroPeriod_ThrowsException()
{
- Assert.Throws(() => new Prs(0));
+ Assert.Throws(() => new Rs(0));
}
[Fact]
public void Constructor_NegativePeriod_ThrowsException()
{
- Assert.Throws(() => new Prs(-1));
+ Assert.Throws(() => new Rs(-1));
}
// ==================== BASIC CALCULATIONS ====================
[Fact]
public void Update_SimpleRatio_ReturnsCorrectValue()
{
- var prs = new Prs();
- var result = prs.Update(100.0, 50.0);
+ var rs = new Rs();
+ var result = rs.Update(100.0, 50.0);
Assert.Equal(2.0, result.Value, 10);
}
[Fact]
public void Update_FractionRatio_ReturnsCorrectValue()
{
- var prs = new Prs();
- var result = prs.Update(50.0, 100.0);
+ var rs = new Rs();
+ var result = rs.Update(50.0, 100.0);
Assert.Equal(0.5, result.Value, 10);
}
[Fact]
public void Update_EqualValues_ReturnsOne()
{
- var prs = new Prs();
- var result = prs.Update(100.0, 100.0);
+ var rs = new Rs();
+ var result = rs.Update(100.0, 100.0);
Assert.Equal(1.0, result.Value, 10);
}
[Fact]
public void Update_DivisionByZero_ReturnsNaN()
{
- var prs = new Prs();
- var result = prs.Update(100.0, 0.0);
+ var rs = new Rs();
+ var result = rs.Update(100.0, 0.0);
Assert.True(double.IsNaN(result.Value));
}
[Fact]
public void RawRatio_MatchesUnsmoothedResult()
{
- var prs = new Prs();
- prs.Update(200.0, 100.0);
- Assert.Equal(2.0, prs.RawRatio, 10);
- Assert.Equal(prs.RawRatio, prs.Last.Value, 10);
+ var rs = new Rs();
+ rs.Update(200.0, 100.0);
+ Assert.Equal(2.0, rs.RawRatio, 10);
+ Assert.Equal(rs.RawRatio, rs.Last.Value, 10);
}
// ==================== SMOOTHING ====================
[Fact]
public void Update_WithSmoothing_SmoothsRatio()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
var results = new List();
// Feed increasing prices with 2:1 base ratio
@@ -89,20 +89,20 @@ public class PrsTests
{
double basePrice = 200.0 + i;
double compPrice = 100.0 + i * 0.5;
- prs.Update(basePrice, compPrice);
- results.Add(prs.Last.Value);
+ rs.Update(basePrice, compPrice);
+ results.Add(rs.Last.Value);
}
// After warmup, smoothed values should be less volatile
- Assert.True(prs.IsHot);
+ Assert.True(rs.IsHot);
Assert.True(results[^1] > 1.5); // Base is outperforming
}
[Fact]
public void Update_SmoothedVsRaw_DifferAfterMultipleUpdates()
{
- var prsRaw = new Prs(1);
- var prsSmoothed = new Prs(10);
+ var prsRaw = new Rs(1);
+ var prsSmoothed = new Rs(10);
var baseBars = new GBM(seed: 42).Fetch(30, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
var compBars = new GBM(seed: 123).Fetch(30, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
@@ -120,36 +120,36 @@ public class PrsTests
[Fact]
public void IsHot_BeforeWarmup_ReturnsFalse()
{
- var prs = new Prs(10);
- prs.Update(100.0, 50.0);
- Assert.False(prs.IsHot);
+ var rs = new Rs(10);
+ rs.Update(100.0, 50.0);
+ Assert.False(rs.IsHot);
}
[Fact]
public void IsHot_AfterWarmup_ReturnsTrue()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
for (int i = 0; i < 10; i++)
{
- prs.Update(100.0 + i, 50.0 + i);
+ rs.Update(100.0 + i, 50.0 + i);
}
- Assert.True(prs.IsHot);
+ Assert.True(rs.IsHot);
}
[Fact]
public void IsHot_NoSmoothing_TrueImmediately()
{
- var prs = new Prs(1);
- prs.Update(100.0, 50.0);
- Assert.True(prs.IsHot);
+ var rs = new Rs(1);
+ rs.Update(100.0, 50.0);
+ Assert.True(rs.IsHot);
}
// ==================== BAR CORRECTION ====================
[Fact]
public void Update_BarCorrection_RestoresState()
{
- var prs1 = new Prs(5);
- var prs2 = new Prs(5);
+ var prs1 = new Rs(5);
+ var prs2 = new Rs(5);
// Feed same initial data
for (int i = 0; i < 10; i++)
@@ -172,42 +172,42 @@ public class PrsTests
[Fact]
public void Update_MultipleCorrections_FinalValueCorrect()
{
- var prs = new Prs(3);
+ var rs = new Rs(3);
for (int i = 0; i < 5; i++)
{
- prs.Update(100.0 + i, 50.0 + i);
+ rs.Update(100.0 + i, 50.0 + i);
}
// Add bar
- prs.Update(110.0, 55.0, true);
+ rs.Update(110.0, 55.0, true);
// Multiple corrections
- prs.Update(115.0, 60.0, false);
- prs.Update(120.0, 65.0, false);
- prs.Update(110.0, 55.0, false); // Back to original
+ rs.Update(115.0, 60.0, false);
+ rs.Update(120.0, 65.0, false);
+ rs.Update(110.0, 55.0, false); // Back to original
- Assert.True(double.IsFinite(prs.Last.Value));
+ Assert.True(double.IsFinite(rs.Last.Value));
}
// ==================== RESET ====================
[Fact]
public void Reset_ClearsState()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
for (int i = 0; i < 10; i++)
{
- prs.Update(100.0 + i, 50.0 + i);
+ rs.Update(100.0 + i, 50.0 + i);
}
- Assert.NotEqual(default, prs.Last);
- Assert.True(prs.IsHot);
+ Assert.NotEqual(default, rs.Last);
+ Assert.True(rs.IsHot);
- prs.Reset();
+ rs.Reset();
- Assert.Equal(default, prs.Last);
- Assert.False(prs.IsHot);
+ Assert.Equal(default, rs.Last);
+ Assert.False(rs.IsHot);
}
// ==================== STATIC CALCULATE ====================
@@ -223,7 +223,7 @@ public class PrsTests
compSeries.Add(new TValue(DateTime.Now.AddMinutes(i), 50.0 + i));
}
- var result = Prs.Batch(baseSeries, compSeries, 5);
+ var result = Rs.Batch(baseSeries, compSeries, 5);
Assert.Equal(baseSeries.Count, result.Count);
}
@@ -243,7 +243,7 @@ public class PrsTests
compSeries.Add(new TValue(DateTime.Now.AddMinutes(i), 50.0 + i));
}
- Assert.Throws(() => Prs.Batch(baseSeries, compSeries));
+ Assert.Throws(() => Rs.Batch(baseSeries, compSeries));
}
[Fact]
@@ -253,7 +253,7 @@ public class PrsTests
double[] compArr = new double[5];
double[] output = new double[10];
- Assert.Throws(() => Prs.Batch(baseArr, compArr, output));
+ Assert.Throws(() => Rs.Batch(baseArr, compArr, output));
}
[Fact]
@@ -263,7 +263,7 @@ public class PrsTests
double[] compArr = new double[10];
double[] output = new double[5];
- Assert.Throws(() => Prs.Batch(baseArr, compArr, output));
+ Assert.Throws(() => Rs.Batch(baseArr, compArr, output));
}
[Fact]
@@ -273,19 +273,19 @@ public class PrsTests
double[] compArr = new double[10];
double[] output = new double[10];
- Assert.Throws(() => Prs.Batch(baseArr, compArr, output, 0));
+ Assert.Throws(() => Rs.Batch(baseArr, compArr, output, 0));
}
// ==================== EDGE CASES ====================
[Fact]
public void Update_NaNInput_UsesLastValidValue()
{
- var prs = new Prs();
+ var rs = new Rs();
- prs.Update(100.0, 50.0);
- _ = prs.Last.Value;
+ rs.Update(100.0, 50.0);
+ _ = rs.Last.Value;
- var result = prs.Update(double.NaN, double.NaN);
+ var result = rs.Update(double.NaN, double.NaN);
// Should use last valid values (100/50 pattern OR fallback)
Assert.True(double.IsFinite(result.Value));
}
@@ -293,19 +293,19 @@ public class PrsTests
[Fact]
public void Update_InfinityInput_UsesLastValidValue()
{
- var prs = new Prs();
+ var rs = new Rs();
- prs.Update(100.0, 50.0);
+ rs.Update(100.0, 50.0);
- var result = prs.Update(double.PositiveInfinity, 50.0);
+ var result = rs.Update(double.PositiveInfinity, 50.0);
Assert.True(double.IsFinite(result.Value));
}
[Fact]
public void Update_VerySmallComparison_HandlesCorrectly()
{
- var prs = new Prs();
- var result = prs.Update(100.0, 1e-15);
+ var rs = new Rs();
+ var result = rs.Update(100.0, 1e-15);
// Values below epsilon (1e-10) are treated as zero -> returns NaN
Assert.True(double.IsNaN(result.Value));
@@ -314,10 +314,10 @@ public class PrsTests
[Fact]
public void Update_NegativeValues_HandlesCorrectly()
{
- var prs = new Prs();
+ var rs = new Rs();
// Negative values (like P&L or temperature)
- var result = prs.Update(-50.0, -25.0);
+ var result = rs.Update(-50.0, -25.0);
Assert.Equal(2.0, result.Value, 10);
}
@@ -325,104 +325,104 @@ public class PrsTests
[Fact]
public void Prime_Single_ThrowsNotSupported()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
double[] data = [100, 101, 102, 103, 104];
- Assert.Throws(() => prs.Prime(data));
+ Assert.Throws(() => rs.Prime(data));
}
[Fact]
public void Prime_Dual_InitializesState()
{
- var prs = new Prs(3);
+ var rs = new Rs(3);
double[] baseData = [100, 102, 104, 106, 108, 110];
double[] compData = [50, 51, 52, 53, 54, 55];
- prs.Prime(baseData, compData);
+ rs.Prime(baseData, compData);
- Assert.NotEqual(default, prs.Last);
+ Assert.NotEqual(default, rs.Last);
}
[Fact]
public void Prime_MismatchedLengths_ThrowsException()
{
- var prs = new Prs(3);
+ var rs = new Rs(3);
double[] baseData = [100, 102, 104];
double[] compData = [50, 51];
- Assert.Throws(() => prs.Prime(baseData, compData));
+ Assert.Throws(() => rs.Prime(baseData, compData));
}
// ==================== NOT SUPPORTED ====================
[Fact]
public void Update_SingleInput_ThrowsNotSupported()
{
- var prs = new Prs();
+ var rs = new Rs();
var input = new TValue(DateTime.Now, 100.0);
- Assert.Throws(() => prs.Update(input));
+ Assert.Throws(() => rs.Update(input));
}
[Fact]
public void Update_TSeries_ThrowsNotSupported()
{
- var prs = new Prs();
+ var rs = new Rs();
var source = new TSeries();
source.Add(new TValue(DateTime.Now, 100.0));
- Assert.Throws(() => prs.Update(source));
+ Assert.Throws(() => rs.Update(source));
}
// ==================== PERFORMANCE SCENARIOS ====================
[Fact]
public void Update_OutperformanceScenario_IncreasingRatio()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
// Base grows faster than comparison
for (int i = 0; i < 20; i++)
{
double basePrice = 100.0 + i * 2; // +2 per bar
double compPrice = 100.0 + i * 1; // +1 per bar
- prs.Update(basePrice, compPrice);
+ rs.Update(basePrice, compPrice);
}
// Ratio should be increasing (base outperforming)
- Assert.True(prs.Last.Value > 1.0);
+ Assert.True(rs.Last.Value > 1.0);
}
[Fact]
public void Update_UnderperformanceScenario_DecreasingRatio()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
// Base grows slower than comparison
for (int i = 0; i < 20; i++)
{
double basePrice = 100.0 + i * 1; // +1 per bar
double compPrice = 100.0 + i * 2; // +2 per bar
- prs.Update(basePrice, compPrice);
+ rs.Update(basePrice, compPrice);
}
// Ratio should be decreasing (base underperforming)
- Assert.True(prs.Last.Value < 1.0);
+ Assert.True(rs.Last.Value < 1.0);
}
[Fact]
public void Update_ParallelMovement_StableRatio()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
// Both grow at same rate
for (int i = 0; i < 20; i++)
{
double basePrice = 100.0 + i * 2;
double compPrice = 50.0 + i * 1;
- prs.Update(basePrice, compPrice);
+ rs.Update(basePrice, compPrice);
}
// Initial ratio was 2.0, should stay around there
- Assert.InRange(prs.Last.Value, 1.9, 2.1);
+ Assert.InRange(rs.Last.Value, 1.9, 2.1);
}
// ==================== BATCH CALCULATION ====================
@@ -435,14 +435,14 @@ public class PrsTests
var compSeries = compBars.Close;
// Batch calculation
- var batchResult = Prs.Batch(baseSeries, compSeries, 5);
+ var batchResult = Rs.Batch(baseSeries, compSeries, 5);
// Streaming calculation
- var prs = new Prs(5);
+ var rs = new Rs(5);
var streamingResults = new List();
for (int i = 0; i < baseSeries.Count; i++)
{
- streamingResults.Add(prs.Update(baseSeries[i], compSeries[i], true).Value);
+ streamingResults.Add(rs.Update(baseSeries[i], compSeries[i], true).Value);
}
// Compare
diff --git a/lib/momentum/prs/tests/Prs.Validation.Tests.cs b/lib/momentum/rs/tests/Rs.Validation.Tests.cs
similarity index 66%
rename from lib/momentum/prs/tests/Prs.Validation.Tests.cs
rename to lib/momentum/rs/tests/Rs.Validation.Tests.cs
index f9e45e64..bb3c7667 100644
--- a/lib/momentum/prs/tests/Prs.Validation.Tests.cs
+++ b/lib/momentum/rs/tests/Rs.Validation.Tests.cs
@@ -4,19 +4,19 @@ using Xunit;
namespace QuanTAlib.Tests;
///
-/// Validation tests for PRS (Price Relative Strength) indicator.
-/// PRS compares relative performance between two assets via their price ratio.
-/// Note: PRS is a unique indicator without direct equivalents in TA-Lib, Skender, etc.
+/// Validation tests for RS (Price Relative Strength) indicator.
+/// RS compares relative performance between two assets via their price ratio.
+/// Note: RS is a unique indicator without direct equivalents in TA-Lib, Skender, etc.
/// These tests validate mathematical correctness and expected behavior.
///
-public class PrsValidationTests
+public class RsValidationTests
{
#region Mathematical Validation
[Fact]
- public void Prs_ManualCalculation_MatchesExpected()
+ public void Rs_ManualCalculation_MatchesExpected()
{
- var prs = new Prs(1); // No smoothing
+ var rs = new Rs(1); // No smoothing
var time = DateTime.UtcNow;
// Test data: base and comparison prices
@@ -26,67 +26,67 @@ public class PrsValidationTests
// Expected: ratios should be 1.0, 1.05, 1.10, 1.15, 1.20
for (int i = 0; i < baseValues.Length; i++)
{
- var result = prs.Update(
+ var result = rs.Update(
new TValue(time.AddSeconds(i), baseValues[i]),
new TValue(time.AddSeconds(i), compValues[i]),
true);
double expected = baseValues[i] / compValues[i];
Assert.Equal(expected, result.Value, 10);
- Assert.Equal(expected, prs.RawRatio, 10);
+ Assert.Equal(expected, rs.RawRatio, 10);
}
}
[Fact]
- public void Prs_EqualPrices_ReturnsOne()
+ public void Rs_EqualPrices_ReturnsOne()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- var result = prs.Update(50.0, 50.0, true);
+ var result = rs.Update(50.0, 50.0, true);
Assert.Equal(1.0, result.Value, 10);
}
[Fact]
- public void Prs_BaseHigherThanComp_ReturnsGreaterThanOne()
+ public void Rs_BaseHigherThanComp_ReturnsGreaterThanOne()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- var result = prs.Update(120.0, 100.0, true);
+ var result = rs.Update(120.0, 100.0, true);
Assert.Equal(1.2, result.Value, 10);
Assert.True(result.Value > 1.0);
}
[Fact]
- public void Prs_BaseLowerThanComp_ReturnsLessThanOne()
+ public void Rs_BaseLowerThanComp_ReturnsLessThanOne()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- var result = prs.Update(80.0, 100.0, true);
+ var result = rs.Update(80.0, 100.0, true);
Assert.Equal(0.8, result.Value, 10);
Assert.True(result.Value < 1.0);
}
[Fact]
- public void Prs_DivisionByZero_ReturnsNaN()
+ public void Rs_DivisionByZero_ReturnsNaN()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- var result = prs.Update(100.0, 0.0, true);
+ var result = rs.Update(100.0, 0.0, true);
Assert.True(double.IsNaN(result.Value));
- Assert.True(double.IsNaN(prs.RawRatio));
+ Assert.True(double.IsNaN(rs.RawRatio));
}
[Fact]
- public void Prs_VerySmallDenominator_ReturnsNaN()
+ public void Rs_VerySmallDenominator_ReturnsNaN()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
// Value smaller than epsilon (1e-10) should be treated as zero
- var result = prs.Update(100.0, 1e-11, true);
+ var result = rs.Update(100.0, 1e-11, true);
Assert.True(double.IsNaN(result.Value));
}
@@ -96,32 +96,32 @@ public class PrsValidationTests
#region Smoothing Validation
[Fact]
- public void Prs_SmoothedFirstValue_EqualsRawRatio()
+ public void Rs_SmoothedFirstValue_EqualsRawRatio()
{
- var prs = new Prs(10);
+ var rs = new Rs(10);
var time = DateTime.UtcNow;
- var result = prs.Update(
+ var result = rs.Update(
new TValue(time, 100.0),
new TValue(time, 50.0),
true);
// First value should equal raw ratio
- Assert.Equal(prs.RawRatio, result.Value, 10);
+ Assert.Equal(rs.RawRatio, result.Value, 10);
Assert.Equal(2.0, result.Value, 10);
}
[Fact]
- public void Prs_SmoothedConvergesToRatio_WhenConstant()
+ public void Rs_SmoothedConvergesToRatio_WhenConstant()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
var time = DateTime.UtcNow;
// Feed constant ratio (100/50 = 2.0) repeatedly
TValue result = default;
for (int i = 0; i < 50; i++)
{
- result = prs.Update(
+ result = rs.Update(
new TValue(time.AddSeconds(i), 100.0),
new TValue(time.AddSeconds(i), 50.0),
true);
@@ -132,9 +132,9 @@ public class PrsValidationTests
}
[Fact]
- public void Prs_NoSmoothing_RawRatioEqualsSmoothed()
+ public void Rs_NoSmoothing_RawRatioEqualsSmoothed()
{
- var prs = new Prs(1); // No smoothing
+ var rs = new Rs(1); // No smoothing
var values = new (double b, double c)[]
{
@@ -146,16 +146,16 @@ public class PrsValidationTests
foreach (var (b, c) in values)
{
- var result = prs.Update(b, c, true);
- Assert.Equal(prs.RawRatio, result.Value, 10);
+ var result = rs.Update(b, c, true);
+ Assert.Equal(rs.RawRatio, result.Value, 10);
}
}
[Fact]
- public void Prs_SmoothingReducesVolatility()
+ public void Rs_SmoothingReducesVolatility()
{
- var prsNoSmooth = new Prs(1);
- var prsSmooth = new Prs(10);
+ var prsNoSmooth = new Rs(1);
+ var prsSmooth = new Rs(10);
// Create volatile ratio series
var baseVals = new double[] { 100, 120, 80, 130, 70, 140, 60, 150 };
@@ -191,9 +191,9 @@ public class PrsValidationTests
#region Trend Interpretation
[Fact]
- public void Prs_IncreasingRatio_IndicatesOutperformance()
+ public void Rs_IncreasingRatio_IndicatesOutperformance()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
// Base outperforms: grows faster than comparison
var results = new List();
@@ -202,7 +202,7 @@ public class PrsValidationTests
{
double basePrice = 100 + (i * 5); // 100, 105, 110...
double compPrice = 100 + (i * 2); // 100, 102, 104...
- var result = prs.Update(basePrice, compPrice, true);
+ var result = rs.Update(basePrice, compPrice, true);
results.Add(result.Value);
}
@@ -215,9 +215,9 @@ public class PrsValidationTests
}
[Fact]
- public void Prs_DecreasingRatio_IndicatesUnderperformance()
+ public void Rs_DecreasingRatio_IndicatesUnderperformance()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
// Base underperforms: grows slower than comparison
var results = new List();
@@ -226,7 +226,7 @@ public class PrsValidationTests
{
double basePrice = 100 + (i * 2); // 100, 102, 104...
double compPrice = 100 + (i * 5); // 100, 105, 110...
- var result = prs.Update(basePrice, compPrice, true);
+ var result = rs.Update(basePrice, compPrice, true);
results.Add(result.Value);
}
@@ -239,9 +239,9 @@ public class PrsValidationTests
}
[Fact]
- public void Prs_SameGrowthRate_ConstantRatio()
+ public void Rs_SameGrowthRate_ConstantRatio()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
// Both grow at same rate - ratio stays constant at 2.0
var results = new List();
@@ -250,7 +250,7 @@ public class PrsValidationTests
{
double basePrice = 100 * (1 + (i * 0.05)); // 5% growth
double compPrice = 50 * (1 + (i * 0.05)); // 5% growth
- var result = prs.Update(basePrice, compPrice, true);
+ var result = rs.Update(basePrice, compPrice, true);
results.Add(result.Value);
}
@@ -266,57 +266,57 @@ public class PrsValidationTests
#region Edge Cases and Robustness
[Fact]
- public void Prs_NegativeValues_HandlesCorrectly()
+ public void Rs_NegativeValues_HandlesCorrectly()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- // While unusual, PRS should handle negative values mathematically
- var result = prs.Update(-100.0, -50.0, true);
+ // While unusual, RS should handle negative values mathematically
+ var result = rs.Update(-100.0, -50.0, true);
Assert.Equal(2.0, result.Value, 10); // -100/-50 = 2.0
}
[Fact]
- public void Prs_MixedSigns_HandlesCorrectly()
+ public void Rs_MixedSigns_HandlesCorrectly()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
// Base positive, comp negative
- var result = prs.Update(100.0, -50.0, true);
+ var result = rs.Update(100.0, -50.0, true);
Assert.Equal(-2.0, result.Value, 10);
}
[Fact]
- public void Prs_VeryLargeValues_MaintainsPrecision()
+ public void Rs_VeryLargeValues_MaintainsPrecision()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- var result = prs.Update(1e15, 1e14, true);
+ var result = rs.Update(1e15, 1e14, true);
// 1e15 / 1e14 = 10
Assert.Equal(10.0, result.Value, 6);
}
[Fact]
- public void Prs_VerySmallValues_MaintainsPrecision()
+ public void Rs_VerySmallValues_MaintainsPrecision()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- var result = prs.Update(1e-5, 1e-6, true);
+ var result = rs.Update(1e-5, 1e-6, true);
// 1e-5 / 1e-6 = 10
Assert.Equal(10.0, result.Value, 6);
}
[Fact]
- public void Prs_NaNBase_PropagatesNaN()
+ public void Rs_NaNBase_PropagatesNaN()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
// Should fallback to last valid or 0, resulting in 0/comp
- prs.Update(100.0, 50.0, true); // First valid value
- var result = prs.Update(double.NaN, 50.0, true);
+ rs.Update(100.0, 50.0, true); // First valid value
+ var result = rs.Update(double.NaN, 50.0, true);
// NaN base with valid comparison should use fallback (previous: 100) or 0
// Result will depend on sanitization logic - actual behavior uses last valid
@@ -324,24 +324,24 @@ public class PrsValidationTests
}
[Fact]
- public void Prs_NaNComp_PropagatesNaN()
+ public void Rs_NaNComp_PropagatesNaN()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- prs.Update(100.0, 50.0, true); // First valid value
- var result = prs.Update(100.0, double.NaN, true);
+ rs.Update(100.0, 50.0, true); // First valid value
+ var result = rs.Update(100.0, double.NaN, true);
// NaN comp should use fallback (previous: 50) -> 100/50 = 2.0
Assert.Equal(2.0, result.Value, 6);
}
[Fact]
- public void Prs_InfinityBase_HandledGracefully()
+ public void Rs_InfinityBase_HandledGracefully()
{
- var prs = new Prs(1);
+ var rs = new Rs(1);
- prs.Update(100.0, 50.0, true); // First valid
- var result = prs.Update(double.PositiveInfinity, 50.0, true);
+ rs.Update(100.0, 50.0, true); // First valid
+ var result = rs.Update(double.PositiveInfinity, 50.0, true);
// Should fallback to last valid (100/50 = 2.0)
Assert.Equal(2.0, result.Value, 6);
@@ -352,10 +352,10 @@ public class PrsValidationTests
#region Batch Calculation Validation
[Fact]
- public void Prs_BatchCalculate_MatchesStreaming()
+ public void Rs_BatchCalculate_MatchesStreaming()
{
int smoothPeriod = 5;
- var prsStream = new Prs(smoothPeriod);
+ var prsStream = new Rs(smoothPeriod);
var baseValues = new double[] { 100, 105, 110, 108, 115, 120, 118, 125, 130, 128 };
var compValues = new double[] { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 };
@@ -369,7 +369,7 @@ public class PrsValidationTests
// Batch calculation
var batchOutput = new double[baseValues.Length];
- Prs.Batch(baseValues, compValues, batchOutput, smoothPeriod);
+ Rs.Batch(baseValues, compValues, batchOutput, smoothPeriod);
// Results should match
for (int i = 0; i < baseValues.Length; i++)
@@ -379,10 +379,10 @@ public class PrsValidationTests
}
[Fact]
- public void Prs_TSeriesCalculate_MatchesStreaming()
+ public void Rs_TSeriesCalculate_MatchesStreaming()
{
int smoothPeriod = 3;
- var prsStream = new Prs(smoothPeriod);
+ var prsStream = new Rs(smoothPeriod);
var time = DateTime.UtcNow;
var baseSeries = new TSeries(10);
@@ -405,7 +405,7 @@ public class PrsValidationTests
}
// TSeries batch
- var batchResult = Prs.Batch(baseSeries, compSeries, smoothPeriod);
+ var batchResult = Rs.Batch(baseSeries, compSeries, smoothPeriod);
for (int i = 0; i < baseValues.Length; i++)
{
@@ -418,60 +418,60 @@ public class PrsValidationTests
#region Properties and State
[Fact]
- public void Prs_IsHot_BecomesTrue_AfterSmoothPeriod()
+ public void Rs_IsHot_BecomesTrue_AfterSmoothPeriod()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
for (int i = 0; i < 10; i++)
{
- prs.Update(100 + i, 100.0, true);
+ rs.Update(100 + i, 100.0, true);
if (i < 4) // 0-4 = first 5 values
{
- Assert.False(prs.IsHot, $"Should not be hot at index {i}");
+ Assert.False(rs.IsHot, $"Should not be hot at index {i}");
}
else
{
- Assert.True(prs.IsHot, $"Should be hot at index {i}");
+ Assert.True(rs.IsHot, $"Should be hot at index {i}");
}
}
}
[Fact]
- public void Prs_Reset_ClearsState()
+ public void Rs_Reset_ClearsState()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
// Add values
for (int i = 0; i < 10; i++)
{
- prs.Update(100 + i, 50.0, true);
+ rs.Update(100 + i, 50.0, true);
}
- Assert.True(prs.IsHot);
- Assert.True(prs.RawRatio > 0);
+ Assert.True(rs.IsHot);
+ Assert.True(rs.RawRatio > 0);
// Reset
- prs.Reset();
+ rs.Reset();
- Assert.False(prs.IsHot);
- Assert.Equal(0.0, prs.RawRatio);
- Assert.Equal(default(TValue), prs.Last);
+ Assert.False(rs.IsHot);
+ Assert.Equal(0.0, rs.RawRatio);
+ Assert.Equal(default(TValue), rs.Last);
}
[Fact]
- public void Prs_Prime_InitializesState()
+ public void Rs_Prime_InitializesState()
{
- var prs = new Prs(5);
+ var rs = new Rs(5);
var baseSource = new double[] { 100, 105, 110, 115, 120, 125, 130 };
var compSource = new double[] { 100, 100, 100, 100, 100, 100, 100 };
- prs.Prime(baseSource, compSource);
+ rs.Prime(baseSource, compSource);
- Assert.True(prs.IsHot);
- Assert.Equal(1.30, prs.RawRatio, 10);
- Assert.Equal(130.0 / 100.0, prs.RawRatio, 10);
+ Assert.True(rs.IsHot);
+ Assert.Equal(1.30, rs.RawRatio, 10);
+ Assert.Equal(130.0 / 100.0, rs.RawRatio, 10);
}
#endregion
@@ -479,29 +479,29 @@ public class PrsValidationTests
#region Performance Properties
[Fact]
- public void Prs_SmoothPeriod_ExposesCorrectValue()
+ public void Rs_SmoothPeriod_ExposesCorrectValue()
{
- var prs = new Prs(14);
+ var rs = new Rs(14);
- Assert.Equal(14, prs.SmoothPeriod);
+ Assert.Equal(14, rs.SmoothPeriod);
}
[Fact]
- public void Prs_WarmupPeriod_EqualsSmoothPeriod()
+ public void Rs_WarmupPeriod_EqualsSmoothPeriod()
{
- var prs = new Prs(20);
+ var rs = new Rs(20);
- Assert.Equal(20, prs.WarmupPeriod);
+ Assert.Equal(20, rs.WarmupPeriod);
}
[Fact]
- public void Prs_Name_IncludesPeriodIfSmoothed()
+ public void Rs_Name_IncludesPeriodIfSmoothed()
{
- var prsNoSmooth = new Prs(1);
- var prsSmooth = new Prs(14);
+ var prsNoSmooth = new Rs(1);
+ var prsSmooth = new Rs(14);
- Assert.Equal("Prs", prsNoSmooth.Name);
- Assert.Equal("Prs(14)", prsSmooth.Name);
+ Assert.Equal("Rs", prsNoSmooth.Name);
+ Assert.Equal("Rs(14)", prsSmooth.Name);
}
#endregion
@@ -511,11 +511,11 @@ public class PrsValidationTests
///
/// Structural validation against Skender GetPrs.
/// Skender PRS computes price ratio between two quote series.
- /// QuanTAlib PRS also computes base/comparison ratio with optional smoothing.
+ /// QuanTAlib RS also computes base/comparison ratio with optional smoothing.
/// With period=1 (no smoothing), raw ratios should match exactly.
///
[Fact]
- public void Validate_Skender_Prs_Streaming()
+ public void Validate_Skender_Rs_Streaming()
{
using var evalData = new ValidationTestData();
// Create a second quote series for comparison (different seed)
@@ -531,27 +531,27 @@ public class PrsValidationTests
Volume = (decimal)b.Volume
}).ToList();
- // QuanTAlib PRS (streaming, no smoothing)
- var prs = new Prs(1);
+ // QuanTAlib RS (streaming, no smoothing)
+ var rs = new Rs(1);
var qResults = new List();
for (int i = 0; i < evalData.Bars.Count; i++)
{
double evalClose = evalData.Bars[i].Close;
double baseClose = baseBars[i].Close;
- qResults.Add(prs.Update(evalClose, baseClose, true).Value);
+ qResults.Add(rs.Update(evalClose, baseClose, true).Value);
}
// Skender PRS: quotesEval.GetPrs(quotesBase)
var sResult = evalData.SkenderQuotes.GetPrs(baseQuotes).ToList();
- // Cross-validate: raw PRS ratio (no smoothing)
+ // Cross-validate: raw RS ratio (no smoothing)
ValidationHelper.VerifyData(qResults, sResult, s => s.Prs, tolerance: ValidationHelper.SkenderTolerance);
}
[Fact]
- public void Prs_Correction_Recomputes()
+ public void Rs_Correction_Recomputes()
{
- var ind = new Prs(smoothPeriod: 5);
+ var ind = new Rs(smoothPeriod: 5);
// Build state well past warmup
for (int i = 0; i < 50; i++)
diff --git a/lib/oscillators/_index.md b/lib/oscillators/_index.md
index 566a468a..a6781d0a 100644
--- a/lib/oscillators/_index.md
+++ b/lib/oscillators/_index.md
@@ -19,7 +19,7 @@ Oscillators fluctuate above and below a centerline or within bounded ranges. Use
| [DEM](dem/Dem.md) | DeMarker Oscillator | Bounded 0-1 oscillator comparing sequential highs and lows. |
| [DOSC](dosc/Dosc.md) | Derivative Oscillator | Double-smoothed RSI minus signal line. Momentum acceleration. |
| [DPO](dpo/Dpo.md) | Detrended Price Oscillator | Removes trend via displaced SMA. Reveals cycles. |
-| [DYMOI](dymoi/Dymoi.md) | Dynamic Momentum Index | RSI with volatility-adaptive period. Shorter in volatile markets. |
+| [DYMI](dymi/Dymi.md) | Dynamic Momentum Index | RSI with volatility-adaptive period. Shorter in volatile markets. |
| [ER](er/Er.md) | Efficiency Ratio | Measures directional efficiency. Net movement / total path length. |
| [ERI](eri/Eri.md) | Elder Ray Index | Separates bull and bear power relative to EMA. |
| [FI](fi/Fi.md) | Force Index | Combines price change, direction, and volume to measure buying/selling power. |
diff --git a/lib/oscillators/dymoi/Dymoi.Quantower.cs b/lib/oscillators/dymi/Dymi.Quantower.cs
similarity index 77%
rename from lib/oscillators/dymoi/Dymoi.Quantower.cs
rename to lib/oscillators/dymi/Dymi.Quantower.cs
index 8e4a01a1..d119b78b 100644
--- a/lib/oscillators/dymoi/Dymoi.Quantower.cs
+++ b/lib/oscillators/dymi/Dymi.Quantower.cs
@@ -5,7 +5,7 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
[SkipLocalsInit]
-public sealed class DymoiIndicator : Indicator, IWatchlistIndicator
+public sealed class DymiIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Base RSI Period", sortIndex: 1, 2, 500, 1, 0)]
public int BasePeriod { get; set; } = 14;
@@ -28,33 +28,33 @@ public sealed class DymoiIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
- private Dymoi _dymoi = null!;
+ private Dymi _dymi = null!;
private readonly LineSeries _series;
public static int MinHistoryDepths => 0;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
public override string ShortName =>
- $"DYMOI ({BasePeriod},{ShortPeriod},{LongPeriod},{MinPeriod},{MaxPeriod})";
+ $"DYMI ({BasePeriod},{ShortPeriod},{LongPeriod},{MinPeriod},{MaxPeriod})";
public override string SourceCodeLink =>
- "https://github.com/mihakralj/QuanTAlib/blob/main/lib/oscillators/dymoi/Dymoi.Quantower.cs";
+ "https://github.com/mihakralj/QuanTAlib/blob/main/lib/oscillators/dymi/Dymi.Quantower.cs";
- public DymoiIndicator()
+ public DymiIndicator()
{
OnBackGround = true;
SeparateWindow = true;
- Name = "DYMOI - Dynamic Momentum Index";
+ Name = "DYMI - Dynamic Momentum Index";
Description = "Volatility-adaptive RSI by Chande & Kroll: period shortens in volatile markets, lengthens in quiet ones.";
- _series = new LineSeries("DYMOI", Color.Yellow, 2, LineStyle.Solid);
+ _series = new LineSeries("DYMI", Color.Yellow, 2, LineStyle.Solid);
AddLineSeries(_series);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void OnInit()
{
- _dymoi = new Dymoi(BasePeriod, ShortPeriod, LongPeriod, MinPeriod, MaxPeriod);
+ _dymi = new Dymi(BasePeriod, ShortPeriod, LongPeriod, MinPeriod, MaxPeriod);
base.OnInit();
}
@@ -66,9 +66,9 @@ public sealed class DymoiIndicator : Indicator, IWatchlistIndicator
double price = priceSelector(item);
TValue input = new(item.TimeLeft, price);
- TValue result = _dymoi.Update(input, args.IsNewBar());
+ TValue result = _dymi.Update(input, args.IsNewBar());
- if (!_dymoi.IsHot && !ShowColdValues)
+ if (!_dymi.IsHot && !ShowColdValues)
{
return;
}
diff --git a/lib/oscillators/dymoi/Dymoi.cs b/lib/oscillators/dymi/Dymi.cs
similarity index 93%
rename from lib/oscillators/dymoi/Dymoi.cs
rename to lib/oscillators/dymi/Dymi.cs
index 60e81ae3..acf2f979 100644
--- a/lib/oscillators/dymoi/Dymoi.cs
+++ b/lib/oscillators/dymi/Dymi.cs
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
///
-/// DYMOI: Dynamic Momentum Index
+/// DYMI: Dynamic Momentum Index
///
///
/// Volatility-adaptive RSI by Tushar Chande and Stanley Kroll (1994).
@@ -18,10 +18,10 @@ namespace QuanTAlib;
///
/// References:
/// Chande, T. & Kroll, S. (1994). The New Technical Trader.
-/// PineScript reference: dymoi.pine
+/// PineScript reference: dymi.pine
///
[SkipLocalsInit]
-public sealed class Dymoi : AbstractBase
+public sealed class Dymi : AbstractBase
{
private readonly int _basePeriod;
private readonly int _shortPeriod;
@@ -57,14 +57,14 @@ public sealed class Dymoi : AbstractBase
private State _s, _ps;
///
- /// Creates DYMOI with specified parameters.
+ /// Creates DYMI with specified parameters.
///
/// Base RSI period (must be >= 2)
/// Short StdDev window (must be >= 2)
/// Long StdDev window (must be >= 2 and > shortPeriod)
/// Minimum dynamic period (must be >= 2)
/// Maximum dynamic period (must be >= minPeriod)
- public Dymoi(int basePeriod = 14, int shortPeriod = 5, int longPeriod = 10,
+ public Dymi(int basePeriod = 14, int shortPeriod = 5, int longPeriod = 10,
int minPeriod = 3, int maxPeriod = 30)
{
if (basePeriod < 2)
@@ -106,14 +106,14 @@ public sealed class Dymoi : AbstractBase
_s = new State(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, true, double.NaN, double.NaN);
_ps = _s;
- Name = $"Dymoi({basePeriod},{shortPeriod},{longPeriod},{minPeriod},{maxPeriod})";
+ Name = $"Dymi({basePeriod},{shortPeriod},{longPeriod},{minPeriod},{maxPeriod})";
WarmupPeriod = longPeriod + maxPeriod;
}
///
- /// Creates DYMOI with event-based source chaining.
+ /// Creates DYMI with event-based source chaining.
///
- public Dymoi(ITValuePublisher source, int basePeriod = 14, int shortPeriod = 5,
+ public Dymi(ITValuePublisher source, int basePeriod = 14, int shortPeriod = 5,
int longPeriod = 10, int minPeriod = 3, int maxPeriod = 30)
: this(basePeriod, shortPeriod, longPeriod, minPeriod, maxPeriod)
{
@@ -234,7 +234,7 @@ public sealed class Dymoi : AbstractBase
}
// ── Stage 3: Wilder RMA RSI with adaptive alpha ──
- double dymoi = 50.0;
+ double dymi = 50.0;
if (!double.IsNaN(s.PrevClose))
{
double alpha = 1.0 / dynPeriod;
@@ -253,7 +253,7 @@ public sealed class Dymoi : AbstractBase
double aG = s.AvgGain * c;
double aL = s.AvgLoss * c;
double total = aG + aL;
- dymoi = total != 0.0 ? 100.0 * aG / total : 50.0;
+ dymi = total != 0.0 ? 100.0 * aG / total : 50.0;
if (s.E <= 1e-10)
{
s.Warmup = false;
@@ -262,15 +262,15 @@ public sealed class Dymoi : AbstractBase
else
{
double total = s.AvgGain + s.AvgLoss;
- dymoi = total != 0.0 ? 100.0 * s.AvgGain / total : 50.0;
+ dymi = total != 0.0 ? 100.0 * s.AvgGain / total : 50.0;
}
}
s.PrevClose = value;
_s = s;
- dymoi = Math.Max(0.0, Math.Min(100.0, dymoi));
- Last = new TValue(input.Time, dymoi);
+ dymi = Math.Max(0.0, Math.Min(100.0, dymi));
+ Last = new TValue(input.Time, dymi);
PubEvent(Last, isNew);
return Last;
}
@@ -322,8 +322,8 @@ public sealed class Dymoi : AbstractBase
public static TSeries Batch(TSeries source, int basePeriod = 14, int shortPeriod = 5,
int longPeriod = 10, int minPeriod = 3, int maxPeriod = 30)
{
- var dymoi = new Dymoi(basePeriod, shortPeriod, longPeriod, minPeriod, maxPeriod);
- return dymoi.Update(source);
+ var dymi = new Dymi(basePeriod, shortPeriod, longPeriod, minPeriod, maxPeriod);
+ return dymi.Update(source);
}
///
@@ -480,7 +480,7 @@ public sealed class Dymoi : AbstractBase
}
// Wilder RMA RSI
- double dymoi = 50.0;
+ double dymi = 50.0;
if (!double.IsNaN(prevClose))
{
double alpha = 1.0 / dynPeriod;
@@ -499,7 +499,7 @@ public sealed class Dymoi : AbstractBase
double aG = avgGain * c;
double aL = avgLoss * c;
double total = aG + aL;
- dymoi = total != 0.0 ? 100.0 * aG / total : 50.0;
+ dymi = total != 0.0 ? 100.0 * aG / total : 50.0;
if (e <= 1e-10)
{
warmup = false;
@@ -508,12 +508,12 @@ public sealed class Dymoi : AbstractBase
else
{
double total = avgGain + avgLoss;
- dymoi = total != 0.0 ? 100.0 * avgGain / total : 50.0;
+ dymi = total != 0.0 ? 100.0 * avgGain / total : 50.0;
}
}
prevClose = val;
- output[i] = Math.Max(0.0, Math.Min(100.0, dymoi));
+ output[i] = Math.Max(0.0, Math.Min(100.0, dymi));
}
}
finally
diff --git a/lib/oscillators/dymoi/Dymoi.md b/lib/oscillators/dymi/dymi.md
similarity index 85%
rename from lib/oscillators/dymoi/Dymoi.md
rename to lib/oscillators/dymi/dymi.md
index c4d82858..49aec3b5 100644
--- a/lib/oscillators/dymoi/Dymoi.md
+++ b/lib/oscillators/dymi/dymi.md
@@ -1,4 +1,4 @@
-# DYMOI: Dynamic Momentum Index
+# DYMI: Dynamic Momentum Index
> *The market is not a fixed-frequency oscillator. Why would you analyze it with one?*
@@ -7,20 +7,20 @@
| **Category** | Oscillator |
| **Inputs** | Source (close) |
| **Parameters** | `basePeriod` (default 14), `shortPeriod` (default 5), `longPeriod` (default 10), `minPeriod` (default 3), `maxPeriod` (default 30) |
-| **Outputs** | Single series (Dymoi) |
+| **Outputs** | Single series (Dymi) |
| **Output range** | Varies (see docs) |
| **Warmup** | 1 bar |
-| **PineScript** | [dymoi.pine](dymoi.pine) |
+| **PineScript** | [dymi.pine](dymi.pine) |
-- DYMOI is a volatility-adaptive RSI: when recent price swings are large relative to longer-term swings, the RSI period shortens and the indicator be...
+- DYMI is a volatility-adaptive RSI: when recent price swings are large relative to longer-term swings, the RSI period shortens and the indicator be...
- **Similar:** [RSI](../../momentum/rsi/Rsi.md), [Stoch](../stoch/Stoch.md) | **Complementary:** ATR | **Trading note:** Dynamic Momentum Index; RSI with variable lookback based on volatility. Faster in calm, slower in volatile markets.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
-DYMOI is a volatility-adaptive RSI: when recent price swings are large relative to longer-term swings, the RSI period shortens and the indicator becomes more responsive; when price action tightens, the period extends and the output smooths. The result is an oscillator that self-adjusts its sensitivity to the market's current state, avoiding both the lag of long fixed-period RSIs in trending regimes and the noise of short-period RSIs in ranging ones.
+DYMI is a volatility-adaptive RSI: when recent price swings are large relative to longer-term swings, the RSI period shortens and the indicator becomes more responsive; when price action tightens, the period extends and the output smooths. The result is an oscillator that self-adjusts its sensitivity to the market's current state, avoiding both the lag of long fixed-period RSIs in trending regimes and the noise of short-period RSIs in ranging ones.
## Historical Context
-Tushar Chande and Stanley Kroll introduced DYMOI in *The New Technical Trader* (1994) as a practical answer to a genuine problem: the standard RSI's fixed period is a blunt instrument. A 14-bar RSI responds identically whether the market has been oscillating ±5% per day or ±0.2%. Chande and Kroll observed that a shorter period in high-volatility environments catches reversals earlier; a longer period in quiet conditions eliminates whipsaws.
+Tushar Chande and Stanley Kroll introduced DYMI in *The New Technical Trader* (1994) as a practical answer to a genuine problem: the standard RSI's fixed period is a blunt instrument. A 14-bar RSI responds identically whether the market has been oscillating ±5% per day or ±0.2%. Chande and Kroll observed that a shorter period in high-volatility environments catches reversals earlier; a longer period in quiet conditions eliminates whipsaws.
The mechanism they chose was straightforward: compute the ratio of short-term to long-term price standard deviation. When this ratio exceeds 1, the market is more volatile than its recent baseline — shorten the period. When the ratio is below 1, lengthen it. The result gets clamped to a configurable `[minPeriod, maxPeriod]` range, and a standard Wilder RSI runs on the resulting dynamic period.
@@ -32,7 +32,7 @@ The indicator has no widely adopted C# open-source implementation, which is why
| :--- | :--- | :---: | :---: |
| RSI (Wilder) | None — fixed period | 0–100 | period+1 |
| CRSI (Connors) | Three-component composite, no period adaptation | 0–100 | rankPeriod+rsiPeriod |
-| DYMOI (Chande/Kroll) | Dual StdDev ratio drives period selection | 0–100 | longPeriod+maxPeriod |
+| DYMI (Chande/Kroll) | Dual StdDev ratio drives period selection | 0–100 | longPeriod+maxPeriod |
| LRSI (Ehlers Laguerre) | Cycle-adaptive Laguerre filter stages | 0–1 | 4 |
## Architecture & Physics
@@ -115,7 +115,7 @@ $$n_t = \operatorname{clamp}\!\left(\left\lfloor \frac{n_{\text{base}}}{V_t} + 0
$$\overline{G}_t = \alpha_t \cdot G_t + (1 - \alpha_t) \cdot \overline{G}_{t-1}$$
-$$\text{DYMOI}_t = 100 \cdot \frac{\overline{G}_t}{\overline{G}_t + \overline{L}_t}$$
+$$\text{DYMI}_t = 100 \cdot \frac{\overline{G}_t}{\overline{G}_t + \overline{L}_t}$$
### Degenerate Cases
@@ -131,7 +131,7 @@ $$\text{DYMOI}_t = 100 \cdot \frac{\overline{G}_t}{\overline{G}_t + \overline{L}
### Operation Count (Streaming Mode)
-DYMOI computes a dynamic momentum oscillator using an EMA-smoothed velocity + acceleration blend.
+DYMI computes a dynamic momentum oscillator using an EMA-smoothed velocity + acceleration blend.
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
@@ -181,7 +181,7 @@ SIMD is not applicable to the streaming `Update` path because the period changes
## Validation
-No external C# library (Skender, TA-Lib, Tulip, Ooples) implements DYMOI. Validation is self-consistency only.
+No external C# library (Skender, TA-Lib, Tulip, Ooples) implements DYMI. Validation is self-consistency only.
| Test | Method | Tolerance | Result |
| :--- | :--- | :---: | :--- |
@@ -193,7 +193,7 @@ No external C# library (Skender, TA-Lib, Tulip, Ooples) implements DYMOI. Valida
| Fixed period identity | minPeriod=maxPeriod=basePeriod | 1e-9 | Pass |
| Determinism | Two identical GBM seeds | 1e-10 | Pass |
-**Mathematical identity test:** When `minPeriod == maxPeriod == basePeriod`, the dynamic period is always fixed at `basePeriod` regardless of the volatility ratio. Under this constraint, DYMOI produces output numerically identical to `Rsi(basePeriod)` (verified at tolerance 1e-9).
+**Mathematical identity test:** When `minPeriod == maxPeriod == basePeriod`, the dynamic period is always fixed at `basePeriod` regardless of the volatility ratio. Under this constraint, DYMI produces output numerically identical to `Rsi(basePeriod)` (verified at tolerance 1e-9).
## Common Pitfalls
@@ -201,9 +201,9 @@ No external C# library (Skender, TA-Lib, Tulip, Ooples) implements DYMOI. Valida
2. **Zero-variance series (flat price)**: When `σ_long = 0`, the ratio is undefined; the implementation defaults to `V = 1` → `n_dyn = n_base`. This is correct — a flat series should produce neutral RSI(=50) at the base period rate, not a degenerate output.
-3. **Warmup period misinterpretation**: `WarmupPeriod = longPeriod + maxPeriod`. The dominant warmup is the Wilder RMA, which takes `maxPeriod` bars to settle after the long StdDev window fills. Using DYMOI output before `IsHot = true` will produce compensated but less accurate values.
+3. **Warmup period misinterpretation**: `WarmupPeriod = longPeriod + maxPeriod`. The dominant warmup is the Wilder RMA, which takes `maxPeriod` bars to settle after the long StdDev window fills. Using DYMI output before `IsHot = true` will produce compensated but less accurate values.
-4. **Period clamp masking pathology**: If `minPeriod` and `maxPeriod` are very close (e.g., both 14), the adaptive behavior is effectively disabled and DYMOI degenerates to standard RSI. This is a valid use case but should be intentional.
+4. **Period clamp masking pathology**: If `minPeriod` and `maxPeriod` are very close (e.g., both 14), the adaptive behavior is effectively disabled and DYMI degenerates to standard RSI. This is a valid use case but should be intentional.
5. **Floating-point drift in running sums**: The O(1) variance formula $E[x^2] - E[x]^2$ is numerically unstable for large values or large windows — specifically, catastrophic cancellation can occur. For price data in the range [0.01, 100000] and periods ≤ 100, drift is negligible in practice. For exotic inputs, a periodic full-recalculation reset (every N steps) would be appropriate; the current implementation does not perform this.
diff --git a/lib/oscillators/dymoi/dymoi.pine b/lib/oscillators/dymi/dymi.pine
similarity index 91%
rename from lib/oscillators/dymoi/dymoi.pine
rename to lib/oscillators/dymi/dymi.pine
index b4b02312..7f45945c 100644
--- a/lib/oscillators/dymoi/dymoi.pine
+++ b/lib/oscillators/dymi/dymi.pine
@@ -1,7 +1,7 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Dynamic Momentum Index (DYMOI)", "DYMOI", overlay=false)
+indicator("Dynamic Momentum Index (DYMI)", "DYMI", overlay=false)
//@description Dynamic Momentum Index by Tushar Chande and Stanley Kroll (1994).
// A three-stage pipeline that produces a volatility-adaptive RSI:
@@ -89,11 +89,11 @@ rsi_wilder(series float source, series int dynPeriod) =>
//@param longPeriod Long StdDev window (default 10)
//@param minPeriod Minimum dynamic period (default 3)
//@param maxPeriod Maximum dynamic period (default 30)
-//@returns DYMOI value in [0, 100]
+//@returns DYMI value in [0, 100]
//@optimized Uses circular buffers for O(1) StdDev; adaptive Wilder RMA for RSI
-dymoi(series float source, simple int basePeriod, simple int shortPeriod, simple int longPeriod, simple int minPeriod, simple int maxPeriod) =>
+dymi(series float source, simple int basePeriod, simple int shortPeriod, simple int longPeriod, simple int minPeriod, simple int maxPeriod) =>
if basePeriod < 2 or shortPeriod < 2 or longPeriod <= shortPeriod or minPeriod < 2 or maxPeriod < minPeriod
- runtime.error("Invalid DYMOI parameters")
+ runtime.error("Invalid DYMI parameters")
// Stage 1: dual StdDev volatility ratio
float sdShort = stddev_circ(source, shortPeriod)
@@ -118,9 +118,9 @@ i_minPeriod = input.int(3, "Min Period", minval=2, maxval=500)
i_maxPeriod = input.int(30, "Max Period", minval=2, maxval=500)
i_source = input.source(close, "Source")
-dymoi_val = dymoi(i_source, i_basePeriod, i_shortPeriod, i_longPeriod, i_minPeriod, i_maxPeriod)
+dymi_val = dymi(i_source, i_basePeriod, i_shortPeriod, i_longPeriod, i_minPeriod, i_maxPeriod)
-plot(dymoi_val, "DYMOI", color=color.yellow, linewidth=2)
+plot(dymi_val, "DYMI", color=color.yellow, linewidth=2)
hline(70, "Overbought", color=color.gray, linestyle=hline.style_dotted)
hline(50, "Midline", color=color.gray, linestyle=hline.style_dotted)
hline(30, "Oversold", color=color.gray, linestyle=hline.style_dotted)
diff --git a/lib/oscillators/dymoi/tests/Dymoi.Quantower.Tests.cs b/lib/oscillators/dymi/tests/Dymi.Quantower.Tests.cs
similarity index 75%
rename from lib/oscillators/dymoi/tests/Dymoi.Quantower.Tests.cs
rename to lib/oscillators/dymi/tests/Dymi.Quantower.Tests.cs
index deef6dc4..51cfbaff 100644
--- a/lib/oscillators/dymoi/tests/Dymoi.Quantower.Tests.cs
+++ b/lib/oscillators/dymi/tests/Dymi.Quantower.Tests.cs
@@ -3,12 +3,12 @@ using QuanTAlib;
namespace QuanTAlib.Tests;
-public sealed class DymoiIndicatorTests
+public sealed class DymiIndicatorTests
{
[Fact]
- public void DymoiIndicator_Constructor_SetsDefaults()
+ public void DymiIndicator_Constructor_SetsDefaults()
{
- var indicator = new DymoiIndicator();
+ var indicator = new DymiIndicator();
Assert.Equal(14, indicator.BasePeriod);
Assert.Equal(5, indicator.ShortPeriod);
@@ -17,25 +17,25 @@ public sealed class DymoiIndicatorTests
Assert.Equal(30, indicator.MaxPeriod);
Assert.Equal(SourceType.Close, indicator.Source);
Assert.True(indicator.ShowColdValues);
- Assert.Equal("DYMOI - Dynamic Momentum Index", indicator.Name);
+ Assert.Equal("DYMI - Dynamic Momentum Index", indicator.Name);
Assert.True(indicator.SeparateWindow);
Assert.True(indicator.OnBackGround);
}
[Fact]
- public void DymoiIndicator_MinHistoryDepths_EqualsZero()
+ public void DymiIndicator_MinHistoryDepths_EqualsZero()
{
- var indicator = new DymoiIndicator();
+ var indicator = new DymiIndicator();
- Assert.Equal(0, DymoiIndicator.MinHistoryDepths);
+ Assert.Equal(0, DymiIndicator.MinHistoryDepths);
IWatchlistIndicator watchlistIndicator = indicator;
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
}
[Fact]
- public void DymoiIndicator_ShortName_IncludesParameters()
+ public void DymiIndicator_ShortName_IncludesParameters()
{
- var indicator = new DymoiIndicator
+ var indicator = new DymiIndicator
{
BasePeriod = 10,
ShortPeriod = 4,
@@ -45,7 +45,7 @@ public sealed class DymoiIndicatorTests
};
indicator.Initialize();
- Assert.Contains("DYMOI", indicator.ShortName, StringComparison.Ordinal);
+ Assert.Contains("DYMI", indicator.ShortName, StringComparison.Ordinal);
Assert.Contains("10", indicator.ShortName, StringComparison.Ordinal);
Assert.Contains("4", indicator.ShortName, StringComparison.Ordinal);
Assert.Contains("8", indicator.ShortName, StringComparison.Ordinal);
@@ -53,18 +53,18 @@ public sealed class DymoiIndicatorTests
}
[Fact]
- public void DymoiIndicator_SourceCodeLink_IsValid()
+ public void DymiIndicator_SourceCodeLink_IsValid()
{
- var indicator = new DymoiIndicator();
+ var indicator = new DymiIndicator();
Assert.Contains("github.com", indicator.SourceCodeLink, StringComparison.Ordinal);
- Assert.Contains("Dymoi.Quantower.cs", indicator.SourceCodeLink, StringComparison.Ordinal);
+ Assert.Contains("Dymi.Quantower.cs", indicator.SourceCodeLink, StringComparison.Ordinal);
}
[Fact]
- public void DymoiIndicator_Initialize_CreatesLineSeries()
+ public void DymiIndicator_Initialize_CreatesLineSeries()
{
- var indicator = new DymoiIndicator
+ var indicator = new DymiIndicator
{
BasePeriod = 14,
ShortPeriod = 5,
@@ -79,9 +79,9 @@ public sealed class DymoiIndicatorTests
}
[Fact]
- public void DymoiIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
+ public void DymiIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
{
- var indicator = new DymoiIndicator
+ var indicator = new DymiIndicator
{
BasePeriod = 14,
ShortPeriod = 5,
@@ -103,13 +103,13 @@ public sealed class DymoiIndicatorTests
double value = indicator.LinesSeries[0].GetValue(0);
Assert.True(double.IsFinite(value));
- Assert.True(value >= 0.0 && value <= 100.0, $"DYMOI={value} out of [0,100]");
+ Assert.True(value >= 0.0 && value <= 100.0, $"DYMI={value} out of [0,100]");
}
[Fact]
- public void DymoiIndicator_ProcessUpdate_NewBar_ComputesValue()
+ public void DymiIndicator_ProcessUpdate_NewBar_ComputesValue()
{
- var indicator = new DymoiIndicator
+ var indicator = new DymiIndicator
{
BasePeriod = 14,
ShortPeriod = 5,
@@ -135,11 +135,11 @@ public sealed class DymoiIndicatorTests
}
[Fact]
- public void DymoiIndicator_DifferentSourceTypes_ComputeWithoutError()
+ public void DymiIndicator_DifferentSourceTypes_ComputeWithoutError()
{
foreach (var sourceType in new[] { SourceType.Close, SourceType.Open, SourceType.High, SourceType.Low })
{
- var indicator = new DymoiIndicator
+ var indicator = new DymiIndicator
{
BasePeriod = 14,
ShortPeriod = 5,
diff --git a/lib/oscillators/dymoi/tests/Dymoi.Tests.cs b/lib/oscillators/dymi/tests/Dymi.Tests.cs
similarity index 79%
rename from lib/oscillators/dymoi/tests/Dymoi.Tests.cs
rename to lib/oscillators/dymi/tests/Dymi.Tests.cs
index 6dc4c4c4..f91114a5 100644
--- a/lib/oscillators/dymoi/tests/Dymoi.Tests.cs
+++ b/lib/oscillators/dymi/tests/Dymi.Tests.cs
@@ -2,7 +2,7 @@ using Xunit;
namespace QuanTAlib.Tests;
-public sealed class DymoiTests
+public sealed class DymiTests
{
private const double Tolerance = 1e-10;
@@ -11,62 +11,62 @@ public sealed class DymoiTests
[Fact]
public void Constructor_BasePeriodOne_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => new Dymoi(basePeriod: 1));
+ var ex = Assert.Throws(() => new Dymi(basePeriod: 1));
Assert.Equal("basePeriod", ex.ParamName);
}
[Fact]
public void Constructor_ShortPeriodOne_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => new Dymoi(shortPeriod: 1));
+ var ex = Assert.Throws(() => new Dymi(shortPeriod: 1));
Assert.Equal("shortPeriod", ex.ParamName);
}
[Fact]
public void Constructor_LongPeriodEqualShortPeriod_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => new Dymoi(shortPeriod: 5, longPeriod: 5));
+ var ex = Assert.Throws(() => new Dymi(shortPeriod: 5, longPeriod: 5));
Assert.Equal("longPeriod", ex.ParamName);
}
[Fact]
public void Constructor_LongPeriodLessThanShortPeriod_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => new Dymoi(shortPeriod: 10, longPeriod: 5));
+ var ex = Assert.Throws(() => new Dymi(shortPeriod: 10, longPeriod: 5));
Assert.Equal("longPeriod", ex.ParamName);
}
[Fact]
public void Constructor_MinPeriodOne_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => new Dymoi(minPeriod: 1));
+ var ex = Assert.Throws(() => new Dymi(minPeriod: 1));
Assert.Equal("minPeriod", ex.ParamName);
}
[Fact]
public void Constructor_MaxPeriodLessThanMinPeriod_ThrowsArgumentException()
{
- var ex = Assert.Throws(() => new Dymoi(minPeriod: 10, maxPeriod: 5));
+ var ex = Assert.Throws(() => new Dymi(minPeriod: 10, maxPeriod: 5));
Assert.Equal("maxPeriod", ex.ParamName);
}
[Fact]
public void Constructor_ValidDefaults_SetsProperties()
{
- var d = new Dymoi();
+ var d = new Dymi();
Assert.Equal(14, d.BasePeriod);
Assert.Equal(5, d.ShortPeriod);
Assert.Equal(10, d.LongPeriod);
Assert.Equal(3, d.MinPeriod);
Assert.Equal(30, d.MaxPeriod);
- Assert.Equal("Dymoi(14,5,10,3,30)", d.Name);
+ Assert.Equal("Dymi(14,5,10,3,30)", d.Name);
Assert.False(d.IsHot);
}
[Fact]
public void Constructor_CustomPeriods_SetsProperties()
{
- var d = new Dymoi(basePeriod: 10, shortPeriod: 3, longPeriod: 7, minPeriod: 2, maxPeriod: 20);
+ var d = new Dymi(basePeriod: 10, shortPeriod: 3, longPeriod: 7, minPeriod: 2, maxPeriod: 20);
Assert.Equal(10, d.BasePeriod);
Assert.Equal(3, d.ShortPeriod);
Assert.Equal(7, d.LongPeriod);
@@ -79,7 +79,7 @@ public sealed class DymoiTests
{
var src = new double[] { 1, 2, 3 };
var out1 = new double[4];
- var ex = Assert.Throws(() => Dymoi.Batch(src, out1));
+ var ex = Assert.Throws(() => Dymi.Batch(src, out1));
Assert.Equal("output", ex.ParamName);
}
@@ -88,7 +88,7 @@ public sealed class DymoiTests
{
var src = new double[] { 1, 2, 3 };
var out1 = new double[3];
- var ex = Assert.Throws(() => Dymoi.Batch(src, out1, basePeriod: 1));
+ var ex = Assert.Throws(() => Dymi.Batch(src, out1, basePeriod: 1));
Assert.Equal("basePeriod", ex.ParamName);
}
@@ -97,7 +97,7 @@ public sealed class DymoiTests
{
var src = new double[] { 1, 2, 3 };
var out1 = new double[3];
- var ex = Assert.Throws(() => Dymoi.Batch(src, out1, shortPeriod: 5, longPeriod: 5));
+ var ex = Assert.Throws(() => Dymi.Batch(src, out1, shortPeriod: 5, longPeriod: 5));
Assert.Equal("longPeriod", ex.ParamName);
}
@@ -106,7 +106,7 @@ public sealed class DymoiTests
{
var src = new double[] { 1, 2, 3 };
var out1 = new double[3];
- var ex = Assert.Throws(() => Dymoi.Batch(src, out1, minPeriod: 5, maxPeriod: 3));
+ var ex = Assert.Throws(() => Dymi.Batch(src, out1, minPeriod: 5, maxPeriod: 3));
Assert.Equal("maxPeriod", ex.ParamName);
}
@@ -115,7 +115,7 @@ public sealed class DymoiTests
[Fact]
public void Update_ReturnsTValue()
{
- var d = new Dymoi();
+ var d = new Dymi();
var result = d.Update(new TValue(DateTime.UtcNow, 100.0));
Assert.IsType(result);
}
@@ -123,29 +123,29 @@ public sealed class DymoiTests
[Fact]
public void Update_OutputInRange0To100()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.3, seed: 42);
var bars = gbm.Fetch(300, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
foreach (var bar in bars.Close)
{
double v = d.Update(bar).Value;
- Assert.True(v >= 0.0 && v <= 100.0, $"DYMOI={v} out of [0,100]");
+ Assert.True(v >= 0.0 && v <= 100.0, $"DYMI={v} out of [0,100]");
}
}
[Fact]
public void Update_NameIsAccessible()
{
- var d = new Dymoi(14, 5, 10, 3, 30);
+ var d = new Dymi(14, 5, 10, 3, 30);
_ = d.Update(new TValue(DateTime.UtcNow, 100.0));
- Assert.Equal("Dymoi(14,5,10,3,30)", d.Name);
+ Assert.Equal("Dymi(14,5,10,3,30)", d.Name);
}
[Fact]
public void Update_LastIsAccessible()
{
- var d = new Dymoi();
+ var d = new Dymi();
var t = new TValue(DateTime.UtcNow, 100.0);
var result = d.Update(t);
Assert.Equal(result, d.Last);
@@ -156,7 +156,7 @@ public sealed class DymoiTests
[Fact]
public void Update_IsNewTrue_AdvancesState()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var t = DateTime.UtcNow;
d.Update(new TValue(t, 100.0), isNew: true);
var v1 = d.Last;
@@ -169,7 +169,7 @@ public sealed class DymoiTests
[Fact]
public void Update_IsNewFalse_RollsBack()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
double[] prices = [100, 102, 104, 103, 105, 107, 106, 108, 110, 109, 111, 113];
var t = DateTime.UtcNow;
for (int i = 0; i < prices.Length; i++)
@@ -191,7 +191,7 @@ public sealed class DymoiTests
[Fact]
public void Update_IterativeCorrections_Restore()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
double[] prices = [100, 102, 98, 105, 103, 107, 101, 108, 100, 109, 102, 110];
var t = DateTime.UtcNow;
for (int i = 0; i < prices.Length; i++)
@@ -214,7 +214,7 @@ public sealed class DymoiTests
[Fact]
public void Update_Reset_ClearsState()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var gbm = new GBM(startPrice: 100.0, mu: 0.01, sigma: 0.2, seed: 7);
var bars = gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
@@ -234,7 +234,7 @@ public sealed class DymoiTests
public void IsHot_FlipsAfterWarmup()
{
// Use small periods to make warmup manageable
- var d = new Dymoi(basePeriod: 5, shortPeriod: 3, longPeriod: 5, minPeriod: 2, maxPeriod: 10);
+ var d = new Dymi(basePeriod: 5, shortPeriod: 3, longPeriod: 5, minPeriod: 2, maxPeriod: 10);
var gbm = new GBM(startPrice: 100.0, mu: 0.01, sigma: 0.3, seed: 11);
var bars = gbm.Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
@@ -249,13 +249,13 @@ public sealed class DymoiTests
}
}
- Assert.True(everHot, "DYMOI should become hot within 200 bars");
+ Assert.True(everHot, "DYMI should become hot within 200 bars");
}
[Fact]
public void WarmupPeriod_IsLongPeriodPlusMaxPeriod()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
Assert.Equal(40, d.WarmupPeriod); // longPeriod(10) + maxPeriod(30)
}
@@ -264,7 +264,7 @@ public sealed class DymoiTests
[Fact]
public void Update_NaN_UsesLastValid()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var t = DateTime.UtcNow;
// Feed valid values first
@@ -281,7 +281,7 @@ public sealed class DymoiTests
[Fact]
public void Update_Infinity_UsesLastValid()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var t = DateTime.UtcNow;
for (int i = 0; i < 20; i++)
@@ -296,7 +296,7 @@ public sealed class DymoiTests
[Fact]
public void Update_BatchNaN_AllFinite()
{
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var t = DateTime.UtcNow;
// Mix NaN into sequence
@@ -319,7 +319,7 @@ public sealed class DymoiTests
TSeries source = bars.Close;
// Streaming
- var streaming = new Dymoi(14, 5, 10, 3, 30);
+ var streaming = new Dymi(14, 5, 10, 3, 30);
var streamVals = new double[source.Count];
for (int i = 0; i < source.Count; i++)
{
@@ -327,7 +327,7 @@ public sealed class DymoiTests
}
// Batch TSeries
- TSeries batchTs = Dymoi.Batch(source, 14, 5, 10, 3, 30);
+ TSeries batchTs = Dymi.Batch(source, 14, 5, 10, 3, 30);
for (int i = 0; i < source.Count; i++)
{
@@ -342,10 +342,10 @@ public sealed class DymoiTests
var bars = gbm.Fetch(300, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
TSeries source = bars.Close;
- TSeries batchTs = Dymoi.Batch(source, 14, 5, 10, 3, 30);
+ TSeries batchTs = Dymi.Batch(source, 14, 5, 10, 3, 30);
var spanOut = new double[source.Count];
- Dymoi.Batch(source.Values, spanOut, 14, 5, 10, 3, 30);
+ Dymi.Batch(source.Values, spanOut, 14, 5, 10, 3, 30);
for (int i = 0; i < source.Count; i++)
{
@@ -361,7 +361,7 @@ public sealed class DymoiTests
TSeries source = bars.Close;
// Streaming
- var streaming = new Dymoi(14, 5, 10, 3, 30);
+ var streaming = new Dymi(14, 5, 10, 3, 30);
var streamVals = new double[source.Count];
for (int i = 0; i < source.Count; i++)
{
@@ -370,12 +370,12 @@ public sealed class DymoiTests
// Event-based
var eventTs = new TSeries();
- var eventDymoi = new Dymoi(eventTs, 14, 5, 10, 3, 30);
+ var eventDymi = new Dymi(eventTs, 14, 5, 10, 3, 30);
var eventVals = new double[source.Count];
for (int i = 0; i < source.Count; i++)
{
eventTs.Add(source[i]);
- eventVals[i] = eventDymoi.Last.Value;
+ eventVals[i] = eventDymi.Last.Value;
}
for (int i = 0; i < source.Count; i++)
@@ -391,7 +391,7 @@ public sealed class DymoiTests
{
var src = Array.Empty();
var out1 = Array.Empty();
- Dymoi.Batch(src, out1);
+ Dymi.Batch(src, out1);
Assert.Empty(out1);
}
@@ -405,7 +405,7 @@ public sealed class DymoiTests
var out1 = new double[n];
// Should not throw StackOverflowException — uses ArrayPool for large buffers
- Dymoi.Batch(src, out1);
+ Dymi.Batch(src, out1);
bool anyFinite = false;
for (int i = 0; i < n; i++)
@@ -428,7 +428,7 @@ public sealed class DymoiTests
var src = bars.Close.Values;
var out1 = new double[src.Length];
- Dymoi.Batch(src, out1);
+ Dymi.Batch(src, out1);
for (int i = 0; i < src.Length; i++)
{
@@ -442,7 +442,7 @@ public sealed class DymoiTests
public void Chainability_PubFires()
{
var source = new TSeries();
- var d = new Dymoi(source, 14, 5, 10, 3, 30);
+ var d = new Dymi(source, 14, 5, 10, 3, 30);
int count = 0;
d.Pub += (object? _, in TValueEventArgs e) => count++;
@@ -460,7 +460,7 @@ public sealed class DymoiTests
public void Chainability_EventBasedChaining_Works()
{
var source = new TSeries();
- var d = new Dymoi(source, 14, 5, 10, 3, 30);
+ var d = new Dymi(source, 14, 5, 10, 3, 30);
var output = new TSeries();
d.Pub += (object? _, in TValueEventArgs e) => output.Add(e.Value);
diff --git a/lib/oscillators/dymoi/tests/Dymoi.Validation.Tests.cs b/lib/oscillators/dymi/tests/Dymi.Validation.Tests.cs
similarity index 82%
rename from lib/oscillators/dymoi/tests/Dymoi.Validation.Tests.cs
rename to lib/oscillators/dymi/tests/Dymi.Validation.Tests.cs
index 3b8eec85..d4ac9d66 100644
--- a/lib/oscillators/dymoi/tests/Dymoi.Validation.Tests.cs
+++ b/lib/oscillators/dymi/tests/Dymi.Validation.Tests.cs
@@ -6,14 +6,14 @@ using OoplesFinance.StockIndicators.Models;
namespace QuanTAlib.Tests;
///
-/// Self-consistency validation for DYMOI.
-/// No external library implements DYMOI in C# bindings, so validation uses:
+/// Self-consistency validation for DYMI.
+/// No external library implements DYMI in C# bindings, so validation uses:
/// 1. Mathematical identity: when shortPeriod == longPeriod → V ≈ 1 → dynPeriod ≈ basePeriod → matches standard RSI(basePeriod)
/// 2. Batch == streaming == span == eventing consistency
/// 3. Output always in [0, 100]
/// 4. Period adapts: shorter in high-vol, longer in low-vol
///
-public sealed class DymoiValidationTests
+public sealed class DymiValidationTests
{
private const double Tolerance = 1e-10;
@@ -27,7 +27,7 @@ public sealed class DymoiValidationTests
TSeries source = bars.Close;
// Streaming
- var streaming = new Dymoi(14, 5, 10, 3, 30);
+ var streaming = new Dymi(14, 5, 10, 3, 30);
var streamVals = new double[source.Count];
for (int i = 0; i < source.Count; i++)
{
@@ -35,7 +35,7 @@ public sealed class DymoiValidationTests
}
// Batch TSeries
- TSeries batchTs = Dymoi.Batch(source, 14, 5, 10, 3, 30);
+ TSeries batchTs = Dymi.Batch(source, 14, 5, 10, 3, 30);
for (int i = 0; i < source.Count; i++)
{
@@ -51,11 +51,11 @@ public sealed class DymoiValidationTests
TSeries source = bars.Close;
// Batch TSeries
- TSeries batchTs = Dymoi.Batch(source, 14, 5, 10, 3, 30);
+ TSeries batchTs = Dymi.Batch(source, 14, 5, 10, 3, 30);
// Span batch
var spanOut = new double[source.Count];
- Dymoi.Batch(source.Values, spanOut, 14, 5, 10, 3, 30);
+ Dymi.Batch(source.Values, spanOut, 14, 5, 10, 3, 30);
for (int i = 0; i < source.Count; i++)
{
@@ -71,7 +71,7 @@ public sealed class DymoiValidationTests
TSeries source = bars.Close;
// Streaming
- var streaming = new Dymoi(14, 5, 10, 3, 30);
+ var streaming = new Dymi(14, 5, 10, 3, 30);
var streamVals = new double[source.Count];
for (int i = 0; i < source.Count; i++)
{
@@ -80,12 +80,12 @@ public sealed class DymoiValidationTests
// Event-based
var eventTs = new TSeries();
- var eventDymoi = new Dymoi(eventTs, 14, 5, 10, 3, 30);
+ var eventDymi = new Dymi(eventTs, 14, 5, 10, 3, 30);
var eventVals = new double[source.Count];
for (int i = 0; i < source.Count; i++)
{
eventTs.Add(source[i]);
- eventVals[i] = eventDymoi.Last.Value;
+ eventVals[i] = eventDymi.Last.Value;
}
for (int i = 0; i < source.Count; i++)
@@ -102,11 +102,11 @@ public sealed class DymoiValidationTests
var gbm = new GBM(startPrice: 50.0, mu: 0.05, sigma: 0.8, seed: 3004);
var bars = gbm.Fetch(500, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
- var d = new Dymoi(14, 5, 10, 3, 30);
+ var d = new Dymi(14, 5, 10, 3, 30);
foreach (var bar in bars.Close)
{
double v = d.Update(bar).Value;
- Assert.True(v >= 0.0 && v <= 100.0, $"DYMOI={v} at high vol");
+ Assert.True(v >= 0.0 && v <= 100.0, $"DYMI={v} at high vol");
}
}
@@ -117,11 +117,11 @@ public sealed class DymoiValidationTests
var gbm = new GBM(startPrice: 100.0, mu: 0.001, sigma: 0.01, seed: 3005);
var bars = gbm.Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
- var d = new Dymoi(14, 5, 10, 3, 30);
+ var d = new Dymi(14, 5, 10, 3, 30);
foreach (var bar in bars.Close)
{
double v = d.Update(bar).Value;
- Assert.True(v >= 0.0 && v <= 100.0, $"DYMOI={v} at low vol");
+ Assert.True(v >= 0.0 && v <= 100.0, $"DYMI={v} at low vol");
}
}
@@ -139,7 +139,7 @@ public sealed class DymoiValidationTests
// For the true identity test: construct a series with constant differences
// such that a window of any size yields the same stddev.
// A simpler verification: at V=1, dynPeriod = round(basePeriod/1) = basePeriod.
- // We verify that DYMOI output matches Rsi(basePeriod) on a constant-drift series.
+ // We verify that DYMI output matches Rsi(basePeriod) on a constant-drift series.
// Construct a series with perfectly constant increments → stddev of close levels
// is the same in short and long windows only if windows cover the same prices,
@@ -147,19 +147,19 @@ public sealed class DymoiValidationTests
// close periods and checking that output is nearly identical to standard RSI.
// Using longPeriod just 1 more than shortPeriod and monitoring range
- var d = new Dymoi(basePeriod: 14, shortPeriod: 9, longPeriod: 10, minPeriod: 14, maxPeriod: 14);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 9, longPeriod: 10, minPeriod: 14, maxPeriod: 14);
var rsi = new Rsi(14);
var gbm = new GBM(startPrice: 100.0, mu: 0.01, sigma: 0.15, seed: 3006);
var bars = gbm.Fetch(500, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
// When minPeriod == maxPeriod == basePeriod, dynPeriod is always fixed at basePeriod
- // → DYMOI is identical to standard RSI(basePeriod)
+ // → DYMI is identical to standard RSI(basePeriod)
foreach (var bar in bars.Close)
{
- double dymoiVal = d.Update(bar).Value;
+ double dymiVal = d.Update(bar).Value;
double rsiVal = rsi.Update(bar).Value;
// With fixed dynPeriod=14, both should match
- Assert.Equal(rsiVal, dymoiVal, 1e-9);
+ Assert.Equal(rsiVal, dymiVal, 1e-9);
}
}
@@ -170,8 +170,8 @@ public sealed class DymoiValidationTests
{
// When short-term vol > long-term vol (V > 1), dynPeriod < basePeriod.
// We test this indirectly: high-vol data should produce faster RSI transitions.
- // In high-vol regime, DYMOI changes more rapidly than fixed-period RSI.
- var d = new Dymoi(basePeriod: 14, shortPeriod: 3, longPeriod: 20, minPeriod: 3, maxPeriod: 30);
+ // In high-vol regime, DYMI changes more rapidly than fixed-period RSI.
+ var d = new Dymi(basePeriod: 14, shortPeriod: 3, longPeriod: 20, minPeriod: 3, maxPeriod: 30);
var gbm = new GBM(startPrice: 100.0, mu: 0.0, sigma: 0.4, seed: 3007);
var bars = gbm.Fetch(200, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
@@ -191,8 +191,8 @@ public sealed class DymoiValidationTests
var bars1 = gbm1.Fetch(150, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
var bars2 = gbm2.Fetch(150, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
- var d1 = new Dymoi(14, 5, 10, 3, 30);
- var d2 = new Dymoi(14, 5, 10, 3, 30);
+ var d1 = new Dymi(14, 5, 10, 3, 30);
+ var d2 = new Dymi(14, 5, 10, 3, 30);
for (int i = 0; i < bars1.Close.Count; i++)
{
@@ -207,7 +207,7 @@ public sealed class DymoiValidationTests
{
var src = Array.Empty();
var out1 = Array.Empty();
- Dymoi.Batch(src, out1);
+ Dymi.Batch(src, out1);
Assert.Empty(out1);
}
@@ -215,7 +215,7 @@ public sealed class DymoiValidationTests
public void Streaming_ConstantPrice_ProducesStable50()
{
// When price is constant, gain=0, loss=0 → RSI = 50
- var d = new Dymoi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
+ var d = new Dymi(basePeriod: 14, shortPeriod: 5, longPeriod: 10, minPeriod: 3, maxPeriod: 30);
var t = DateTime.UtcNow;
double last = 50.0;
for (int i = 0; i < 100; i++)
@@ -228,7 +228,7 @@ public sealed class DymoiValidationTests
}
[Fact]
- public void Dymoi_MatchesOoples_Structural()
+ public void Dymi_MatchesOoples_Structural()
{
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.15, seed: 42);
var bars = gbm.Fetch(500, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
diff --git a/lib/reversals/_index.md b/lib/reversals/_index.md
index cd4056c3..01305bd6 100644
--- a/lib/reversals/_index.md
+++ b/lib/reversals/_index.md
@@ -13,7 +13,7 @@ Reversal indicators identify potential turning points where price may change dir
| [PIVOTEXT](pivotext/Pivotext.md) | Extended Traditional Pivots | Extended pivots with 11 levels (R1-R5, S1-S5) for volatile markets. |
| [PIVOTFIB](pivotfib/Pivotfib.md) | Fibonacci Pivot Points | Fibonacci-ratio based pivots; Golden Ratio (61.8%) at R2/S2. |
| [PIVOTWOOD](pivotwood/Pivotwood.md) | Woodie's Pivot Points | Weighted close pivots (2× close weight) for intraday trading. |
-| [PSAR](psar/Psar.md) | Parabolic Stop And Reverse | Trailing stop that accelerates with trend; SAR dots mark entry/exit signals. |
+| [SAR](sar/Sar.md) | Parabolic Stop And Reverse | Trailing stop that accelerates with trend; SAR dots mark entry/exit signals. |
| [SAREXT](sarext/Sarext.md) | Parabolic SAR Extended | PSAR with asymmetric long/short acceleration factors. Sign-encoded output. |
| [SWINGS](swings/Swings.md) | Swing High/Low Detection | Configurable-lookback pattern detector for swing highs/lows; dual SwingHigh/SwingLow. |
| [TTM_SCALPER](ttm_scalper/TtmScalper.md) | TTM Scalper Alert | 3-bar pivot high/low detection for scalping entries. John Carter. |
diff --git a/lib/reversals/chandelier/Chandelier.md b/lib/reversals/chandelier/Chandelier.md
index ed0b705d..bcd4fd6f 100644
--- a/lib/reversals/chandelier/Chandelier.md
+++ b/lib/reversals/chandelier/Chandelier.md
@@ -13,7 +13,7 @@
| **PineScript** | [chandelier.pine](chandelier.pine) |
- The Chandelier Exit computes ATR-based trailing stop levels that hang from the highest high (for longs) or rise from the lowest low (for shorts) ov...
-- **Similar:** [PSAR](../psar/Psar.md), [Super](../../dynamics/super/Super.md) | **Complementary:** ADX for trend strength | **Trading note:** Chandelier Exit; ATR-based trailing stop from highest high. Risk management tool.
+- **Similar:** [SAR](../sar/Sar.md), [Super](../../dynamics/super/Super.md) | **Complementary:** ADX for trend strength | **Trading note:** Chandelier Exit; ATR-based trailing stop from highest high. Risk management tool.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Chandelier Exit computes ATR-based trailing stop levels that hang from the highest high (for longs) or rise from the lowest low (for shorts) over a lookback period. It produces two overlay lines: ExitLong (trailing stop for long positions) and ExitShort (trailing stop for short positions). Developed by Charles Le Beau and popularized by Alexander Elder. Default parameters: period 22, multiplier 3.0.
diff --git a/lib/reversals/ckstop/Ckstop.md b/lib/reversals/ckstop/Ckstop.md
index e0486a48..0431f366 100644
--- a/lib/reversals/ckstop/Ckstop.md
+++ b/lib/reversals/ckstop/Ckstop.md
@@ -13,7 +13,7 @@
| **PineScript** | [ckstop.pine](ckstop.pine) |
- The Chande Kroll Stop computes adaptive trailing stop levels using ATR-smoothed volatility envelopes around rolling extremes.
-- **Similar:** [Chandelier](../chandelier/Chandelier.md), [PSAR](../psar/Psar.md) | **Complementary:** ATR | **Trading note:** Chuck LeBeau's Chandelier stop; ATR trailing stop with configurable multiplier.
+- **Similar:** [Chandelier](../chandelier/Chandelier.md), [SAR](../sar/Sar.md) | **Complementary:** ATR | **Trading note:** Chuck LeBeau's Chandelier stop; ATR trailing stop with configurable multiplier.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Chande Kroll Stop computes adaptive trailing stop levels using ATR-smoothed volatility envelopes around rolling extremes. It produces two lines: StopLong (support) and StopShort (resistance). When price trades above both stops, the trend is bullish. When below both, bearish. Crossovers between the two stops signal potential reversals.
diff --git a/lib/reversals/psar/Psar.Quantower.cs b/lib/reversals/sar/Sar.Quantower.cs
similarity index 76%
rename from lib/reversals/psar/Psar.Quantower.cs
rename to lib/reversals/sar/Sar.Quantower.cs
index 648cf97d..4d9d6e45 100644
--- a/lib/reversals/psar/Psar.Quantower.cs
+++ b/lib/reversals/sar/Sar.Quantower.cs
@@ -5,7 +5,7 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
[SkipLocalsInit]
-public sealed class PsarIndicator : Indicator, IWatchlistIndicator
+public sealed class SarIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Start AF", sortIndex: 0, 0.001, 1.0, 0.001, 3)]
public double AfStart { get; set; } = 0.02;
@@ -19,20 +19,20 @@ public sealed class PsarIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
- private Psar _indicator = null!;
+ private Sar _indicator = null!;
private readonly LineSeries _sarSeries;
public static int MinHistoryDepths => 0;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
- public override string ShortName => $"PSAR({AfStart:F2},{AfIncrement:F2},{AfMax:F2})";
- public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/reversals/psar/Psar.cs";
+ public override string ShortName => $"SAR({AfStart:F2},{AfIncrement:F2},{AfMax:F2})";
+ public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/reversals/sar/Sar.cs";
- public PsarIndicator()
+ public SarIndicator()
{
OnBackGround = true;
SeparateWindow = false;
- Name = "PSAR - Parabolic Stop And Reverse";
+ Name = "SAR - Parabolic Stop And Reverse";
Description = "Trend-following trailing stop indicator. SAR accelerates toward price as trend progresses, flipping on reversal.";
_sarSeries = new LineSeries(name: "SAR", color: Color.DodgerBlue, width: 2, style: LineStyle.Dot);
@@ -43,7 +43,7 @@ public sealed class PsarIndicator : Indicator, IWatchlistIndicator
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void OnInit()
{
- _indicator = new Psar(AfStart, AfIncrement, AfMax);
+ _indicator = new Sar(AfStart, AfIncrement, AfMax);
base.OnInit();
}
@@ -52,6 +52,6 @@ public sealed class PsarIndicator : Indicator, IWatchlistIndicator
{
_ = _indicator.Update(this.GetInputBar(args), args.IsNewBar());
- _sarSeries.SetValue(_indicator.Sar, _indicator.IsHot, ShowColdValues);
+ _sarSeries.SetValue(_indicator.SarValue, _indicator.IsHot, ShowColdValues);
}
}
diff --git a/lib/reversals/psar/Psar.cs b/lib/reversals/sar/Sar.cs
similarity index 93%
rename from lib/reversals/psar/Psar.cs
rename to lib/reversals/sar/Sar.cs
index 235cf38b..65b0a9ce 100644
--- a/lib/reversals/psar/Psar.cs
+++ b/lib/reversals/sar/Sar.cs
@@ -1,4 +1,4 @@
-// PSAR: Parabolic Stop And Reverse (Wilder, 1978)
+// SAR: Parabolic Stop And Reverse (Wilder, 1978)
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
///
-/// PSAR: Parabolic Stop And Reverse
+/// SAR: Parabolic Stop And Reverse
///
///
/// Trend-following overlay indicator developed by J. Welles Wilder Jr. (1978).
@@ -30,9 +30,9 @@ namespace QuanTAlib;
/// - SAR clamped to prior 2 bars' extremes to prevent crossover artifacts
/// - Default parameters: afStart=0.02, afIncrement=0.02, afMax=0.20 (Wilder's originals)
///
-/// Detailed documentation
+/// Detailed documentation
[SkipLocalsInit]
-public sealed class Psar : ITValuePublisher
+public sealed class Sar : ITValuePublisher
{
private const double DefaultAfStart = 0.02;
private const double DefaultAfIncrement = 0.02;
@@ -80,9 +80,9 @@ public sealed class Psar : ITValuePublisher
public int WarmupPeriod { get; }
/// Current SAR value (the stop level).
- public double Sar { get; private set; }
+ public double SarValue { get; private set; }
- /// True when the PSAR is in long (uptrend) mode.
+ /// True when the SAR is in long (uptrend) mode.
public bool IsLong => _s.IsLong;
/// Primary output value (SAR as TValue for overlay plotting).
@@ -99,7 +99,7 @@ public sealed class Psar : ITValuePublisher
/// Initial acceleration factor (default 0.02).
/// AF increment per new extreme (default 0.02).
/// Maximum acceleration factor (default 0.20).
- public Psar(double afStart = DefaultAfStart, double afIncrement = DefaultAfIncrement, double afMax = DefaultAfMax)
+ public Sar(double afStart = DefaultAfStart, double afIncrement = DefaultAfIncrement, double afMax = DefaultAfMax)
{
if (afStart <= 0)
{
@@ -138,7 +138,7 @@ public sealed class Psar : ITValuePublisher
LastValidClose: double.NaN);
_ps = _s;
- Name = $"Psar({afStart:F2},{afIncrement:F2},{afMax:F2})";
+ Name = $"Sar({afStart:F2},{afIncrement:F2},{afMax:F2})";
WarmupPeriod = 1;
_barHandler = HandleBar;
}
@@ -146,7 +146,7 @@ public sealed class Psar : ITValuePublisher
///
/// Creates a Parabolic SAR chained to a TBarSeries source.
///
- public Psar(TBarSeries source, double afStart = DefaultAfStart, double afIncrement = DefaultAfIncrement, double afMax = DefaultAfMax)
+ public Sar(TBarSeries source, double afStart = DefaultAfStart, double afIncrement = DefaultAfIncrement, double afMax = DefaultAfMax)
: this(afStart, afIncrement, afMax)
{
Prime(source);
@@ -297,7 +297,7 @@ public sealed class Psar : ITValuePublisher
}
}
- Sar = sarResult;
+ SarValue = sarResult;
_s = s;
Last = new TValue(input.Time, sarResult);
@@ -389,7 +389,7 @@ public sealed class Psar : ITValuePublisher
LastValidLow: double.NaN,
LastValidClose: double.NaN);
_ps = _s;
- Sar = double.NaN;
+ SarValue = double.NaN;
Last = default;
}
@@ -432,7 +432,7 @@ public sealed class Psar : ITValuePublisher
}
// Compute via streaming instance for correctness (state machine prevents SIMD)
- var indicator = new Psar(afStart, afIncrement, afMax);
+ var indicator = new Sar(afStart, afIncrement, afMax);
long baseTime = DateTime.UtcNow.Ticks;
for (int i = 0; i < len; i++)
@@ -440,7 +440,7 @@ public sealed class Psar : ITValuePublisher
_ = indicator.Update(
new TBar(baseTime + i, open[i], high[i], low[i], close[i], 0),
isNew: true);
- output[i] = indicator.Sar;
+ output[i] = indicator.SarValue;
}
}
@@ -467,10 +467,10 @@ public sealed class Psar : ITValuePublisher
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static (TSeries Results, Psar Indicator) Calculate(
+ public static (TSeries Results, Sar Indicator) Calculate(
TBarSeries source, double afStart = DefaultAfStart, double afIncrement = DefaultAfIncrement, double afMax = DefaultAfMax)
{
- var indicator = new Psar(afStart, afIncrement, afMax);
+ var indicator = new Sar(afStart, afIncrement, afMax);
var results = indicator.Update(source);
return (results, indicator);
}
diff --git a/lib/reversals/psar/Psar.md b/lib/reversals/sar/sar.md
similarity index 77%
rename from lib/reversals/psar/Psar.md
rename to lib/reversals/sar/sar.md
index 6c66540d..21af0703 100644
--- a/lib/reversals/psar/Psar.md
+++ b/lib/reversals/sar/sar.md
@@ -1,4 +1,4 @@
-# PSAR: Parabolic Stop And Reverse
+# SAR: Parabolic Stop And Reverse
> *The trend is your friend until the end when it bends.*
@@ -7,28 +7,28 @@
| **Category** | Reversal |
| **Inputs** | OHLCV bar (TBar) |
| **Parameters** | `afStart` (default 0.02), `afIncrement` (default 0.02), `afMax` (default 0.20) |
-| **Outputs** | Single series (Psar) |
+| **Outputs** | Single series (Sar) |
| **Output range** | Varies (see docs) |
| **Warmup** | `1` bars |
-| **PineScript** | [psar.pine](psar.pine) |
+| **PineScript** | [sar.pine](sar.pine) |
-- The Parabolic Stop And Reverse (PSAR) is a trend-following overlay indicator created by J.
+- The Parabolic Stop And Reverse (SAR) is a trend-following overlay indicator created by J.
- **Similar:** [Super](../../dynamics/super/Super.md), [Chandelier](../chandelier/Chandelier.md) | **Complementary:** ADX for trend confirmation | **Trading note:** Wilder's Parabolic SAR; trailing stop that accelerates. Dots flip on reversal. Classic trend-following exit.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
## Introduction
-The Parabolic Stop And Reverse (PSAR) is a trend-following overlay indicator created by J. Welles Wilder Jr. in 1978. It produces a trailing stop level that accelerates toward price as the trend extends, then flips to the opposite side when price crosses the stop. The acceleration mechanism is the key differentiator: SAR starts slow and tightens progressively, creating the characteristic parabolic curve that gives the indicator its name. Default parameters (0.02 start, 0.02 increment, 0.20 maximum) produce approximately 10–30 reversals per 500 bars on typical equity data.
+The Parabolic Stop And Reverse (SAR) is a trend-following overlay indicator created by J. Welles Wilder Jr. in 1978. It produces a trailing stop level that accelerates toward price as the trend extends, then flips to the opposite side when price crosses the stop. The acceleration mechanism is the key differentiator: SAR starts slow and tightens progressively, creating the characteristic parabolic curve that gives the indicator its name. Default parameters (0.02 start, 0.02 increment, 0.20 maximum) produce approximately 10–30 reversals per 500 bars on typical equity data.
## Historical Context
-Wilder introduced PSAR alongside RSI, ATR, and ADX in *New Concepts in Technical Trading Systems* (1978). Unlike fixed-percentage trailing stops, PSAR uses an acceleration factor (AF) that increases each time price makes a new extreme in the trend direction, creating time-dependent tightening. This was novel for 1978: most trailing stops were static. The parabolic shape emerges because SAR converges on price at an accelerating rate, mathematically similar to a particle under constant acceleration. Most implementations today follow Wilder's original specification with minor variations in initialization logic (first-bar handling).
+Wilder introduced SAR alongside RSI, ATR, and ADX in *New Concepts in Technical Trading Systems* (1978). Unlike fixed-percentage trailing stops, SAR uses an acceleration factor (AF) that increases each time price makes a new extreme in the trend direction, creating time-dependent tightening. This was novel for 1978: most trailing stops were static. The parabolic shape emerges because SAR converges on price at an accelerating rate, mathematically similar to a particle under constant acceleration. Most implementations today follow Wilder's original specification with minor variations in initialization logic (first-bar handling).
## Architecture and Physics
### 1. State Machine
-PSAR operates as a two-state machine: **Long** (uptrend) and **Short** (downtrend). Each state tracks three variables:
+SAR operates as a two-state machine: **Long** (uptrend) and **Short** (downtrend). Each state tracks three variables:
- **SAR**: Current stop level
- **EP** (Extreme Point): Highest high in long mode, lowest low in short mode
@@ -115,7 +115,7 @@ O(1) per bar. FMA computes SAR update in a single instruction. Reversal branchin
### SIMD Analysis
-PSAR cannot be vectorized. The state machine has data-dependent branches (reversal detection) and sequential dependencies (SAR depends on prior SAR). The Batch API delegates to streaming for correctness.
+SAR cannot be vectorized. The state machine has data-dependent branches (reversal detection) and sequential dependencies (SAR depends on prior SAR). The Batch API delegates to streaming for correctness.
### Quality Metrics (1–10 Scale)
@@ -139,7 +139,7 @@ Note: Different libraries may vary on first-bar initialization (close > open vs.
## Common Pitfalls
-1. **Whipsaw in ranges**: PSAR reverses on every price crossover. In tight ranges, this produces rapid alternation. Mitigation: combine with ADX filter (only follow PSAR when ADX > 25). Impact: 30–50% of signals may be false in ranging markets.
+1. **Whipsaw in ranges**: SAR reverses on every price crossover. In tight ranges, this produces rapid alternation. Mitigation: combine with ADX filter (only follow SAR when ADX > 25). Impact: 30–50% of signals may be false in ranging markets.
2. **AF sensitivity**: Setting afStart too high (e.g., 0.10) makes SAR track price so tightly that minor retracements trigger reversals. Setting afMax too low (e.g., 0.05) makes SAR lag badly in strong trends.
diff --git a/lib/reversals/psar/psar.pine b/lib/reversals/sar/sar.pine
similarity index 84%
rename from lib/reversals/psar/psar.pine
rename to lib/reversals/sar/sar.pine
index b5882ffe..3bfbc32e 100644
--- a/lib/reversals/psar/psar.pine
+++ b/lib/reversals/sar/sar.pine
@@ -1,7 +1,7 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Parabolic SAR", "PSAR", overlay=true)
+indicator("Parabolic SAR", "SAR", overlay=true)
//@function Calculates Parabolic Stop And Reverse (SAR)
//@param af_start Initial acceleration factor (Wilder's original: 0.02)
@@ -9,7 +9,7 @@ indicator("Parabolic SAR", "PSAR", overlay=true)
//@param af_max Maximum acceleration factor (Wilder's original: 0.20)
//@returns SAR value (stop level for current trend)
//@optimized Minimal state variables, O(1) per bar
-psar(simple float af_start=0.02, simple float af_increment=0.02, simple float af_max=0.20) =>
+sar(simple float af_start=0.02, simple float af_increment=0.02, simple float af_max=0.20) =>
if af_start <= 0 or af_start > af_max
runtime.error("Start AF must be > 0 and <= Max AF")
if af_increment <= 0
@@ -67,11 +67,11 @@ i_af_increment = input.float(0.02, "AF Increment", minval=0.001, maxval=1.0, ste
i_af_max = input.float(0.20, "Max AF", minval=0.001, maxval=1.0, step=0.01)
// Calculation
-psar = psar(i_af_start, i_af_increment, i_af_max)
-psar_above = psar > close ? psar : na
-psar_below = psar < close ? psar : na
+sar = sar(i_af_start, i_af_increment, i_af_max)
+psar_above = sar > close ? sar : na
+psar_below = sar < close ? sar : na
// Plot
-plot(psar_above, "PSAR Above", color=color.red, style=plot.style_linebr, linewidth=2)
-plot(psar_below, "PSAR Below", color=color.green, style=plot.style_linebr, linewidth=2)
+plot(psar_above, "SAR Above", color=color.red, style=plot.style_linebr, linewidth=2)
+plot(psar_below, "SAR Below", color=color.green, style=plot.style_linebr, linewidth=2)
diff --git a/lib/reversals/psar/tests/Psar.Quantower.Tests.cs b/lib/reversals/sar/tests/Sar.Quantower.Tests.cs
similarity index 66%
rename from lib/reversals/psar/tests/Psar.Quantower.Tests.cs
rename to lib/reversals/sar/tests/Sar.Quantower.Tests.cs
index f580243a..4490b332 100644
--- a/lib/reversals/psar/tests/Psar.Quantower.Tests.cs
+++ b/lib/reversals/sar/tests/Sar.Quantower.Tests.cs
@@ -3,55 +3,55 @@ using QuanTAlib;
namespace QuanTAlib.Tests;
-public sealed class PsarIndicatorTests
+public sealed class SarIndicatorTests
{
[Fact]
- public void PsarIndicator_Constructor_SetsDefaults()
+ public void SarIndicator_Constructor_SetsDefaults()
{
- var indicator = new PsarIndicator();
+ var indicator = new SarIndicator();
Assert.Equal(0.02, indicator.AfStart);
Assert.Equal(0.02, indicator.AfIncrement);
Assert.Equal(0.20, indicator.AfMax);
Assert.True(indicator.ShowColdValues);
- Assert.Contains("PSAR", indicator.Name, StringComparison.Ordinal);
+ Assert.Contains("SAR", indicator.Name, StringComparison.Ordinal);
Assert.False(indicator.SeparateWindow);
Assert.True(indicator.OnBackGround);
}
[Fact]
- public void PsarIndicator_MinHistoryDepths_EqualsZero()
+ public void SarIndicator_MinHistoryDepths_EqualsZero()
{
- var indicator = new PsarIndicator();
+ var indicator = new SarIndicator();
- Assert.Equal(0, PsarIndicator.MinHistoryDepths);
+ Assert.Equal(0, SarIndicator.MinHistoryDepths);
IWatchlistIndicator watchlistIndicator = indicator;
Assert.Equal(0, watchlistIndicator.MinHistoryDepths);
}
[Fact]
- public void PsarIndicator_ShortName_IncludesParameters()
+ public void SarIndicator_ShortName_IncludesParameters()
{
- var indicator = new PsarIndicator { AfStart = 0.02, AfMax = 0.20 };
+ var indicator = new SarIndicator { AfStart = 0.02, AfMax = 0.20 };
indicator.Initialize();
- Assert.Contains("PSAR", indicator.ShortName, StringComparison.Ordinal);
+ Assert.Contains("SAR", indicator.ShortName, StringComparison.Ordinal);
Assert.Contains("0.02", indicator.ShortName, StringComparison.Ordinal);
}
[Fact]
- public void PsarIndicator_SourceCodeLink_IsValid()
+ public void SarIndicator_SourceCodeLink_IsValid()
{
- var indicator = new PsarIndicator();
+ var indicator = new SarIndicator();
Assert.Contains("github.com", indicator.SourceCodeLink, StringComparison.Ordinal);
- Assert.Contains("Psar", indicator.SourceCodeLink, StringComparison.Ordinal);
+ Assert.Contains("Sar", indicator.SourceCodeLink, StringComparison.Ordinal);
}
[Fact]
- public void PsarIndicator_Initialize_CreatesInternalIndicator()
+ public void SarIndicator_Initialize_CreatesInternalIndicator()
{
- var indicator = new PsarIndicator();
+ var indicator = new SarIndicator();
indicator.Initialize();
@@ -60,9 +60,9 @@ public sealed class PsarIndicatorTests
}
[Fact]
- public void PsarIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
+ public void SarIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
{
- var indicator = new PsarIndicator { AfStart = 0.02, AfIncrement = 0.02, AfMax = 0.20 };
+ var indicator = new SarIndicator { AfStart = 0.02, AfIncrement = 0.02, AfMax = 0.20 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -80,9 +80,9 @@ public sealed class PsarIndicatorTests
}
[Fact]
- public void PsarIndicator_ProcessUpdate_NewBar_ComputesValue()
+ public void SarIndicator_ProcessUpdate_NewBar_ComputesValue()
{
- var indicator = new PsarIndicator { AfStart = 0.02, AfIncrement = 0.02, AfMax = 0.20 };
+ var indicator = new SarIndicator { AfStart = 0.02, AfIncrement = 0.02, AfMax = 0.20 };
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -104,9 +104,9 @@ public sealed class PsarIndicatorTests
}
[Fact]
- public void PsarIndicator_SingleLineSeries_IsPresent()
+ public void SarIndicator_SingleLineSeries_IsPresent()
{
- var indicator = new PsarIndicator();
+ var indicator = new SarIndicator();
indicator.Initialize();
var now = DateTime.UtcNow;
@@ -122,9 +122,9 @@ public sealed class PsarIndicatorTests
}
[Fact]
- public void PsarIndicator_Description_IsSet()
+ public void SarIndicator_Description_IsSet()
{
- var indicator = new PsarIndicator();
+ var indicator = new SarIndicator();
Assert.NotNull(indicator.Description);
Assert.NotEmpty(indicator.Description);
diff --git a/lib/reversals/psar/tests/Psar.Tests.cs b/lib/reversals/sar/tests/Sar.Tests.cs
similarity index 57%
rename from lib/reversals/psar/tests/Psar.Tests.cs
rename to lib/reversals/sar/tests/Sar.Tests.cs
index c31be50c..23816e1d 100644
--- a/lib/reversals/psar/tests/Psar.Tests.cs
+++ b/lib/reversals/sar/tests/Sar.Tests.cs
@@ -1,85 +1,85 @@
-// PSAR Tests - Parabolic Stop And Reverse
+// SAR Tests - Parabolic Stop And Reverse
namespace QuanTAlib.Tests;
// ── A) Constructor Validation ────────────────────────────────────────────
-public sealed class PsarConstructorTests
+public sealed class SarConstructorTests
{
[Fact]
public void Constructor_ZeroAfStart_Throws()
{
- var ex = Assert.Throws(() => new Psar(afStart: 0));
+ var ex = Assert.Throws(() => new Sar(afStart: 0));
Assert.Equal("afStart", ex.ParamName);
}
[Fact]
public void Constructor_NegativeAfStart_Throws()
{
- var ex = Assert.Throws(() => new Psar(afStart: -0.01));
+ var ex = Assert.Throws(() => new Sar(afStart: -0.01));
Assert.Equal("afStart", ex.ParamName);
}
[Fact]
public void Constructor_ZeroAfIncrement_Throws()
{
- var ex = Assert.Throws(() => new Psar(afIncrement: 0));
+ var ex = Assert.Throws(() => new Sar(afIncrement: 0));
Assert.Equal("afIncrement", ex.ParamName);
}
[Fact]
public void Constructor_NegativeAfIncrement_Throws()
{
- var ex = Assert.Throws(() => new Psar(afIncrement: -0.01));
+ var ex = Assert.Throws(() => new Sar(afIncrement: -0.01));
Assert.Equal("afIncrement", ex.ParamName);
}
[Fact]
public void Constructor_AfMaxEqualAfStart_Throws()
{
- var ex = Assert.Throws(() => new Psar(afStart: 0.02, afMax: 0.02));
+ var ex = Assert.Throws(() => new Sar(afStart: 0.02, afMax: 0.02));
Assert.Equal("afMax", ex.ParamName);
}
[Fact]
public void Constructor_AfMaxLessThanAfStart_Throws()
{
- var ex = Assert.Throws(() => new Psar(afStart: 0.10, afMax: 0.05));
+ var ex = Assert.Throws(() => new Sar(afStart: 0.10, afMax: 0.05));
Assert.Equal("afStart", ex.ParamName);
}
[Fact]
public void Constructor_ValidDefaults_SetsProperties()
{
- var psar = new Psar();
+ var sar = new Sar();
- Assert.Equal(0.02, psar.AfStart);
- Assert.Equal(0.02, psar.AfIncrement);
- Assert.Equal(0.20, psar.AfMax);
- Assert.Equal(1, psar.WarmupPeriod);
- Assert.Contains("Psar", psar.Name, StringComparison.Ordinal);
+ Assert.Equal(0.02, sar.AfStart);
+ Assert.Equal(0.02, sar.AfIncrement);
+ Assert.Equal(0.20, sar.AfMax);
+ Assert.Equal(1, sar.WarmupPeriod);
+ Assert.Contains("Sar", sar.Name, StringComparison.Ordinal);
}
[Fact]
public void Constructor_CustomParams_SetsProperties()
{
- var psar = new Psar(afStart: 0.01, afIncrement: 0.01, afMax: 0.10);
+ var sar = new Sar(afStart: 0.01, afIncrement: 0.01, afMax: 0.10);
- Assert.Equal(0.01, psar.AfStart);
- Assert.Equal(0.01, psar.AfIncrement);
- Assert.Equal(0.10, psar.AfMax);
+ Assert.Equal(0.01, sar.AfStart);
+ Assert.Equal(0.01, sar.AfIncrement);
+ Assert.Equal(0.10, sar.AfMax);
}
}
// ── B) Basic Calculation ─────────────────────────────────────────────────
-public sealed class PsarBasicTests
+public sealed class SarBasicTests
{
[Fact]
public void Update_ReturnsTValue()
{
- var psar = new Psar();
+ var sar = new Sar();
var bar = new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000);
- TValue result = psar.Update(bar);
+ TValue result = sar.Update(bar);
Assert.IsType(result);
}
@@ -87,123 +87,123 @@ public sealed class PsarBasicTests
[Fact]
public void Update_Last_IsAccessible()
{
- var psar = new Psar();
+ var sar = new Sar();
var bar = new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000);
- _ = psar.Update(bar);
+ _ = sar.Update(bar);
- Assert.True(double.IsFinite(psar.Last.Value) || double.IsNaN(psar.Last.Value));
+ Assert.True(double.IsFinite(sar.Last.Value) || double.IsNaN(sar.Last.Value));
}
[Fact]
public void Update_Sar_IsAccessible()
{
- var psar = new Psar();
+ var sar = new Sar();
// Feed enough bars
for (int i = 0; i < 5; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(DateTime.UtcNow.AddMinutes(i),
+ _ = sar.Update(new TBar(DateTime.UtcNow.AddMinutes(i),
price + 2, price - 2, price + 1, price, 1000));
}
- Assert.True(double.IsFinite(psar.Sar));
+ Assert.True(double.IsFinite(sar.SarValue));
}
[Fact]
public void Name_ContainsParameters()
{
- var psar = new Psar(afStart: 0.01, afIncrement: 0.02, afMax: 0.10);
+ var sar = new Sar(afStart: 0.01, afIncrement: 0.02, afMax: 0.10);
- Assert.Contains("0.01", psar.Name, StringComparison.Ordinal);
- Assert.Contains("0.10", psar.Name, StringComparison.Ordinal);
+ Assert.Contains("0.01", sar.Name, StringComparison.Ordinal);
+ Assert.Contains("0.10", sar.Name, StringComparison.Ordinal);
}
[Fact]
public void FirstBar_Uptrend_SarEqualsLow()
{
- var psar = new Psar();
+ var sar = new Sar();
// Close(105) > Open(95) → long mode → SAR = low(90)
- _ = psar.Update(new TBar(DateTime.UtcNow, 95, 110, 90, 105, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow, 95, 110, 90, 105, 1000));
- Assert.Equal(90.0, psar.Sar);
- Assert.True(psar.IsLong);
+ Assert.Equal(90.0, sar.SarValue);
+ Assert.True(sar.IsLong);
}
[Fact]
public void FirstBar_Downtrend_SarEqualsHigh()
{
- var psar = new Psar();
+ var sar = new Sar();
// Close(90) < Open(105) → short mode → SAR = high(110)
- _ = psar.Update(new TBar(DateTime.UtcNow, 105, 110, 85, 90, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow, 105, 110, 85, 90, 1000));
- Assert.Equal(110.0, psar.Sar);
- Assert.False(psar.IsLong);
+ Assert.Equal(110.0, sar.SarValue);
+ Assert.False(sar.IsLong);
}
[Fact]
public void Sar_BelowPrice_InUptrend()
{
- var psar = new Psar();
+ var sar = new Sar();
// Steady uptrend - SAR should trail below
for (int i = 0; i < 20; i++)
{
double price = 100.0 + i * 2;
- _ = psar.Update(new TBar(DateTime.UtcNow.AddMinutes(i),
+ _ = sar.Update(new TBar(DateTime.UtcNow.AddMinutes(i),
price + 1, price - 1, price + 0.5, price, 1000));
}
double lastClose = 100.0 + 19 * 2;
- Assert.True(psar.Sar < lastClose, "SAR should be below price in uptrend");
- Assert.True(psar.IsLong, "Should be in long mode during uptrend");
+ Assert.True(sar.SarValue < lastClose, "SAR should be below price in uptrend");
+ Assert.True(sar.IsLong, "Should be in long mode during uptrend");
}
[Fact]
public void Sar_AbovePrice_InDowntrend()
{
- var psar = new Psar();
+ var sar = new Sar();
// Steady downtrend - SAR should trail above
for (int i = 0; i < 20; i++)
{
double price = 200.0 - i * 2;
- _ = psar.Update(new TBar(DateTime.UtcNow.AddMinutes(i),
+ _ = sar.Update(new TBar(DateTime.UtcNow.AddMinutes(i),
price + 1, price - 1, price + 0.5, price, 1000));
}
double lastClose = 200.0 - 19 * 2;
- Assert.True(psar.Sar > lastClose, "SAR should be above price in downtrend");
- Assert.False(psar.IsLong, "Should be in short mode during downtrend");
+ Assert.True(sar.SarValue > lastClose, "SAR should be above price in downtrend");
+ Assert.False(sar.IsLong, "Should be in short mode during downtrend");
}
[Fact]
public void IsHot_TrueAfterFirstBar()
{
- var psar = new Psar();
+ var sar = new Sar();
- Assert.False(psar.IsHot);
+ Assert.False(sar.IsHot);
- _ = psar.Update(new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000));
- Assert.True(psar.IsHot);
+ Assert.True(sar.IsHot);
}
}
// ── C) State + Bar Correction ────────────────────────────────────────────
-public sealed class PsarStateCorrectionTests
+public sealed class SarStateCorrectionTests
{
[Fact]
public void IsNew_True_AdvancesState()
{
- var psar = new Psar();
+ var sar = new Sar();
- _ = psar.Update(new TBar(DateTime.UtcNow, 105, 95, 100, 100, 1000), isNew: true);
- var first = psar.Last;
+ _ = sar.Update(new TBar(DateTime.UtcNow, 105, 95, 100, 100, 1000), isNew: true);
+ var first = sar.Last;
- _ = psar.Update(new TBar(DateTime.UtcNow.AddMinutes(1), 110, 100, 105, 105, 1000), isNew: true);
- var second = psar.Last;
+ _ = sar.Update(new TBar(DateTime.UtcNow.AddMinutes(1), 110, 100, 105, 105, 1000), isNew: true);
+ var second = sar.Last;
Assert.NotEqual(first.Time, second.Time);
}
@@ -211,28 +211,28 @@ public sealed class PsarStateCorrectionTests
[Fact]
public void IsNew_False_CorrectionRestoresState()
{
- var psar = new Psar();
+ var sar = new Sar();
var dt = DateTime.UtcNow;
// Feed some bars to warm up
for (int i = 0; i < 5; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000), isNew: true);
+ _ = sar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000), isNew: true);
}
// New bar
- _ = psar.Update(new TBar(dt.AddMinutes(5), 110, 105, 108, 107, 1000), isNew: true);
+ _ = sar.Update(new TBar(dt.AddMinutes(5), 110, 105, 108, 107, 1000), isNew: true);
// Correct the bar (isNew=false with different values)
- _ = psar.Update(new TBar(dt.AddMinutes(5), 111, 104, 109, 108, 1000), isNew: false);
+ _ = sar.Update(new TBar(dt.AddMinutes(5), 111, 104, 109, 108, 1000), isNew: false);
// Another correction should produce same result
- _ = psar.Update(new TBar(dt.AddMinutes(5), 111, 104, 109, 108, 1000), isNew: false);
- var corrected1 = psar.Sar;
+ _ = sar.Update(new TBar(dt.AddMinutes(5), 111, 104, 109, 108, 1000), isNew: false);
+ var corrected1 = sar.SarValue;
- _ = psar.Update(new TBar(dt.AddMinutes(5), 111, 104, 109, 108, 1000), isNew: false);
- var corrected2 = psar.Sar;
+ _ = sar.Update(new TBar(dt.AddMinutes(5), 111, 104, 109, 108, 1000), isNew: false);
+ var corrected2 = sar.SarValue;
Assert.Equal(corrected1, corrected2);
}
@@ -240,23 +240,23 @@ public sealed class PsarStateCorrectionTests
[Fact]
public void IterativeCorrections_ProduceSameResult()
{
- var psar = new Psar();
+ var sar = new Sar();
var dt = DateTime.UtcNow;
for (int i = 0; i < 5; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000), isNew: true);
+ _ = sar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000), isNew: true);
}
// Add new bar then correct 3 times
- _ = psar.Update(new TBar(dt.AddMinutes(5), 110, 100, 108, 105, 1000), isNew: true);
+ _ = sar.Update(new TBar(dt.AddMinutes(5), 110, 100, 108, 105, 1000), isNew: true);
double[] results = new double[3];
for (int i = 0; i < 3; i++)
{
- _ = psar.Update(new TBar(dt.AddMinutes(5), 112, 101, 110, 107, 1000), isNew: false);
- results[i] = psar.Sar;
+ _ = sar.Update(new TBar(dt.AddMinutes(5), 112, 101, 110, 107, 1000), isNew: false);
+ results[i] = sar.SarValue;
}
Assert.Equal(results[0], results[1]);
@@ -266,100 +266,100 @@ public sealed class PsarStateCorrectionTests
[Fact]
public void Reset_ClearsAllState()
{
- var psar = new Psar();
+ var sar = new Sar();
for (int i = 0; i < 10; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(DateTime.UtcNow.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
}
- Assert.True(psar.IsHot);
+ Assert.True(sar.IsHot);
- psar.Reset();
+ sar.Reset();
- Assert.False(psar.IsHot);
- Assert.True(double.IsNaN(psar.Sar));
+ Assert.False(sar.IsHot);
+ Assert.True(double.IsNaN(sar.SarValue));
}
}
// ── D) Warmup / Convergence ──────────────────────────────────────────────
-public sealed class PsarWarmupTests
+public sealed class SarWarmupTests
{
[Fact]
public void IsHot_FlipsAfterFirstBar()
{
- var psar = new Psar();
+ var sar = new Sar();
- Assert.False(psar.IsHot);
+ Assert.False(sar.IsHot);
- _ = psar.Update(new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000));
- Assert.True(psar.IsHot);
+ Assert.True(sar.IsHot);
}
[Fact]
public void WarmupPeriod_EqualsOne()
{
- var psar = new Psar();
+ var sar = new Sar();
- Assert.Equal(1, psar.WarmupPeriod);
+ Assert.Equal(1, sar.WarmupPeriod);
}
}
// ── E) Robustness ────────────────────────────────────────────────────────
-public sealed class PsarRobustnessTests
+public sealed class SarRobustnessTests
{
[Fact]
public void NaN_Input_UsesLastValidValue()
{
- var psar = new Psar();
+ var sar = new Sar();
var dt = DateTime.UtcNow;
// Feed valid bars
for (int i = 0; i < 5; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
+ _ = sar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
}
// Feed NaN bar
- _ = psar.Update(new TBar(dt.AddMinutes(5), double.NaN, double.NaN, double.NaN, double.NaN, 0));
+ _ = sar.Update(new TBar(dt.AddMinutes(5), double.NaN, double.NaN, double.NaN, double.NaN, 0));
- Assert.True(double.IsFinite(psar.Sar));
+ Assert.True(double.IsFinite(sar.SarValue));
}
[Fact]
public void Infinity_Input_UsesLastValidValue()
{
- var psar = new Psar();
+ var sar = new Sar();
var dt = DateTime.UtcNow;
for (int i = 0; i < 5; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
+ _ = sar.Update(new TBar(dt.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
}
- _ = psar.Update(new TBar(dt.AddMinutes(5),
+ _ = sar.Update(new TBar(dt.AddMinutes(5),
double.PositiveInfinity, double.NegativeInfinity, double.PositiveInfinity, double.PositiveInfinity, 0));
- Assert.True(double.IsFinite(psar.Sar));
+ Assert.True(double.IsFinite(sar.SarValue));
}
[Fact]
public void FirstBar_NaN_ReturnsNaN()
{
- var psar = new Psar();
+ var sar = new Sar();
- _ = psar.Update(new TBar(DateTime.UtcNow, double.NaN, double.NaN, double.NaN, double.NaN, 0));
+ _ = sar.Update(new TBar(DateTime.UtcNow, double.NaN, double.NaN, double.NaN, double.NaN, 0));
- Assert.True(double.IsNaN(psar.Last.Value));
+ Assert.True(double.IsNaN(sar.Last.Value));
}
}
// ── F) Consistency ───────────────────────────────────────────────────────
-public sealed class PsarConsistencyTests
+public sealed class SarConsistencyTests
{
private static TBarSeries CreateGbmBars(int count = 500)
{
@@ -373,16 +373,16 @@ public sealed class PsarConsistencyTests
var bars = CreateGbmBars();
// Streaming
- var streaming = new Psar();
+ var streaming = new Sar();
var streamResults = new double[bars.Count];
for (int i = 0; i < bars.Count; i++)
{
_ = streaming.Update(bars[i], isNew: true);
- streamResults[i] = streaming.Sar;
+ streamResults[i] = streaming.SarValue;
}
// Batch
- var batchResults = Psar.Batch(bars);
+ var batchResults = Sar.Batch(bars);
for (int i = 0; i < bars.Count; i++)
{
@@ -393,8 +393,8 @@ public sealed class PsarConsistencyTests
[Fact]
public void TValue_Update_MatchesTBar_Update()
{
- var ch1 = new Psar();
- var ch2 = new Psar();
+ var ch1 = new Sar();
+ var ch2 = new Sar();
double[] prices = [100, 102, 98, 105, 99, 103, 107, 95, 110, 108];
@@ -407,35 +407,35 @@ public sealed class PsarConsistencyTests
_ = ch2.Update(new TValue(DateTime.UtcNow.AddMinutes(i), p), isNew: true);
}
- Assert.Equal(ch1.Sar, ch2.Sar);
+ Assert.Equal(ch1.SarValue, ch2.SarValue);
}
[Fact]
public void Reversal_DetectedOnPriceCrossover()
{
- var psar = new Psar();
+ var sar = new Sar();
var dt = DateTime.UtcNow;
// Start in uptrend
- _ = psar.Update(new TBar(dt, 100, 90, 95, 105, 1000), isNew: true);
- Assert.True(psar.IsLong);
+ _ = sar.Update(new TBar(dt, 100, 90, 95, 105, 1000), isNew: true);
+ Assert.True(sar.IsLong);
// Continue uptrend
for (int i = 1; i <= 5; i++)
{
double price = 105 + i * 2;
- _ = psar.Update(new TBar(dt.AddMinutes(i),
+ _ = sar.Update(new TBar(dt.AddMinutes(i),
price + 1, price - 1, price + 0.5, price, 1000), isNew: true);
}
- Assert.True(psar.IsLong);
+ Assert.True(sar.IsLong);
// Sharp reversal — price drops below SAR
- double sarBeforeReversal = psar.Sar;
- _ = psar.Update(new TBar(dt.AddMinutes(10),
+ double sarBeforeReversal = sar.SarValue;
+ _ = sar.Update(new TBar(dt.AddMinutes(10),
sarBeforeReversal - 5, sarBeforeReversal - 20,
sarBeforeReversal - 18, sarBeforeReversal - 15, 1000), isNew: true);
- Assert.False(psar.IsLong, "Should reverse to short after price crosses below SAR");
+ Assert.False(sar.IsLong, "Should reverse to short after price crosses below SAR");
}
[Fact]
@@ -444,29 +444,29 @@ public sealed class PsarConsistencyTests
var bars = CreateGbmBars(100);
// Streaming
- var streaming = new Psar();
+ var streaming = new Sar();
for (int i = 0; i < bars.Count; i++)
{
_ = streaming.Update(bars[i], isNew: true);
}
- double streamLast = streaming.Sar;
+ double streamLast = streaming.SarValue;
// TSeries batch
- var batch = new Psar();
+ var batch = new Sar();
_ = batch.Update(bars);
- Assert.Equal(streamLast, batch.Sar, precision: 10);
+ Assert.Equal(streamLast, batch.SarValue, precision: 10);
}
}
// ── G) Span API Tests ────────────────────────────────────────────────────
-public sealed class PsarSpanTests
+public sealed class SarSpanTests
{
[Fact]
public void Batch_Span_InvalidAfStart_Throws()
{
var ex = Assert.Throws(() =>
- Psar.Batch(new double[10], new double[10], new double[10], new double[10], new double[10], afStart: 0));
+ Sar.Batch(new double[10], new double[10], new double[10], new double[10], new double[10], afStart: 0));
Assert.Equal("afStart", ex.ParamName);
}
@@ -474,7 +474,7 @@ public sealed class PsarSpanTests
public void Batch_Span_MismatchedLengths_Throws()
{
var ex = Assert.Throws(() =>
- Psar.Batch(new double[10], new double[10], new double[5], new double[10], new double[10]));
+ Sar.Batch(new double[10], new double[10], new double[5], new double[10], new double[10]));
Assert.Equal("high", ex.ParamName);
}
@@ -482,7 +482,7 @@ public sealed class PsarSpanTests
public void Batch_Span_OutputTooShort_Throws()
{
var ex = Assert.Throws(() =>
- Psar.Batch(new double[10], new double[10], new double[10], new double[10], new double[5]));
+ Sar.Batch(new double[10], new double[10], new double[10], new double[10], new double[5]));
Assert.Equal("output", ex.ParamName);
}
@@ -491,24 +491,24 @@ public sealed class PsarSpanTests
{
var output = Array.Empty();
var ex = Record.Exception(() =>
- Psar.Batch(ReadOnlySpan.Empty, ReadOnlySpan.Empty,
+ Sar.Batch(ReadOnlySpan.Empty, ReadOnlySpan.Empty,
ReadOnlySpan.Empty, ReadOnlySpan.Empty, output.AsSpan()));
Assert.Null(ex);
}
}
// ── H) Event / Chainability ──────────────────────────────────────────────
-public sealed class PsarEventTests
+public sealed class SarEventTests
{
[Fact]
public void Pub_FiresOnUpdate()
{
- var psar = new Psar();
+ var sar = new Sar();
int fireCount = 0;
- psar.Pub += (object? _, in TValueEventArgs _e) => { fireCount++; };
+ sar.Pub += (object? _, in TValueEventArgs _e) => { fireCount++; };
- _ = psar.Update(new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow, 100, 95, 98, 97, 1000));
Assert.Equal(1, fireCount);
}
@@ -516,15 +516,15 @@ public sealed class PsarEventTests
[Fact]
public void Pub_FiresOnEachUpdate()
{
- var psar = new Psar();
+ var sar = new Sar();
int fireCount = 0;
- psar.Pub += (object? _, in TValueEventArgs _e) => { fireCount++; };
+ sar.Pub += (object? _, in TValueEventArgs _e) => { fireCount++; };
for (int i = 0; i < 5; i++)
{
double price = 100.0 + i;
- _ = psar.Update(new TBar(DateTime.UtcNow.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
+ _ = sar.Update(new TBar(DateTime.UtcNow.AddMinutes(i), price + 2, price - 2, price + 1, price, 1000));
}
Assert.Equal(5, fireCount);
@@ -532,7 +532,7 @@ public sealed class PsarEventTests
}
// ── I) Prime Tests ───────────────────────────────────────────────────────
-public sealed class PsarPrimeTests
+public sealed class SarPrimeTests
{
[Fact]
public void Prime_TBarSeries_SetsState()
@@ -540,21 +540,21 @@ public sealed class PsarPrimeTests
var gbm = new GBM(startPrice: 100.0, mu: 0.05, sigma: 0.20, seed: 42);
var bars = gbm.Fetch(50, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
- var psar = new Psar();
- psar.Prime(bars);
+ var sar = new Sar();
+ sar.Prime(bars);
- Assert.True(psar.IsHot);
- Assert.True(double.IsFinite(psar.Sar));
+ Assert.True(sar.IsHot);
+ Assert.True(double.IsFinite(sar.SarValue));
}
[Fact]
public void Prime_EmptySource_NoException()
{
- var psar = new Psar();
+ var sar = new Sar();
var bars = new TBarSeries();
- var ex = Record.Exception(() => psar.Prime(bars));
+ var ex = Record.Exception(() => sar.Prime(bars));
Assert.Null(ex);
- Assert.False(psar.IsHot);
+ Assert.False(sar.IsHot);
}
}
diff --git a/lib/reversals/psar/tests/Psar.Validation.Tests.cs b/lib/reversals/sar/tests/Sar.Validation.Tests.cs
similarity index 83%
rename from lib/reversals/psar/tests/Psar.Validation.Tests.cs
rename to lib/reversals/sar/tests/Sar.Validation.Tests.cs
index fef48163..9290928e 100644
--- a/lib/reversals/psar/tests/Psar.Validation.Tests.cs
+++ b/lib/reversals/sar/tests/Sar.Validation.Tests.cs
@@ -1,4 +1,4 @@
-// PSAR Validation Tests - Parabolic Stop And Reverse
+// SAR Validation Tests - Parabolic Stop And Reverse
// Cross-validated against Skender.Stock.Indicators GetParabolicSar(), TALib SAR, and OoplesFinance CalculateParabolicSAR.
using OoplesFinance.StockIndicators;
@@ -8,7 +8,7 @@ using TALib;
namespace QuanTAlib.Tests;
-public sealed class PsarValidationTests
+public sealed class SarValidationTests
{
private static TBarSeries CreateGbmBars(int count = 500, int seed = 42)
{
@@ -29,12 +29,12 @@ public sealed class PsarValidationTests
.ToList();
// QuanTAlib streaming
- var psar = new Psar(afStart: 0.02, afIncrement: 0.02, afMax: 0.20);
+ var sar = new Sar(afStart: 0.02, afIncrement: 0.02, afMax: 0.20);
var ourValues = new double[_data.Bars.Count];
for (int i = 0; i < _data.Bars.Count; i++)
{
- _ = psar.Update(_data.Bars[i], isNew: true);
- ourValues[i] = psar.Sar;
+ _ = sar.Update(_data.Bars[i], isNew: true);
+ ourValues[i] = sar.SarValue;
}
// Compare warm values (skip first bar where SAR is initialization)
@@ -63,16 +63,16 @@ public sealed class PsarValidationTests
var bars = CreateGbmBars();
// Streaming
- var streaming = new Psar();
+ var streaming = new Sar();
var streamValues = new double[bars.Count];
for (int i = 0; i < bars.Count; i++)
{
_ = streaming.Update(bars[i], isNew: true);
- streamValues[i] = streaming.Sar;
+ streamValues[i] = streaming.SarValue;
}
// Batch
- var batchResults = Psar.Batch(bars);
+ var batchResults = Sar.Batch(bars);
for (int i = 0; i < bars.Count; i++)
{
@@ -88,17 +88,17 @@ public sealed class PsarValidationTests
var bars = CreateGbmBars();
// Streaming
- var streaming = new Psar();
+ var streaming = new Sar();
var streamValues = new double[bars.Count];
for (int i = 0; i < bars.Count; i++)
{
_ = streaming.Update(bars[i], isNew: true);
- streamValues[i] = streaming.Sar;
+ streamValues[i] = streaming.SarValue;
}
// Span
var spanOutput = new double[bars.Count];
- Psar.Batch(bars.OpenValues, bars.HighValues, bars.LowValues, bars.CloseValues, spanOutput);
+ Sar.Batch(bars.OpenValues, bars.HighValues, bars.LowValues, bars.CloseValues, spanOutput);
for (int i = 0; i < bars.Count; i++)
{
@@ -113,8 +113,8 @@ public sealed class PsarValidationTests
{
var bars = CreateGbmBars(count: 100);
- var slow = new Psar(afStart: 0.01, afIncrement: 0.01, afMax: 0.20);
- var fast = new Psar(afStart: 0.10, afIncrement: 0.05, afMax: 0.50);
+ var slow = new Sar(afStart: 0.01, afIncrement: 0.01, afMax: 0.20);
+ var fast = new Sar(afStart: 0.10, afIncrement: 0.05, afMax: 0.50);
for (int i = 0; i < bars.Count; i++)
{
@@ -124,8 +124,8 @@ public sealed class PsarValidationTests
// Higher AF = more responsive = SAR closer to price
// Just verify both produce finite output (direction depends on data)
- Assert.True(double.IsFinite(slow.Sar));
- Assert.True(double.IsFinite(fast.Sar));
+ Assert.True(double.IsFinite(slow.SarValue));
+ Assert.True(double.IsFinite(fast.SarValue));
}
// ── Determinism ──────────────────────────────────────────────────────
@@ -135,8 +135,8 @@ public sealed class PsarValidationTests
{
var bars = CreateGbmBars(count: 200, seed: 123);
- var psar1 = new Psar();
- var psar2 = new Psar();
+ var psar1 = new Sar();
+ var psar2 = new Sar();
for (int i = 0; i < bars.Count; i++)
{
@@ -144,7 +144,7 @@ public sealed class PsarValidationTests
_ = psar2.Update(bars[i], isNew: true);
}
- Assert.Equal(psar1.Sar, psar2.Sar);
+ Assert.Equal(psar1.SarValue, psar2.SarValue);
}
// ── Calculate Returns Valid Indicator ─────────────────────────────────
@@ -154,12 +154,12 @@ public sealed class PsarValidationTests
{
var bars = CreateGbmBars(count: 100);
- var (results, indicator) = Psar.Calculate(bars);
+ var (results, indicator) = Sar.Calculate(bars);
Assert.NotNull(results);
Assert.Equal(bars.Count, results.Count);
Assert.True(indicator.IsHot);
- Assert.True(double.IsFinite(indicator.Sar));
+ Assert.True(double.IsFinite(indicator.SarValue));
}
// ── Reversal Count Is Reasonable ─────────────────────────────────────
@@ -168,20 +168,20 @@ public sealed class PsarValidationTests
public void ReversalCount_IsReasonable()
{
var bars = CreateGbmBars(count: 500);
- var psar = new Psar();
+ var sar = new Sar();
int reversals = 0;
bool prevIsLong = true;
for (int i = 0; i < bars.Count; i++)
{
- _ = psar.Update(bars[i], isNew: true);
+ _ = sar.Update(bars[i], isNew: true);
- if (i > 0 && psar.IsLong != prevIsLong)
+ if (i > 0 && sar.IsLong != prevIsLong)
{
reversals++;
}
- prevIsLong = psar.IsLong;
+ prevIsLong = sar.IsLong;
}
// In 500 bars of GBM data, expect several reversals but not every bar
@@ -216,12 +216,12 @@ public sealed class PsarValidationTests
Assert.True(length > 100, $"TALib SAR produced only {length} values");
// QuanTAlib streaming
- var psar = new Psar(afStart: afStep, afIncrement: afStep, afMax: afMax);
+ var sar = new Sar(afStart: afStep, afIncrement: afStep, afMax: afMax);
var qlSar = new double[_data.Bars.Count];
for (int i = 0; i < _data.Bars.Count; i++)
{
- _ = psar.Update(_data.Bars[i], isNew: true);
- qlSar[i] = psar.Sar;
+ _ = sar.Update(_data.Bars[i], isNew: true);
+ qlSar[i] = sar.SarValue;
}
// Skip the first ~5 bars (initialization divergence), then require exact match.
@@ -250,13 +250,13 @@ public sealed class PsarValidationTests
///
/// Structural validation against Ooples CalculateParabolicSAR.
- /// Ooples PSAR uses the same Wilder acceleration factor algorithm (start=0.02, increment=0.02, max=0.2).
+ /// Ooples SAR uses the same Wilder acceleration factor algorithm (start=0.02, increment=0.02, max=0.2).
/// Cross-library numeric equality is not asserted because reversal-point initialization
/// diverges across implementations when the very first bar direction is ambiguous.
/// Both must produce finite, positive output on the same OHLCV data.
///
[Fact]
- public void Psar_MatchesOoples_Structural()
+ public void Sar_MatchesOoples_Structural()
{
var _data = new ValidationTestData();
@@ -274,14 +274,14 @@ public sealed class PsarValidationTests
var oResult = stockData.CalculateParabolicSAR(start: 0.02, increment: 0.02, maximum: 0.2);
var oValues = oResult.OutputValues.Values.First();
- var psar = new Psar(afStart: 0.02, afIncrement: 0.02, afMax: 0.20);
+ var sar = new Sar(afStart: 0.02, afIncrement: 0.02, afMax: 0.20);
var qValues = new System.Collections.Generic.List();
foreach (var bar in _data.Data)
{
- qValues.Add(psar.Update(bar).Value);
+ qValues.Add(sar.Update(bar).Value);
}
- Assert.True(oValues.Count > 0, "Ooples PSAR must produce output");
+ Assert.True(oValues.Count > 0, "Ooples SAR must produce output");
int finiteCount = 0;
int warmup = 5;
@@ -293,7 +293,7 @@ public sealed class PsarValidationTests
}
}
- Assert.True(finiteCount > 100, $"Expected >100 finite positive PSAR pairs, got {finiteCount}");
+ Assert.True(finiteCount > 100, $"Expected >100 finite positive SAR pairs, got {finiteCount}");
_data.Dispose();
}
diff --git a/lib/reversals/swings/Swings.md b/lib/reversals/swings/Swings.md
index dd706054..bf18e987 100644
--- a/lib/reversals/swings/Swings.md
+++ b/lib/reversals/swings/Swings.md
@@ -13,7 +13,7 @@
| **PineScript** | [swings.pine](swings.pine) |
- Swing High/Low detection identifies local price extremes using a configurable lookback window.
-- **Similar:** [Fractal](../../oscillators/fisher/Fisher.md), [ZigZag](../psar/Psar.md) | **Complementary:** Volume for confirmation | **Trading note:** Swing high/low detector; identifies pivots for support/resistance and chart pattern analysis.
+- **Similar:** [Fractal](../../oscillators/fisher/Fisher.md), [ZigZag](../sar/Sar.md) | **Complementary:** Volume for confirmation | **Trading note:** Swing high/low detector; identifies pivots for support/resistance and chart pattern analysis.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Swing High/Low detection identifies local price extremes using a configurable lookback window. A Swing High marks a bar whose high strictly exceeds the highs of all bars within the lookback window on each side. A Swing Low marks a bar whose low is strictly less than all corresponding lows. The lookback parameter controls sensitivity: larger lookback windows require more confirmation and produce fewer, more significant signals. This generalizes Williams' fixed five-bar Fractals into a flexible structural analysis tool.
diff --git a/lib/statistics/_index.md b/lib/statistics/_index.md
index 4e14d173..d4aa75fd 100644
--- a/lib/statistics/_index.md
+++ b/lib/statistics/_index.md
@@ -9,7 +9,7 @@ Statistical tools applied to price and returns. These indicators quantify relati
| [BETA](beta/Beta.md) | Beta Coefficient | Asset volatility relative to market. β=1 means market-matched risk. |
| [CMA](cma/Cma.md) | Cumulative Moving Average | Running average of all values. Welford's algorithm. No window. |
| [COINTEGRATION](cointegration/Cointegration.md) | Cointegration | Tests if series share long-term equilibrium. Pairs trading foundation. |
-| [CORRELATION](correlation/Correlation.md) | Correlation | Linear relationship between two variables. Range: -1 to +1. |
+| [CORREL](correl/Correl.md) | Correlation | Linear relationship between two variables. Range: -1 to +1. |
| [COVARIANCE](covariance/Covariance.md) | Covariance | Joint variability of two random variables. Building block for β. |
| [ENTROPY](entropy/Entropy.md) | Shannon Entropy | Measures uncertainty/randomness. Higher entropy = less predictable. |
| [GEOMEAN](geomean/Geomean.md) | Geometric Mean | nth root of product. Use for growth rates and ratios. |
diff --git a/lib/statistics/acf/Acf.md b/lib/statistics/acf/Acf.md
index a87ed077..27deab83 100644
--- a/lib/statistics/acf/Acf.md
+++ b/lib/statistics/acf/Acf.md
@@ -13,7 +13,7 @@
| **PineScript** | [acf.pine](acf.pine) |
- The Autocorrelation Function (ACF) measures the correlation of a time series with a lagged copy of itself.
-- **Similar:** [PACF](../pacf/Pacf.md), [Correlation](../correlation/Correlation.md) | **Trading note:** Autocorrelation function; detects mean-reversion (negative ACF) vs momentum (positive ACF) in returns.
+- **Similar:** [PACF](../pacf/Pacf.md), [Correl](../correl/Correl.md) | **Trading note:** Autocorrelation function; detects mean-reversion (negative ACF) vs momentum (positive ACF) in returns.
- Validated against mathematical properties and theoretical AR-process expectations.
The Autocorrelation Function (ACF) measures the correlation of a time series with a lagged copy of itself. It is fundamental for identifying repeating patterns, seasonal effects, and determining the order of time series models like ARMA/ARIMA.
diff --git a/lib/statistics/adf/Adf.md b/lib/statistics/adf/Adf.md
index b0af5b57..13a395eb 100644
--- a/lib/statistics/adf/Adf.md
+++ b/lib/statistics/adf/Adf.md
@@ -13,7 +13,9 @@
| **PineScript** | [adf.pine](adf.pine) |
- Tests the null hypothesis that a time series contains a unit root (non-stationary). Output near **0** → stationary; output near **1** → unit root.
-- **Similar:** [Hurst](../hurst/Hurst.md), [Cointegration](../cointegration/Cointegration.md) | **Complementary:** Z-Score, Variance | **Trading note:** ADF < 0.05 confirms mean-reversion suitability.
+- **Similar indicators:** [Hurst](../hurst/Hurst.md), [Cointegration](../cointegration/Cointegration.md)
+- **Complementary indicators:** [Z-Score](../zscore/Zscore.md), [Variance](../variance/Variance.md)
+- **Trading note:** ADF < 0.05 confirms mean-reversion suitability.
- Validated against Python `statsmodels.tsa.stattools.adfuller` reference implementation.
The Augmented Dickey-Fuller test is the gold standard for detecting whether a financial time series is stationary or contains a unit root. Unlike the original Dickey-Fuller test, the augmented version includes lagged difference terms $\Delta y_{t-i}$ to absorb serial correlation, ensuring the test statistic follows the correct distribution. The p-value output uses MacKinnon (1994, 2010) polynomial interpolation with a standard normal CDF approximation, providing machine-precision results without lookup tables.
diff --git a/lib/statistics/beta/Beta.md b/lib/statistics/beta/Beta.md
index 14d7f502..ec322515 100644
--- a/lib/statistics/beta/Beta.md
+++ b/lib/statistics/beta/Beta.md
@@ -13,7 +13,7 @@
| **PineScript** | [beta.pine](beta.pine) |
- Beta measures the volatility of an asset in relation to the overall market.
-- **Similar:** [Correlation](../correlation/Correlation.md), [Covariance](../covariance/Covariance.md) | **Trading note:** Beta coefficient; measures systematic risk vs benchmark. β>1 = amplifies market moves.
+- **Similar:** [Correl](../correl/Correl.md), [Covariance](../covariance/Covariance.md) | **Trading note:** Beta coefficient; measures systematic risk vs benchmark. β>1 = amplifies market moves.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Beta measures the volatility of an asset in relation to the overall market. It's the slope of the regression line between the asset's returns and the market's returns. A beta of 1.0 means the asset moves in lockstep with the market. A beta of 2.0 means the asset is twice as volatile as the market.
diff --git a/lib/statistics/cointegration/Cointegration.md b/lib/statistics/cointegration/Cointegration.md
index b0ebfab2..e8c15ed3 100644
--- a/lib/statistics/cointegration/Cointegration.md
+++ b/lib/statistics/cointegration/Cointegration.md
@@ -13,7 +13,7 @@
| **PineScript** | [cointegration.pine](cointegration.pine) |
- The Cointegration indicator measures the long-run equilibrium relationship between two price series using the Engle-Granger two-step method with an...
-- **Similar:** [Correlation](../correlation/Correlation.md), [Granger](../granger/Granger.md) | **Trading note:** Tests if two series share a long-run equilibrium. Foundation of statistical arbitrage (pairs trading).
+- **Similar:** [Correl](../correl/Correl.md), [Granger](../granger/Granger.md) | **Trading note:** Tests if two series share a long-run equilibrium. Foundation of statistical arbitrage (pairs trading).
- Validated against TradingView PineScript reference and statistical property tests.
The Cointegration indicator measures the long-run equilibrium relationship between two price series using the Engle-Granger two-step method with an Augmented Dickey-Fuller (ADF) test. Unlike correlation, which measures short-term co-movement, cointegration tests whether two non-stationary series share a common stochastic trend—meaning they may diverge temporarily but are statistically bound to revert to their equilibrium relationship.
diff --git a/lib/statistics/correlation/Correlation.cs b/lib/statistics/correl/Correl.cs
similarity index 94%
rename from lib/statistics/correlation/Correlation.cs
rename to lib/statistics/correl/Correl.cs
index 49ec13a1..348e31ea 100644
--- a/lib/statistics/correlation/Correlation.cs
+++ b/lib/statistics/correl/Correl.cs
@@ -28,7 +28,7 @@ namespace QuanTAlib;
/// - |r| < 0.3: Weak correlation
///
[SkipLocalsInit]
-public sealed class Correlation : AbstractBase
+public sealed class Correl : AbstractBase
{
private readonly RingBuffer _bufferX;
private readonly RingBuffer _bufferY;
@@ -58,10 +58,10 @@ public sealed class Correlation : AbstractBase
public override bool IsHot => _bufferX.Count >= WarmupPeriod;
///
- /// Creates a new Correlation indicator.
+ /// Creates a new Correl indicator.
///
/// Lookback period for calculation (must be > 1)
- public Correlation(int period = 20)
+ public Correl(int period = 20)
{
if (period <= 1)
{
@@ -71,7 +71,7 @@ public sealed class Correlation : AbstractBase
_bufferX = new RingBuffer(period);
_bufferY = new RingBuffer(period);
- Name = $"Correlation({period})";
+ Name = $"Correl({period})";
WarmupPeriod = period;
}
@@ -118,7 +118,7 @@ public sealed class Correlation : AbstractBase
ProcessBarCorrection(x, y);
}
- double correlation = CalculateCorrelation();
+ double correlation = CalculateCorrel();
Last = new TValue(seriesX.Time, correlation);
PubEvent(Last);
@@ -143,13 +143,13 @@ public sealed class Correlation : AbstractBase
/// Not supported for bi-input indicator. Use Update(seriesX, seriesY) instead.
public override TValue Update(TValue input, bool isNew = true)
{
- throw new NotSupportedException("Correlation requires two inputs (seriesX and seriesY). Use Update(seriesX, seriesY).");
+ throw new NotSupportedException("Correl requires two inputs (seriesX and seriesY). Use Update(seriesX, seriesY).");
}
/// Not supported. This indicator requires two inputs; use instead.
/// Not supported for bi-input indicator. Use Calculate(seriesX, seriesY, period) instead.
public override TSeries Update(TSeries source)
{
- throw new NotSupportedException("Correlation requires two inputs. Use Batch(seriesX, seriesY, period).");
+ throw new NotSupportedException("Correl requires two inputs. Use Batch(seriesX, seriesY, period).");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -316,7 +316,7 @@ public sealed class Correlation : AbstractBase
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private double CalculateCorrelation()
+ private double CalculateCorrel()
{
int n = _bufferX.Count;
if (n < 2)
@@ -355,7 +355,7 @@ public sealed class Correlation : AbstractBase
/// Not supported. This indicator requires two input spans.
public override void Prime(ReadOnlySpan source, TimeSpan? step = null)
{
- throw new NotSupportedException("Correlation requires two inputs.");
+ throw new NotSupportedException("Correl requires two inputs.");
}
///
@@ -414,7 +414,7 @@ public sealed class Correlation : AbstractBase
throw new ArgumentException("Period must be greater than 1", nameof(period));
}
- var indicator = new Correlation(period);
+ var indicator = new Correl(period);
for (int i = 0; i < seriesX.Length; i++)
{
@@ -426,14 +426,14 @@ public sealed class Correlation : AbstractBase
///
/// Calculates Pearson correlation for two time series and returns both the result series and the live indicator instance.
///
- public static (TSeries Results, Correlation Indicator) Calculate(TSeries seriesX, TSeries seriesY, int period = 20)
+ public static (TSeries Results, Correl Indicator) Calculate(TSeries seriesX, TSeries seriesY, int period = 20)
{
if (seriesX.Count != seriesY.Count)
{
throw new ArgumentException("Series must have the same length", nameof(seriesY));
}
- var indicator = new Correlation(period);
+ var indicator = new Correl(period);
var result = new TSeries(seriesX.Count);
var timesX = seriesX.Times;
diff --git a/lib/statistics/correlation/Correlation.md b/lib/statistics/correl/Correl.md
similarity index 97%
rename from lib/statistics/correlation/Correlation.md
rename to lib/statistics/correl/Correl.md
index 57315ca5..33f6eba4 100644
--- a/lib/statistics/correlation/Correlation.md
+++ b/lib/statistics/correl/Correl.md
@@ -10,7 +10,7 @@
| **Outputs** | Single series (Pearson r) |
| **Output range** | Varies (see docs) |
| **Warmup** | `period` bars |
-| **PineScript** | [correlation.pine](correlation.pine) |
+| **PineScript** | [correl.pine](correl.pine) |
- The Pearson Correlation Coefficient measures the linear relationship between two variables, returning a value from -1 (perfect negative correlation...
- **Similar:** [Spearman](../spearman/Spearman.md), [Kendall](../kendall/Kendall.md) | **Trading note:** Pearson correlation; measures linear relationship strength. Used for portfolio diversification and pairs trading.
@@ -189,7 +189,7 @@ Monitor correlation stability:
### Streaming Mode (Bi-Input)
```csharp
-var corr = new Correlation(period: 20);
+var corr = new Correl(period: 20);
foreach (var (priceA, priceB) in pricePairs)
{
var result = corr.Update(priceA, priceB);
@@ -206,7 +206,7 @@ foreach (var (priceA, priceB) in pricePairs)
var seriesA = new TSeries();
var seriesB = new TSeries();
// ... populate series ...
-var results = Correlation.Calculate(seriesA, seriesB, period: 20);
+var results = Correl.Calculate(seriesA, seriesB, period: 20);
```
### Span Mode (Zero Allocation)
@@ -216,13 +216,13 @@ double[] pricesA = new double[1000];
double[] pricesB = new double[1000];
double[] output = new double[1000];
// ... populate inputs ...
-Correlation.Batch(pricesA.AsSpan(), pricesB.AsSpan(), output.AsSpan(), period: 20);
+Correl.Batch(pricesA.AsSpan(), pricesB.AsSpan(), output.AsSpan(), period: 20);
```
### Bar Correction Support
```csharp
-var corr = new Correlation(20);
+var corr = new Correl(20);
// New bar
corr.Update(100.0, 50.0, isNew: true); // r = 0.85
diff --git a/lib/statistics/correlation/correlation.pine b/lib/statistics/correl/correl.pine
similarity index 96%
rename from lib/statistics/correlation/correlation.pine
rename to lib/statistics/correl/correl.pine
index b79ebb90..fd83eb0e 100644
--- a/lib/statistics/correlation/correlation.pine
+++ b/lib/statistics/correl/correl.pine
@@ -1,7 +1,7 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Pearson's Correlation (CORRELATION)", "CORRELATION", overlay=false)
+indicator("Pearson's Correlation (CORREL)", "CORREL", overlay=false)
//@function Calculates Pearson correlation coefficient using single pass with circular buffer
//@param src1 series float First series to analyze
diff --git a/lib/statistics/correlation/tests/Correlation.Tests.cs b/lib/statistics/correl/tests/Correl.Tests.cs
similarity index 83%
rename from lib/statistics/correlation/tests/Correlation.Tests.cs
rename to lib/statistics/correl/tests/Correl.Tests.cs
index 6a368ab2..37f76e15 100644
--- a/lib/statistics/correlation/tests/Correlation.Tests.cs
+++ b/lib/statistics/correl/tests/Correl.Tests.cs
@@ -1,42 +1,42 @@
namespace QuanTAlib.Tests;
-public class CorrelationTests
+public class CorrelTests
{
[Fact]
public void Constructor_ValidPeriod_CreatesIndicator()
{
- var indicator = new Correlation(20);
- Assert.Equal("Correlation(20)", indicator.Name);
+ var indicator = new Correl(20);
+ Assert.Equal("Correl(20)", indicator.Name);
Assert.Equal(20, indicator.WarmupPeriod);
}
[Fact]
public void Constructor_MinimumValidPeriod_CreatesIndicator()
{
- var indicator = new Correlation(2);
- Assert.Equal("Correlation(2)", indicator.Name);
+ var indicator = new Correl(2);
+ Assert.Equal("Correl(2)", indicator.Name);
}
[Fact]
public void Constructor_InvalidPeriod_ThrowsArgumentException()
{
- Assert.Throws(() => new Correlation(1));
- Assert.Throws(() => new Correlation(0));
- Assert.Throws(() => new Correlation(-5));
+ Assert.Throws(() => new Correl(1));
+ Assert.Throws(() => new Correl(0));
+ Assert.Throws(() => new Correl(-5));
}
[Fact]
public void Update_SingleValue_ReturnsNaN()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
var result = indicator.Update(100.0, 200.0, true);
Assert.True(double.IsNaN(result.Value));
}
[Fact]
- public void Update_TwoValues_ReturnsValidCorrelation()
+ public void Update_TwoValues_ReturnsValidCorrel()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
indicator.Update(100.0, 200.0, true);
var result = indicator.Update(102.0, 204.0, true);
Assert.True(double.IsFinite(result.Value));
@@ -45,7 +45,7 @@ public class CorrelationTests
[Fact]
public void Update_PerfectPositiveCorrelation_ReturnsOne()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Same values scaled by constant should give correlation = 1
for (int i = 0; i < 10; i++)
@@ -62,7 +62,7 @@ public class CorrelationTests
[Fact]
public void Update_PerfectNegativeCorrelation_ReturnsMinusOne()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Opposite movements should give correlation = -1
for (int i = 0; i < 10; i++)
@@ -79,7 +79,7 @@ public class CorrelationTests
[Fact]
public void Update_ConstantValues_ReturnsNaN()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Constant values have zero variance, so correlation is undefined
for (int i = 0; i < 10; i++)
@@ -93,8 +93,8 @@ public class CorrelationTests
[Fact]
public void Update_BarCorrection_RestoresState()
{
- var indicator1 = new Correlation(5);
- var indicator2 = new Correlation(5);
+ var indicator1 = new Correl(5);
+ var indicator2 = new Correl(5);
// Feed same initial data
for (int i = 0; i < 10; i++)
@@ -119,8 +119,8 @@ public class CorrelationTests
[Fact]
public void Update_IterativeCorrections_Restore()
{
- var corrected = new Correlation(5);
- var direct = new Correlation(5);
+ var corrected = new Correl(5);
+ var direct = new Correl(5);
// Feed identical initial state
for (int i = 0; i < 8; i++)
@@ -151,7 +151,7 @@ public class CorrelationTests
[Fact]
public void Update_NaNInput_UsesLastValidValue()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Add valid data
for (int i = 0; i < 5; i++)
@@ -169,7 +169,7 @@ public class CorrelationTests
[Fact]
public void Update_InfinityInput_UsesLastValidValue()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Add valid data
for (int i = 0; i < 5; i++)
@@ -185,7 +185,7 @@ public class CorrelationTests
[Fact]
public void IsHot_BelowPeriod_ReturnsFalse()
{
- var indicator = new Correlation(10);
+ var indicator = new Correl(10);
indicator.Update(100.0, 200.0, true);
Assert.False(indicator.IsHot);
}
@@ -193,7 +193,7 @@ public class CorrelationTests
[Fact]
public void IsHot_AtPeriod_ReturnsTrue()
{
- var indicator = new Correlation(10);
+ var indicator = new Correl(10);
for (int i = 0; i < 10; i++)
{
indicator.Update(100.0 + i, 200.0 + i, true);
@@ -204,7 +204,7 @@ public class CorrelationTests
[Fact]
public void Reset_ClearsState()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Add data
for (int i = 0; i < 10; i++)
@@ -224,14 +224,14 @@ public class CorrelationTests
[Fact]
public void Update_TValue_ThrowsNotSupportedException()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
Assert.Throws(() => indicator.Update(new TValue(DateTime.UtcNow, 100.0)));
}
[Fact]
public void Update_TSeries_ThrowsNotSupportedException()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
var series = new TSeries(10);
Assert.Throws(() => indicator.Update(series));
}
@@ -239,7 +239,7 @@ public class CorrelationTests
[Fact]
public void Prime_ThrowsNotSupportedException()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
Assert.Throws(() => indicator.Prime(new double[] { 1, 2, 3 }));
}
@@ -255,7 +255,7 @@ public class CorrelationTests
seriesY.Add(new TValue(DateTime.UtcNow.AddMinutes(i), 200.0 + (i * 2)));
}
- var result = Correlation.Batch(seriesX, seriesY, 5);
+ var result = Correl.Batch(seriesX, seriesY, 5);
Assert.Equal(20, result.Count);
}
@@ -275,7 +275,7 @@ public class CorrelationTests
seriesY.Add(new TValue(DateTime.UtcNow.AddMinutes(i), 200.0 + i));
}
- Assert.Throws(() => Correlation.Batch(seriesX, seriesY, 5));
+ Assert.Throws(() => Correl.Batch(seriesX, seriesY, 5));
}
[Fact]
@@ -291,7 +291,7 @@ public class CorrelationTests
seriesY[i] = 200.0 + (i * 2);
}
- Correlation.Batch(seriesX, seriesY, output, 5);
+ Correl.Batch(seriesX, seriesY, output, 5);
// First value should be NaN (not enough data)
Assert.True(double.IsNaN(output[0]));
@@ -307,7 +307,7 @@ public class CorrelationTests
double[] seriesY = new double[15];
double[] output = new double[10];
- Assert.Throws(() => Correlation.Batch(seriesX, seriesY, output, 5));
+ Assert.Throws(() => Correl.Batch(seriesX, seriesY, output, 5));
}
[Fact]
@@ -317,7 +317,7 @@ public class CorrelationTests
double[] seriesY = new double[20];
double[] output = new double[10];
- Assert.Throws(() => Correlation.Batch(seriesX, seriesY, output, 5));
+ Assert.Throws(() => Correl.Batch(seriesX, seriesY, output, 5));
}
[Fact]
@@ -327,13 +327,13 @@ public class CorrelationTests
double[] seriesY = new double[20];
double[] output = new double[20];
- Assert.Throws(() => Correlation.Batch(seriesX, seriesY, output, 1));
+ Assert.Throws(() => Correl.Batch(seriesX, seriesY, output, 1));
}
[Fact]
public void CorrelationRange_AlwaysBetweenMinusOneAndOne()
{
- var indicator = new Correlation(10);
+ var indicator = new Correl(10);
var gbmX = new GBM(startPrice: 100, mu: 0.02, sigma: 0.3, seed: 12345);
var gbmY = new GBM(startPrice: 200, mu: 0.01, sigma: 0.5, seed: 54321);
@@ -369,7 +369,7 @@ public class CorrelationTests
}
// Streaming calculation
- var indicator = new Correlation(period);
+ var indicator = new Correl(period);
double[] streamingResults = new double[length];
for (int i = 0; i < length; i++)
{
@@ -378,7 +378,7 @@ public class CorrelationTests
// Batch calculation
double[] batchResults = new double[length];
- Correlation.Batch(seriesX, seriesY, batchResults, period);
+ Correl.Batch(seriesX, seriesY, batchResults, period);
// Compare last 50 values (after warmup)
for (int i = length - 50; i < length; i++)
diff --git a/lib/statistics/correlation/tests/Correlation.Validation.Tests.cs b/lib/statistics/correl/tests/Correl.Validation.Tests.cs
similarity index 93%
rename from lib/statistics/correlation/tests/Correlation.Validation.Tests.cs
rename to lib/statistics/correl/tests/Correl.Validation.Tests.cs
index 324d7cb9..85065547 100644
--- a/lib/statistics/correlation/tests/Correlation.Validation.Tests.cs
+++ b/lib/statistics/correl/tests/Correl.Validation.Tests.cs
@@ -8,13 +8,13 @@ namespace QuanTAlib.Tests;
/// Validation tests for Correlation (Pearson Correlation Coefficient) indicator.
/// Validates against Skender.Stock.Indicators.GetCorrelation and mathematical properties.
///
-public sealed class CorrelationValidationTests : IDisposable
+public sealed class CorrelValidationTests : IDisposable
{
private const double Tolerance = 1e-10;
private readonly ValidationTestData _data;
private readonly ITestOutputHelper _output;
- public CorrelationValidationTests(ITestOutputHelper output)
+ public CorrelValidationTests(ITestOutputHelper output)
{
_data = new ValidationTestData();
_output = output;
@@ -29,7 +29,7 @@ public sealed class CorrelationValidationTests : IDisposable
#region External Library Validation — Skender
[Fact]
- public void Validate_Skender_Correlation()
+ public void Validate_Skender_Correl()
{
// === DESCRIPTION ===
// Compares QuanTAlib Correlation against Skender.Stock.Indicators.GetCorrelation
@@ -65,7 +65,7 @@ public sealed class CorrelationValidationTests : IDisposable
var sResult = quotesA.GetCorrelation(quotesB, period).ToList();
// --- QuanTAlib: streaming API ---
- var corr = new Correlation(period);
+ var corr = new Correl(period);
var qValues = new List();
for (int i = 0; i < _data.Count; i++)
@@ -126,7 +126,7 @@ public sealed class CorrelationValidationTests : IDisposable
{
var sResult = _data.SkenderQuotes.GetCorrelation(quotesB, period).ToList();
- var corr = new Correlation(period);
+ var corr = new Correl(period);
int matched = 0;
for (int i = 0; i < _data.Count; i++)
@@ -175,7 +175,7 @@ public sealed class CorrelationValidationTests : IDisposable
var sResult = quotesA.GetCorrelation(quotesB, period).ToList();
- var corr = new Correlation(period);
+ var corr = new Correl(period);
int matched = 0;
for (int i = 0; i < _data.Count; i++)
@@ -207,7 +207,7 @@ public sealed class CorrelationValidationTests : IDisposable
public void Correlation_PerfectLinearPositive_ReturnsOne()
{
// y = a + b*x with b > 0 should give r = 1
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
for (int i = 0; i < 50; i++)
{
@@ -223,7 +223,7 @@ public sealed class CorrelationValidationTests : IDisposable
public void Correlation_PerfectLinearNegative_ReturnsMinusOne()
{
// y = a + b*x with b < 0 should give r = -1
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
for (int i = 0; i < 50; i++)
{
@@ -238,9 +238,9 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_SymmetryProperty_XY_Equals_YX()
{
- // Correlation(X, Y) should equal Correlation(Y, X)
- var indicatorXY = new Correlation(10);
- var indicatorYX = new Correlation(10);
+ // Correl(X, Y) should equal Correl(Y, X)
+ var indicatorXY = new Correl(10);
+ var indicatorYX = new Correl(10);
var gbmX = new GBM(startPrice: 100, mu: 0.02, sigma: 0.2, seed: 12345);
var gbmY = new GBM(startPrice: 50, mu: 0.01, sigma: 0.15, seed: 54321);
@@ -260,8 +260,8 @@ public sealed class CorrelationValidationTests : IDisposable
{
// Correlation is invariant under positive linear transformations
// corr(X, Y) = corr(aX + b, cY + d) when a, c > 0
- var indicator1 = new Correlation(10);
- var indicator2 = new Correlation(10);
+ var indicator1 = new Correl(10);
+ var indicator2 = new Correl(10);
var gbmX = new GBM(startPrice: 100, mu: 0.02, sigma: 0.2, seed: 12345);
var gbmY = new GBM(startPrice: 50, mu: 0.01, sigma: 0.15, seed: 54321);
@@ -283,7 +283,7 @@ public sealed class CorrelationValidationTests : IDisposable
public void Correlation_BoundedProperty_AlwaysBetweenMinusOneAndOne()
{
// Correlation coefficient is always in [-1, 1]
- var indicator = new Correlation(10);
+ var indicator = new Correl(10);
var gbmX = new GBM(startPrice: 100, mu: 0.1, sigma: 0.5, seed: 12345);
var gbmY = new GBM(startPrice: 50, mu: -0.05, sigma: 0.3, seed: 54321);
@@ -304,7 +304,7 @@ public sealed class CorrelationValidationTests : IDisposable
public void Correlation_ZeroVariance_ReturnsNaN()
{
// When one or both series have zero variance, correlation is undefined
- var indicator = new Correlation(10);
+ var indicator = new Correl(10);
for (int i = 0; i < 20; i++)
{
@@ -332,7 +332,7 @@ public sealed class CorrelationValidationTests : IDisposable
// r = Cov(X,Y) / sqrt(Var(X) * Var(Y)) = 1.2 / sqrt(2 * 1.2) = 1.2 / sqrt(2.4)
// = 1.2 / 1.5492 ≈ 0.7746
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
double[] x = [1, 2, 3, 4, 5];
double[] y = [2, 4, 5, 4, 5];
@@ -346,11 +346,11 @@ public sealed class CorrelationValidationTests : IDisposable
}
[Fact]
- public void Correlation_KnownValues_NoCorrelation()
+ public void Correlation_KnownValues_NoCorrel()
{
// X = [1, 2, 3, 4, 5], Y = [3, 3, 3, 3, 3] (constant)
// Should be NaN (or 0 with special handling)
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
double[] x = [1, 2, 3, 4, 5];
double[] y = [3, 3, 3, 3, 3];
@@ -383,10 +383,10 @@ public sealed class CorrelationValidationTests : IDisposable
}
// Batch calculation
- var batchResult = Correlation.Batch(seriesX, seriesY, 20);
+ var batchResult = Correl.Batch(seriesX, seriesY, 20);
// Streaming calculation
- var streamingIndicator = new Correlation(20);
+ var streamingIndicator = new Correl(20);
for (int i = 0; i < seriesX.Count; i++)
{
streamingIndicator.Update(seriesX[i].Value, seriesY[i].Value);
@@ -420,10 +420,10 @@ public sealed class CorrelationValidationTests : IDisposable
}
// Span calculation
- Correlation.Batch(seriesX, seriesY, output, 20);
+ Correl.Batch(seriesX, seriesY, output, 20);
// Streaming calculation
- var streamingIndicator = new Correlation(20);
+ var streamingIndicator = new Correl(20);
for (int i = 0; i < length; i++)
{
streamingIndicator.Update(seriesX[i], seriesY[i]);
@@ -443,7 +443,7 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_ResetProducesSameResults()
{
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
var gbmX = new GBM(startPrice: 100, mu: 0.02, sigma: 0.2, seed: 12345);
var gbmY = new GBM(startPrice: 50, mu: 0.01, sigma: 0.15, seed: 54321);
@@ -475,7 +475,7 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_SlidingWindow_MovesCorrectly()
{
- var indicator = new Correlation(5);
+ var indicator = new Correl(5);
// Build up with known values for period 5
// After 5 values, window should be full
@@ -501,7 +501,7 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_SlidingWindow_DropsOldValues()
{
- var indicator = new Correlation(3);
+ var indicator = new Correl(3);
// First window: perfectly correlated
indicator.Update(1, 2);
@@ -522,7 +522,7 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_LargeValues_MaintainsStability()
{
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
for (int i = 0; i < 50; i++)
{
@@ -538,7 +538,7 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_SmallValues_MaintainsStability()
{
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
// Use values that are small but not so small they cause numerical issues
for (int i = 0; i < 50; i++)
@@ -555,7 +555,7 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_MixedMagnitudes_HandlesCorrectly()
{
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
for (int i = 0; i < 50; i++)
{
@@ -576,7 +576,7 @@ public sealed class CorrelationValidationTests : IDisposable
public void Correlation_HighPositiveCorrelation_DetectedCorrectly()
{
// Create two series with high positive correlation (r ≈ 0.95+)
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
// Use deterministic data that creates high correlation
for (int i = 0; i < 100; i++)
@@ -593,7 +593,7 @@ public sealed class CorrelationValidationTests : IDisposable
public void Correlation_NegativeCorrelation_DetectedCorrectly()
{
// Create two series with negative correlation
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
var random = new GBM(startPrice: 100.0, sigma: 1.0, seed: 42);
for (int i = 0; i < 100; i++)
@@ -612,7 +612,7 @@ public sealed class CorrelationValidationTests : IDisposable
// Create two series with weak correlation: pure independent noise, no shared trend.
// Use two independent GBMs (different seeds) and feed their incremental log-returns directly.
// With period=20 and fully independent noise sequences, correlation should be near zero.
- var indicator = new Correlation(20);
+ var indicator = new Correl(20);
var gbmX = new GBM(startPrice: 100.0, sigma: 0.2, seed: 43);
var gbmY = new GBM(startPrice: 100.0, sigma: 0.2, seed: 9871);
var barsX = gbmX.Fetch(101, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1));
@@ -637,9 +637,9 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_DifferentPeriods_ProduceDifferentResults()
{
- var indicator5 = new Correlation(5);
- var indicator20 = new Correlation(20);
- var indicator50 = new Correlation(50);
+ var indicator5 = new Correl(5);
+ var indicator20 = new Correl(20);
+ var indicator50 = new Correl(50);
var gbmX = new GBM(startPrice: 100, mu: 0.02, sigma: 0.2, seed: 12345);
var gbmY = new GBM(startPrice: 50, mu: 0.01, sigma: 0.15, seed: 54321);
@@ -660,8 +660,8 @@ public sealed class CorrelationValidationTests : IDisposable
[Fact]
public void Correlation_SmallPeriod_MoreVolatile()
{
- var indicator3 = new Correlation(3);
- var indicator30 = new Correlation(30);
+ var indicator3 = new Correl(3);
+ var indicator30 = new Correl(30);
var gbmX = new GBM(startPrice: 100, mu: 0.02, sigma: 0.2, seed: 12345);
var gbmY = new GBM(startPrice: 50, mu: 0.01, sigma: 0.15, seed: 54321);
@@ -732,7 +732,7 @@ public sealed class CorrelationValidationTests : IDisposable
Assert.True(length > 100, $"TALib Correl produced only {length} values");
// QuanTAlib streaming
- var corr = new Correlation(period);
+ var corr = new Correl(period);
var qlValues = new double[_data.Count];
for (int i = 0; i < _data.Count; i++)
{
@@ -770,7 +770,7 @@ public sealed class CorrelationValidationTests : IDisposable
(int offset, int length) = outRange.GetOffsetAndLength(taOut.Length);
- var corr = new Correlation(period);
+ var corr = new Correl(period);
var qlValues = new double[_data.Count];
for (int i = 0; i < _data.Count; i++)
{
diff --git a/lib/statistics/covariance/Covariance.md b/lib/statistics/covariance/Covariance.md
index 72f38eeb..4042f492 100644
--- a/lib/statistics/covariance/Covariance.md
+++ b/lib/statistics/covariance/Covariance.md
@@ -13,7 +13,7 @@
| **PineScript** | [covariance.pine](covariance.pine) |
- Covariance measures the joint variability of two random variables.
-- **Similar:** [Correlation](../correlation/Correlation.md), [Beta](../beta/Beta.md) | **Trading note:** Rolling covariance; measures how two assets move together. Foundation of portfolio theory.
+- **Similar:** [Correl](../correl/Correl.md), [Beta](../beta/Beta.md) | **Trading note:** Rolling covariance; measures how two assets move together. Foundation of portfolio theory.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Covariance measures the joint variability of two random variables. It indicates the direction of the linear relationship between variables.
diff --git a/lib/statistics/granger/Granger.md b/lib/statistics/granger/Granger.md
index 718356a8..276c01c7 100644
--- a/lib/statistics/granger/Granger.md
+++ b/lib/statistics/granger/Granger.md
@@ -13,7 +13,7 @@
| **PineScript** | [granger.pine](granger.pine) |
- The Granger Causality test asks a precise, falsifiable question: does knowing the history of series X improve your ability to predict series Y, bey...
-- **Similar:** [Cointegration](../cointegration/Cointegration.md), [Correlation](../correlation/Correlation.md) | **Trading note:** Granger causality test; determines if one time series can forecast another. Lead-lag detection.
+- **Similar:** [Cointegration](../cointegration/Cointegration.md), [Correl](../correl/Correl.md) | **Trading note:** Granger causality test; determines if one time series can forecast another. Lead-lag detection.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
## Introduction
diff --git a/lib/statistics/spearman/Spearman.md b/lib/statistics/spearman/Spearman.md
index d619a054..179881de 100644
--- a/lib/statistics/spearman/Spearman.md
+++ b/lib/statistics/spearman/Spearman.md
@@ -13,7 +13,7 @@
| **PineScript** | [spearman.pine](spearman.pine) |
- Spearman's ρ (rho) measures the strength and direction of monotonic association between two variables.
-- **Similar:** [Correlation](../correlation/Correlation.md), [Kendall](../kendall/Kendall.md) | **Trading note:** Spearman rank correlation; non-parametric, detects monotonic (not just linear) relationships.
+- **Similar:** [Correl](../correl/Correl.md), [Kendall](../kendall/Kendall.md) | **Trading note:** Spearman rank correlation; non-parametric, detects monotonic (not just linear) relationships.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
Spearman's ρ (rho) measures the strength and direction of monotonic association between two variables. Unlike Pearson's correlation, which measures linear relationship, Spearman captures any monotonic relationship. A portfolio of stocks whose returns move monotonically together has different risk than one whose components merely share a linear trend. Spearman detects both.
diff --git a/lib/statistics/theil/Theil.md b/lib/statistics/theil/Theil.md
index 470d5ef5..2627f36d 100644
--- a/lib/statistics/theil/Theil.md
+++ b/lib/statistics/theil/Theil.md
@@ -13,7 +13,7 @@
| **PineScript** | [theil.pine](theil.pine) |
- The Theil T Index is an information-theoretic measure of inequality (or concentration) within a distribution of positive values.
-- **Similar:** [Correlation](../correlation/Correlation.md), [LinReg](../linreg/LinReg.md) | **Trading note:** Theil–Sen estimator; robust slope calculation using medians of pairwise slopes. Resistant to outliers.
+- **Similar:** [Correl](../correl/Correl.md), [LinReg](../linreg/LinReg.md) | **Trading note:** Theil–Sen estimator; robust slope calculation using medians of pairwise slopes. Resistant to outliers.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
## Introduction
diff --git a/lib/trends_IIR/_index.md b/lib/trends_IIR/_index.md
index 7f507a89..9c49c9d2 100644
--- a/lib/trends_IIR/_index.md
+++ b/lib/trends_IIR/_index.md
@@ -15,7 +15,7 @@ Trend indicators based on Infinite Impulse Response (IIR) filters. Recursive arc
| [GDEMA](gdema/Gdema.md) | Generalized Double Exponential MA | Generalized DEMA with configurable volume factor for tunable lag/smoothness trade-off. |
| [HEMA](hema/Hema.md) | Hull Exponential MA | EMA-domain Hull analog using half-life timing and de-lagged EMA cascade. |
| [HOLT](holt/Holt.md) | Holt Exponential Smoothing | Double exponential smoothing with separate level and trend components for adaptive trend-following. |
-| [HTIT](htit/Htit.md) | Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE) | Utilizes Hilbert Transform to isolate instantaneous trend component, providing zero-lag trendline with hybrid FIR-in-IIR design. |
+| [HT_TRENDLINE](ht_trendline/HtTrendline.md) | Ehlers Hilbert Transform Instantaneous Trend | Utilizes Hilbert Transform to isolate instantaneous trend component, providing zero-lag trendline with hybrid FIR-in-IIR design. |
| [HWMA](hwma/Hwma.md) | Holt-Winters MA | Triple exponential smoothing. Tracks level, velocity, acceleration. Recursive IIR structure. |
| [JMA](jma/Jma.md) | Jurik MA | Adaptive filter achieving high noise reduction and low phase delay through multi-stage volatility normalization and dynamic parameter optimization. |
| [KAMA](kama/Kama.md) | Kaufman Adaptive MA | Automatically adjusts sensitivity based on market volatility using Efficiency Ratio, balancing responsiveness and stability. |
diff --git a/lib/trends_IIR/decycler/Decycler.md b/lib/trends_IIR/decycler/Decycler.md
index e8315100..d3c36858 100644
--- a/lib/trends_IIR/decycler/Decycler.md
+++ b/lib/trends_IIR/decycler/Decycler.md
@@ -14,7 +14,7 @@
| **Signature** | [decycler_signature](decycler_signature.md) |
- The Ehlers Decycler extracts the trend component from a price series by subtracting a 2-pole Butterworth high-pass filter from the source signal.
-- **Similar:** [EMA](../ema/ema.md), [HTIT](../htit/htit.md) | **Complementary:** Cycle indicators | **Trading note:** Ehlers Decycler; high-pass complement removes cycle components to isolate trend.
+- **Similar:** [EMA](../ema/ema.md), [HT_TRENDLINE](../ht_trendline/HtTrendline.md) | **Complementary:** Cycle indicators | **Trading note:** Ehlers Decycler; high-pass complement removes cycle components to isolate trend.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
The Ehlers Decycler extracts the trend component from a price series by subtracting a 2-pole Butterworth high-pass filter from the source signal. Where most moving averages blur the boundary between trend and cycle, the Decycler defines it with a frequency-domain cutoff: cycles shorter than the specified period are removed, everything longer stays. The result is an overlay that hugs price with near-zero lag during trends and rejects short-term oscillations without the smoothing artifacts of convolution-based averages.
diff --git a/lib/trends_IIR/htit/Htit.Quantower.cs b/lib/trends_IIR/ht_trendline/HtTrendline.Quantower.cs
similarity index 77%
rename from lib/trends_IIR/htit/Htit.Quantower.cs
rename to lib/trends_IIR/ht_trendline/HtTrendline.Quantower.cs
index faa1fb70..c850fd34 100644
--- a/lib/trends_IIR/htit/Htit.Quantower.cs
+++ b/lib/trends_IIR/ht_trendline/HtTrendline.Quantower.cs
@@ -5,7 +5,7 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
[SkipLocalsInit]
-public sealed class HtitIndicator : Indicator, IWatchlistIndicator
+public sealed class HtTrendlineIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Period", sortIndex: 1, 1, 2000, 1, 0)]
public int Period { get; set; } = 50; // Not used in calculation but kept for consistency
@@ -16,7 +16,7 @@ public sealed class HtitIndicator : Indicator, IWatchlistIndicator
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
- private Htit _htit = null!;
+ private HtTrendline _htit = null!;
private readonly LineSeries _series;
private string _sourceName = null!;
private Func _priceSelector = null!;
@@ -24,15 +24,15 @@ public sealed class HtitIndicator : Indicator, IWatchlistIndicator
public static int MinHistoryDepths => 0;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
- public override string ShortName => $"HTIT:{_sourceName}";
+ public override string ShortName => $"HT_TRENDLINE:{_sourceName}";
- public HtitIndicator()
+ public HtTrendlineIndicator()
{
OnBackGround = true;
SeparateWindow = false;
- Name = "HTIT - Ehlers Hilbert Transform Instantaneous Trend";
+ Name = "HT_TRENDLINE - Ehlers Hilbert Transform Instantaneous Trend";
Description = "Ehlers Hilbert Transform Instantaneous Trend";
- _series = new LineSeries(name: "HTIT", color: Color.Orange, width: 2, style: LineStyle.Solid);
+ _series = new LineSeries(name: "HT_TRENDLINE", color: Color.Orange, width: 2, style: LineStyle.Solid);
AddLineSeries(_series);
}
@@ -40,7 +40,7 @@ public sealed class HtitIndicator : Indicator, IWatchlistIndicator
{
_priceSelector = Source.GetPriceSelector();
_sourceName = Source.ToString();
- _htit = new Htit();
+ _htit = new HtTrendline();
base.OnInit();
}
diff --git a/lib/trends_IIR/htit/Htit.cs b/lib/trends_IIR/ht_trendline/HtTrendline.cs
similarity index 96%
rename from lib/trends_IIR/htit/Htit.cs
rename to lib/trends_IIR/ht_trendline/HtTrendline.cs
index 9777e085..b5a5c75c 100644
--- a/lib/trends_IIR/htit/Htit.cs
+++ b/lib/trends_IIR/ht_trendline/HtTrendline.cs
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
namespace QuanTAlib;
///
-/// HTIT: Hilbert Transform Instantaneous Trendline (also known as HT_TRENDLINE)
+/// HT_TRENDLINE: Hilbert Transform Instantaneous Trendline (also known as HT_TRENDLINE)
///
///
/// Ehlers' adaptive trendline using Hilbert Transform cycle measurement.
@@ -12,10 +12,10 @@ namespace QuanTAlib;
///
/// Key features: homodyne discriminator, period-adaptive averaging window.
///
-/// Detailed documentation
-/// Reference Pine Script implementation
+/// Detailed documentation
+/// Reference Pine Script implementation
[SkipLocalsInit]
-public sealed class Htit : AbstractBase
+public sealed class HtTrendline : AbstractBase
{
public override bool IsHot => _state.Index >= WarmupPeriod;
@@ -48,9 +48,9 @@ public sealed class Htit : AbstractBase
private const double TwoPi = 2.0 * Math.PI;
private const double MinDeltaRadians = Math.PI / 180.0; // 1 degree in radians
- public Htit()
+ public HtTrendline()
{
- Name = "Htit";
+ Name = "HtTrendline";
WarmupPeriod = 12;
_handler = Handle;
@@ -66,7 +66,7 @@ public sealed class Htit : AbstractBase
Init();
}
- public Htit(ITValuePublisher source) : this()
+ public HtTrendline(ITValuePublisher source) : this()
{
source.Pub += _handler;
}
@@ -266,7 +266,7 @@ public sealed class Htit : AbstractBase
/// For high-performance batch-only processing, use the static Calculate method instead.
///
/// Input time series
- /// Output time series with HTIT values
+ /// Output time series with HT_TRENDLINE values
public override TSeries Update(TSeries source)
{
if (source.Count == 0)
@@ -303,8 +303,8 @@ public sealed class Htit : AbstractBase
public static TSeries Batch(TSeries source)
{
- var htit = new Htit();
- return htit.Update(source);
+ var httrendline = new HtTrendline();
+ return httrendline.Update(source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -513,9 +513,9 @@ public sealed class Htit : AbstractBase
}
}
- public static (TSeries Results, Htit Indicator) Calculate(TSeries source)
+ public static (TSeries Results, HtTrendline Indicator) Calculate(TSeries source)
{
- var indicator = new Htit();
+ var indicator = new HtTrendline();
TSeries results = indicator.Update(source);
return (results, indicator);
}
diff --git a/lib/trends_IIR/htit/Htit.md b/lib/trends_IIR/ht_trendline/HtTrendline.md
similarity index 83%
rename from lib/trends_IIR/htit/Htit.md
rename to lib/trends_IIR/ht_trendline/HtTrendline.md
index c04b3cbb..64b6093b 100644
--- a/lib/trends_IIR/htit/Htit.md
+++ b/lib/trends_IIR/ht_trendline/HtTrendline.md
@@ -1,29 +1,29 @@
-# HTIT: Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE)
+# HT_TRENDLINE: Ehlers Hilbert Transform Instantaneous Trend (also known as HT_TRENDLINE)
-> *John Ehlers brought rocket science to trading. Literally. HTIT uses signal processing to find the trend by removing the cycle. It's not smoothing; it's extraction.*
+> *John Ehlers brought rocket science to trading. Literally. HT_TRENDLINE uses signal processing to find the trend by removing the cycle. It's not smoothing; it's extraction.*
| Property | Value |
| ---------------- | -------------------------------- |
| **Category** | Trend (IIR MA) |
| **Inputs** | Source (close) |
| **Parameters** | None |
-| **Outputs** | Single series (HTIT) |
+| **Outputs** | Single series (HT_TRENDLINE) |
| **Output range** | Tracks input |
| **Warmup** | `12` bars |
-| **PineScript** | [htit.pine](htit.pine) |
-| **Signature** | [htit_signature](htit_signature.md) |
+| **PineScript** | [ht_trendline.pine](ht_trendline.pine) |
+| **Signature** | [ht_trendline_signature](ht_trendline_signature.md) |
-- HTIT (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging.
+- HT_TRENDLINE (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging.
- **Similar:** [MAMA](../mama/mama.md), [DEMA](../dema/dema.md) | **Complementary:** HT_DCPeriod | **Trading note:** Hilbert Transform trendline; cycle-adaptive smoothing.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
-HTIT (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging. Instead, it uses the Hilbert Transform to measure the dominant cycle period of the market and then computes a trendline that filters out that specific cycle. It adapts to the market's rhythm rather than imposing a fixed period.
+HT_TRENDLINE (Hilbert Transform Instantaneous Trend) is a trend-following indicator that doesn't rely on simple averaging. Instead, it uses the Hilbert Transform to measure the dominant cycle period of the market and then computes a trendline that filters out that specific cycle. It adapts to the market's rhythm rather than imposing a fixed period.
## Historical Context
John Ehlers, a pioneer in applying DSP to trading, introduced this in his book *Rocket Science for Traders*. He recognized that markets have cyclic components (noise) and trend components. By identifying the cycle, you can mathematically subtract it to reveal the pure trend.
-Most trend indicators (SMA, EMA) are low-pass filters: they let low frequencies (trend) pass and block high frequencies (noise). The problem is that "noise" in markets isn't random white noise; it's often cyclic. A fixed-period SMA might filter out a 10-day cycle perfectly but amplify a 20-day cycle. HTIT solves this by measuring the cycle first, then tuning the filter to kill exactly that frequency.
+Most trend indicators (SMA, EMA) are low-pass filters: they let low frequencies (trend) pass and block high frequencies (noise). The problem is that "noise" in markets isn't random white noise; it's often cyclic. A fixed-period SMA might filter out a 10-day cycle perfectly but amplify a 20-day cycle. HT_TRENDLINE solves this by measuring the cycle first, then tuning the filter to kill exactly that frequency.
## Architecture & Physics
@@ -84,11 +84,11 @@ Where $\text{DC}$ is the integer part of the smoothed dominant cycle period.
The Instantaneous Trend is smoothed again using the same 4-bar WMA to remove any residual stepping artifacts from the integer period changes.
-$$ \text{HTIT}_t = \frac{4 \text{IT}_t + 3 \text{IT}_{t-1} + 2 \text{IT}_{t-2} + \text{IT}_{t-3}}{10} $$
+$$ \text{HT_TRENDLINE}_t = \frac{4 \text{IT}_t + 3 \text{IT}_{t-1} + 2 \text{IT}_{t-2} + \text{IT}_{t-3}}{10} $$
## Mathematical Precision & Implementation Philosophy
-Like our MAMA implementation, QuanTAlib's HTIT prioritizes mathematical correctness over blind porting.
+Like our MAMA implementation, QuanTAlib's HT_TRENDLINE prioritizes mathematical correctness over blind porting.
| Aspect | Other Libraries | QuanTAlib | Rationale |
| :----------------------- | :----------------- | :---------------------- | :-------------------------------------------- |
@@ -102,7 +102,7 @@ We use `atan2` for robust phase calculation and maintain full double precision t
## Performance Profile
-HTIT is computationally heavier than a simple MA but lighter than MAMA. The main cost is the loop for the Instantaneous Trend calculation, which sums up to 50 past prices.
+HT_TRENDLINE is computationally heavier than a simple MA but lighter than MAMA. The main cost is the loop for the Instantaneous Trend calculation, which sums up to 50 past prices.
### Operation Count (Streaming Mode, Scalar)
@@ -144,7 +144,7 @@ HTIT is computationally heavier than a simple MA but lighter than MAMA. The main
### Batch Mode (SIMD Analysis)
-HTIT is **not SIMD-parallelizable** across bars due to:
+HT_TRENDLINE is **not SIMD-parallelizable** across bars due to:
1. Recursive feedback in Hilbert transforms (I2, Q2 depend on previous values)
2. Period-dependent IT summation loop (variable iteration count)
3. Homodyne discriminator state dependencies
diff --git a/lib/trends_IIR/htit/htit.pine b/lib/trends_IIR/ht_trendline/ht_trendline.pine
similarity index 85%
rename from lib/trends_IIR/htit/htit.pine
rename to lib/trends_IIR/ht_trendline/ht_trendline.pine
index 755a328b..10a3a84d 100644
--- a/lib/trends_IIR/htit/htit.pine
+++ b/lib/trends_IIR/ht_trendline/ht_trendline.pine
@@ -1,13 +1,13 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Ehlers Hilbert Transform Instantaneous Trend (HTIT)", "HTIT", overlay=true)
+indicator("Ehlers Hilbert Transform Instantaneous Trend (HT_TRENDLINE)", "HT_TRENDLINE", overlay=true)
-//@function Calculates the Hilbert Transform Instantaneous Trendline (HTIT)
-//@param source Series to calculate HTIT from
-//@returns HTIT value using Hilbert Transform with adaptive period estimation
+//@function Calculates the Hilbert Transform Instantaneous Trendline (HT_TRENDLINE)
+//@param source Series to calculate HT_TRENDLINE from
+//@returns HT_TRENDLINE value using Hilbert Transform with adaptive period estimation
//@optimized Uses Hilbert Transform quadrature components for O(1) complexity per bar
-htit(series float source) =>
+httrendline(series float source) =>
var float price = na
var float smooth = na
var float detrender = 0.0
@@ -56,7 +56,7 @@ htit(series float source) =>
i_source = input.source(close, "Source")
// Calculation
-htit_value = htit(i_source)
+htit_value = httrendline(i_source)
// Plot
-plot(htit_value, "HTIT", color=color.yellow, linewidth=2)
+plot(htit_value, "HT_TRENDLINE", color=color.yellow, linewidth=2)
diff --git a/lib/trends_IIR/ht_trendline/ht_trendline_signature.md b/lib/trends_IIR/ht_trendline/ht_trendline_signature.md
new file mode 100644
index 00000000..d3dbc8e1
--- /dev/null
+++ b/lib/trends_IIR/ht_trendline/ht_trendline_signature.md
@@ -0,0 +1,3 @@
+# HOLT Signature
+
+
\ No newline at end of file
diff --git a/lib/trends_IIR/htit/htit_signature.svg b/lib/trends_IIR/ht_trendline/ht_trendline_signature.svg
similarity index 99%
rename from lib/trends_IIR/htit/htit_signature.svg
rename to lib/trends_IIR/ht_trendline/ht_trendline_signature.svg
index 6922917f..a1c5452c 100644
--- a/lib/trends_IIR/htit/htit_signature.svg
+++ b/lib/trends_IIR/ht_trendline/ht_trendline_signature.svg
@@ -19508,7 +19508,7 @@ L 1441.19952 978.875435
-
+
();
foreach (var item in data)
{
- streamingResults.Add(htit.Update(item).Value);
+ streamingResults.Add(httrendline.Update(item).Value);
}
for (int i = 0; i < resultSeries.Count; i++)
@@ -48,16 +48,16 @@ public class HtitTests
[Fact]
public void Calculate_Span_Matches_Update()
{
- var htit = new Htit();
+ var httrendline = new HtTrendline();
var data = _gbm.Fetch(100, DateTime.UtcNow.Ticks, TimeSpan.FromMinutes(1)).Close;
var series = data;
- var resultSeries = htit.Update(series);
+ var resultSeries = httrendline.Update(series);
var spanInput = data.Values.ToArray();
var spanOutput = new double[spanInput.Length];
- Htit.Batch(spanInput, spanOutput);
+ HtTrendline.Batch(spanInput, spanOutput);
for (int i = 0; i < resultSeries.Count; i++)
{
@@ -68,38 +68,38 @@ public class HtitTests
[Fact]
public void Handles_NaN()
{
- var htit = new Htit();
- htit.Update(new TValue(DateTime.UtcNow.Ticks, 100.0));
- htit.Update(new TValue(DateTime.UtcNow.Ticks, double.NaN));
+ var httrendline = new HtTrendline();
+ httrendline.Update(new TValue(DateTime.UtcNow.Ticks, 100.0));
+ httrendline.Update(new TValue(DateTime.UtcNow.Ticks, double.NaN));
- Assert.Equal(100.0, htit.Last.Value);
+ Assert.Equal(100.0, httrendline.Last.Value);
}
[Fact]
public void Htit_Calc_IsNew_AcceptsParameter()
{
- var htit = new Htit();
- htit.Update(new TValue(DateTime.UtcNow, 100), isNew: true);
- Assert.Equal(100, htit.Last.Value);
+ var httrendline = new HtTrendline();
+ httrendline.Update(new TValue(DateTime.UtcNow, 100), isNew: true);
+ Assert.Equal(100, httrendline.Last.Value);
}
[Fact]
public void Htit_Reset_ClearsState()
{
- var htit = new Htit();
- htit.Update(new TValue(DateTime.UtcNow, 100));
- htit.Update(new TValue(DateTime.UtcNow, 110));
+ var httrendline = new HtTrendline();
+ httrendline.Update(new TValue(DateTime.UtcNow, 100));
+ httrendline.Update(new TValue(DateTime.UtcNow, 110));
- htit.Reset();
+ httrendline.Reset();
- Assert.True(double.IsNaN(htit.Last.Value));
- Assert.False(htit.IsHot);
+ Assert.True(double.IsNaN(httrendline.Last.Value));
+ Assert.False(httrendline.IsHot);
}
[Fact]
public void Htit_IterativeCorrections_RestoreToOriginalState()
{
- var htit = new Htit();
+ var httrendline = new HtTrendline();
var gbm = new GBM(startPrice: 100.0, mu: 0.02, sigma: 0.1);
// Feed 20 new values (needs > 12 for warmup)
@@ -108,21 +108,21 @@ public class HtitTests
{
var bar = gbm.Next(isNew: true);
lastInput = new TValue(bar.Time, bar.Close);
- htit.Update(lastInput, isNew: true);
+ httrendline.Update(lastInput, isNew: true);
}
// Remember state after 20 values
- double valueAfterTwenty = htit.Last.Value;
+ double valueAfterTwenty = httrendline.Last.Value;
// Generate 9 corrections with isNew=false (different values)
for (int i = 0; i < 9; i++)
{
var bar = gbm.Next(isNew: false);
- htit.Update(new TValue(bar.Time, bar.Close), isNew: false);
+ httrendline.Update(new TValue(bar.Time, bar.Close), isNew: false);
}
// Feed the remembered 20th input again with isNew=false
- TValue finalValue = htit.Update(lastInput, isNew: false);
+ TValue finalValue = httrendline.Update(lastInput, isNew: false);
// Should match the original state after 20 values
Assert.Equal(valueAfterTwenty, finalValue.Value, 1e-9);
@@ -134,7 +134,7 @@ public class HtitTests
double[] source = [1, 2, 3, 4, 5];
double[] wrongSizeOutput = new double[3];
- Assert.Throws(() => Htit.Batch(source.AsSpan(), wrongSizeOutput.AsSpan()));
+ Assert.Throws(() => HtTrendline.Batch(source.AsSpan(), wrongSizeOutput.AsSpan()));
}
[Fact]
@@ -143,7 +143,7 @@ public class HtitTests
double[] source = [100, 110, double.NaN, 120, 130];
double[] output = new double[5];
- Htit.Batch(source.AsSpan(), output.AsSpan());
+ HtTrendline.Batch(source.AsSpan(), output.AsSpan());
foreach (var val in output)
{
@@ -160,18 +160,18 @@ public class HtitTests
var series = bars.Close;
// 1. Batch Mode
- var batchSeries = Htit.Batch(series);
+ var batchSeries = HtTrendline.Batch(series);
double expected = batchSeries.Last.Value;
// 2. Span Mode
var tValues = series.Values.ToArray();
var spanInput = new ReadOnlySpan(tValues);
var spanOutput = new double[tValues.Length];
- Htit.Batch(spanInput, spanOutput);
+ HtTrendline.Batch(spanInput, spanOutput);
double spanResult = spanOutput[^1];
// 3. Streaming Mode
- var streamingInd = new Htit();
+ var streamingInd = new HtTrendline();
for (int i = 0; i < series.Count; i++)
{
streamingInd.Update(series[i]);
@@ -180,7 +180,7 @@ public class HtitTests
// 4. Eventing Mode
var pubSource = new TSeries();
- var eventingInd = new Htit(pubSource);
+ var eventingInd = new HtTrendline(pubSource);
for (int i = 0; i < series.Count; i++)
{
pubSource.Add(series[i]);
diff --git a/lib/trends_IIR/htit/tests/Htit.Validation.Tests.cs b/lib/trends_IIR/ht_trendline/tests/HtTrendline.Validation.Tests.cs
similarity index 84%
rename from lib/trends_IIR/htit/tests/Htit.Validation.Tests.cs
rename to lib/trends_IIR/ht_trendline/tests/HtTrendline.Validation.Tests.cs
index fbebef84..c0de6d41 100644
--- a/lib/trends_IIR/htit/tests/Htit.Validation.Tests.cs
+++ b/lib/trends_IIR/ht_trendline/tests/HtTrendline.Validation.Tests.cs
@@ -5,12 +5,12 @@ using TALib;
namespace QuanTAlib.Tests;
-public sealed class HtitValidationTests : IDisposable
+public sealed class HtTrendlineValidationTests : IDisposable
{
private readonly ValidationTestData _data;
private bool _disposed;
- public HtitValidationTests()
+ public HtTrendlineValidationTests()
{
_data = new ValidationTestData(10000);
}
@@ -38,16 +38,16 @@ public sealed class HtitValidationTests : IDisposable
[Fact]
public void Validate_TaLib()
{
- // Calculate TA-Lib HTIT
+ // Calculate TA-Lib HT_TRENDLINE
var input = _data.RawData.Span;
var output = new double[input.Length];
var retCode = TALib.Functions.HtTrendline(input, 0..^0, output, out var outRange);
Assert.Equal(TALib.Core.RetCode.Success, retCode);
- // Calculate QuanTAlib HTIT
- var htit = new Htit();
- var quantalibResults = htit.Update(_data.Data);
+ // Calculate QuanTAlib HT_TRENDLINE
+ var httrendline = new HtTrendline();
+ var quantalibResults = httrendline.Update(_data.Data);
// Compare results
// TA-Lib HT_TRENDLINE has a lookback of 63
@@ -67,13 +67,13 @@ public sealed class HtitValidationTests : IDisposable
[Fact]
public void Validate_Skender_Batch()
{
- // Calculate Skender HTIT
+ // Calculate Skender HT_TRENDLINE
var skenderResults = _data.SkenderQuotes.GetHtTrendline().ToList();
- // Calculate QuanTAlib HTIT
- var htit = new Htit();
+ // Calculate QuanTAlib HT_TRENDLINE
+ var httrendline = new HtTrendline();
var series = _data.Data;
- var quantalibResults = htit.Update(series);
+ var quantalibResults = httrendline.Update(series);
// Compare results
// Skip warmup period (Skender needs 100 periods for convergence, but we can check after 50)
@@ -97,16 +97,16 @@ public sealed class HtitValidationTests : IDisposable
[Fact]
public void Validate_Skender_Streaming()
{
- // Calculate Skender HTIT
+ // Calculate Skender HT_TRENDLINE
var skenderResults = _data.SkenderQuotes.GetHtTrendline().ToList();
- // Calculate QuanTAlib HTIT Streaming
- var htit = new Htit();
+ // Calculate QuanTAlib HT_TRENDLINE Streaming
+ var httrendline = new HtTrendline();
var streamingResults = new List();
foreach (var item in _data.Data)
{
- streamingResults.Add(htit.Update(item).Value);
+ streamingResults.Add(httrendline.Update(item).Value);
}
// Compare results
@@ -139,14 +139,14 @@ public sealed class HtitValidationTests : IDisposable
Volume = (double)q.Volume
}).ToList();
- // Calculate Ooples HTIT
+ // Calculate Ooples HT_TRENDLINE
var stockData = new StockData(ooplesData);
var oResult = stockData.CalculateEhlersInstantaneousTrendlineV1();
var oValues = oResult.OutputValues["Eit"];
- // Calculate QuanTAlib HTIT
- var htit = new Htit();
- var quantalibResults = htit.Update(_data.Data);
+ // Calculate QuanTAlib HT_TRENDLINE
+ var httrendline = new HtTrendline();
+ var quantalibResults = httrendline.Update(_data.Data);
// Compare results
// Ooples might have different warmup or calculation details
diff --git a/lib/trends_IIR/htit/htit_signature.md b/lib/trends_IIR/htit/htit_signature.md
deleted file mode 100644
index d33c9d6d..00000000
--- a/lib/trends_IIR/htit/htit_signature.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# HOLT Signature
-
-
\ No newline at end of file
diff --git a/lib/volume/_index.md b/lib/volume/_index.md
index b253078a..7805aa50 100644
--- a/lib/volume/_index.md
+++ b/lib/volume/_index.md
@@ -4,7 +4,7 @@ Volume is market fuel. Price tells what happened; volume tells how hard the mark
| Indicator | Full Name | Description |
| :--- | :--- | :--- |
-| [ADL](adl/Adl.md) | Accumulation/Distribution Line | Correlates price location within range to volume. Grandfather of volume flow analysis. |
+| [AD](ad/Ad.md) | Accumulation/Distribution Line | Correlates price location within range to volume. Grandfather of volume flow analysis. |
| [ADOSC](adosc/Adosc.md) | Chaikin A/D Oscillator | Momentum indicator for AD Line. Predicts reversals by measuring acceleration of money flow. |
| [AOBV](aobv/Aobv.md) | Archer On-Balance Volume | Dual EMA smoothing of OBV for cleaner crossover signals. |
| [CMF](cmf/Cmf.md) | Chaikin Money Flow | Measures money flow volume over set period (typically 20-21 days). |
@@ -27,7 +27,7 @@ Volume is market fuel. Price tells what happened; volume tells how hard the mark
| [VF](vf/Vf.md) | Volume Force | Measures force of volume behind price movements using EMA smoothing. |
| [VO](vo/Vo.md) | Volume Oscillator | Difference between short and long volume moving averages. Shows volume momentum. |
| [VROC](vroc/Vroc.md) | Volume Rate of Change | Measures speed at which volume is changing over time. |
-| [VWAD](vwad/Vwad.md) | Volume Weighted A/D | Similar to ADL but weights accumulation/distribution by volume. |
+| [VWAD](vwad/Vwad.md) | Volume Weighted A/D | Similar to AD but weights accumulation/distribution by volume. |
| [VWAP](vwap/Vwap.md) | Volume Weighted Average Price | Average price weighted by volume. Common execution benchmark and intraday reference. |
| [VWMA](vwma/Vwma.md) | Volume Weighted Moving Average | Moving average where each price point is weighted by its volume over a sliding window. |
| [WAD](wad/Wad.md) | Williams Accumulation/Distribution | Measures cumulative buying/selling pressure using True Range and volume. |
diff --git a/lib/volume/adl/Adl.Quantower.cs b/lib/volume/ad/Ad.Quantower.cs
similarity index 63%
rename from lib/volume/adl/Adl.Quantower.cs
rename to lib/volume/ad/Ad.Quantower.cs
index b45c798e..56dbb069 100644
--- a/lib/volume/adl/Adl.Quantower.cs
+++ b/lib/volume/ad/Ad.Quantower.cs
@@ -5,35 +5,35 @@ using TradingPlatform.BusinessLayer;
namespace QuanTAlib;
[SkipLocalsInit]
-public sealed class AdlIndicator : Indicator, IWatchlistIndicator
+public sealed class AdIndicator : Indicator, IWatchlistIndicator
{
[InputParameter("Show cold values", sortIndex: 21)]
public bool ShowColdValues { get; set; } = true;
- private Adl _adl = null!;
+ private Ad _ad = null!;
private readonly LineSeries _series;
public static int MinHistoryDepths => 0;
int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
- public override string ShortName => "ADL";
- public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/volume/adl/Adl.Quantower.cs";
+ public override string ShortName => "AD";
+ public override string SourceCodeLink => "https://github.com/mihakralj/QuanTAlib/blob/main/lib/volume/ad/Ad.Quantower.cs";
- public AdlIndicator()
+ public AdIndicator()
{
OnBackGround = true;
SeparateWindow = true;
- Name = "ADL - Accumulation/Distribution Line";
+ Name = "AD - Accumulation/Distribution Line";
Description = "Accumulation/Distribution Line";
- _series = new LineSeries(name: "ADL", color: Color.Blue, width: 2, style: LineStyle.Solid);
+ _series = new LineSeries(name: "AD", color: Color.Blue, width: 2, style: LineStyle.Solid);
AddLineSeries(_series);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override void OnInit()
{
- _adl = new Adl();
+ _ad = new Ad();
base.OnInit();
}
@@ -41,8 +41,8 @@ public sealed class AdlIndicator : Indicator, IWatchlistIndicator
protected override void OnUpdate(UpdateArgs args)
{
TBar bar = this.GetInputBar(args);
- TValue result = _adl.Update(bar, args.IsNewBar());
+ TValue result = _ad.Update(bar, args.IsNewBar());
- _series.SetValue(result.Value, _adl.IsHot, ShowColdValues);
+ _series.SetValue(result.Value, _ad.IsHot, ShowColdValues);
}
}
diff --git a/lib/volume/adl/Adl.cs b/lib/volume/ad/Ad.cs
similarity index 84%
rename from lib/volume/adl/Adl.cs
rename to lib/volume/ad/Ad.cs
index ca4404df..b9d78d69 100644
--- a/lib/volume/adl/Adl.cs
+++ b/lib/volume/ad/Ad.cs
@@ -4,33 +4,33 @@ using System.Numerics;
namespace QuanTAlib;
///
-/// ADL: Accumulation/Distribution Line
+/// AD: Accumulation/Distribution Line
///
///
/// Cumulative indicator using volume and price to assess accumulation or distribution.
-/// Rising ADL confirms accumulation; falling confirms distribution.
+/// Rising AD confirms accumulation; falling confirms distribution.
///
/// Calculation: MFM = [(Close - Low) - (High - Close)] / (High - Low),
-/// MFV = MFM × Volume, ADL = prev_ADL + MFV. If High equals Low, MFM is 0.
+/// MFV = MFM × Volume, AD = prev_AD + MFV. If High equals Low, MFM is 0.
///
-/// Detailed documentation
-/// Reference Pine Script implementation
+/// Detailed documentation
+/// Reference Pine Script implementation
[SkipLocalsInit]
-public sealed class Adl : ITValuePublisher
+public sealed class Ad : ITValuePublisher
{
- private double _adl;
- private double _p_adl;
+ private double _ad;
+ private double _p_ad;
private bool _isInitialized;
///
/// Display name for the indicator.
///
- public static string Name => "ADL";
+ public static string Name => "AD";
public event TValuePublishedHandler? Pub;
///
- /// Current ADL value.
+ /// Current AD value.
///
public TValue Last { get; private set; }
@@ -45,9 +45,9 @@ public sealed class Adl : ITValuePublisher
public bool IsHot => _isInitialized;
///
- /// Creates a new ADL indicator.
+ /// Creates a new AD indicator.
///
- public Adl()
+ public Ad()
{
_isInitialized = false;
}
@@ -58,8 +58,8 @@ public sealed class Adl : ITValuePublisher
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
- _adl = 0;
- _p_adl = 0;
+ _ad = 0;
+ _p_ad = 0;
_isInitialized = false;
Last = default;
}
@@ -69,11 +69,11 @@ public sealed class Adl : ITValuePublisher
{
if (isNew)
{
- _p_adl = _adl;
+ _p_ad = _ad;
}
else
{
- _adl = _p_adl;
+ _ad = _p_ad;
}
double highLowRange = input.High - input.Low;
@@ -85,19 +85,19 @@ public sealed class Adl : ITValuePublisher
}
double mfv = mfm * input.Volume;
- _adl += mfv;
+ _ad += mfv;
_isInitialized = true;
- Last = new TValue(input.Time, _adl);
+ Last = new TValue(input.Time, _ad);
Pub?.Invoke(this, new TValueEventArgs { Value = Last, IsNew = isNew });
return Last;
}
///
- /// Updates ADL with a TValue input.
+ /// Updates AD with a TValue input.
///
///
- /// ADL requires OHLCV bar data to calculate the Money Flow Multiplier and Volume.
+ /// AD requires OHLCV bar data to calculate the Money Flow Multiplier and Volume.
/// Use Update(TBar) instead.
///
#pragma warning disable S2325 // Method signature must match ITValuePublisher contract
@@ -105,7 +105,7 @@ public sealed class Adl : ITValuePublisher
#pragma warning restore S2325
{
throw new NotSupportedException(
- "ADL requires OHLCV bar data to calculate the Money Flow Multiplier and Volume. " +
+ "AD requires OHLCV bar data to calculate the Money Flow Multiplier and Volume. " +
"Use Update(TBar) instead.");
}
@@ -220,9 +220,9 @@ public sealed class Adl : ITValuePublisher
}
}
- public static (TSeries Results, Adl Indicator) Calculate(TBarSeries source)
+ public static (TSeries Results, Ad Indicator) Calculate(TBarSeries source)
{
- var indicator = new Adl();
+ var indicator = new Ad();
TSeries results = indicator.Update(source);
return (results, indicator);
}
diff --git a/lib/volume/adl/Adl.md b/lib/volume/ad/ad.md
similarity index 62%
rename from lib/volume/adl/Adl.md
rename to lib/volume/ad/ad.md
index b3707f1e..b614bed7 100644
--- a/lib/volume/adl/Adl.md
+++ b/lib/volume/ad/ad.md
@@ -1,4 +1,4 @@
-# ADL: Accumulation/Distribution Line
+# AD: Accumulation/Distribution Line
> *Volume precedes price.*
@@ -7,26 +7,26 @@
| **Category** | Volume |
| **Inputs** | OHLCV bar (TBar) |
| **Parameters** | None |
-| **Outputs** | Single series (ADL) |
+| **Outputs** | Single series (AD) |
| **Output range** | Unbounded |
| **Warmup** | 1 bar |
-| **PineScript** | [adl.pine](adl.pine) |
+| **PineScript** | [ad.pine](ad.pine) |
-- The Accumulation/Distribution Line (ADL) is the bedrock of volume analysis.
+- The Accumulation/Distribution Line (AD) is the bedrock of volume analysis.
- No configurable parameters; computation is stateless per bar.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
-The Accumulation/Distribution Line (ADL) is the bedrock of volume analysis. It attempts to answer a single, vital question: "Are the big players buying or selling?"
+The Accumulation/Distribution Line (AD) is the bedrock of volume analysis. It attempts to answer a single, vital question: "Are the big players buying or selling?"
-Unlike On-Balance Volume (OBV), which treats every up-day as 100% buying, ADL is nuanced. It looks at *where* the price closed within the day's range. A close near the high on massive volume screams "Accumulation." A close near the low on massive volume screams "Distribution."
+Unlike On-Balance Volume (OBV), which treats every up-day as 100% buying, AD is nuanced. It looks at *where* the price closed within the day's range. A close near the high on massive volume screams "Accumulation." A close near the low on massive volume screams "Distribution."
## Historical Context
-Developed by Marc Chaikin, the ADL was originally designed to spot divergences. Chaikin noticed that if a stock made a new high but the ADL failed to make a new high, a crash was imminent. He essentially quantified the "smart money" flow.
+Developed by Marc Chaikin, the AD was originally designed to spot divergences. Chaikin noticed that if a stock made a new high but the AD failed to make a new high, a crash was imminent. He essentially quantified the "smart money" flow.
## Architecture & Physics
-ADL is a cumulative indicator, meaning it has infinite memory. Today's value depends on the sum of all yesterdays.
+AD is a cumulative indicator, meaning it has infinite memory. Today's value depends on the sum of all yesterdays.
The core mechanic is the **Money Flow Multiplier (MFM)**, also known as the Close Location Value (CLV). This value ranges from -1 to +1:
@@ -50,23 +50,23 @@ $$
MFV = MFM \times Volume
$$
-### 3. Accumulation/Distribution Line (ADL)
+### 3. Accumulation/Distribution Line (AD)
$$
-ADL_t = ADL_{t-1} + MFV_t
+AD_t = AD_{t-1} + MFV_t
$$
## Performance Profile
### Operation Count (Streaming Mode)
-ADL computes Money Flow Multiplier (MFM) from bar data, multiplies by volume, and accumulates cumulatively — O(1).
+AD computes Money Flow Multiplier (MFM) from bar data, multiplies by volume, and accumulates cumulatively — O(1).
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
| MFM = ((C-L)-(H-C)) / (H-L) | 1 | 5 cy | ~5 cy |
| MFV = MFM * Volume | 1 | 3 cy | ~3 cy |
-| ADL += MFV (cumulative sum) | 1 | 1 cy | ~1 cy |
+| AD += MFV (cumulative sum) | 1 | 1 cy | ~1 cy |
| Zero guard on H-L | 1 | 2 cy | ~2 cy |
| NaN guard + state update | 1 | 2 cy | ~2 cy |
| **Total** | **O(1)** | — | **~13 cy** |
@@ -89,12 +89,12 @@ O(1) cumulative indicator — no window, no buffer. Throughput ~4 ns/bar. Divisi
| :--- | :--- | :--- |
| **QuanTAlib** | ✅ | Validated. |
| **TA-Lib** | ✅ | Matches `TA_AD` exactly. |
-| **Skender** | ✅ | Matches `GetAdl` exactly. |
+| **Skender** | ✅ | Matches `GetAd` exactly. |
| **Tulip** | ✅ | Matches `ad` exactly. |
| **Ooples** | ✅ | Matches `CalculateAccumulationDistributionLine`. |
### Common Pitfalls
-* **Gaps**: ADL ignores gaps. If a stock gaps up but closes near its low, ADL will register distribution, even if the price is higher than yesterday.
-* **Scale**: The absolute value of ADL is meaningless; it depends on the start date of the data. Only the *trend* and *divergence* matter.
-* **Volume Spikes**: A single bad data point with erroneous volume can permanently skew the ADL. Sanitize your data.
\ No newline at end of file
+* **Gaps**: AD ignores gaps. If a stock gaps up but closes near its low, AD will register distribution, even if the price is higher than yesterday.
+* **Scale**: The absolute value of AD is meaningless; it depends on the start date of the data. Only the *trend* and *divergence* matter.
+* **Volume Spikes**: A single bad data point with erroneous volume can permanently skew the AD. Sanitize your data.
\ No newline at end of file
diff --git a/lib/volume/adl/adl.pine b/lib/volume/ad/ad.pine
similarity index 63%
rename from lib/volume/adl/adl.pine
rename to lib/volume/ad/ad.pine
index 838f0d98..1c6bec98 100644
--- a/lib/volume/adl/adl.pine
+++ b/lib/volume/ad/ad.pine
@@ -1,15 +1,15 @@
// Licensed under the Apache License, Version 2.0
// © mihakralj
//@version=6
-indicator("Accumulation/Distribution Line (ADL)", "ADL", overlay=false)
+indicator("Accumulation/Distribution Line (AD)", "AD", overlay=false)
-//@function Calculates the Accumulation/Distribution Line (ADL), a volume-based indicator that measures money flow into and out of a security
+//@function Calculates the Accumulation/Distribution Line (AD), a volume-based indicator that measures money flow into and out of a security
//@param src_high The high price (default: built-in high)
//@param src_low The low price (default: built-in low)
//@param src_close The close price (default: built-in close)
//@param src_vol The volume (default: built-in volume)
-//@returns The cumulative ADL value representing buying/selling pressure
-adl(src_high = high, src_low = low, src_close = close, src_vol = volume) =>
+//@returns The cumulative AD value representing buying/selling pressure
+ad(src_high = high, src_low = low, src_close = close, src_vol = volume) =>
float mfm = 0.0
if not na(src_high) and not na(src_low) and not na(src_close)
mfm := (src_close - src_low) - (src_high - src_close)
@@ -22,7 +22,7 @@ adl(src_high = high, src_low = low, src_close = close, src_vol = volume) =>
// ---------- Inputs ----------
// ---------- Calculations ----------
-adl_val = adl(high, low, close, volume)
+ad_val = ad(high, low, close, volume)
// ---------- Plotting ----------
-plot(adl_val, "ADL", color=color.yellow, linewidth=2)
+plot(ad_val, "AD", color=color.yellow, linewidth=2)
diff --git a/lib/volume/adl/tests/Adl.Quantower.Tests.cs b/lib/volume/ad/tests/Ad.Quantower.Tests.cs
similarity index 65%
rename from lib/volume/adl/tests/Adl.Quantower.Tests.cs
rename to lib/volume/ad/tests/Ad.Quantower.Tests.cs
index d28fbbff..1bcabb33 100644
--- a/lib/volume/adl/tests/Adl.Quantower.Tests.cs
+++ b/lib/volume/ad/tests/Ad.Quantower.Tests.cs
@@ -3,39 +3,39 @@ using QuanTAlib;
namespace QuanTAlib.Tests;
-public class AdlIndicatorTests
+public class AdIndicatorTests
{
[Fact]
- public void AdlIndicator_Constructor_SetsDefaults()
+ public void AdIndicator_Constructor_SetsDefaults()
{
- var indicator = new AdlIndicator();
+ var indicator = new AdIndicator();
- Assert.Equal("ADL - Accumulation/Distribution Line", indicator.Name);
+ Assert.Equal("AD - Accumulation/Distribution Line", indicator.Name);
Assert.True(indicator.SeparateWindow);
Assert.True(indicator.OnBackGround);
- Assert.Equal(0, AdlIndicator.MinHistoryDepths);
+ Assert.Equal(0, AdIndicator.MinHistoryDepths);
}
[Fact]
- public void AdlIndicator_ShortName_IsCorrect()
+ public void AdIndicator_ShortName_IsCorrect()
{
- var indicator = new AdlIndicator();
- Assert.Equal("ADL", indicator.ShortName);
+ var indicator = new AdIndicator();
+ Assert.Equal("AD", indicator.ShortName);
}
[Fact]
- public void AdlIndicator_MinHistoryDepths_EqualsZero()
+ public void AdIndicator_MinHistoryDepths_EqualsZero()
{
- var indicator = new AdlIndicator();
+ var indicator = new AdIndicator();
- Assert.Equal(0, AdlIndicator.MinHistoryDepths);
+ Assert.Equal(0, AdIndicator.MinHistoryDepths);
Assert.Equal(0, ((IWatchlistIndicator)indicator).MinHistoryDepths);
}
[Fact]
- public void AdlIndicator_Initialize_CreatesInternalAdl()
+ public void AdIndicator_Initialize_CreatesInternalAd()
{
- var indicator = new AdlIndicator();
+ var indicator = new AdIndicator();
// Initialize should not throw
indicator.Initialize();
@@ -45,9 +45,9 @@ public class AdlIndicatorTests
}
[Fact]
- public void AdlIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
+ public void AdIndicator_ProcessUpdate_HistoricalBar_ComputesValue()
{
- var indicator = new AdlIndicator();
+ var indicator = new AdIndicator();
indicator.Initialize();
// Add historical data
@@ -67,9 +67,9 @@ public class AdlIndicatorTests
}
[Fact]
- public void AdlIndicator_ProcessUpdate_NewBar_ComputesValue()
+ public void AdIndicator_ProcessUpdate_NewBar_ComputesValue()
{
- var indicator = new AdlIndicator();
+ var indicator = new AdIndicator();
indicator.Initialize();
var now = DateTime.UtcNow;
diff --git a/lib/volume/adl/tests/Adl.Tests.cs b/lib/volume/ad/tests/Ad.Tests.cs
similarity index 60%
rename from lib/volume/adl/tests/Adl.Tests.cs
rename to lib/volume/ad/tests/Ad.Tests.cs
index 6c72f7ca..83ef5ae8 100644
--- a/lib/volume/adl/tests/Adl.Tests.cs
+++ b/lib/volume/ad/tests/Ad.Tests.cs
@@ -1,122 +1,122 @@
namespace QuanTAlib.Tests;
-public class AdlTests
+public class AdTests
{
[Fact]
- public void Adl_BasicCalculation_ReturnsExpectedValues()
+ public void Ad_BasicCalculation_ReturnsExpectedValues()
{
// Arrange
- var adl = new Adl();
+ var ad = new Ad();
var time = DateTime.UtcNow;
// Bar 1: Close=10, High=12, Low=8. Range=4.
// MFM = ((10-8) - (12-10)) / 4 = (2 - 2) / 4 = 0.
- // Vol = 100. MFV = 0. ADL = 0.
+ // Vol = 100. MFV = 0. AD = 0.
var bar1 = new TBar(time, 10, 12, 8, 10, 100);
- var val1 = adl.Update(bar1);
+ var val1 = ad.Update(bar1);
Assert.Equal(0, val1.Value);
// Bar 2: Close=12, High=12, Low=8. Range=4.
// MFM = ((12-8) - (12-12)) / 4 = (4 - 0) / 4 = 1.
- // Vol = 200. MFV = 200. ADL = 0 + 200 = 200.
+ // Vol = 200. MFV = 200. AD = 0 + 200 = 200.
var bar2 = new TBar(time.AddMinutes(1), 10, 12, 8, 12, 200);
- var val2 = adl.Update(bar2);
+ var val2 = ad.Update(bar2);
Assert.Equal(200, val2.Value);
// Bar 3: Close=8, High=12, Low=8. Range=4.
// MFM = ((8-8) - (12-8)) / 4 = (0 - 4) / 4 = -1.
- // Vol = 100. MFV = -100. ADL = 200 - 100 = 100.
+ // Vol = 100. MFV = -100. AD = 200 - 100 = 100.
var bar3 = new TBar(time.AddMinutes(2), 12, 12, 8, 8, 100);
- var val3 = adl.Update(bar3);
+ var val3 = ad.Update(bar3);
Assert.Equal(100, val3.Value);
}
[Fact]
- public void Adl_IsNew_False_UpdatesSameBar()
+ public void Ad_IsNew_False_UpdatesSameBar()
{
- var adl = new Adl();
+ var ad = new Ad();
var time = DateTime.UtcNow;
// Initial update
- // MFM = 1, Vol = 100 -> ADL = 100
+ // MFM = 1, Vol = 100 -> AD = 100
var bar1 = new TBar(time, 10, 12, 8, 12, 100);
- adl.Update(bar1, isNew: true);
- Assert.Equal(100, adl.Last.Value);
+ ad.Update(bar1, isNew: true);
+ Assert.Equal(100, ad.Last.Value);
// Update same bar with different volume
- // MFM = 1, Vol = 200 -> ADL = 200 (replaces previous 100)
+ // MFM = 1, Vol = 200 -> AD = 200 (replaces previous 100)
var bar1Update = new TBar(time, 10, 12, 8, 12, 200);
- adl.Update(bar1Update, isNew: false);
- Assert.Equal(200, adl.Last.Value);
+ ad.Update(bar1Update, isNew: false);
+ Assert.Equal(200, ad.Last.Value);
}
[Fact]
- public void Adl_Reset_ClearsState()
+ public void Ad_Reset_ClearsState()
{
- var adl = new Adl();
+ var ad = new Ad();
var bar = new TBar(DateTime.UtcNow, 10, 12, 8, 12, 100);
- adl.Update(bar);
+ ad.Update(bar);
- Assert.True(adl.IsHot);
- Assert.NotEqual(0, adl.Last.Value);
+ Assert.True(ad.IsHot);
+ Assert.NotEqual(0, ad.Last.Value);
- adl.Reset();
- Assert.False(adl.IsHot);
- Assert.Equal(0, adl.Last.Value);
+ ad.Reset();
+ Assert.False(ad.IsHot);
+ Assert.Equal(0, ad.Last.Value);
}
[Fact]
- public void Adl_HighEqualsLow_HandlesDivisionByZero()
+ public void Ad_HighEqualsLow_HandlesDivisionByZero()
{
- var adl = new Adl();
+ var ad = new Ad();
// High = Low = 10. Range = 0. MFM should be 0.
var bar = new TBar(DateTime.UtcNow, 10, 10, 10, 10, 100);
- var val = adl.Update(bar);
+ var val = ad.Update(bar);
Assert.Equal(0, val.Value);
}
[Fact]
- public void Adl_TValueUpdate_ThrowsNotSupportedException()
+ public void Ad_TValueUpdate_ThrowsNotSupportedException()
{
- var adl = new Adl();
+ var ad = new Ad();
var bar = new TBar(DateTime.UtcNow, 10, 12, 8, 12, 100);
- adl.Update(bar); // ADL = 100
+ ad.Update(bar); // AD = 100
- // Update with TValue should throw since ADL requires OHLCV bar data
- Assert.Throws(() => adl.Update(new TValue(DateTime.UtcNow, 15)));
+ // Update with TValue should throw since AD requires OHLCV bar data
+ Assert.Throws(() => ad.Update(new TValue(DateTime.UtcNow, 15)));
}
[Fact]
- public void Adl_Name_IsCorrect()
+ public void Ad_Name_IsCorrect()
{
- Assert.Equal("ADL", Adl.Name);
+ Assert.Equal("AD", Ad.Name);
}
[Fact]
- public void Adl_PubEvent_FiresOnUpdate()
+ public void Ad_PubEvent_FiresOnUpdate()
{
- var adl = new Adl();
+ var ad = new Ad();
bool eventFired = false;
- adl.Pub += (object? sender, in TValueEventArgs args) => eventFired = true;
+ ad.Pub += (object? sender, in TValueEventArgs args) => eventFired = true;
- adl.Update(new TBar(DateTime.UtcNow, 10, 12, 8, 10, 100));
+ ad.Update(new TBar(DateTime.UtcNow, 10, 12, 8, 10, 100));
Assert.True(eventFired);
}
[Fact]
- public void Adl_UpdateTBarSeries_ReturnsCorrectSeries()
+ public void Ad_UpdateTBarSeries_ReturnsCorrectSeries()
{
- var adl = new Adl();
+ var ad = new Ad();
var bars = new TBarSeries();
var time = DateTime.UtcNow;
// Add same bars as in BasicCalculation
- bars.Add(new TBar(time, 10, 12, 8, 10, 100)); // ADL=0
- bars.Add(new TBar(time.AddMinutes(1), 10, 12, 8, 12, 200)); // ADL=200
- bars.Add(new TBar(time.AddMinutes(2), 12, 12, 8, 8, 100)); // ADL=100
+ bars.Add(new TBar(time, 10, 12, 8, 10, 100)); // AD=0
+ bars.Add(new TBar(time.AddMinutes(1), 10, 12, 8, 12, 200)); // AD=200
+ bars.Add(new TBar(time.AddMinutes(2), 12, 12, 8, 8, 100)); // AD=100
- var result = adl.Update(bars);
+ var result = ad.Update(bars);
Assert.Equal(3, result.Count);
Assert.Equal(0, result[0].Value);
@@ -125,7 +125,7 @@ public class AdlTests
}
[Fact]
- public void Adl_CalculateTBarSeries_ReturnsCorrectSeries()
+ public void Ad_CalculateTBarSeries_ReturnsCorrectSeries()
{
var bars = new TBarSeries();
var time = DateTime.UtcNow;
@@ -134,7 +134,7 @@ public class AdlTests
bars.Add(new TBar(time.AddMinutes(1), 10, 12, 8, 12, 200));
bars.Add(new TBar(time.AddMinutes(2), 12, 12, 8, 8, 100));
- var result = Adl.Batch(bars);
+ var result = Ad.Batch(bars);
Assert.Equal(3, result.Count);
Assert.Equal(0, result[0].Value);
@@ -143,7 +143,7 @@ public class AdlTests
}
[Fact]
- public void Adl_CalculateSpan_ReturnsCorrectValues()
+ public void Ad_CalculateSpan_ReturnsCorrectValues()
{
double[] high = { 12, 12, 12 };
double[] low = { 8, 8, 8 };
@@ -151,7 +151,7 @@ public class AdlTests
double[] volume = { 100, 200, 100 };
double[] output = new double[3];
- Adl.Batch(high, low, close, volume, output);
+ Ad.Batch(high, low, close, volume, output);
Assert.Equal(0, output[0]);
Assert.Equal(200, output[1]);
@@ -159,7 +159,7 @@ public class AdlTests
}
[Fact]
- public void Adl_CalculateSpan_ThrowsOnMismatchedLengths()
+ public void Ad_CalculateSpan_ThrowsOnMismatchedLengths()
{
double[] high = { 10, 11 };
double[] low = { 9, 10 };
@@ -168,19 +168,19 @@ public class AdlTests
double[] output = new double[2];
Assert.Throws(() =>
- Adl.Batch(high, low, close, volume, output));
+ Ad.Batch(high, low, close, volume, output));
}
[Fact]
- public void Adl_Calculate_EmptySeries_ReturnsEmpty()
+ public void Ad_Calculate_EmptySeries_ReturnsEmpty()
{
var bars = new TBarSeries();
- var result = Adl.Batch(bars);
+ var result = Ad.Batch(bars);
Assert.Empty(result);
}
[Fact]
- public void Adl_CalculateSpan_SimdPath_ReturnsCorrectValues()
+ public void Ad_CalculateSpan_SimdPath_ReturnsCorrectValues()
{
const int count = 100; // Enough to trigger SIMD
double[] high = new double[count];
@@ -190,7 +190,7 @@ public class AdlTests
double[] output = new double[count];
// Setup: High=12, Low=8, Close=12 (MFM=1), Vol=10
- // Expected ADL increments by 10 each step.
+ // Expected AD increments by 10 each step.
for (int i = 0; i < count; i++)
{
high[i] = 12;
@@ -199,7 +199,7 @@ public class AdlTests
volume[i] = 10;
}
- Adl.Batch(high, low, close, volume, output);
+ Ad.Batch(high, low, close, volume, output);
for (int i = 0; i < count; i++)
{
diff --git a/lib/volume/adl/tests/Adl.Validation.Tests.cs b/lib/volume/ad/tests/Ad.Validation.Tests.cs
similarity index 84%
rename from lib/volume/adl/tests/Adl.Validation.Tests.cs
rename to lib/volume/ad/tests/Ad.Validation.Tests.cs
index 0db8a76a..f42c32db 100644
--- a/lib/volume/adl/tests/Adl.Validation.Tests.cs
+++ b/lib/volume/ad/tests/Ad.Validation.Tests.cs
@@ -4,35 +4,35 @@ using OoplesFinance.StockIndicators.Models;
namespace QuanTAlib.Tests;
-public class AdlValidationTests
+public class AdValidationTests
{
private readonly ValidationTestData _data;
- public AdlValidationTests()
+ public AdValidationTests()
{
_data = new ValidationTestData();
}
[Fact]
- public void Adl_Matches_Skender()
+ public void Ad_Matches_Skender()
{
// Skender
var skenderResults = _data.SkenderQuotes.GetAdl();
var skenderValues = skenderResults.Select(x => x.Adl).ToArray();
// QuanTAlib
- var adl = new Adl();
+ var ad = new Ad();
var quantalibValues = new List();
foreach (var bar in _data.Bars)
{
- quantalibValues.Add(adl.Update(bar).Value);
+ quantalibValues.Add(ad.Update(bar).Value);
}
ValidationHelper.VerifyData(quantalibValues.ToArray(), skenderValues, 0, 100, ValidationHelper.SkenderTolerance);
}
[Fact]
- public void Adl_Matches_Talib()
+ public void Ad_Matches_Talib()
{
// TA-Lib
var high = _data.Bars.High.Values.ToArray();
@@ -45,18 +45,18 @@ public class AdlValidationTests
Assert.Equal(TALib.Core.RetCode.Success, retCode);
// QuanTAlib
- var adl = new Adl();
+ var ad = new Ad();
var quantalibValues = new List();
foreach (var bar in _data.Bars)
{
- quantalibValues.Add(adl.Update(bar).Value);
+ quantalibValues.Add(ad.Update(bar).Value);
}
ValidationHelper.VerifyData(quantalibValues.ToArray(), talibValues, outRange, 0, 100, ValidationHelper.TalibTolerance);
}
[Fact]
- public void Adl_Matches_Tulip()
+ public void Ad_Matches_Tulip()
{
// Tulip
var high = _data.Bars.High.Values.ToArray();
@@ -73,18 +73,18 @@ public class AdlValidationTests
var tulipValues = outputs[0];
// QuanTAlib
- var adl = new Adl();
+ var ad = new Ad();
var quantalibValues = new List();
foreach (var bar in _data.Bars)
{
- quantalibValues.Add(adl.Update(bar).Value);
+ quantalibValues.Add(ad.Update(bar).Value);
}
ValidationHelper.VerifyData(quantalibValues.ToArray(), tulipValues, 0, 100, ValidationHelper.TulipTolerance);
}
[Fact]
- public void Adl_Matches_Ooples()
+ public void Ad_Matches_Ooples()
{
// Ooples
var ooplesData = _data.SkenderQuotes.Select(q => new TickerData
@@ -102,11 +102,11 @@ public class AdlValidationTests
var oValues = oResult.OutputValues["Adl"];
// QuanTAlib
- var adl = new Adl();
+ var ad = new Ad();
var quantalibValues = new List();
foreach (var bar in _data.Bars)
{
- quantalibValues.Add(adl.Update(bar).Value);
+ quantalibValues.Add(ad.Update(bar).Value);
}
ValidationHelper.VerifyData(quantalibValues.ToArray(), oValues.ToArray(), 0, 100, ValidationHelper.OoplesTolerance);
diff --git a/lib/volume/adosc/Adosc.cs b/lib/volume/adosc/Adosc.cs
index 515a5197..edccde6b 100644
--- a/lib/volume/adosc/Adosc.cs
+++ b/lib/volume/adosc/Adosc.cs
@@ -16,7 +16,7 @@ namespace QuanTAlib;
[SkipLocalsInit]
public sealed class Adosc : ITValuePublisher
{
- private readonly Adl _adl;
+ private readonly Ad _ad;
private readonly Ema _emaFast;
private readonly Ema _emaSlow;
@@ -64,7 +64,7 @@ public sealed class Adosc : ITValuePublisher
throw new ArgumentException("Fast period must be less than slow period", nameof(fastPeriod));
}
- _adl = new Adl();
+ _ad = new Ad();
_emaFast = new Ema(fastPeriod);
_emaSlow = new Ema(slowPeriod);
WarmupPeriod = slowPeriod;
@@ -77,7 +77,7 @@ public sealed class Adosc : ITValuePublisher
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset()
{
- _adl.Reset();
+ _ad.Reset();
_emaFast.Reset();
_emaSlow.Reset();
Last = default;
@@ -110,7 +110,7 @@ public sealed class Adosc : ITValuePublisher
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue Update(TBar input, bool isNew = true)
{
- var adl = _adl.Update(input, isNew);
+ var adl = _ad.Update(input, isNew);
return Update(adl, isNew);
}
diff --git a/lib/volume/vwad/Vwad.Quantower.cs b/lib/volume/vwad/Vwad.Quantower.cs
index f5f726a1..bd79fb8e 100644
--- a/lib/volume/vwad/Vwad.Quantower.cs
+++ b/lib/volume/vwad/Vwad.Quantower.cs
@@ -27,7 +27,7 @@ public sealed class VwadIndicator : Indicator, IWatchlistIndicator
OnBackGround = true;
SeparateWindow = true;
Name = "VWAD - Volume Weighted Accumulation/Distribution";
- Description = "Volume Weighted Accumulation/Distribution enhances ADL by weighting each bar's contribution based on relative volume";
+ Description = "Volume Weighted Accumulation/Distribution enhances AD by weighting each bar's contribution based on relative volume";
_series = new LineSeries(name: "VWAD", color: Color.Yellow, width: 2, style: LineStyle.Solid);
AddLineSeries(_series);
diff --git a/lib/volume/vwad/Vwad.cs b/lib/volume/vwad/Vwad.cs
index 013fe01e..59471f8f 100644
--- a/lib/volume/vwad/Vwad.cs
+++ b/lib/volume/vwad/Vwad.cs
@@ -8,7 +8,7 @@ namespace QuanTAlib;
/// each bar's contribution based on its volume relative to the rolling volume sum.
///
///
-/// VWAD enhances ADL by weighting volume contributions:
+/// VWAD enhances AD by weighting volume contributions:
/// MFM = [(Close - Low) - (High - Close)] / (High - Low),
/// VolWeight = Volume / Σ(Volume, period),
/// VWAD = Σ(Volume × MFM × VolWeight).
diff --git a/ndepend/badges/classes.svg b/ndepend/badges/classes.svg
index 79191afa..fb9bb5a6 100644
--- a/ndepend/badges/classes.svg
+++ b/ndepend/badges/classes.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/ndepend/badges/complexity.svg b/ndepend/badges/complexity.svg
index a6ffaf1b..3ac85d40 100644
--- a/ndepend/badges/complexity.svg
+++ b/ndepend/badges/complexity.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/ndepend/badges/files.svg b/ndepend/badges/files.svg
index fc4099f6..27b5797f 100644
--- a/ndepend/badges/files.svg
+++ b/ndepend/badges/files.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/ndepend/badges/loc.svg b/ndepend/badges/loc.svg
index 072962d9..d9f9e57c 100644
--- a/ndepend/badges/loc.svg
+++ b/ndepend/badges/loc.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/ndepend/badges/methods.svg b/ndepend/badges/methods.svg
index 9a4430ec..bdda1b0b 100644
--- a/ndepend/badges/methods.svg
+++ b/ndepend/badges/methods.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/ndepend/badges/public-api.svg b/ndepend/badges/public-api.svg
index fe996dae..8e547b56 100644
--- a/ndepend/badges/public-api.svg
+++ b/ndepend/badges/public-api.svg
@@ -1,6 +1,6 @@
\ No newline at end of file
diff --git a/python/README.md b/python/README.md
index d1d7f80b..fa502730 100644
--- a/python/README.md
+++ b/python/README.md
@@ -69,7 +69,7 @@ The `ctypes` call adds 5-15 μs overhead. For arrays above a few hundred bars, N
| Category | Module | Examples |
| -------- | ------ | -------- |
-| Channels | `channels` | bbands, kchannel, dchannel, aberr |
+| Channels | `channels` | bbands, kc, dc, aberr |
| Core | `core` | ha, midpoint, avgprice, typprice |
| Cycles | `cycles` | ht_dcperiod, ht_sine, cg, dsp |
| Dynamics | `dynamics` | adx, aroon, ichimoku, supertrend |
@@ -78,8 +78,8 @@ The `ctypes` call adds 5-15 μs overhead. For arrays above a few hundred bars, N
| Momentum | `momentum` | rsi, macd, roc, mom, tsi |
| Numerics | `numerics` | fft, normalize, sigmoid, slope |
| Oscillators | `oscillators` | stoch, cci, fisher, qqe, willr |
-| Reversals | `reversals` | psar, pivot, fractals, swings |
-| Statistics | `statistics` | zscore, correlation, entropy, linreg |
+| Reversals | `reversals` | sar, pivot, fractals, swings |
+| Statistics | `statistics` | zscore, correl, entropy, linreg |
| Trends FIR | `trends_fir` | sma, wma, hma, alma, trima |
| Trends IIR | `trends_iir` | ema, dema, tema, kama, jma |
| Volatility | `volatility` | atr, bbw, stddev, hv, tr |
diff --git a/python/SPEC.md b/python/SPEC.md
index 65472e22..eca9e822 100644
--- a/python/SPEC.md
+++ b/python/SPEC.md
@@ -218,7 +218,7 @@ int qtl_obv(double* close, double* volume, int n, double* out);
#### Pattern H: Dual-input statistics (seriesX + seriesY)
-**C# source:** `Correlation.Batch(ReadOnlySpan seriesX, ..seriesY, Span output, int period)`
+**C# source:** `Correl.Batch(ReadOnlySpan seriesX, ..seriesY, Span output, int period)`
**C ABI:**
@@ -257,7 +257,7 @@ int qtl_pvo(double* volume, int n,
| KDJ | 3 | K, D, J |
| SMI | 2+ | smi, signal |
| BBands | 3 | upper, middle, lower |
-| KChannel | 3 | upper, middle, lower |
+| KC | 3 | upper, middle, lower |
| DCChannel | 3 | upper, middle, lower |
| AccBands | 3 | upper, middle, lower |
| AtrBands | 3 | upper, middle, lower |
@@ -621,7 +621,7 @@ packages = ["quantalib"]
| mom | `Mom` | A | period |
| pmo | `Pmo` | A | timePeriods, smoothPeriods, signalPeriods |
| ppo | `Ppo` | A | fastPeriod, slowPeriod |
-| prs | `Prs` | H (dual) | period |
+| rs | `Rs` | H (dual) | period |
| roc | `Roc` | A | period |
| rocp | `Rocp` | A | period |
| rocr | `Rocr` | A | period |
@@ -650,7 +650,7 @@ packages = ["quantalib"]
| dem | `Dem` | E (HLC) | period |
| dpo | `Dpo` | A | period |
| dosc | `Dosc` | A | rsiPeriod, ema1Period, ema2Period, sigPeriod |
-| dymoi | `Dymoi` | A | basePeriod, shortPeriod, longPeriod... |
+| dymi | `Dymi` | A | basePeriod, shortPeriod, longPeriod... |
| er | `Er` | A | period |
| fisher | `Fisher` | A | period, alpha |
| fisher04 | `Fisher04` | A | period |
@@ -733,7 +733,7 @@ packages = ["quantalib"]
| gdema | `Gdema` | A | period, vfactor |
| hema | `Hema` | A | period |
| holt | `Holt` | A | period, gamma |
-| htit | `Htit` | A | — |
+| ht_trendline | `HtTrendline` | A | — |
| hwma | `Hwma` | A | period |
| jma | `Jma` | A | period, phase, power |
| kama | `Kama` | A | period, fastPeriod, slowPeriod |
@@ -771,14 +771,14 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| apz | `Apz` | B (HLC) | period |
| atrbands | `AtrBands` | B (HLC) | period, multiplier |
| bbands | `Bbands` | A | period, multiplier |
-| dchannel | `Dchannel` | D (HL) | period |
+| dc | `Dc` | D (HL) | period |
| decaychannel | `Decaychannel` | B (HLC) | period |
| fcb | `Fcb` | D (HL) | period |
| jbands | `Jbands` | A | period, phase, power |
-| kchannel | `Kchannel` | B (HLC) | period, multiplier |
+| kc | `Kc` | B (HLC) | period, multiplier |
| maenv | `Maenv` | A | period, pct |
| mmchannel | `Mmchannel` | D (HL) | period |
-| pchannel | `Pchannel` | D (HL) | period |
+| pc | `Pc` | D (HL) | period |
| regchannel | `Regchannel` | A | period |
| sdchannel | `Sdchannel` | A | period |
| starchannel | `Starchannel` | B (HLC) | period |
@@ -820,7 +820,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| Indicator | C# Class | Pattern | Key params |
|-----------|----------|---------|------------|
-| adl | `Adl` | B (HLCV) | — |
+| ad | `Ad` | B (HLCV) | — |
| adosc | `Adosc` | B (HLCV) | fastPeriod, slowPeriod |
| aobv | `Aobv` | I (CV→2) | — |
| cmf | `Cmf` | B (HLCV) | period |
@@ -854,7 +854,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
|-----------|----------|---------|------------|
| acf | `Acf` | A | period, lag |
| cma | `Cma` | A | — |
-| correlation | `Correlation` | H | period |
+| correl | `Correl` | H | period |
| covariance | `Covariance` | H | period, isPopulation |
| entropy | `Entropy` | A | period |
| geomean | `Geomean` | A | period |
@@ -1005,7 +1005,7 @@ All channel indicators output 3 spans: upper, middle, lower (Pattern I).
| pivotext | `Pivotext` | E (HLC→multi) | — |
| pivotfib | `Pivotfib` | E (HLC→multi) | — |
| pivotwood | `Pivotwood` | E (HLC→multi) | — |
-| psar | `Psar` | C (OHLC) | accelStart, accelMax |
+| sar | `Sar` | C (OHLC) | accelStart, accelMax |
| swings | `Swings` | D (HL→multi) | period |
| ttm_scalper | `TtmScalper` | D (HL→multi) | period |
diff --git a/python/quantalib/_bridge.py b/python/quantalib/_bridge.py
index cb6a3792..b5964a52 100644
--- a/python/quantalib/_bridge.py
+++ b/python/quantalib/_bridge.py
@@ -110,7 +110,7 @@ HAS_CCI = _bind("qtl_cci", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _dp])
HAS_MACD = _bind("qtl_macd", [_dp, _dp, _ci, _ci, _ci])
HAS_PMO = _bind("qtl_pmo", [_dp, _dp, _ci, _ci, _ci, _ci])
HAS_PPO = _bind("qtl_ppo", [_dp, _dp, _ci, _ci, _ci])
-HAS_PRS = _bind("qtl_prs", [_dp, _dp, _dp, _ci, _ci])
+HAS_RS = _bind("qtl_rs", [_dp, _dp, _dp, _ci, _ci])
HAS_ROCP = _bind("qtl_rocp", [_dp, _dp, _ci, _ci])
HAS_ROCR = _bind("qtl_rocr", [_dp, _dp, _ci, _ci])
HAS_SAM = _bind("qtl_sam", [_dp, _dp, _ci, _cd, _ci])
@@ -168,7 +168,7 @@ HAS_RWMA = _bind("qtl_rwma", [_dp, _dp, _dp, _dp, _ci, _ci])
HAS_ADXVMA = _bind("qtl_adxvma", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _dp])
HAS_FRAMA = _bind("qtl_frama", [_dp, _dp, _dp, _ci, _ci])
HAS_HOLT = _bind("qtl_holt", [_dp, _dp, _ci, _ci, _cd])
-HAS_HTIT = _bind("qtl_htit", [_dp, _dp, _ci])
+HAS_HT_TRENDLINE = _bind("qtl_httrendline", [_dp, _dp, _ci])
HAS_HWMA = _bind("qtl_hwma", [_dp, _dp, _ci, _ci])
HAS_JMA = _bind("qtl_jma", [_dp, _dp, _ci, _ci, _ci])
HAS_KAMA = _bind("qtl_kama", [_dp, _dp, _ci, _ci, _ci, _ci])
@@ -197,14 +197,14 @@ HAS_ZLTEMA = _bind("qtl_zltema", [_dp, _dp, _ci, _ci])
# ═══════════════════════════════════════════════════════════════════════════
HAS_ACCBANDS = _bind("qtl_accbands", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd])
HAS_APZ = _bind("qtl_apz", [_dp, _dp, _dp, _dp, _dp, _ci, _cd, _ci, _dp, _dp, _dp])
-HAS_DCHANNEL = _bind("qtl_dchannel", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
+HAS_DC = _bind("qtl_dc", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
HAS_DECAYCHANNEL = _bind("qtl_decaychannel", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
HAS_FCB = _bind("qtl_fcb", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
HAS_JBANDS = _bind("qtl_jbands", [_dp, _dp, _dp, _dp, _ci, _ci, _ci])
-HAS_KCHANNEL = _bind("qtl_kchannel", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd])
+HAS_KC = _bind("qtl_kc", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd])
HAS_MAENV = _bind("qtl_maenv", [_dp, _dp, _dp, _dp, _ci, _ci, _cd, _ci])
HAS_MMCHANNEL = _bind("qtl_mmchannel", [_dp, _dp, _dp, _dp, _ci, _ci])
-HAS_PCHANNEL = _bind("qtl_pchannel", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
+HAS_PC = _bind("qtl_pc", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
HAS_REGCHANNEL = _bind("qtl_regchannel", [_dp, _dp, _dp, _dp, _ci, _ci, _cd])
HAS_SDCHANNEL = _bind("qtl_sdchannel", [_dp, _dp, _dp, _dp, _ci, _ci, _cd])
HAS_STARCHANNEL = _bind("qtl_starchannel", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci, _cd, _ci])
@@ -239,7 +239,7 @@ HAS_YZV = _bind("qtl_yzv", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
# ═══════════════════════════════════════════════════════════════════════════
# Volume
# ═══════════════════════════════════════════════════════════════════════════
-HAS_ADL = _bind("qtl_adl", [_dp, _dp, _dp, _dp, _dp, _ci])
+HAS_AD = _bind("qtl_ad", [_dp, _dp, _dp, _dp, _dp, _ci])
HAS_ADOSC = _bind("qtl_adosc", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci])
HAS_III = _bind("qtl_iii", [_dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci])
HAS_KVO = _bind("qtl_kvo", [_dp, _dp, _dp, _dp, _dp, _dp, _ci, _ci, _ci, _ci])
@@ -408,7 +408,7 @@ HAS_PIVOTDEM = _bind("qtl_pivotdem", [_dp, _dp, _dp, _dp, _dp, _ci])
HAS_PIVOTEXT = _bind("qtl_pivotext", [_dp, _dp, _dp, _dp, _ci])
HAS_PIVOTFIB = _bind("qtl_pivotfib", [_dp, _dp, _dp, _dp, _ci])
HAS_PIVOTWOOD = _bind("qtl_pivotwood", [_dp, _dp, _dp, _dp, _ci])
-HAS_PSAR = _bind("qtl_psar", [_dp, _dp, _dp, _dp, _dp, _ci, _cd, _cd, _cd])
+HAS_SAR = _bind("qtl_sar", [_dp, _dp, _dp, _dp, _dp, _ci, _cd, _cd, _cd])
HAS_SWINGS = _bind("qtl_swings", [_dp, _dp, _dp, _dp, _ci, _ci])
HAS_TTMSCALPER = _bind("qtl_ttmscalper", [_dp, _dp, _dp, _dp, _dp, _ci, _ci])
@@ -440,7 +440,7 @@ HAS_KRI = _bind("qtl_kri", [_dp, _ci, _dp, _ci])
HAS_PSL = _bind("qtl_psl", [_dp, _ci, _dp, _ci])
HAS_DECO = _bind("qtl_deco", [_dp, _ci, _dp, _ci, _ci])
HAS_DOSC = _bind("qtl_dosc", [_dp, _ci, _dp, _ci, _ci, _ci, _ci])
-HAS_DYMOI = _bind("qtl_dymoi", [_dp, _ci, _dp, _ci, _ci, _ci, _ci, _ci])
+HAS_DYMI = _bind("qtl_dymi", [_dp, _ci, _dp, _ci, _ci, _ci, _ci, _ci])
HAS_CRSI = _bind("qtl_crsi", [_dp, _ci, _dp, _ci, _ci, _ci])
HAS_BBB = _bind("qtl_bbb", [_dp, _ci, _dp, _ci, _cd])
HAS_BBI = _bind("qtl_bbi", [_dp, _ci, _dp, _ci, _ci, _ci, _ci])
@@ -526,7 +526,7 @@ HAS_PVO = _bind("qtl_pvo", [_dp, _ci, _dp, _dp, _dp, _ci, _ci, _ci])
HAS_ZSCORE = _bind("qtl_zscore", [_dp, _ci, _dp, _ci])
HAS_CMA = _bind("qtl_cma", [_dp, _ci, _dp])
HAS_ENTROPY = _bind("qtl_entropy", [_dp, _ci, _dp, _ci])
-HAS_CORRELATION = _bind("qtl_correlation", [_dp, _dp, _ci, _dp, _ci])
+HAS_CORREL = _bind("qtl_correl", [_dp, _dp, _ci, _dp, _ci])
HAS_COVARIANCE = _bind("qtl_covariance", [_dp, _dp, _ci, _dp, _ci, _ci])
HAS_COINTEGRATION = _bind("qtl_cointegration", [_dp, _dp, _ci, _dp, _ci])
diff --git a/python/quantalib/channels.py b/python/quantalib/channels.py
index a38c4b07..44917a02 100644
--- a/python/quantalib/channels.py
+++ b/python/quantalib/channels.py
@@ -14,14 +14,14 @@ __all__ = [
"apz",
"atrbands",
"bbands",
- "dchannel",
+ "dc",
"decaychannel",
"fcb",
"jbands",
- "kchannel",
+ "kc",
"maenv",
"mmchannel",
- "pchannel",
+ "pc",
"regchannel",
"sdchannel",
"starchannel",
@@ -77,7 +77,7 @@ def apz(open: object, high: object, low: object, close: object, volume: object,
return _wrap_multi({"dstMiddle": dstMiddle, "dstUpper": dstUpper, "dstLower": dstLower}, idx, "channels", offset)
-def dchannel(high: object, low: object, period: int = 14, offset: int = 0, **kwargs) -> object:
+def dc(high: object, low: object, period: int = 14, offset: int = 0, **kwargs) -> object:
"""Donchian Channel."""
period = int(kwargs.get("length", period))
offset = int(offset)
@@ -86,7 +86,7 @@ def dchannel(high: object, low: object, period: int = 14, offset: int = 0, **kwa
middle = _out(n)
upper = _out(n)
lower = _out(n)
- _check(_lib.qtl_dchannel(_ptr(h), _ptr(l), _ptr(middle), _ptr(upper), _ptr(lower), n, period))
+ _check(_lib.qtl_dc(_ptr(h), _ptr(l), _ptr(middle), _ptr(upper), _ptr(lower), n, period))
return _wrap_multi({"middle": middle, "upper": upper, "lower": lower}, idx, "channels", offset)
@@ -130,7 +130,7 @@ def jbands(close: object, period: int = 14, phase: int = 0, offset: int = 0, **k
return _wrap_multi({"middle": middle, "upper": upper, "lower": lower}, idx, "channels", offset)
-def kchannel(high: object, low: object, close: object, period: int = 14, multiplier: float = 2.0, offset: int = 0, **kwargs) -> object:
+def kc(high: object, low: object, close: object, period: int = 14, multiplier: float = 2.0, offset: int = 0, **kwargs) -> object:
"""Keltner Channel."""
period = int(kwargs.get("length", period))
multiplier = float(multiplier)
@@ -140,7 +140,7 @@ def kchannel(high: object, low: object, close: object, period: int = 14, multipl
middle = _out(n)
upper = _out(n)
lower = _out(n)
- _check(_lib.qtl_kchannel(_ptr(h), _ptr(l), _ptr(c), _ptr(middle), _ptr(upper), _ptr(lower), n, period, multiplier))
+ _check(_lib.qtl_kc(_ptr(h), _ptr(l), _ptr(c), _ptr(middle), _ptr(upper), _ptr(lower), n, period, multiplier))
return _wrap_multi({"middle": middle, "upper": upper, "lower": lower}, idx, "channels", offset)
@@ -171,7 +171,7 @@ def mmchannel(high: object, low: object, period: int = 14, offset: int = 0, **kw
return _wrap_multi({"upper": upper, "lower": lower}, idx, "channels", offset)
-def pchannel(high: object, low: object, period: int = 14, offset: int = 0, **kwargs) -> object:
+def pc(high: object, low: object, period: int = 14, offset: int = 0, **kwargs) -> object:
"""Price Channel."""
period = int(kwargs.get("length", period))
offset = int(offset)
@@ -180,7 +180,7 @@ def pchannel(high: object, low: object, period: int = 14, offset: int = 0, **kwa
middle = _out(n)
upper = _out(n)
lower = _out(n)
- _check(_lib.qtl_pchannel(_ptr(h), _ptr(l), _ptr(middle), _ptr(upper), _ptr(lower), n, period))
+ _check(_lib.qtl_pc(_ptr(h), _ptr(l), _ptr(middle), _ptr(upper), _ptr(lower), n, period))
return _wrap_multi({"middle": middle, "upper": upper, "lower": lower}, idx, "channels", offset)
diff --git a/python/quantalib/indicators.py b/python/quantalib/indicators.py
index 0e0aeace..54b183df 100644
--- a/python/quantalib/indicators.py
+++ b/python/quantalib/indicators.py
@@ -14,8 +14,8 @@ Category submodules:
quantalib.momentum — RSI, MACD, ROC, MOM, etc.
quantalib.numerics — FFT, sigmoid, slope, distributions, etc.
quantalib.oscillators — Stochastic, Fisher, Williams %R, etc.
- quantalib.reversals — Pivot points, PSAR, fractals, etc.
- quantalib.statistics — Z-score, correlation, linreg, etc.
+ quantalib.reversals — Pivot points, SAR, fractals, etc.
+ quantalib.statistics — Z-score, correl, linreg, etc.
quantalib.trends_fir — SMA, WMA, HMA, ALMA, etc.
quantalib.trends_iir — EMA, DEMA, TEMA, JMA, KAMA, etc.
quantalib.volatility — ATR, TR, Bollinger Width, etc.
diff --git a/python/quantalib/momentum.py b/python/quantalib/momentum.py
index 619ec967..ee48bf57 100644
--- a/python/quantalib/momentum.py
+++ b/python/quantalib/momentum.py
@@ -13,7 +13,7 @@ __all__ = [
"macd",
"pmo",
"ppo",
- "prs",
+ "rs",
"rocp",
"rocr",
"sam",
@@ -90,15 +90,15 @@ def ppo(close: object, fastPeriod: int = 12, slowPeriod: int = 26, offset: int =
return _wrap(destination, idx, f"PPO_{fastPeriod}", "momentum", offset)
-def prs(x: object, y: object, smoothPeriod: int = 5, offset: int = 0, **kwargs) -> object:
+def rs(x: object, y: object, smoothPeriod: int = 5, offset: int = 0, **kwargs) -> object:
"""Price Relative Strength."""
smoothPeriod = int(smoothPeriod)
offset = int(offset)
xarr, idx = _arr(x); yarr, _ = _arr(y)
n = len(xarr)
output = _out(n)
- _check(_lib.qtl_prs(_ptr(xarr), _ptr(yarr), _ptr(output), n, smoothPeriod))
- return _wrap(output, idx, f"PRS_{smoothPeriod}", "momentum", offset)
+ _check(_lib.qtl_rs(_ptr(xarr), _ptr(yarr), _ptr(output), n, smoothPeriod))
+ return _wrap(output, idx, f"RS_{smoothPeriod}", "momentum", offset)
def rocp(close: object, period: int = 14, offset: int = 0, **kwargs) -> object:
diff --git a/python/quantalib/oscillators.py b/python/quantalib/oscillators.py
index fa5b00a8..26bdb291 100644
--- a/python/quantalib/oscillators.py
+++ b/python/quantalib/oscillators.py
@@ -47,7 +47,7 @@ __all__ = [
"psl",
"deco",
"dosc",
- "dymoi",
+ "dymi",
"crsi",
"bbb",
"bbi",
@@ -493,16 +493,16 @@ def dosc(close: object, rsi_period: int = 14, ema1_period: int = 5,
return _wrap(dst, idx, f"DOSC_{rsi_period}", "oscillators", offset)
-def dymoi(close: object, base_period: int = 14, short_period: int = 5,
- long_period: int = 10, min_period: int = 3, max_period: int = 30,
- offset: int = 0, **kwargs) -> object:
+def dymi(close: object, base_period: int = 14, short_period: int = 5,
+ long_period: int = 10, min_period: int = 3, max_period: int = 30,
+ offset: int = 0, **kwargs) -> object:
"""Dynamic Momentum Index."""
offset = int(offset)
src, idx = _arr(close); n = len(src); dst = _out(n)
- _check(_lib.qtl_dymoi(_ptr(src), n, _ptr(dst),
- int(base_period), int(short_period), int(long_period),
- int(min_period), int(max_period)))
- return _wrap(dst, idx, "DYMOI", "oscillators", offset)
+ _check(_lib.qtl_dymi(_ptr(src), n, _ptr(dst),
+ int(base_period), int(short_period), int(long_period),
+ int(min_period), int(max_period)))
+ return _wrap(dst, idx, "DYMI", "oscillators", offset)
def crsi(close: object, rsi_period: int = 3, streak_period: int = 2,
diff --git a/python/quantalib/reversals.py b/python/quantalib/reversals.py
index 20dc0a34..76fa2338 100644
--- a/python/quantalib/reversals.py
+++ b/python/quantalib/reversals.py
@@ -17,7 +17,7 @@ __all__ = [
"pivotext",
"pivotfib",
"pivotwood",
- "psar",
+ "sar",
"sarext",
"swings",
"ttm_scalper",
@@ -120,7 +120,7 @@ def pivotwood(high: object, low: object, close: object, offset: int = 0, **kwarg
return _wrap(ppOutput, idx, "PIVOTWOOD", "reversals", offset)
-def psar(open: object, high: object, low: object, close: object, afStart: float = 0.02, afIncrement: float = 0.02, afMax: float = 0.2, offset: int = 0, **kwargs) -> object:
+def sar(open: object, high: object, low: object, close: object, afStart: float = 0.02, afIncrement: float = 0.02, afMax: float = 0.2, offset: int = 0, **kwargs) -> object:
"""Parabolic SAR."""
afStart = float(afStart)
afIncrement = float(afIncrement)
@@ -129,8 +129,8 @@ def psar(open: object, high: object, low: object, close: object, afStart: float
o, idx = _arr(open); h, _ = _arr(high); l, _ = _arr(low); c, _ = _arr(close)
n = len(o)
output = _out(n)
- _check(_lib.qtl_psar(_ptr(o), _ptr(h), _ptr(l), _ptr(c), _ptr(output), n, afStart, afIncrement, afMax))
- return _wrap(output, idx, "PSAR", "reversals", offset)
+ _check(_lib.qtl_sar(_ptr(o), _ptr(h), _ptr(l), _ptr(c), _ptr(output), n, afStart, afIncrement, afMax))
+ return _wrap(output, idx, "SAR", "reversals", offset)
def sarext(open: object, high: object, low: object, close: object, startValue: float = 0.0, offsetOnReverse: float = 0.0, afInitLong: float = 0.02, afLong: float = 0.02, afMaxLong: float = 0.2, afInitShort: float = 0.02, afShort: float = 0.02, afMaxShort: float = 0.2, offset: int = 0, **kwargs) -> object:
diff --git a/python/quantalib/statistics.py b/python/quantalib/statistics.py
index b4bed830..5b03e8c4 100644
--- a/python/quantalib/statistics.py
+++ b/python/quantalib/statistics.py
@@ -38,7 +38,7 @@ __all__ = [
"zscore",
"cma",
"entropy",
- "correlation",
+ "correl",
"covariance",
"cointegration",
]
@@ -378,13 +378,13 @@ def entropy(close: object, period: int = 10, offset: int = 0, **kwargs) -> objec
return _wrap(dst, idx, f"ENTROPY_{period}", "statistics", offset)
-def correlation(x: object, y: object, period: int = 20,
- offset: int = 0, **kwargs) -> object:
+def correl(x: object, y: object, period: int = 20,
+ offset: int = 0, **kwargs) -> object:
"""Pearson Correlation."""
period = int(kwargs.get("length", period)); offset = int(offset)
xarr, idx = _arr(x); yarr, _ = _arr(y)
n = len(xarr); dst = _out(n)
- _check(_lib.qtl_correlation(_ptr(xarr), _ptr(yarr), n, _ptr(dst), period))
+ _check(_lib.qtl_correl(_ptr(xarr), _ptr(yarr), n, _ptr(dst), period))
return _wrap(dst, idx, f"CORR_{period}", "statistics", offset)
diff --git a/python/quantalib/trends_iir.py b/python/quantalib/trends_iir.py
index a58ae774..97207ff9 100644
--- a/python/quantalib/trends_iir.py
+++ b/python/quantalib/trends_iir.py
@@ -11,7 +11,7 @@ __all__ = [
"adxvma",
"frama",
"holt",
- "htit",
+ "ht_trendline",
"hwma",
"jma",
"kama",
@@ -86,14 +86,14 @@ def holt(close: object, period: int = 14, gamma: float = 0.0, offset: int = 0, *
return _wrap(output, idx, f"HOLT_{period}", "trends_iir", offset)
-def htit(close: object, offset: int = 0, **kwargs) -> object:
+def ht_trendline(close: object, offset: int = 0, **kwargs) -> object:
"""Hilbert Transform Instantaneous Trendline."""
offset = int(offset)
src, idx = _arr(close)
n = len(src)
output = _out(n)
- _check(_lib.qtl_htit(_ptr(src), _ptr(output), n))
- return _wrap(output, idx, "HTIT", "trends_iir", offset)
+ _check(_lib.qtl_httrendline(_ptr(src), _ptr(output), n))
+ return _wrap(output, idx, "HT_TRENDLINE", "trends_iir", offset)
def hwma(close: object, period: int = 14, offset: int = 0, **kwargs) -> object:
diff --git a/python/quantalib/volume.py b/python/quantalib/volume.py
index 28567722..9611666c 100644
--- a/python/quantalib/volume.py
+++ b/python/quantalib/volume.py
@@ -8,7 +8,7 @@ from ._helpers import _arr, _ptr, _out, _wrap, _wrap_multi, _check, _lib
__all__ = [
- "adl",
+ "ad",
"adosc",
"iii",
"kvo",
@@ -38,14 +38,14 @@ __all__ = [
]
-def adl(high: object, low: object, close: object, volume: object, offset: int = 0, **kwargs) -> object:
+def ad(high: object, low: object, close: object, volume: object, offset: int = 0, **kwargs) -> object:
"""Accumulation/Distribution Line."""
offset = int(offset)
h, idx = _arr(high); l, _ = _arr(low); c, _ = _arr(close); v, _ = _arr(volume)
n = len(h)
output = _out(n)
- _check(_lib.qtl_adl(_ptr(h), _ptr(l), _ptr(c), _ptr(v), _ptr(output), n))
- return _wrap(output, idx, "ADL", "volume", offset)
+ _check(_lib.qtl_ad(_ptr(h), _ptr(l), _ptr(c), _ptr(v), _ptr(output), n))
+ return _wrap(output, idx, "AD", "volume", offset)
def adosc(high: object, low: object, close: object, volume: object, fastPeriod: int = 12, slowPeriod: int = 26, offset: int = 0, **kwargs) -> object:
diff --git a/python/src/Exports.Generated.cs b/python/src/Exports.Generated.cs
index cef96a0c..64b1a717 100644
--- a/python/src/Exports.Generated.cs
+++ b/python/src/Exports.Generated.cs
@@ -118,14 +118,14 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_adl")]
- public static int QtlAdl(double* high, double* low, double* close, double* volume, double* output, int n)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_ad")]
+ public static int QtlAd(double* high, double* low, double* close, double* volume, double* output, int n)
{
if (high == null || low == null || close == null || volume == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Adl.Batch(Src(high, n), Src(low, n), Src(close, n), Src(volume, n), Dst(output, n));
+ Ad.Batch(Src(high, n), Src(low, n), Src(close, n), Src(volume, n), Dst(output, n));
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
@@ -449,14 +449,14 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_dchannel")]
- public static int QtlDchannel(double* high, double* low, double* middle, double* upper, double* lower, int n, int period)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_dc")]
+ public static int QtlDc(double* high, double* low, double* middle, double* upper, double* lower, int n, int period)
{
if (high == null || low == null || middle == null || upper == null || lower == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Dchannel.Batch(Src(high, n), Src(low, n), Dst(middle, n), Dst(upper, n), Dst(lower, n), period);
+ Dc.Batch(Src(high, n), Src(low, n), Dst(middle, n), Dst(upper, n), Dst(lower, n), period);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
@@ -929,14 +929,14 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_htit")]
- public static int QtlHtit(double* source, double* output, int n)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_httrendline")]
+ public static int QtlHtTrendline(double* source, double* output, int n)
{
if (source == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Htit.Batch(Src(source, n), Dst(output, n));
+ HtTrendline.Batch(Src(source, n), Dst(output, n));
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
@@ -1240,14 +1240,14 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_kchannel")]
- public static int QtlKchannel(double* high, double* low, double* close, double* middle, double* upper, double* lower, int n, int period, double multiplier)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_kc")]
+ public static int QtlKc(double* high, double* low, double* close, double* middle, double* upper, double* lower, int n, int period, double multiplier)
{
if (high == null || low == null || close == null || middle == null || upper == null || lower == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Kchannel.Batch(Src(high, n), Src(low, n), Src(close, n), Dst(middle, n), Dst(upper, n), Dst(lower, n), period, multiplier);
+ Kc.Batch(Src(high, n), Src(low, n), Src(close, n), Dst(middle, n), Dst(upper, n), Dst(lower, n), period, multiplier);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
@@ -2018,14 +2018,14 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_pchannel")]
- public static int QtlPchannel(double* high, double* low, double* middle, double* upper, double* lower, int n, int period)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_pc")]
+ public static int QtlPc(double* high, double* low, double* middle, double* upper, double* lower, int n, int period)
{
if (high == null || low == null || middle == null || upper == null || lower == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Pchannel.Batch(Src(high, n), Src(low, n), Dst(middle, n), Dst(upper, n), Dst(lower, n), period);
+ Pc.Batch(Src(high, n), Src(low, n), Dst(middle, n), Dst(upper, n), Dst(lower, n), period);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
@@ -2251,27 +2251,27 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_prs")]
- public static int QtlPrs(double* baseSeries, double* compSeries, double* output, int n, int smoothPeriod)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_rs")]
+ public static int QtlRs(double* baseSeries, double* compSeries, double* output, int n, int smoothPeriod)
{
if (baseSeries == null || compSeries == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Prs.Batch(Src(baseSeries, n), Src(compSeries, n), Dst(output, n), smoothPeriod);
+ Rs.Batch(Src(baseSeries, n), Src(compSeries, n), Dst(output, n), smoothPeriod);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- [UnmanagedCallersOnly(EntryPoint = "qtl_psar")]
- public static int QtlPsar(double* open, double* high, double* low, double* close, double* output, int n, double afStart, double afIncrement, double afMax)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_sar")]
+ public static int QtlSar(double* open, double* high, double* low, double* close, double* output, int n, double afStart, double afIncrement, double afMax)
{
if (open == null || high == null || low == null || close == null || output == null) return StatusCodes.QTL_ERR_NULL_PTR;
if (n <= 0) return StatusCodes.QTL_ERR_INVALID_LENGTH;
try
{
- Psar.Batch(Src(open, n), Src(high, n), Src(low, n), Src(close, n), Dst(output, n), afStart, afIncrement, afMax);
+ Sar.Batch(Src(open, n), Src(high, n), Src(low, n), Src(close, n), Dst(output, n), afStart, afIncrement, afMax);
return StatusCodes.QTL_OK;
}
catch { return StatusCodes.QTL_ERR_INTERNAL; }
diff --git a/python/src/Exports.cs b/python/src/Exports.cs
index 4f6b4d30..d598321b 100644
--- a/python/src/Exports.cs
+++ b/python/src/Exports.cs
@@ -386,12 +386,12 @@ public static unsafe partial class Exports
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
- // Dymoi: Pattern A (src, out, int p1..p5)
- [UnmanagedCallersOnly(EntryPoint = "qtl_dymoi")]
- public static int QtlDymoi(double* src, int n, double* dst, int p1, int p2, int p3, int p4, int p5)
+ // Dymi: Pattern A (src, out, int p1..p5)
+ [UnmanagedCallersOnly(EntryPoint = "qtl_dymi")]
+ public static int QtlDymi(double* src, int n, double* dst, int p1, int p2, int p3, int p4, int p5)
{
int v = Chk1(src, dst, n); if (v != 0) return v;
- try { Dymoi.Batch(Src(src, n), Dst(dst, n), p1, p2, p3, p4, p5); return StatusCodes.QTL_OK; }
+ try { Dymi.Batch(Src(src, n), Dst(dst, n), p1, p2, p3, p4, p5); return StatusCodes.QTL_OK; }
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
@@ -1155,7 +1155,7 @@ public static unsafe partial class Exports
{
int v = Chk2(x, y, dst, n); if (v != 0) return v;
v = ChkPeriod(period); if (v != 0) return v;
- try { Correlation.Batch(Src(x, n), Src(y, n), Dst(dst, n), period); return StatusCodes.QTL_OK; }
+ try { Correl.Batch(Src(x, n), Src(y, n), Dst(dst, n), period); return StatusCodes.QTL_OK; }
catch { return StatusCodes.QTL_ERR_INTERNAL; }
}
@@ -1480,10 +1480,10 @@ public static unsafe partial class Exports
// Afirma already exported above in §8.4
// ═══════════════════════════════════════════════════════════════════════
- // Prs: Pattern H (seriesX, seriesY, out, int period) — Momentum/Statistics
+ // Rs: Pattern H (seriesX, seriesY, out, int period) — Momentum/Statistics
// ═══════════════════════════════════════════════════════════════════════
- // Note: Prs might not have a span overload — check at build time
+ // Note: Rs might not have a span overload — check at build time
}
#pragma warning restore CA1031
diff --git a/python/tests/run_all_exported_pandasta_validation.py b/python/tests/run_all_exported_pandasta_validation.py
index 59c999c8..dc303a82 100644
--- a/python/tests/run_all_exported_pandasta_validation.py
+++ b/python/tests/run_all_exported_pandasta_validation.py
@@ -101,9 +101,9 @@ NO_PTA_EQUIVALENT: set[str] = {
"baxterking", "bbb", "bbi", "bbwn", "bbwp", "bessel", "betadist",
"bilateral", "binomdist", "blma", "bpf", "butter2", "butter3",
"bwma", "ccor", "ccv", "ccyc", "cfitz", "change", "cheby1", "cheby2",
- "cointegration", "conv", "coral", "correlation", "covariance", "crma",
+ "cointegration", "conv", "coral", "correl", "covariance", "crma",
"cv", "cvi", "cwt", "deco", "decycler", "dem", "dema_alpha", "dosc",
- "dsma", "dsp", "dwma", "dwt", "dymoi", "eacp", "edcf", "elliptic",
+ "dsma", "dsp", "dwma", "dwt", "dymi", "eacp", "edcf", "elliptic",
"ema_alpha", "etherm", "evwma", "ewma", "expdist", "exptrans",
"fisher04", "gdema", "hanma", "hema", "kri", "lema", "lsma", "mae",
"mape", "mse", "parzen", "pvd", "rain", "rmse", "sgma", "sinema",
diff --git a/python/tests/test_shapes.py b/python/tests/test_shapes.py
index c3d18f07..87f59d46 100644
--- a/python/tests/test_shapes.py
+++ b/python/tests/test_shapes.py
@@ -36,7 +36,7 @@ MULTI_PARAM = [
("apo", {"fast": 12, "slow": 26}),
("deco", {"short_period": 30, "long_period": 60}),
("dosc", {"rsi_period": 14, "ema1_period": 5, "ema2_period": 3, "signal_period": 9}),
- ("dymoi", {"base_period": 14, "short_period": 5, "long_period": 10, "min_period": 3, "max_period": 30}),
+ ("dymi", {"base_period": 14, "short_period": 5, "long_period": 10, "min_period": 3, "max_period": 30}),
("crsi", {"rsi_period": 3, "streak_period": 2, "rank_period": 100}),
("bbb", {"length": 20, "mult": 2.0}),
("bbi", {"p1": 3, "p2": 6, "p3": 12, "p4": 24}),
diff --git a/python/tests/test_signatures.py b/python/tests/test_signatures.py
index d54d5903..867a117b 100644
--- a/python/tests/test_signatures.py
+++ b/python/tests/test_signatures.py
@@ -97,7 +97,7 @@ class TestVolumeIndicatorsHaveVolumeParam:
"""Volume indicators that use _ptr(volume) must have volume in their signature."""
VOLUME_REQUIRED = [
- "adl", "adosc", "iii", "kvo", "va", "vwad", "vwap", "wad",
+ "ad", "adosc", "iii", "kvo", "va", "vwad", "vwap", "wad",
"obv", "pvt", "pvr", "vf", "nvi", "pvi", "tvi", "pvd",
"vwma", "evwma", "efi", "aobv", "mfi", "cmf", "eom", "pvo",
]