# Stock Indicators for .NET ## Library identity - NuGet package: `FacioQuo.Stock.Indicators` - Namespace: `FacioQuo.Stock.Indicators` - Documentation version: v3 (current) - Former package: `Skender.Stock.Indicators` (v2, superseded; see the migration guide below) - Install: `dotnet add package FacioQuo.Stock.Indicators` - Repository: https://github.com/facioquo/stock-indicators-dotnet - Documentation: https://dotnet.stockindicators.dev - Markdown: append `.md` to any page URL, or request it with `Accept: text/markdown` - Generated: 2026-10-15T06:15:05.027Z from commit 4044e66ee11c1611f024b18d0096806706a00c9d --- url: /guide/getting-started.md description: >- Install the Stock Indicators for .NET library and calculate your first indicator in minutes. --- # Getting started Install the library, give it your price history, and calculate your first indicator. Using a coding agent? Paste this prompt; it runs the library on live market data itself and shows you the result: ```prompt Use the guide at https://dotnet.stockindicators.dev/agents/start.md to show me what Stock Indicators for .NET can do on today's market data. If you can run code, do the work yourself. ``` See [Agent setup](/guide/agent-setup.md) for what it covers. ## Installation and setup Install the [FacioQuo.Stock.Indicators](https://www.nuget.org/packages/FacioQuo.Stock.Indicators) NuGet package into your project. > **-group** > > ```bash [.NET CLI] > dotnet add package FacioQuo.Stock.Indicators > ``` > > ```powershell [Package Manager] > Install-Package FacioQuo.Stock.Indicators > ``` ## Calculate your first indicator Indicators take a list of historical price bars, either the library's [`Bar` class](#historical-bars) or [your own bar class](#using-custom-bar-classes). Parameters have standard defaults where an industry convention exists, and you can override any of them. This page uses the **[Batch (Series)](/guide/styles/batch.md)** style, the simplest starting point, which converts a full collection at once. For bars that arrive one at a time, see **[Buffer lists](/guide/styles/buffer.md)** and **[Stream hubs](/guide/styles/stream.md)**, or [compare all three styles](/guide/styles.md). ### Try it now This complete console app downloads real Microsoft daily bars, so you can see results before connecting a market data provider. ```bash dotnet new console -n FirstIndicator cd FirstIndicator dotnet add package FacioQuo.Stock.Indicators ``` Replace the contents of `Program.cs`, then run `dotnet run`. ```csharp using System.Globalization; using FacioQuo.Stock.Indicators; // 32 years of real Microsoft daily bars from the library's test data string url = "https://raw.githubusercontent.com/facioquo/stock-indicators-dotnet/main/tests/Library/TestData/quotes/msft.csv"; string csv = await new HttpClient().GetStringAsync(url); List bars = csv .Split('\n', StringSplitOptions.RemoveEmptyEntries) .Skip(1) // header: date,open,high,low,close,volume .Select(line => line.Trim().Split(',')) .Select(f => new Bar( Timestamp: DateTime.Parse(f[0], CultureInfo.InvariantCulture), Open: decimal.Parse(f[1], CultureInfo.InvariantCulture), High: decimal.Parse(f[2], CultureInfo.InvariantCulture), Low: decimal.Parse(f[3], CultureInfo.InvariantCulture), Close: decimal.Parse(f[4], CultureInfo.InvariantCulture), Volume: decimal.Parse(f[5], CultureInfo.InvariantCulture))) .ToList(); // calculate 50-period SMA and 14-period RSI IReadOnlyList sma = bars.ToSma(50); IReadOnlyList rsi = bars.ToRsi(14); for (int i = bars.Count - 5; i < bars.Count; i++) { Console.WriteLine($"{bars[i].Timestamp:yyyy-MM-dd} close {bars[i].Close:N2} SMA {sma[i].Sma:N2} RSI {rsi[i].Rsi:N1}"); } ``` ```console 2022-03-04 close 289.86 SMA 307.53 RSI 43.1 2022-03-07 close 278.91 SMA 306.45 RSI 36.8 2022-03-08 close 275.85 SMA 305.29 RSI 35.3 2022-03-09 close 288.50 SMA 304.23 RSI 45.5 2022-03-10 close 285.59 SMA 303.13 RSI 43.8 ``` ### Use your own price data Replace the downloaded bars with historical bars from your data provider. `GetBarsFromFeed()` stands in for [your own data acquisition](#where-can-i-get-historical-bar-data). ```csharp using FacioQuo.Stock.Indicators; [..] // fetch historical price bars from your feed (your method) IReadOnlyList bars = GetBarsFromFeed("MSFT"); // calculate 20-period SMA IReadOnlyList results = bars .ToSma(20); // use results as needed for your use case (example only) foreach (SmaResult r in results) { Console.WriteLine($"SMA on {r.Timestamp:d} was ${r.Sma:N4}"); } ``` ```console SMA on 4/19/2018 was $255.0590 SMA on 4/20/2018 was $255.2015 SMA on 4/23/2018 was $255.6135 SMA on 4/24/2018 was $255.5105 SMA on 4/25/2018 was $255.6570 SMA on 4/26/2018 was $255.9705 .. ``` ### Next steps * Browse the [indicators](/indicators.md) for each one's parameters, warmup, and results. * Explore [example code](/examples.md) and the [demo charts](https://charts.stockindicators.dev). * Read the [Guide](/guide.md) for indicator styles, chaining, and custom indicators. ## Historical bars Provide historical price bars as an OHLCV `IReadOnlyList`, `List`, or `ICollection`, with a consistent frequency (day, hour, minute, etc.). To use your own class instead, see [using custom bar classes](#using-custom-bar-classes). | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Close date | | `Open`*`decimal`* | Open price | | `High`*`decimal`* | High price | | `Low`*`decimal`* | Low price | | `Close`*`decimal`* | Close price | | `Volume`*`decimal`* | Volume | ### Where can I get historical bar data? > [!NOTE] > **BYOD: bring your own data** > > You must get price bar data from your own provider. *The `GetBarsFromFeed()` method shown in our examples represents your own acquisition of price data and **is not part of this library**.* Check with your brokerage or a commercial data provider. For free developer APIs, see our ongoing [discussion on market data](https://github.com/facioquo/stock-indicators-dotnet/discussions/579) for ideas. ### How much historical bar data do I need? Each indicator page lists its minimum, but **most use cases need more than the minimum**. As a rule of thumb, 750 bars (about 3 years of daily data) is safe. > [!WARNING] > **๐Ÿšฉ Use more than the minimum history** > > Supplying only the *minimum* bar history is NOT a good optimization. Some indicators use smoothing that converges to better precision over time, and two-decimal precision often needs 250 or more preceding bars. > > For example, if you are using daily data and want one year of precise EMA(250) data, you need to provide 3 years of historical price bars (1 extra year for the lookback period and 1 extra year for convergence); thereafter, you would discard or not use the first two years of results. Occasionally, even more is required for optimal precision. > > See [discussion on warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) for more information. ### Using custom bar classes To use your own bar class without converting to the library `Bar` class, add the `IBar` interface. ```csharp using FacioQuo.Stock.Indicators; [..] public record MyCustomBar : IBar { // required base properties public DateTime Timestamp { get; set; } public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } public decimal Close { get; set; } public decimal Volume { get; set; } // IReusable interface (enables chaining) [JsonIgnore] public double Value => (double)Close; // custom properties public int MyOtherProperty { get; set; } } ``` ```csharp // fetch historical price bars from your favorite feed IReadOnlyList myBars = GetBarsFromFeed("MSFT"); // example: get 20-period simple moving average IReadOnlyList results = myBars.ToSma(20); ``` > [!WARNING] > **๐Ÿšฉ Custom bars must have value based equality** > > Make your custom bar type a `record` class or implement `IEquatable`, so stream hubs can detect duplicate bars. ## Chaining indicators To calculate an indicator of an indicator, such as an SMA of ADX or an [RSI of OBV](https://medium.com/@robswc/this-is-what-happens-when-you-combine-the-obv-and-rsi-indicators-6616d991773d), ***chain*** them: ```csharp // fetch historical price bars from your feed (your method) IReadOnlyList bars = GetBarsFromFeed("SPY"); // calculate RSI of OBV IReadOnlyList results = bars .ToObv() .ToRsi(14); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Utilities See [Utilities and helper functions](/utilities.md) for additional tools. --- --- url: /guide.md description: Learn how to use the Stock Indicators for .NET library --- > [!TIP] > **โœจ Interactive demo** > > See our [charts.stockindicators.dev](https://charts.stockindicators.dev) for interactive visualization demo of library indicators. ## Use the guide Start with [installation and setup](/guide/getting-started.md), then choose the [indicator style](/guide/styles.md) that fits your application. The guide also covers [indicator chaining](/guide/chaining.md), [custom indicators](/guide/customization.md), and [custom stream observers](/guide/custom-observers.md). Using a coding agent? Copy a page as Markdown from its title or begin with [Agent setup](/guide/agent-setup.md). --- --- url: /indicators.md description: >- The Stock Indicators for .NET library contains financial market technical analysis methods to view price patterns or to develop your own trading strategies in Microsoft .NET programming languages and developer platforms. --- The Stock Indicators for .NET library contains financial market technical analysis methods to view price patterns or to develop your own trading strategies in Microsoft .NET programming languages and developer platforms. ## Complete list ### [Price trends](/indicators/price-trends.md) * [Aroon](/indicators/aroon.md) * [ATR Trailing Stop](/indicators/atr-stop.md) * [Average Directional Index (ADX / DMI)](/indicators/adx.md) * [Elder-ray Index](/indicators/elder-ray.md) * [Gator Oscillator](/indicators/gator.md) * [Hurst Exponent](/indicators/hurst.md) * [Ichimoku Cloud](/indicators/ichimoku.md) * [Moving Average Convergence / Divergence (MACD)](/indicators/macd.md) * [SuperTrend](/indicators/super-trend.md) * [Vortex Indicator (VI)](/indicators/vortex.md) * [Williams Alligator](/indicators/alligator.md) ### [Price channels](/indicators/price-channels.md) * [Bollinger Bandsยฎ](/indicators/bollinger-bands.md) * [Donchian Channels](/indicators/donchian.md) * [Fractal Chaos Bands (FCB)](/indicators/fcb.md) * [Keltner Channels](/indicators/keltner.md) * [Moving Average Envelopes](/indicators/ma-envelopes.md) * [Pivot Points](/indicators/pivot-points.md) * [Price Channels](/indicators/donchian.md) * [Rolling Pivot Points](/indicators/rolling-pivots.md) * [STARC Bands](/indicators/starc-bands.md) * [Standard Deviation Channels](/indicators/std-dev-channels.md) ### [Oscillators](/indicators/oscillators.md) * [Awesome Oscillator (AO)](/indicators/awesome.md) * [Chande Momentum Oscillator (CMO)](/indicators/cmo.md) * [Commodity Channel Index (CCI)](/indicators/cci.md) * [ConnorsRSI](/indicators/connors-rsi.md) * [Detrended Price Oscillator (DPO)](/indicators/dpo.md) * [Gator Oscillator](/indicators/gator.md) * [Price Momentum Oscillator (PMO)](/indicators/pmo.md) * [Relative Strength Index (RSI)](/indicators/rsi.md) * [Schaff Trend Cycle (STC)](/indicators/stc.md) * [Stochastic Momentum Index (SMI)](/indicators/smi.md) * [Stochastic Oscillator (KDJ)](/indicators/stoch.md) * [Stochastic RSI](/indicators/stoch-rsi.md) * [Triple EMA Oscillator (TRIX)](/indicators/trix.md) * [True Strength Index (TSI)](/indicators/tsi.md) * [Ultimate Oscillator](/indicators/ultimate.md) * [Williams %R](/indicators/williams-r.md) ### [Stop and reverse](/indicators/stop-and-reverse.md) * [ATR Trailing Stop](/indicators/atr-stop.md) * [Chandelier Exit](/indicators/chandelier.md) * [Parabolic SAR](/indicators/parabolic-sar.md) * [SuperTrend](/indicators/super-trend.md) * [Volatility Stop](/indicators/volatility-stop.md) ### [Candlestick patterns](/indicators/candlestick-patterns.md) * [Doji](/indicators/doji.md) * [Marubozu](/indicators/marubozu.md) #### Other price patterns * [Pivots](/indicators/pivots.md) * [Williams Fractal](/indicators/fractal.md) ### [Volume-based](/indicators/volume-based.md) * [Accumulation Distribution Line (ADL)](/indicators/adl.md) * [Chaikin Money Flow (CMF)](/indicators/cmf.md) * [Chaikin Oscillator](/indicators/chaikin-osc.md) * [Force Index](/indicators/force-index.md) * [Klinger Volume Oscillator (KVO)](/indicators/kvo.md) * [Money Flow Index (MFI)](/indicators/mfi.md) * [On-Balance Volume (OBV)](/indicators/obv.md) * [Percentage Volume Oscillator (PVO)](/indicators/pvo.md) * [Volume Weighted Average Price (VWAP)](/indicators/vwap.md) * [Volume Weighted Moving Average (VWMA)](/indicators/vwma.md) ### [Moving Averages](/indicators/moving-averages.md) * [Arnaud Legoux Moving Average (ALMA)](/indicators/alma.md) * [Double Exponential Moving Average (DEMA)](/indicators/dema.md) * [Endpoint Moving Average (EPMA)](/indicators/epma.md) * [Exponential Moving Average (EMA)](/indicators/ema.md) * [Hilbert Transform Instantaneous Trendline](/indicators/ht-trendline.md) * [Hull Moving Average (HMA)](/indicators/hma.md) * [Kaufman's Adaptive Moving Average (KAMA)](/indicators/kama.md) * [Least Squares Moving Average (LSMA)](/indicators/epma.md) * [McGinley Dynamic](/indicators/dynamic.md) * [MESA Adaptive Moving Average (MAMA)](/indicators/mama.md) * [Modified Moving Average (MMA)](/indicators/smma.md) * [Running Moving Average (RMA)](/indicators/smma.md) * [Simple Moving Average (SMA)](/indicators/sma.md) * [Smoothed Moving Average (SMMA)](/indicators/smma.md) * [Tillson T3 Moving Average](/indicators/t3.md) * [Triple Exponential Moving Average (TEMA)](/indicators/tema.md) * [Volume Weighted Average Price (VWAP)](/indicators/vwap.md) * [Volume Weighted Moving Average (VWMA)](/indicators/vwma.md) * [Weighted Moving Average (WMA)](/indicators/wma.md) See also: [Moving Average Envelopes](/indicators/ma-envelopes.md) ### [Price Transforms](/indicators/price-transforms.md) * [Basic price bar transforms (HL2, HLC3, OC2, OHL3, OHLC4)](/indicators/bar-part.md) * [Ehlers Fisher Transform](/indicators/fisher-transform.md) * [Heikin Ashi](/indicators/heikin-ashi.md) * [Renko Charts](/indicators/renko.md) * [ZigZag](/indicators/zig-zag.md) ### [Price Characteristics](/indicators/price-characteristics.md) * [Average True Range (ATR)](/indicators/atr.md) * [Balance of Power (BOP)](/indicators/bop.md) * [Bull and Bear Power](/indicators/elder-ray.md) * [Choppiness Index (CHOP)](/indicators/chop.md) * [Comparative Relative Strength](/indicators/prs.md) * [Dominant Cycle Periods](/indicators/ht-trendline.md) * [Historical Volatility (HV)](/indicators/std-dev.md) * [Hurst Exponent](/indicators/hurst.md) * [Momentum Oscillator (MO)](/indicators/roc.md) * [Normalized Average True Range](/indicators/atr.md) * [Price Momentum Oscillator (PMO)](/indicators/pmo.md) * [Price Relative Strength (PRS)](/indicators/prs.md) * [Rate of Change (ROC)](/indicators/roc.md) * [Rate of Change with Bands](/indicators/roc-wb.md) * [Rescaled Range Analysis](/indicators/hurst.md) * [True Range (TR)](/indicators/tr.md) * [True Strength Index (TSI)](/indicators/tsi.md) * [Ulcer Index (UI)](/indicators/ulcer-index.md) #### Numerical analysis * [Beta coefficient (ฮฒ)](/indicators/beta.md) * [Coefficient of determination](/indicators/correlation.md) * [Correlation coefficient](/indicators/correlation.md) * [Linear regression (best-fit line)](/indicators/slope.md) * [Mean absolute deviation (MAD)](/indicators/sma-analysis.md) * [Mean absolute percentage error (MAPE)](/indicators/sma-analysis.md) * [Mean square error (MSE)](/indicators/sma-analysis.md) * [R-squared (Rยฒ)](/indicators/correlation.md) * [Slope (gradient)](/indicators/slope.md) * [Standard deviation (ฯƒ)](/indicators/std-dev.md) * [Z-score (standard score)](/indicators/std-dev.md) --- --- url: /utilities.md description: >- The Stock Indicators for .NET library includes utilities to help you use and transform historical price bars and indicator results, and to create custom indicators. --- ## [Utilities for price bars](/utilities/bars.md) * [use alternate price](/utilities/bars.md#use-alternate-price) * [sort price bars](/utilities/bars.md#sort-bars) * [validate bar history](/utilities/bars.md#validate-bar-history) * [resize price bar history](/utilities/bars.md#resize-bar-history) * [streaming aggregator hubs](/utilities/bars.md#streaming-aggregator-hubs) * [extended candle properties](/utilities/bars.md#extended-candle-properties) ## [Utilities for indicator results](/utilities/results.md) * [remove warmup periods](/utilities/results.md#remove-warmup-periods) * [condense](/utilities/results.md#condense) * [find by date](/utilities/results.md#find-by-date) * [sort results](/utilities/results.md#sort-results). ## [Additional helper utilities](/utilities/helpers.md) Numerical and math tools for [custom indicator development](/guide/customization.md): * [numerical methods](/utilities/helpers.md#numerical-methods) (slope, standard deviation) * [`NullMath`](/utilities/helpers.md#nullmath) null-safe math operations * [`DeMath`](/utilities/helpers.md#demath) deterministic precision math ## [Indicator catalog utility](/utilities/catalog.md) Use the [Indicator catalog](/utilities/catalog.md) to discover indicator metadata programmatically โ€” build pickers and configuration UIs, or execute a selected indicator without hard-coding its method call. --- --- url: /migration/v3.md description: >- Guide for migrating from Stock Indicators v2 to v3, including breaking changes, API updates, and new streaming capabilities. --- # Migration from v2 to v3 This guide provides a comprehensive migration path from v2 to v3 of the Stock Indicators library. It includes all technical changes to the public API, syntax changes, and specific examples of deprecated and breaking changes. > [!NOTE] > **Yes, there are *minor breaking changes*, but ...** > > Most of the deprecated v2 syntax has been shimmed in library version `3.0` with `[Obsolete]` code analysis warning flags to aid migrations. These shims are (or will be) removed in version `3.1`, so start with `3.0.2` or later before upgrading further. > **Why `3.0.2` and not `3.0.0`? โ€” defects in the v2 shims** > > `3.0.0` shipped several `[Obsolete]` shims that did not faithfully reproduce the v2 methods they stand in for. All are corrected in `3.0.2`. None of them affect the `ToX()` methods, only the deprecated `GetX()` names. > > **Two returned different values than v2 did.** If you ran either on `3.0.0`, stored results โ€” and any thresholds tuned against them โ€” need revalidating: > > * **`GetPrs()` returned the inverted ratio.** It passed its two series in the wrong order, returning `base / eval`, the reciprocal of the ratio [documented for PRS](/indicators/prs.md). `3.0.2` returns `eval / base`, matching `ToPrs()` and v2. > * **`GetPvo()` used the wrong default periods.** It declared `fastPeriods: 9, slowPeriods: 12` where v2 and `ToPvo()` declare `12` and `26`, so an argument-less call silently computed a differently-parameterized PVO. Callers who passed explicit periods were unaffected. > > **The rest rejected calls that v2 accepted** โ€” these failed loudly at build or run time, never with quietly wrong values: > > * **`GetKeltner()` rejected a fractional multiplier.** It declared `int multiplier` where v2 declares `double`, so `quotes.GetKeltner(20, 2.5, 10)` would not compile. Do not round the multiplier to work around it; that silently changes your channel widths. Upgrade to `3.0.2`, or move to `ToKeltner()`, which always accepted these values. > * **`GetStdDevChannels()` rejected a null lookback** through its tuple overload, declaring `int lookbackPeriods` where v2 declares `int?`, whose `null` means "use the whole series". Do not substitute a fixed lookback; that computes a different channel. The bar overload of the same method still accepts `null` โ€” and its own deprecation message points you there โ€” so the two forms disagreed. Upgrade to `3.0.2`, or move to `ToStdDevChannels()`. > * **`GetPrs()` with no `lookbackPeriods` threw.** The unspecified lookback mapped to `0`, which validation rejects, so the shim's own default raised `ArgumentOutOfRangeException`. `3.0.2` computes with a null `PrsPercent`, as v2 did. > * **`GetPrs()` and `GetRoc()` tuple overloads silently ignored `smaPeriods`.** They still accept and discard the argument โ€” v3 has no `PrsSma`/`RocSma` field โ€” but as of `3.0.2` their deprecation messages say so. > > None of these fixes reach you by dropping the `3.0.2` assembly next to code compiled against `3.0.0` โ€” **recompile.** `GetKeltner()` and `GetStdDevChannels()` changed parameter types, so existing call sites no longer bind to a method that exists. And `GetPvo()`'s corrected defaults are baked into the *caller's* IL at compile time, so an argument-less call keeps computing `9`/`12` until it is rebuilt. Recompiling from source requires no edits. ## Summary of breaking changes We've renamed the package from `Skender.Stock.Indicators` to `FacioQuo.Stock.Indicators`, so you'll have to search and replace your `usings` statements. ### API method naming **All static time-series API methods**: Renamed from `GetX()` to `ToX()` > Example: `GetSma(...)` โ†’ `ToSma(...)` ### Market-data type renames (`Quote` โ†’ `Bar`) To align with industry-standard terminology (an OHLCV aggregate is universally a *bar*; a *quote* is a bid/ask snapshot), the core market-data types were renamed in v3. The old names remain as deprecated aliases during a migration window, so existing code keeps working โ€” update to the new names as the deprecation warnings guide you: * **`Quote` โ†’ `Bar`** (and the built-in `Quote` record โ†’ `Bar` record) * **`IQuote` โ†’ `IBar`** โ€” custom market-data types now implement `IBar` * **`PeriodSize` โ†’ `BarInterval`** (aggregation interval enum; member names unchanged) The old `Quote`, `IQuote`, `PeriodSize`, `IReusableResult`, and `BasicData` names remain as **warning-level `[Obsolete]` aliases** of the new types, so existing code keeps compiling and running while deprecation warnings guide each rename. `Quote`/`IQuote` flow through the new generic API directly, and `PeriodSize` keeps working via obsolete `Aggregate(PeriodSize)`/`GetPivotPoints(PeriodSize)` forwarding overloads. Migrate at your own pace โ€” and finish related member renames such as `Date` โ†’ `Timestamp` to clear all warnings. These shims will be removed in a future major version. > **New:** `BarInterval` now has a bidirectional string-code map โ€” `interval.ToCode()` (e.g. `BarInterval.FiveMinutes` โ†’ `"5m"`) and `"5m".ToBarInterval()` (case-insensitive, with aliases like `"5min"`/`"1day"`). ### Quote/Bar interface details * **`Bar` type** (formerly `Quote`): Immutable `record` type * **`IBar.Date` property**: Renamed to `IBar.Timestamp`. `Date` remains as an `[Obsolete]` alias in v3.x for backward compatibility and will be removed in v3.1 โ€” update consumers to `Timestamp` now. * **`IBar` interface** (formerly `IQuote`): Now a reusable (chainable) type * **Custom bar types**: Must implement the `IReusable` interface * **`IReusableResult`**: Renamed to `IReusable` * **`IReusable.Value` property**: Changed to non-nullable, returns `double.NaN` instead of `null` ### Indicator return types * **All indicator results**: Changed from `sealed class` to immutable `record` types * **`BasicData` type**: Renamed to `TimeValue` * **`AtrStopResult` values**: Changed from `decimal` to `double` * **`UlcerIndexResult.UI` property**: Renamed to `UlcerIndex` * **`SmaAnalysis` model**: Renamed to `SmaAnalysisResult` ### Removed features * **`GetBaseQuote()` indicator**: Removed โ€” use the `Use(CandlePart)` utility instead * **`SyncSeries()` utility**: Removed along with `SyncType` enum * **`Find()` and `FindIndex()` utilities**: Deprecated * **`ToTupleCollection()` utility**: Deprecated * **`ToCollection()` utility**: Deprecated * **`PrsResult.PrsSma` and `RocResult.RocSma`**: Removed. v2 returned a moving average alongside these indicators; v3 has no such field. Chain `results.ToSma(smaPeriods)` onto `ToPrs()` or `ToRoc()` instead. ### Other changes * **Indicator method parameters**: v2 generic signatures like `GetSma(this IEnumerable)` are now interface-typed, like `ToSma(this IReadOnlyList)` and `ToFractal(this IReadOnlyList)`. Concrete lists (e.g. `List`) convert automatically, but an `IEnumerable` does not: pass the list you already hold, or type your own wrapper methods as `IReadOnlyList` with a `class` constraint (see [Step 4](#step-4-update-generic-extension-methods)). Call `.ToSortedList()` only on a source that is truly an `IEnumerable` (see [Step 1](#step-1-update-method-names)) * **`Use()` method**: `candlePart` parameter now required (no default) * **`Use()` return type**: Now returns chainable `TimeValue` instead of tuple * **`Numerix` class**: Renamed to `Numerical` * **Internal signals**: Deprecated for several indicators * **GetX tuple interfaces**: Deprecated * Minor **ADXR calculation** correction to fix a one-period shift in the lookback period ## Quick reference table | v2 API | v3 API | | ------ | ------ | | `quotes.GetSma(20)` | `quotes.ToSma(20)`Method prefix changed | | `Quote` | `Bar`Type renamed (OHLCV bar) | | `IQuote` | `IBar`Interface renamed | | `PeriodSize` | `BarInterval`Enum renamed | | `IBar.Date` | `IBar.Timestamp`Property renamed | | `quotes.Use()` | `quotes.Use(CandlePart.Close)`Parameter now required | | `result.Value == null` | `double.IsNaN(result.Value)`Null handling changed | | `Numerix` | `Numerical`Class renamed | | `BasicData` | `TimeValue`Type renamed | | `SmaAnalysis` | `SmaAnalysisResult`Type renamed | | `UlcerIndexResult.UI` | `UlcerIndexResult.UlcerIndex`Property renamed | | `SyncSeries()` | (removed)Use manual alignment | | `Find()` / `FindIndex()` | LINQ methodsUse `.FirstOrDefault()` etc. | | `GetBaseQuote()` | `Use(CandlePart)`Use utility instead | ## Migration steps This section is for those of you who need a more detailed walk-through for migration. ### Step 1: Update method names Replace all `GetX()` method calls with `ToX()`: ```csharp var smaResults = quotes.GetSma(20); // removed var smaResults = quotes.ToSma(20); // added ``` v3 methods take a time-sorted `IReadOnlyList` and no longer sort or copy their input. v2 did both on every call, which was redundant work for the many callers who already hold a sorted list. Where an `IEnumerable` no longer compiles, prefer these fixes in order: 1. Pass the sorted `List` you already hold. A `List` passes straight in with no conversion. 2. Change your own methods that type bars as `IEnumerable` to take `IReadOnlyList` with a `class` constraint, as in [Step 4](#step-4-update-generic-extension-methods), so the list flows through unconverted. 3. Call `.ToSortedList()` only on a source that is truly an `IEnumerable`, such as a LINQ or Entity Framework query. It copies and sorts on each call, so call it once and reuse the result. ```csharp List bars = LoadSortedBars(); var vwapResults = bars.GetVwap(); // removed var vwapResults = bars.ToVwap(); // added ``` When the source is a query, sort it once: ```csharp IEnumerable query = GetBarsFromDatabase(); var vwapResults = query.GetVwap(); // removed var vwapResults = query.ToSortedList().ToVwap(); // added ``` ### Step 2: Update `IBar` property names Rename `Date` to `Timestamp` in all custom bar classes (and implement `IBar` in place of the obsolete `IQuote`): ```csharp public class MyBar : IQuote // removed public class MyBar : IBar // added { public DateTime Date { get; set; } // removed public DateTime Timestamp { get; set; } // added public decimal Open { get; set; } public decimal High { get; set; } public decimal Low { get; set; } public decimal Close { get; set; } public decimal Volume { get; set; } } ``` ### Step 3: Update custom bar types If you had a custom quote type, change it to a `record` type and derive from `IBar` to replace `IQuote`. ```csharp // v2 public class MyQuote : IQuote { // properties... [JsonIgnore] public double Value => (double)Close; } // v3 - option 1: use record public record MyBar : IBar { // properties... } // v3 - option 2: implement value-based equality public class MyBar : IBar, IEquatable { // properties... // implement value-based equality public override bool Equals(object obj) { /* ... */ } public override int GetHashCode() { /* ... */ } } ``` ### Step 4: Update generic extension methods v2 indicator methods were generic, e.g. `GetSma(...) where TQuote : IQuote`. In v3, they instead accept interface-typed lists โ€” `IReadOnlyList` for bar-based indicators and `IReadOnlyList` for chainable ones. Calls on concrete collections like `List` work unchanged through covariance. If you wrote your own generic extension methods over bars, type the source as `IReadOnlyList` rather than `IEnumerable`, and add a `class` constraint. Covariance (`IReadOnlyList` โ†’ `IReadOnlyList`) only applies when the compiler knows `TBar` is a reference type: ```csharp public static double MyVolumeSma( this IEnumerable bars, // removed this IReadOnlyList bars, // added int lookbackPeriods) where TBar : IBar // removed where TBar : class, IBar // added { IReadOnlyList results = bars .Use(CandlePart.Volume) .ToSma(lookbackPeriods); // ... } ``` Additional interface constraints still compose normally, e.g. `where TBar : class, IBar, IMyOther`. Since all practical `IBar` implementations are reference types (`record` or `class`), the added constraint does not limit usage. ### Step 5: Update `Use()` method calls Add explicit `candlePart` parameter: ```csharp // v2 - candlePart defaulted to Close var quoteParts = quotes.Use(); // v3 - candlePart required var barParts = bars.Use(CandlePart.Close); ``` Handle new `TimeValue` return type: ```csharp // v2 var (timestamp, value) = quotes.Use(CandlePart.Close); // v3 IReadOnlyList barParts = bars.Use(CandlePart.Close); ``` ### Step 6: `Value` is now non-nullable The chainable `IReusable.Value` property changed from `double?` to `double`, returning `double.NaN` instead of `null`. This property exists mainly for internal chaining, so most code is unaffected โ€” named result properties such as `SmaResult.Sma` remain nullable `double?`. If you do read `.Value` directly, replace `result.Value == null` checks with `double.IsNaN(result.Value)`. ### Step 7: Update class references * `Numerix` โ†’ `Numerical` * `BasicData` โ†’ `TimeValue` * `SmaAnalysis` โ†’ `SmaAnalysisResult` * `UlcerIndexResult.UI` โ†’ `UlcerIndexResult.UlcerIndex` ### Step 8: Remove deprecated utilities Replace or remove calls to: * `SyncSeries()` - manually align data instead * `Find()` - use LINQ `.FirstOrDefault()` * `FindIndex()` - use LINQ `.Select((item, index) => ...)` with `.FirstOrDefault()` * `GetBaseQuote()` - use `Use(CandlePart)` utility instead ### Step 9: Update ADXR expectations If using ADXR, expect slight changes in values and timing: ```csharp // v2 - first ADXR at index 40 (for lookbackPeriods=14) var adxResults = quotes.GetAdx(14); var firstAdxr = adxResults[40].Adxr; // not null // v3 - first ADXR at index 41 var adxResults = bars.ToAdx(14); var firstAdxr = adxResults[41].Adxr; // not null // adxResults[40].Adxr is now null ``` ## Behavior changes since 3.0.0 Some indicators now reject inputs that previously produced invalid output, and one buffer list now matches its Series result. Code that passes values in the ranges below needs a different argument. ### Rejected arguments Each case throws `ArgumentOutOfRangeException` that names the failing parameter. | indicator | argument | previously | now | | --------- | -------- | ---------- | --- | | [MACD](/indicators/macd.md) and [PVO](/indicators/pvo.md) | `signalPeriods` of `0` | Accepted; results contained `Infinity` | Must be greater than 0 | | [STC](/indicators/stc.md) | `cyclePeriods` of `0` | Accepted; the stochastic step had no window | Must be greater than 0 | | [EPMA](/indicators/epma.md) buffer list | `lookbackPeriods` of `1` | Accepted by the buffer list only | Must be greater than 1 in every style | | [Chandelier Exit](/indicators/chandelier.md) stream hub | `lookbackPeriods` of `1` | Accepted by the hub only | Must be greater than 1 in every style | ### STC buffer list `StcList` now matches `ToStc` and `StcHub` at short cycles. It previously started its stochastic window at the first bar that had a MACD value, so short cycle and period combinations returned `null` where the Series and hub return values (for example `cyclePeriods: 1, fastPeriods: 1, slowPeriods: 2` at indices 3 to 5). Positions that were `null` there now carry the values `ToStc` returns. ### Buffer list names The `Name` of the buffer lists for ALMA, ATR Stop, Bollinger Bands, Chandelier, Dynamic, Fractal, Renko, Rolling Pivots, Stochastic, StochRSI, and T3 now reports the arguments the list was built with. It previously reported the defaults. ## New v3 feature: streaming capabilities v3 adds incremental and real-time processing. Alongside the v2 **Series** (batch) style, most indicators now also support two streaming styles: * **BufferList** โ€” self-managed incremental updates, ideal for growing datasets. * **StreamHub** โ€” reactive, observable hubs with cascading updates for live feeds. See the [Indicator styles guide](/guide/styles.md) for a full feature comparison and detailed usage. The examples below show the v2โ†’v3 transition for each style. > [!CAUTION] > **Optional migrations** > > These migrations to streaming style indicators are only appropriate if you have advanced incremental or live-streaming uses cases. > > **Our time series (batch) style indicators are still the best choice for processing of complete historical OHLCV aggregate price datasets**, and are functionally unchanged from v2. ### Migration examples #### From v2 Series to v3 BufferList If you were building up results incrementally in v2, you can now use BufferList for better performance: ```csharp // v2 approach (inefficient for incremental updates) List quotes = new(); void OnQuoteReceived(Quote newQuote) // your feed's message handler { quotes.Add(newQuote); var results = quotes.ToSma(20); // Recalculates everything! // Use results... } // v3 BufferList (efficient incremental updates) SmaList smaList = new(20); void OnBarReceived(Bar newBar) // your feed's message handler { smaList.Add(newBar); SmaResult latest = smaList[^1]; // Use latest... } ``` #### From v2 Series to v3 StreamHub If you need to coordinate multiple indicators with live data: ```csharp // v2 approach (requires maintaining separate lists) List quotes = new(); void OnQuoteReceived(Quote newQuote) // your feed's message handler { quotes.Add(newQuote); var smaResults = quotes.ToSma(20); var rsiResults = quotes.ToRsi(14); var macdResults = quotes.ToMacd(); // Process results... } // v3 StreamHub (coordinated real-time updates) BarHub barHub = new(); SmaHub smaHub = barHub.ToSmaHub(20); RsiHub rsiHub = barHub.ToRsiHub(14); MacdHub macdHub = barHub.ToMacdHub(); void OnBarReceived(Bar newBar) // your feed's message handler { barHub.Add(newBar); // Single update propagates to all observers // Access latest results from each hub } ``` ### Streaming documentation For indicator-specific streaming examples, see the documentation for each indicator. Indicators with streaming support include a "Streaming" section with BufferList and StreamHub examples. Popular indicators with complete streaming documentation: * Moving Averages: [SMA](/indicators/sma.md), [EMA](/indicators/ema.md), [WMA](/indicators/wma.md) * Oscillators: [RSI](/indicators/rsi.md), [MACD](/indicators/macd.md), [Stochastic](/indicators/stoch.md) * Channels: [Bollinger Bands](/indicators/bollinger-bands.md), [Keltner](/indicators/keltner.md) ## Need help? * [Guide and Pro tips](/guide/getting-started.md) - Getting started with v3 * [Indicators](/indicators.md) - Indicator-specific documentation * [GitHub Discussions](https://github.com/facioquo/stock-indicators-dotnet/discussions) - Ask questions and share ideas * [GitHub Issues](https://github.com/facioquo/stock-indicators-dotnet/issues) - Report bugs or request features --- --- url: /contributing.md description: >- This NuGet package is an open-source project. Learn how to contribute issues, fixes, new indicators, new features, or to our discussions. --- # Contributing guidelines **Thanks for taking the time to contribute!** This project is simpler than most, so it's a good place to start contributing to the open source community, even if you're a newbie. We are accepting these sorts of changes and requests: * Bug reports and fixes * Usability improvements * Documentation updates * New reputable "by the book" indicators and overlays We are not accepting things that should be done in your own wrapper code: * Personal customizations and preferences * Modified or augmented outputs that are not intrinsic If you have general interest in contributing, but are not sure where to start, please [contact us](#contact-info) and we can help to find work in an area of interest. ## Reporting bugs and feature requests If you suspect a problem, please [report a bug Issue](https://github.com/facioquo/stock-indicators-dotnet/issues/new?labels=bug\&template=bug_report.yml) with a detailed description of the problem, steps to reproduce, code samples, and any reference materials. For enhancements, [create a feature Issue](https://github.com/facioquo/stock-indicators-dotnet/issues/new?labels=enhancement\&template=feature_request.yml). Use the [Discussions](https://github.com/facioquo/stock-indicators-dotnet/discussions) area for general ideation and help/usage questions. ## Project management * Planned work is managed in [the backlog](https://github.com/orgs/facioquo/projects/27). * Work items are primarily entered as Notes (not Issues), except where an issue or feature is user reported. With that said, Notes can be converted to Issues if in-progress and collaborative discussion is needed. ## Developing * Read this first: [A Step by Step Guide to Making Your First GitHub Contribution](https://codeburst.io/a-step-by-step-guide-to-making-your-first-github-contribution-5302260a2940). I also have a discussion [on Forking](https://github.com/facioquo/stock-indicators-dotnet/discussions/503) if you have questions. * If you want to work on something specific, please mention your intention on the related [Issue](https://github.com/facioquo/stock-indicators-dotnet/issues). If an Issue does not exist for your contribution, please create one before starting. This will help us reserve that feature and avoid duplicative efforts. * If you are adding a new indicator, the easiest way to do this is to copy the folder of an existing indicator and rename everything using the same [naming conventions](https://github.com/facioquo/stock-indicators-dotnet/blob/main/src/AGENTS.md#per-indicator-facade-class) and taxonomy. All new indicators should include [tests](#testing). * Do not commingle multiple contributions on different topics. Please keep changes small and separate. ### Initial setup The recommended setup uses VS Code with the dev container (automated) or the **Setup: Dev tools** VS Code task to check for prerequisites. For manual setup, run these commands once after cloning: Install the .NET 10 SDK for local development. The library targets .NET 8, 9, and 10, but the test project defaults to .NET 10; the CI matrix also runs the unit tests on other supported .NET framwork versions. You do not need the .NET 8 or 9 SDKs for the normal local test workflow. ```bash dotnet tool restore # install .NET CLI tools dotnet restore # restore NuGet packages ``` ## Testing * Review the `tests/Library` folder for examples of unit tests. Just copy one of these. * New indicators should be tested against manually calculated, proven, accurate results. It is helpful to include your manual calculations spreadsheet in the appropriate indicator test folder when [submitting changes](#submitting-changes). * Historical Stock Bars are automatically added to unit test methods. A `Data.Quotes.xlsx` Excel file is included in the `tests/Library/TestData` folder that is an exact copy of what is used in the unit tests. Use a copy of this file for your manual calculations to ensure that it is correct. Do not commit changes to the original file. * We expect all unit tests to execute successfully and all Errors and Warning resolved before you submit your code. * Failed builds or unit testing will block acceptance of your Pull Request when submitting changes. ### Regression baseline testing Regression baselines detect unintended behavioral changes in indicators. Each baseline is a JSON file with expected outputs for standard test data. ```bash # run all regression baseline tests dotnet test --filter "TestCategory=Regression" # regenerate all baselines (locally) dotnet run --project tools/baselining -- --all # regenerate specific baseline dotnet run --project tools/baselining -- --indicator SMA ``` Regenerate baselines after intentional algorithm changes, .NET upgrades, or test data changes. Use the [Regenerate Baselines workflow](https://github.com/facioquo/stock-indicators-dotnet/actions/workflows/regenerate-baselines.yml) for automated regeneration via GitHub Actions. When reviewing PRs with baseline changes, verify the reason is documented, review numeric differences, and ensure no unexpected indicators were affected. ### Performance benchmarking Performance (timing) benchmarks run through one script: `tools/performance/perf.sh`. Copy/paste these exact commands โ€” run them from the repository root. Do not add options; the defaults keep runs comparable to the committed baselines. Requirements: .NET SDK and [`jq`](https://jqlang.github.io/jq/) (for `evaluate` and `spot`). ```bash # Check one indicator against the baselines (fast โ€” use this in your dev loop) bash tools/performance/perf.sh spot Ema # Run the full suite and report regressions vs baselines (~1 hour) bash tools/performance/perf.sh evaluate # Regenerate and replace the committed baselines (~1 hour); review git diff after bash tools/performance/perf.sh reset ``` That is everything most contributors need. For the baseline set, single-style spot checks, CI workflows, and raw BenchmarkDotNet usage, see the [benchmarking guide](https://github.com/facioquo/stock-indicators-dotnet/blob/main/tools/performance/benchmarking.md). ## Documentation This site uses [VitePress](https://vitepress.dev) with Vue components and Markdown. Our documentation site code is in the `docs` folder. Build the site locally to test that it works properly. The site renders charts with the `@facioquo/indy-charts` package from GitHub Packages, so installing needs a token with `read:packages` access, stored in your user-level `~/.npmrc` (not the project `.npmrc`): ```bash # one-time: grant your gh CLI token read:packages access gh auth refresh --scopes read:packages # write the registry and token to your user-level ~/.npmrc pnpm config set @facioquo:registry https://npm.pkg.github.com pnpm config set "//npm.pkg.github.com/:_authToken" "$(gh auth token)" ``` ```bash # from the docs folder; the site opens at http://localhost:5173/ cd docs pnpm install pnpm run docs:dev ``` When adding or updating indicators: * Add or update the `/docs/indicators/` documentation files. * Indicator charts render live from the chart API, not from image files: * Add `` to the page. Most oscillator pages add `withOverlay` to their first chart. * Add the same `Name` as a key in `docs/.vitepress/theme/chart-indicators.ts`. You choose the key (PascalCase, like `Macd` or `AroonOsc`); its `uiid` is the API's own id for the indicator, which can differ (`Aroon` is `AROON UP/DOWN`). * Refresh the [chart snapshot](#chart-snapshot). * A chart needs the indicator listed in the [facioquo/stock-charts](https://github.com/facioquo/stock-charts) API first; open the change there. Until the API serves it, leave the chart out of the page. * Other page image assets go in `/docs/.vitepress/public/assets/` and can be optimized to `webp` format using [ImageMagick](https://imagemagick.org) or the [cwebp Encoder CLI](https://developers.google.com/speed/webp/docs/cwebp) and a command like `cwebp -resize 832 0 -q 100 examples.png -o examples-832.webp` **Local cache cleanup note:** To clear documentation or workspace build caches, run the VS Code task `Clean: All` (or run platform-specific clean tasks). See `.vscode/tasks.json` for exact task names and behavior. ### Accessibility testing * Use Lighthouse in Chrome, or * Run the automated task: **Tasks: Run Task** โ†’ `Test: Website a11y (axe-core)`, or * Run it manually: `pnpm run test:a11y` (builds, then scans every sitemap page with axe-core against WCAG 2.1 A/AA; tests the `localhost` build, not production) Broken-link checking is a separate task, `Test: Website links (htmlproofer)`, or `pnpm run test:links`. ### Chart snapshot Charts fall back to a snapshot of the chart API committed under `docs/.vitepress/public/data/chart-api/`, so they still render when the API is unreachable. The `Refresh chart snapshot` workflow regenerates it monthly and opens a pull request; `docs:build` never fetches it. * Regenerate it by hand with `pnpm run snapshot:charts` after adding a chart to `.vitepress/theme/chart-indicators.ts` or changing the landing overlays. * `pnpm run test:offline` blocks the API and checks that every chart renders from the snapshot. * `pnpm run test:charts` and the a11y scan also serve charts from the snapshot, so a chart missing from it fails them too. ## Submitting changes By submitting changes to this repo you are also acknowledging and agree to the terms in both the [Developer Certificate of Origin (DCO) 1.1](https://developercertificate.org) and the [Apache 2.0 license](https://opensource.org/licenses/Apache-2.0). These are standard open-source terms and conditions. When ready, submit a [Pull Request](https://docs.github.com/en/pull-requests) with a clear description of what you've done and why it's important. ### Pull Request naming convention Pull Request titles must follow the [Conventional Commits](https://www.conventionalcommits.org) format: `type: Subject` where: * `type` is one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, plan (lowercase) * `Subject` starts with an uppercase letter Examples: `feat: Add RSI indicator`, `fix: Resolve calculation error in MACD`, `docs: Update API documentation`, `plan: Define technical implementation approach` Always write a clear log message for your commits. One-line messages are fine for most changes. After a Pull Request is reviewed, accepted, and *squash* merged to `main`, we may batch changes before publishing a new package version to the [public NuGet repository](https://www.nuget.org/packages/FacioQuo.Stock.Indicators). Please be patient with turnaround time. ## Code reviews and admin If you want to contribute administratively, do code reviews, or provide general user support, we're also currently seeking a few core people to help. Please [contact us](#contact-info) if interested. ## Standards and guidelines * [Guiding principles for this project](https://github.com/facioquo/stock-indicators-dotnet/discussions/648) * [.NET Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines) * [NuGet Best Practices](https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/nuget) * [Semantic Version 2.0](https://semver.org) ## Versioning & deployment We use [GitVersion](https://gitversion.net) for automated [semantic versioning](https://semver.org). Version numbers are automatically generated based on branch names, commit messages, and Git history. | Component | Format | Description | | --------- | --------- | ----------- | | Major | `x.-.-` | Breaking changes. Use `+semver: major` in commit message. | | Minor | `-.x.-` | New features, backward compatible. Use `+semver: minor` in commit message. | | Patch | `-.-.x` | Bug fixes, documentation. Use `+semver: patch` (or default auto-increment). | | Suffix | `-label.N` | Pre-release identifier. Automatically set based on branch and build type. | ### Branch-based versioning GitVersion automatically determines version suffixes based on the branch: * **main branch**: The current stable line, produces `3.x.x` versions * CI builds: `3.0.2-ci.345` (includes build metadata) * Production: `3.0.2` (no suffix) * **v2 branch** (support): Legacy compatibility line, produces stable `2.x.x` maintenance patch versions only (no preview suffix) โ€” accepts security/compatibility fixes, not new features * **Release branches** (`v4`, `v5`, โ€ฆ matching the next major): Produce `x.x.x-preview.N` versions while the next major version is under development * **Feature branches**: `x.x.x-{branch-name}.N` (branch name becomes suffix) ### Controlling version increments Add semver tags to PR merge commit messages to control version bumps: * `+semver: major` โ†’ increments major version (breaking changes) * `+semver: minor` โ†’ increments minor version (new features) * `+semver: patch` โ†’ increments patch version (bug fixes, default behavior) * `+semver: none` โ†’ no version increment Example merge commit: `feat: Add new indicator (+semver: minor)` ### Creating preview releases from stable branches To publish a preview/pre-release version from `main` or `v2` (normally stable branches), create a Git tag with the desired suffix before triggering deployment: ```bash # Create preview tag git tag 3.1.0-preview.1 git push origin 3.1.0-preview.1 # Then trigger manual workflow deployment with preview=true ``` GitVersion will honor the tag's pre-release suffix. This approach works for any pre-release label (preview, rc, beta, alpha). ### Package deployment and tagging Packages are deployed via two separate GitHub Actions workflows: #### CI Package deploy (automatic) **Trigger:** Push to main or `v*` branches * Published to GitHub Packages only * Version format: `{Major}.{Minor}.{Patch}-ci.{run_number}` * Examples: `3.0.2-ci.567` (main), `2.7.2-ci.1234` (v2) * Idempotent: Each commit gets unique incrementing run number * No Git tags created **Workflow:** `.github/workflows/deploy-package-github.yml` #### Production package deploy (manual) **Trigger:** Publishing a GitHub Release * Published to `nuget.org` only * Version comes directly from release tag (strips 'v' prefix) * Examples: * Tag `3.0.2` โ†’ deploys `3.0.2` (stable, from `main`) * Tag `2.8.0` โ†’ deploys `2.8.0` (stable, from `v2` support branch) * Tag `4.0.0-preview.2` โ†’ deploys `4.0.0-preview.2` (preview, from a future major-version release branch) * Published releases: Full deployment to nuget.org * Manual dry-run: Run the workflow from the Actions tab ("Run workflow") and supply the release tag โ€” build only, no deploy * Saving a draft release does **not** trigger the workflow * Git tag already exists (from release creation) **Workflow:** `.github/workflows/deploy-package-nuget.yml` ### Version examples | Scenario | Trigger | Version | Registry | Notes | | :------- | :------ | :------ | :------- | :---- | | CI build | Push to main | `3.0.2-ci.1234` | GitHub Packages | Run 1234 | | CI build | Push to main | `3.0.2-ci.1235` | GitHub Packages | Run 1235 (next commit) | | CI build | Push to v2 | `2.7.2-ci.567` | GitHub Packages | Run 567 | | Production | Release tag `3.0.2` | `3.0.2` | nuget.org | Stable version, main | | Production | Release tag `2.8.0` | `2.8.0` | nuget.org | Stable maintenance version, v2 | | Production | Release tag `4.0.0-preview.2` | `4.0.0-preview.2` | nuget.org | Preview version, future major-version release branch | | Dry-run | Manual run, tag `3.0.2` | `3.0.2` | None (dry-run) | Build only, no deploy | For technical details, see: * GitVersion configuration: [`src/gitversion.yml`](https://github.com/facioquo/stock-indicators-dotnet/blob/main/src/gitversion.yml) * CI workflow: [`deploy-package-github.yml`](https://github.com/facioquo/stock-indicators-dotnet/blob/main/.github/workflows/deploy-package-github.yml) * Production workflow: [`deploy-package-nuget.yml`](https://github.com/facioquo/stock-indicators-dotnet/blob/main/.github/workflows/deploy-package-nuget.yml) ## License [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) This repository uses a standard Apache 2.0 open-source license. It enables open-source community development by protecting the project and contributors from certain legal risks while allowing the widest range of uses, including in closed source software. Please review the [license](https://opensource.org/licenses/Apache-2.0) before using or contributing to the software. ## Contact info [Start a new discussion](https://github.com/facioquo/stock-indicators-dotnet/discussions) or [submit an issue](https://github.com/facioquo/stock-indicators-dotnet/issues) if it is publicly relevant. You can also direct message [@daveskender](https://twitter.com/messages/compose?recipient_id=27475431). Thanks, Dave Skender --- --- url: /about.md description: Information about Stock Indicators for .NET, sources, and attributions --- # About this library **Stock Indicators for .NET** is an open-source C# [NuGet library](https://www.nuget.org/packages/FacioQuo.Stock.Indicators) that transforms historical price bars into technical indicators. The library provides industry-standard technical analysis indicators with a focus on accuracy, performance, and developer ergonomics. ## Project information | | | | --- | --- | | **License** | [Apache 2.0](https://opensource.org/licenses/Apache-2.0) | | **Source code** | [GitHub repository](https://github.com/facioquo/stock-indicators-dotnet) | | **Package** | [FacioQuo.Stock.Indicators](https://www.nuget.org/packages/FacioQuo.Stock.Indicators) (NuGet.org) | | **Documentation** | [dotnet.stockindicators.dev](https://dotnet.stockindicators.dev) | | **Demo site** | [charts.stockindicators.dev](https://charts.stockindicators.dev) | An alternate Python variant of this library is available at [python.stockindicators.dev](https://python.stockindicators.dev). ## Attributions This NuGet library project is developed with support from [community contributors](/#contribute-to-help-others). Our documentation, examples, and testing is additionally built upon the work of several excellent open-source projects and data providers. > [!TIP] > **Documentation and testing only** > > The `FacioQuo.Stock.Indicators` NuGet library itself does not contain work products or inherit licensing from any of these providers. ### Chart libraries * **[indy-charts](https://github.com/facioquo/stock-charts)** - Reusable stock indicator chart components used throughout this documentation. Developed by [facioquo](https://github.com/facioquo) and licensed under the Apache License 2.0. * **[Chart.js](https://www.chartjs.org)** - Underlying charting library used by chart components (including our [demo site](https://charts.stockindicators.dev)). Licensed under the MIT License. ### Data providers and SDK libraries * **[Alpaca Markets](https://alpaca.markets)** - Market data used for bars in our [demo site](https://charts.stockindicators.dev) and examples projects. * **[Coinbase](https://www.coinbase.com)** - Live cryptocurrency market data via WebSocket feeds for real-time testing. * **[JKorf/Coinbase.Net](https://github.com/JKorf/Coinbase.Net)** - .NET client library for Coinbase WebSocket API used for live market data streaming in testing tools. Licensed under the MIT License. ### Development tools and frameworks * **[VitePress](https://vitepress.dev)** - Static site generator used for this documentation website (Vue.js and Vite). * **[BenchmarkDotNet](https://benchmarkdotnet.org)** - Performance benchmarking framework used for indicator optimization and regression testing. Licensed under the MIT License. * **[Cloudflare Pages](https://pages.cloudflare.com)** - Documentation hosting and deployment platform. ## Community and support ### Get help * [GitHub Discussions](https://github.com/facioquo/stock-indicators-dotnet/discussions) - Ask questions and share ideas * [GitHub Issues](https://github.com/facioquo/stock-indicators-dotnet/issues) - Report bugs or request features * [Twitter/X](https://twitter.com/messages/compose?recipient_id=27475431) - Direct message @daveskender ### Contributing This library is maintained by [Dave Skender](https://github.com/DaveSkender) and community contributors. If you'd like to contribute: * Review the [contributing guidelines](/contributing.md) * Check the [project backlog](https://github.com/orgs/facioquo/projects/27) * Submit pull requests with improvements Special thanks to all [community contributors](https://github.com/facioquo/stock-indicators-dotnet/graphs/contributors) who have helped improve this library. ### Support the project If you find this library useful, consider: * Adding a โญ on [GitHub](https://github.com/facioquo/stock-indicators-dotnet) * [Becoming a sponsor](https://github.com/sponsors/facioquo) * Sharing the library with others ## Technical details ### Target frameworks The library targets actively supported Microsoft .NET platforms: * .NET 10.0 * .NET 9.0 * .NET 8.0 The compiled library is [Common Language Specification (CLS) compliant](https://docs.microsoft.com/en-us/dotnet/standard/common-type-system) and can be used in other .NET languages. ### Quality standards This library follows strict quality standards: * Mathematical precision validation against authoritative sources and against manually calculated results. * Comprehensive unit test and regression test coverage * Performance testing and benchmarking with `BenchmarkDotNet` * Continuous integration and automated testing * Designed with a balanced set of [guiding principles](https://github.com/facioquo/stock-indicators-dotnet/discussions/648) ## Resources and references Technical indicator formulas and methodologies are based on industry-standard references from [reputable authors](https://github.com/facioquo/stock-indicators-dotnet/discussions/1024) including: * Published financial analysis textbooks * Academic papers on technical analysis * Established trading platforms and charting software * Community-validated calculations Specific references are documented in individual indicator pages where applicable. ## Related projects * [Stock Indicators for Python](https://python.stockindicators.dev) - Python port * [Demo chart site](https://charts.stockindicators.dev) - Interactive visualization --- --- url: /guide/agent-setup.md description: >- Paste one prompt into a coding agent and watch it run Stock Indicators for .NET on live market data, then narrate what it finds. --- # Agent setup The first run needs: * **An agent that can run code**, such as Claude Code, Codex, or GitHub Copilot's coding agent. A chat without code execution can describe the library but cannot run it, and will tell you so. * **The .NET 10 SDK**, which the reference apps target. A cloud sandbox installs it on its own; on your machine, the agent asks before installing it. The library itself also runs on .NET 8 and 9. * **Optional:** a free [Alpaca](https://alpaca.markets/) API key in `ALPACA_KEY` and `ALPACA_SECRET`, for US stocks. Crypto markets need no key. Paste this prompt into that agent: ```prompt Use the guide at https://dotnet.stockindicators.dev/agents/start.md to show me what Stock Indicators for .NET can do on today's market data. If you can run code, do the work yourself. ``` ## What your agent will do The [agent guide](https://dotnet.stockindicators.dev/agents/start.md) tells your agent to show you the library working before it asks you to install anything: * **If it can run code**, it does the work itself: installs the .NET SDK in its sandbox if needed, runs the library on live market data, and hands back a chart, findings backed by numbers, and the code that produced them. * **If it can't**, it says so in its first sentence, before writing any code, and hands you the setup prompt above to run in an agent that can. * **It never fakes results.** No invented prices, no indicators recalculated in another language, and no old test data passed off as current. With no other instructions, it starts the live market desk and lists the other playbooks so you can redirect it: | Playbook | You end up with | | -------- | --------------- | | [Live market desk](https://dotnet.stockindicators.dev/agents/live-signals.md) (default) | Eight crypto markets streaming live with no API key: each one's daily regime, its rotation against Bitcoin, explained buy and sell signals, and insights your agent narrates as they happen | | [Backtest](https://dotnet.stockindicators.dev/agents/backtest.md) | Your rules run over years of real daily history up to the latest close, with an equity curve and drawdown against buy-and-hold | | [Integrate](https://dotnet.stockindicators.dev/agents/integrate.md) | Indicators calculated inside your own app, from your own bar type, with a test that pins the results | | [Migrate](https://dotnet.stockindicators.dev/agents/migrate.md) | A v2 codebase moved to v3, with output checked against the old results | | [Tour](https://dotnet.stockindicators.dev/agents/tour.md) | Short, runnable demos of what sets the library apart, and ideas for what you could build | The desk also runs US stocks and sector ETFs; for those, have a free [Alpaca](https://alpaca.markets/) API key ready in `ALPACA_KEY` and `ALPACA_SECRET`. ## Work with a coding agent For later tasks, ask for something specific. Agents get better results when you also paste the relevant page, using **Copy page** beside its title. For example: ```prompt Add a stream hub to my price feed handler that tracks MACD and ADX, and raises an event when MACD crosses its signal line while ADX is above 25. ``` ```prompt Backtest an RSI(2) mean-reversion rule on SPY daily bars, trading only above the 200-day SMA, and compare the result with buy-and-hold. ``` ```prompt Find the indicators that measure trend strength, then show each on the same real price history and explain how their warmup periods differ. ``` Point agents at the [`/llms.txt`](/llms.txt) index so they fetch only the pages they need. Save [`/llms-full.txt`](/llms-full.txt), all the documentation in one large file, for broad analysis; for routine questions it crowds your own code out of the agent's context. ## Machine-readable documentation * **Markdown pages**: add `.md` to any page URL, such as `/indicators/sma.md`, or request the page with an `Accept: text/markdown` header. * **Agent skill**: agents that support [Agent Skills](https://agentskills.io) can install the skill listed at [`/.well-known/agent-skills/index.json`](/.well-known/agent-skills/index.json). * **Browser tools**: in browsers that support [WebMCP](https://webmachinelearning.github.io/webmcp/), every page exposes read-only tools to search the documentation, get any indexed page as Markdown, and get the current page as Markdown. --- --- url: /guide/styles.md description: Three distinct indicator styles to support different use cases. --- ## Feature comparison The library provides three distinct indicator styles to support different use cases. | Feature | [Batch (Series)](/guide/styles/batch.md) | [Buffer lists](/guide/styles/buffer.md) | [Stream hubs](/guide/styles/stream.md) | | ------- | ------------ | ------------ | ----------- | | Incrementing | no | yes | yes | | Batch speed | fastest | faster | fast | | Scaling | low | moderate | high | | Class type | static | instance | instance | | Base interface | `IReadOnlyList` | `IReadOnlyList` (+ `Add`) | `IStreamHub` | | Complexity | lowest | moderate | highest | | Chainable | yes | manual | yes | | Pruning | with utility | auto-preset | auto-preset | | Healing | no | no | yes | Healing: Built-in handling of out-of-order and de-duplication of incoming data. ## Which style to use? Start with **Batch (Series)** style unless you have a specific need for incremental processing or real-time streaming. * Use **[Batch](/guide/styles/batch.md)** when you have a complete historical dataset and need to calculate indicators once. Fastest and simplest. * Use **[Buffer lists](/guide/styles/buffer.md)** when bars arrive one at a time and you need incremental processing without the overhead of a full hub infrastructure. * Use **[Stream hubs](/guide/styles/stream.md)** when you need coordinated, automatic updates across multiple chained indicators from a live data feed with self-healing. ### Bars that arrive one at a time Don't re-run a Series method for every new bar. Its cost grows with the history it recalculates, while a stream hub's cost per new bar stays flat. For EMA, SMA, RSI and MACD, the hub is already cheaper once the history passes about 12 to 20 bars, and at 1,000 bars the Series method costs 40 to 110 times as much per bar. To measure it on your own hardware, run the `StreamCrossover` benchmark in `tools/performance`. ## Getting started See [Getting started](/guide/getting-started.md) for installation, first steps, and example usage. --- --- url: /guide/styles/batch.md description: >- Learn how to use Series batch style indicators for one-time bulk conversions of historical bar data --- # Series style indicators for batch processing Series batch style is the fastest and simplest way to calculate indicators from complete historical datasets. Use this when you have all your historical price bars available and need to convert them to indicators in one operation. ## When to use batch style **Ideal for:** * Complete historical datasets ready for conversion * One-time indicator calculations * Backtesting and historical analysis * Performance-critical batch processing * Simplest implementation with minimal code **Not ideal for:** * Real-time or streaming data (use [Stream hubs](/guide/styles/stream.md)) * Incremental bar-by-bar processing (use [Buffer lists](/guide/styles/buffer.md)) * Live data feeds requiring continuous updates ## Basic usage All series-style indicators produce complete results for the entire bar history provided. The results are returned as a time series dataset, not just a single value. ```csharp using FacioQuo.Stock.Indicators; // fetch historical price bars from your data source IReadOnlyList bars = GetBarsFromFeed("MSFT"); // calculate 20-period SMA IReadOnlyList results = bars.ToSma(20); // iterate through all results foreach (SmaResult r in results) { Console.WriteLine($"SMA on {r.Timestamp:d} was ${r.Sma:N4}"); } ``` ## Chaining indicators Create sophisticated analysis by chaining indicators together. For the broader concept, see [Chaining indicators](/guide/chaining.md). ```csharp // calculate RSI of On-Balance Volume IReadOnlyList results = bars .ToObv() .ToRsi(14); // or use alternate candle price IReadOnlyList results = bars .Use(CandlePart.HL2) .ToEma(20); ``` ## Performance characteristics * **Speed:** Fastest indicator style (~baseline performance) * **Memory:** Minimal overhead, returns `IReadOnlyList` * **Thread safety:** Results are immutable and thread-safe * **Scalability:** Limited to single calculation per dataset ## Processing results ### Find specific dates ```csharp IReadOnlyList results = bars.ToSma(20); DateTime lookupDate = DateTime.Parse("2024-01-15"); SmaResult? result = results.Find(lookupDate); ``` ### Remove warmup periods ```csharp // auto-remove recommended warmup IReadOnlyList results = bars .ToAdx(14) .RemoveWarmupPeriods(); // or specify custom amount IReadOnlyList results = bars .ToAdx(14) .RemoveWarmupPeriods(100); ``` ### Condense results Remove non-essential data points (useful for candlestick patterns): ```csharp // only return records with signals IReadOnlyList signals = bars .ToMarubozu() .Condense(); ``` ## See also * [Buffer lists](/guide/styles/buffer.md) for incremental processing * [Stream hubs](/guide/styles/stream.md) for real-time data * [Guide](/guide.md) for detailed examples * [Indicators](/indicators.md) for available indicators --- --- url: /guide/styles/buffer.md description: >- Learn how to use Buffer list style indicators for efficient incremental bar-by-bar processing --- # Buffer style indicators for incremental processing Buffer list style provides efficient incremental processing for growing datasets. Use this when bars arrive sequentially and you need to add them one at a time without the overhead of a full hub infrastructure. ## When to use buffer lists **Ideal for:** * Building up historical data incrementally * Processing data feeds where bars arrive sequentially * Self-managed incremental calculations * Scenarios without multi-indicator coordination needs * Memory-efficient processing with auto-pruning **Not ideal for:** * Complete historical datasets (use [Batch style](/guide/styles/batch.md)) * Multiple indicators needing coordinated updates (use [Stream hubs](/guide/styles/stream.md)) * One-time calculations (use [Batch style](/guide/styles/batch.md)) ## Basic usage Buffer lists maintain incremental state as you add new bars: ```csharp using FacioQuo.Stock.Indicators; // create buffer list with lookback period SmaList smaList = new(lookbackPeriods: 20); // call from your WebSocket or SSE message handler for each closed bar void OnBarReceived(Bar bar) { smaList.Add(bar); SmaResult r = smaList[^1]; // use result (SMA is null during warmup period) if (r.Sma is not null) { Console.WriteLine($"{r.Timestamp:d}: SMA = {r.Sma:N2}"); } } ``` Indexing `[^1]` right after `Add` is safe for indicators that emit one result per bar. `RenkoList` can add zero or several bricks per bar, so check `Count` before reading it. > [!WARNING] > **๐Ÿšฉ Add bars in chronological order** > > A buffer list is a single-pass accumulator: every `Add` assumes the new value is the newest one. It does **not** reorder input, detect duplicates, or correct revised values โ€” feeding an out-of-order, repeated, or late-arriving bar produces silently incorrect results. If your data can arrive out of order (e.g. a raw WebSocket feed, or merging two sources), sort by timestamp before adding, or use a [Stream hub](/guide/styles/stream.md) instead โ€” stream hubs are built for late arrivals, same-timestamp corrections, and rollback. ## Key features ### List interface Buffer lists are read-only result lists you append to. The base `BufferList` implements `IReadOnlyList` โ€” so you get indexer access, `Count`, and enumeration โ€” plus indicator-specific `Add` overloads for feeding new values and a small set of list helpers (`Clear`, `Contains`, `CopyTo`): ```csharp SmaList smaList = new(20); // add individual bars smaList.Add(bar); // or add batches smaList.Add(barList); // read-only list access int count = smaList.Count; bool isEmpty = smaList.Count == 0; SmaResult latest = smaList[^1]; ``` Note that a buffer list is not a general-purpose mutable collection: it does not implement `ICollection`, so there is no `Remove`, no insert-at-index, and `Add` appends a computed result rather than an arbitrary item. Pruning of old results is automatic (see [Memory management](#memory-management)). ### Automatic buffer management Buffer lists automatically manage internal buffers needed for calculations: * Maintains lookback periods internally * No manual state management required * Efficient incremental updates (typically O(1) or O(log n)) ### Memory management Control memory usage with `MaxListSize`: ```csharp SmaList smaList = new(20) { MaxListSize = 1000 // keep only last 1000 results }; ``` When the list exceeds `MaxListSize`, older results are automatically pruned. Default is 100,000 elements. ## Chaining indicators Chain buffer lists for derived indicators. For the broader concept, see [Chaining indicators](/guide/chaining.md). > [!NOTE] > **Operator-orchestrated** > > Unlike series or stream-hub chaining, this is orchestrated by you rather than the library: you manually take each result from one list and add it to the next. The library does not coordinate the cascade. ```csharp ObvList obvList = new(); RsiList rsiList = new(14); // call from your WebSocket or SSE message handler void OnBarReceived(Bar bar) { // add the bar to OBV, then chain the new OBV result into RSI obvList.Add(bar); rsiList.Add(obvList[^1]); RsiResult latest = rsiList[^1]; // RSI of OBV } ``` ## Performance characteristics * **Overhead:** ~10-20% slower than batch style for equivalent datasets * **Memory:** Maintains internal buffers for lookback periods * **Latency:** Optimized for per-bar updates, typically O(1) or O(log n) * **Thread safety:** Not thread-safe; synchronize external access if needed ## Usage patterns ### Server-sent events feed A buffer list fits a feed that delivers one closed bar per event. This example reads an SSE stream with `SseParser` from `System.Net.ServerSentEvents`, which handles multi-line events and dispatch; it ships with .NET 10, and .NET 8 and 9 need the `System.Net.ServerSentEvents` NuGet package. Mapping each event's data to a `Bar` depends on your provider's payload. ```csharp using System.Net.ServerSentEvents; SmaList smaList = new(20); using HttpClient http = new(); using Stream stream = await http.GetStreamAsync("https://example.com/bars/stream", cancellationToken); await foreach (SseItem item in SseParser.Create(stream).EnumerateAsync(cancellationToken)) { Bar bar = ParseBar(item.Data); // your payload mapping smaList.Add(bar); SmaResult latest = smaList[^1]; Console.WriteLine($"{latest.Timestamp:d}: SMA = {latest.Sma:N2}"); } ``` ### Batch addition with incremental updates ```csharp SmaList smaList = new(20); // seed with history before the live feed starts smaList.Add(historicalBars); // then add each live bar from your WebSocket or SSE message handler void OnBarReceived(Bar bar) { smaList.Add(bar); ProcessLatestResult(smaList[^1]); } ``` ### WebSocket feed The `OnBarReceived` handlers above assume your client invokes the handler one message at a time, in arrival order. A callback-based WebSocket client can invoke its handler concurrently, and a lock around `Add` only prevents corruption โ€” it does not restore arrival order once two callbacks race for it, and a buffer list mis-computes silently on an out-of-order `Add`. When your client does that, drive `Add` from a single sequential receive loop instead, so only one bar is ever in flight. Only use a buffer list when the feed guarantees chronological order โ€” see the [order warning](#basic-usage) above; an out-of-order feed needs a [Stream hub](/guide/styles/stream.md). ```csharp SmaList smaList = new(20); using ClientWebSocket socket = new(); await socket.ConnectAsync(feedUri, CancellationToken.None); byte[] buffer = new byte[4096]; using MemoryStream message = new(); while (socket.State == WebSocketState.Open) { WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { break; } // a message can arrive as several frames; parse only once it is complete message.Write(buffer, 0, result.Count); if (!result.EndOfMessage) { continue; } WebSocketBar wsBar = ParseBar(message.GetBuffer().AsSpan(0, (int)message.Length)); message.SetLength(0); Bar bar = new(wsBar.Timestamp, wsBar.Open, wsBar.High, wsBar.Low, wsBar.Close, wsBar.Volume); smaList.Add(bar); SmaResult latest = smaList[^1]; Console.WriteLine($"{latest.Timestamp:d}: SMA = {latest.Sma:N2}"); } ``` `ReceiveAsync` returns one frame at a time and the loop accumulates frames until `EndOfMessage`, so `Add` and every read of `smaList` happen on this one loop โ€” no lock needed. If your client library only exposes a callback rather than a receive loop you control, have the callback enqueue onto a single-consumer `Channel`, and do the `Add` and every read from the one loop that drains it, instead of locking around `Add` in the callback. ## See also * [Batch style](/guide/styles/batch.md) for one-time calculations * [Stream hubs](/guide/styles/stream.md) for coordinated real-time updates * [Indicators](/indicators.md) for available buffer list indicators --- --- url: /guide/styles/stream.md description: >- Learn how to use Stream hub style for real-time indicator processing with observable patterns and coordinated updates --- # Hub style indicators for advanced streaming Stream hub style provides real-time processing with observable patterns and state management. Multiple indicators can subscribe to a central `BarHub` for coordinated updates, making it ideal for live data feeds and complex event-driven architectures. ## When to use stream hubs **Ideal for:** * Live data feeds and WebSocket integration * Multiple indicators requiring synchronized updates * Trading applications with low-latency requirements * Real-time dashboards and monitoring * Complex event-driven architectures **Not ideal for:** * One-time historical analysis (use [Batch style](/guide/styles/batch.md)) * Simple incremental processing (use [Buffer lists](/guide/styles/buffer.md)) * Scenarios without real-time requirements ## Basic usage Create a bar hub and subscribe indicators as observers: ```csharp using FacioQuo.Stock.Indicators; // create bar hub (the data source) BarHub barHub = new(); // subscribe indicators to the hub SmaHub smaHub = barHub.ToSmaHub(20); RsiHub rsiHub = barHub.ToRsiHub(14); // call from your WebSocket or SSE message handler as each bar arrives void OnBarReceived(Bar bar) { // adding to barHub automatically updates all subscribers barHub.Add(bar); SmaResult sma = smaHub.Results[^1]; RsiResult rsi = rsiHub.Results[^1]; // use results for trading logic, alerts, etc. if (sma.Sma is not null && rsi.Rsi > 70) { Console.WriteLine($"{bar.Timestamp:d}: Overbought at {bar.Close:C2}"); } } ``` ## Key features ### Observable pattern The hub-observer architecture ensures coordinated updates: * Single bar update propagates to all subscribed indicators * Automatic cascade execution in correct sequence * Reduced coordination complexity * Minimal latency overhead ### Chaining indicators Create sophisticated derived indicators. For the broader concept, see [Chaining indicators](/guide/chaining.md). ```csharp BarHub barHub = new(); // chain RSI from EMA EmaHub emaHub = barHub.ToEmaHub(20); RsiHub rsiHub = emaHub.ToRsiHub(14); // RSI of EMA // or chain directly RsiHub rsiOfEma = barHub .ToEmaHub(20) .ToRsiHub(14); // publish bar - both EMA and RSI update automatically barHub.Add(newBar); ``` ### State management and rollback Handle late-arriving data and corrections: ```csharp BarHub barHub = new(); SmaHub smaHub = barHub.ToSmaHub(20); // add bars normally barHub.Add(bar1); barHub.Add(bar2); // late-arriving data with earlier timestamp barHub.Add(lateBar); // triggers recalculation in dependent hubs // correct a bad value: re-add a bar with the same timestamp barHub.Add(correctedBar); // same timestamp, revised values โ†’ rebuild // or remove a bad bar (and everything at/after its timestamp) barHub.RemoveRange(badBar.Timestamp, notify: true); // triggers recalculation // or remove a single bar by its timestamp barHub.Remove(badBar); // finds the entry by timestamp, then rebuilds ``` The hub automatically handles state rollback and recalculation when data arrives out of order or needs correction. To revise a single value in place, re-`Add` a bar with the same `Timestamp`; to drop data, remove it by timestamp with `RemoveRange(fromTimestamp, notify)`, by position with `RemoveAt(cacheIndex)`, or by bar with `Remove(bar)` (which locates the entry by timestamp). These mutations are **enforced to run on the root hub only** โ€” the `BarHub` (or `TradeTickHub`) you add bars to. Calling `Add`, `RemoveAt`, `RemoveRange`, `Remove`, or `Reinitialize` on a subscribed/chained hub throws `InvalidOperationException`; mutate the root hub, which cascades every change to the dependent hubs (see [Thread safety](#thread-safety)). ### Refused bars A bar older than everything the hub still holds is refused when it falls inside history the hub already pruned (`PrunedHistory`), or when the cache is full (`CacheFull`). When both apply, the reason is `PrunedHistory`. `BarHub` raises `BarRejected` for each refused bar and leaves its cache unchanged: ```csharp barHub.BarRejected += (_, e) => Console.WriteLine($"Refused bar at {e.Bar.Timestamp:u}: {e.Reason}"); ``` The event fires while the hub holds its lock, so keep handlers fast and non-throwing. A handler exception propagates out of `Add` and stops a batch at the refused bar. ## Performance characteristics * **Overhead:** ~20-30% slower than batch style for equivalent datasets * **Memory:** Maintains cache and state for all subscribed indicators * **Latency:** Optimized for real-time updates, typically <1ms per bar * **Scalability:** Supports multiple concurrent observers with single propagation ## Thread safety Stream hubs follow a **single-writer** model. Internal locking protects cache *integrity* during the rebuild and rollback that out-of-order data triggers โ€” but it does **not** make concurrent mutation safe. Two threads calling mutating methods at the same time can still interleave incorrectly. * **Internal locking** guards the cache so a rebuild never exposes a half-updated state to readers. * **Serialize all mutating calls** โ€” `Add`, `RemoveAt`, `RemoveRange`, `Reinitialize`, and `Rebuild` must come from a single writer (one thread, or funneled through a lock / `Channel`). * **Single-threaded usage** requires no additional synchronization. * **Multi-threaded usage** must serialize every external call that mutates the hub; reads of `Results` should also be coordinated with the writer (see the example below). ### What's safe, and what to avoid The library deliberately keeps the mutation surface small: `Results` is a read-only view, so you can't reach the underlying cache with `List` / `ICollection` methods. Stay within that surface: * โœ… **Do** feed and correct data through the **root** hub โ€” the `BarHub` (or `TradeTickHub`) you created and add bars to. It cascades every change to the dependent hubs automatically. * โœ… **Do** read results through `Results`; if you hand them to another thread, call `Snapshot()` (an atomic, immutable copy taken under the hub's lock) rather than enumerating the live `Results` view while the writer mutates it. * โŒ **Don't** call `Add` / `RemoveAt` / `RemoveRange` / `Remove` / `Reinitialize` on a *subscribed* (chained) hub such as a `SmaHub` โ€” these throw `InvalidOperationException`. Those hubs are driven by their provider; mutating one directly would desynchronize it from that provider, and a later rebuild could produce wrong results the hub can't heal from. Feed and correct through the root hub instead. * โŒ **Don't** mutate from more than one thread at a time (see the example below). This single-writer expectation isn't unique to this library: most built-in .NET collections (`List`, `Dictionary`, `Queue`) are likewise unsafe for concurrent writers. The hub adds internal locking to keep its *own* cache consistent during a rebuild, but coordinating *your* calls is still your responsibility โ€” exactly as it would be for any ordinary collection. ### Thread-safe external access example ```csharp BarHub barHub = new(); SmaHub smaHub = barHub.ToSmaHub(20); // Use lock for coordinated multi-threaded access object hubLock = new(); // Feed thread: WebSocket or SSE callbacks adding bars void OnBarReceived(Bar bar) { lock (hubLock) { barHub.Add(bar); } } // Reader thread: consuming results Task consumer = Task.Run(() => { while (running) { SmaResult? latest; lock (hubLock) { latest = smaHub.Results.Count > 0 ? smaHub.Results[^1] : null; } ProcessResult(latest); } }); ``` > [!WARNING] > **๐Ÿšฉ Data corruption risk** > > Internal thread-safety protects cache integrity during rebuilds (e.g., out-of-order data reconciliation). However, a hub instance is not safe for concurrent external use from multiple threads. Callers must ensure only one thread accesses a given instance at a time. ## Advanced patterns ### Reactive strategies ```csharp BarHub barHub = new(); EmaHub emaFast = barHub.ToEmaHub(50); EmaHub emaSlow = barHub.ToEmaHub(200); // add bars to barHub (from stream) barHub.Add(newBar); // and the 2 EmaHub will be in sync if(emaFast.Results[^2].Ema < emaSlow.Results[^2].Ema && emaFast.Results[^1].Ema > emaSlow.Results[^1].Ema) { // cross over occurred } ``` ### Event-driven alerts ```csharp BarHub barHub = new(); RsiHub rsiHub = barHub.ToRsiHub(14); void ProcessLiveData(Bar bar) { barHub.Add(bar); RsiResult latest = rsiHub.Results[^1]; if (latest?.Rsi > 70) { TriggerAlert("Overbought", bar.Close, latest.Rsi); } else if (latest?.Rsi < 30) { TriggerAlert("Oversold", bar.Close, latest.Rsi); } } ``` ## WebSocket/SSE integration This example demonstrates how to connect stream hubs to a live WebSocket feed. The pattern applies to any real-time data source (WebSocket, SSE, message queue, etc.) where bars arrive asynchronously. The hub's `Add` method integrates each incoming bar, automatically propagating updates to all subscribed indicators. If your feed delivers raw trade ticks or finer-grained bars than you want to analyze, quantize them first with a [streaming aggregator hub](/utilities/bars.md#streaming-aggregator-hubs) โ€” `TradeTickHub` โ†’ `TradeTickAggregatorHub` for ticks, or `BarAggregatorHub` for bar-to-bar resampling โ€” then chain your indicator hubs off the aggregated output. Because socket callbacks can fire concurrently, every `Add` must go through a single writer (see [Thread safety](#thread-safety) above). The handler below serializes with a lock; for higher throughput, post incoming bars onto a single-consumer `Channel` and call `Add` from one drain loop instead. ```csharp // setup hubs BarHub barHub = new(); SmaHub smaHub = barHub.ToSmaHub(20); // single-writer gate: serialize every Add to the hub object hubLock = new(); // WebSocket message handler (may be invoked concurrently) void OnBarReceived(WebSocketBar wsBar) { // convert WebSocket bar to library Bar Bar bar = new(wsBar.Timestamp, wsBar.Open, wsBar.High, wsBar.Low, wsBar.Close, wsBar.Volume); // update hub through the single-writer gate - // all observers cascade automatically lock (hubLock) { barHub.Add(bar); } // in this example, the subscribing SmaHub will // auto-generate the next corresponding SMA value } ``` ## Memory management Stream hubs automatically prune old results when the cache exceeds the configured maximum size: ```csharp // default max cache size (100,000 items) BarHub barHub = new(); // or configure custom max cache size BarHub limitedHub = new(maxCacheSize: 500); // automatic FIFO pruning when limit reached SmaHub smaHub = limitedHub.ToSmaHub(20); // as new bars arrive, oldest results are removed automatically void OnBarReceived(Bar bar) => limitedHub.Add(bar); // oldest pruned if over limit ``` The default cache size is 100,000 items. For applications with different requirements, specify a custom `maxCacheSize` when creating the BarHub. ## Fault handling and recovery A hub guards against runaway feedback (for example, an accidental circular chain, or a provider stuck re-sending the same tick). If the **same timestamp with identical values** is re-sent more than 100 times in a row, the hub faults: * `IsFaulted` flips to `true`, * subscribed observers receive `OnError(OverflowException)`, and * the offending `Add` throws `OverflowException` ("A repeated stream update exceeded the 100 attempt thresholdโ€ฆ"). ```csharp BarHub barHub = new(); SmaHub smaHub = barHub.ToSmaHub(20); try { barHub.Add(bar); } catch (OverflowException) { // a circular chain or a stuck provider // re-sent an identical tick 100+ times } if (barHub.IsFaulted) { // clear the fault and resume streaming barHub.ResetFault(); } ``` `ResetFault()` clears the faulted state so the hub can keep processing. The threshold only trips on *byte-identical* repeats โ€” a normal correction (same timestamp, **different** values) is handled as a rollback, not a fault. If your feed legitimately re-sends identical trade prints, dedupe upstream or vary a field before calling `Add` so a real burst of identical ticks doesn't trip the guard. > [!WARNING] > **๐Ÿšฉ Reinitialize is a single-writer operation** > > `Reinitialize()` unsubscribes, rebuilds the cache from the provider, then re-subscribes. Bars that arrive in the brief window between the rebuild and the re-subscribe can be missed. Call `Reinitialize()` only when no concurrent `Add` is in flight โ€” i.e. from the same single writer that feeds the hub. > [!NOTE] > **Cache size inheritance** > > Hubs will inherit the `maxCacheSize` of its provider. For example, if you set a size of 1,000 for your `BarHub`, then a chained `SmaHub` will also have a maximum cache size of 1,000. > [!TIP] > **โœจ Optimize cache size for your use case** > > Set your `maxCacheSize` according to how you use the data produced in the hub cache. If you only need the latest indicator values and don't read history, a smaller cache saves memory. > > ```csharp > // configure a modest cache that still clears every warmup floor > BarHub limitedHub = new(maxCacheSize: 500); > > // automatic FIFO pruning when limit reached > SmaHub smaHub = limitedHub.ToSmaHub(20); > ``` > > Don't size the cache down to the indicator's lookback, though โ€” two things bite: > > * **Inheritance.** The `BarHub`'s `maxCacheSize` flows to every chained hub (above), so the cache must satisfy the *largest* warmup in the whole chain, not just one indicator. > * **Warmup floor.** Each hub validates `maxCacheSize` against its own warmup requirement at construction and throws `ArgumentOutOfRangeException` if it's too small. That requirement is often a multiple of the lookback: RSI needs ~2ร— its period, TEMA and TRIX ~3ร—, and the Hilbert-transform trendline needs a fixed 63 periods regardless of lookback. > > Pick a floor comfortably above the deepest warmup in the chain (with headroom for late-arrival rollbacks), rather than the bare indicator minimum. > [!WARNING] > **๐Ÿšฉ Corrections after pruning are approximate for stateful indicators** > > Once the cache has pruned old bars, a correction or late arrival that triggers a rebuild can no longer see the pruned history, so the rebuilt results don't match a hub that received the same data in order: > > * **Indicators with a warmup/lookback** (window indicators like SMA, and recursive smoothers like EMA and the Wilder family) lose their leading results to `null` โ€” the earliest retained bars no longer have enough history to recompute their warmup. For a pure window indicator like SMA the *remaining* values stay exact; the leading ones just disappear. > * **Recursive smoothers** (EMA and everything built on it; the Wilder family โ€” RSI, ATR, ADX, SMMA) additionally re-seed from the truncated history and drift, re-converging as the recursion re-stabilizes. The Wilder hubs that re-derive their seed on *every* rollback (RSI, ADX, SMMA) drift on any correction once the cache has pruned, not only deep ones. > * **Cumulative indicators** (OBV, ADL) have no warmup to lose, so no leading `null`s โ€” instead they restart their running total from the truncated history and carry a *permanent* offset across every result. > > This only happens when `maxCacheSize` is **smaller than the history you revise**. Size `maxCacheSize` to retain the full history you may need to correct โ€” with an adequate cache, a rebuild reproduces an in-order hub exactly. ## See also * [Batch style](/guide/styles/batch.md) for one-time calculations * [Buffer lists](/guide/styles/buffer.md) for simple incremental processing * [Custom observers](/guide/custom-observers.md) for subscribing an observer to a hub to push results into a UI, persistence, or alerting pipeline * [Streaming aggregator hubs](/utilities/bars.md#streaming-aggregator-hubs) for quantizing live ticks or bars into larger period bars in real time * [Indicators](/indicators.md) for available stream hub indicators --- --- url: /guide/chaining.md description: >- Combine indicators to build indicators of indicators โ€” feed one indicator's results into another across any indicator style. --- # Chaining indicators **Chaining** lets you calculate an *indicator of indicators* โ€” for example an SMA of an [ADX](/indicators/adx.md), or an [RSI of OBV](https://medium.com/@robswc/this-is-what-happens-when-you-combine-the-obv-and-rsi-indicators-6616d991773d). Instead of feeding raw bars into a single indicator, you feed the **results** of one indicator into the next. ```csharp // RSI of On-Balance Volume IReadOnlyList results = bars .ToObv() .ToRsi(14); ``` You can also start a chain from a chosen price field with [`Use(CandlePart)`](/utilities/bars.md#use-alternate-price): ```csharp // EMA of the HL2 price (average of high and low) IReadOnlyList results = bars .Use(CandlePart.HL2) .ToEma(20); ``` ## How it works Chaining works through the `IReusable` interface. Any result that implements `IReusable` exposes a single representative `Value`, which the next indicator consumes as its input series: * **Chainable inputs** โ€” most indicators accept a reusable series, so they can take either raw `bars` or another indicator's results. * **Chainable outputs** โ€” a result type is chainable only when it implements `IReusable` (it exposes a `Value`). Indicators that produce multiple primary outputs (e.g. bands or channels) may not be chainable as a source. See [Creating custom indicators](/guide/customization.md) to make your own indicators chainable. ## Chaining in each style Chaining is available in all three [indicator styles](/guide/styles.md); the concept is the same, but the mechanics differ: * **[Batch (Series)](/guide/styles/batch.md#chaining-indicators)** โ€” chain extension methods fluently (`bars.ToObv().ToRsi(14)`). The standard, default approach. * **[Buffer lists](/guide/styles/buffer.md#chaining-indicators)** โ€” feed one buffer list's results into another as values arrive. Unlike the other styles, this chaining is orchestrated by you, not the library: you decide when to pass each result onward. * **[Stream hubs](/guide/styles/stream.md#chaining-indicators)** โ€” subscribe one hub to another so chained indicators update automatically as new bars stream in. ## See also * [Indicator styles](/guide/styles.md) โ€” compare batch, buffer, and stream * [Use alternate price](/utilities/bars.md#use-alternate-price) โ€” start a chain from a chosen price field * [Creating custom indicators](/guide/customization.md) โ€” make your own indicators chainable --- --- url: /guide/customization.md description: >- Learn how to create your own custom technical indicators using the Stock Indicators for .NET library as a foundation. This guide shows you how to build custom indicators that integrate seamlessly with the library's existing features. --- # Creating custom indicators At some point in your journey, you may want to create your own custom indicators. The following guide shows you how to create custom indicators that work seamlessly with this library. > [!WARNING] > **๐Ÿšฉ Series (batch) style only** > > Custom indicators are currently supported for the **Batch (Series)** style only. Creating custom [Buffer list](/guide/styles/buffer.md) or [Stream hub](/guide/styles/stream.md) indicators is not yet supported as a first-class extension point; support is planned for a future release (buffer lists: [#2096](https://github.com/facioquo/stock-indicators-dotnet/issues/2096), stream hubs: [#2097](https://github.com/facioquo/stock-indicators-dotnet/issues/2097)). To integrate custom logic with streaming data today, see [Custom observers](/guide/custom-observers.md). > โœจ Working example code is available in the [CustomIndicatorsLibrary](https://github.com/facioquo/stock-indicators-dotnet/tree/main/docs/examples/CustomIndicatorsLibrary) project. ## Creating a custom indicator ### Step 1: Create the result class Create your results class by implementing the `IReusable` interface or inheriting from existing result patterns. This allows your custom indicator to be chainable with other indicators. ```csharp using FacioQuo.Stock.Indicators; namespace Custom.Indicators; // Custom results class public record AtrWmaResult : IReusable { public DateTime Timestamp { get; init; } public double? AtrWma { get; init; } // Identify value to propagate in chains double IReusable.Value => AtrWma.Null2NaN(); } ``` ### Step 2: Create your custom indicator Create your custom algorithm following the same patterns as the main library. ```csharp using FacioQuo.Stock.Indicators; namespace Custom.Indicators; public static class CustomIndicators { /// /// ATR-weighted moving average (custom indicator example) /// /// Historical price bars /// Lookback period /// Collection of AtrWmaResult public static IReadOnlyList ToAtrWma( this IReadOnlyList bars, int lookbackPeriods) { // Validate parameters ArgumentNullException.ThrowIfNull(bars); if (lookbackPeriods <= 0) { throw new ArgumentOutOfRangeException( nameof(lookbackPeriods), "Lookback periods must be greater than 0."); } // sort price bars (optional) List barsList = bars .OrderBy(x => x.Timestamp) .ToList(); // initialize results List results = new(barsList.Count); // get pre-requisite ATR values List atrResults = barsList .ToAtr(lookbackPeriods) .ToList(); // roll through source values for (int i = 0; i < barsList.Count; i++) { IBar b = barsList[i]; double atrWma = double.NaN; // only do calculations after uncalculable periods if (i >= lookbackPeriods - 1) { double sumWma = 0; double sumAtr = 0; for (int p = i - lookbackPeriods + 1; p <= i; p++) { double close = (double)barsList[p].Close; double atr = atrResults[p].Atr ?? double.NaN; sumWma += atr * close; sumAtr += atr; } atrWma = sumWma / sumAtr; } // add record to results results.Add(new AtrWmaResult( Timestamp: b.Timestamp, AtrWma: atrWma.NaN2Null())); } return results; } } ``` ### Step 3: Use your custom indicator Use your custom indicator just like the built-in indicators: ```csharp using FacioQuo.Stock.Indicators; using Custom.Indicators; // Get historical price bars IReadOnlyList bars = GetBarsFromFeed("MSFT"); // Calculate custom indicator IReadOnlyList results = bars.ToAtrWma(10); // Use results foreach (AtrWmaResult r in results) { Console.WriteLine( $"ATR WMA on {r.Timestamp:d} was {r.AtrWma:N4}"); } ``` ## Advanced patterns ### Chainable indicators By implementing `IReusable`, your custom indicator can be chained with other indicators: ```csharp // Chain your custom indicator with RSI var rsiOfAtrWma = bars .ToAtrWma(10) .ToRsi(14); ``` ### Using multiple indicator styles You can also implement your custom indicator in other styles: * **Buffer list style** - For incremental processing * **Stream hub style** - For real-time data feeds See the [Guide](/guide.md) for more information about different indicator styles. ### Best practices When creating custom indicators: 1. **Validate inputs** - Always validate parameters and bars 2. **Handle edge cases** - Check for insufficient data, null values 3. **Follow naming conventions** - Use `To{IndicatorName}` pattern 4. **Implement IReusable** - Enable chaining with other indicators 5. **Add XML documentation** - Document parameters and return values 6. **Test thoroughly** - Verify calculations against reference data ## Example projects For complete working examples, see: * [CustomIndicatorsLibrary](https://github.com/facioquo/stock-indicators-dotnet/tree/main/docs/examples/CustomIndicatorsLibrary) - Shows how to create custom indicators * [CustomIndicatorsUsage](https://github.com/facioquo/stock-indicators-dotnet/tree/main/docs/examples/CustomIndicatorsUsage) - Shows how to use custom indicators ## See also * [Custom stream hub observers](/guide/custom-observers.md) - React to new stream data * [Utilities and helpers](/utilities.md) - Tools for working with bars and results * [Contributing guidelines](/contributing.md) - How to contribute indicators to the library --- --- url: /guide/custom-observers.md description: >- Implement an observer of a streaming hub for UI dispatch, persistence, logging, or alerting without subclassing. --- # Custom observers (external integration) Subscribing a **custom observer** is the supported way to integrate with a hub's event stream. Use it to *react to a hub's output* โ€” push updates into a UI, persist values to a database, ship them to a message bus, feed an alerting pipeline, or filter/transform results and re-emit them to downstream indicators. ## When to use an observer | Goal | Pattern | | ---- | ------- | | Forward hub results to a UI thread | Subscribe a custom `IStreamObserver` | | Persist results to a database | Subscribe a custom `IStreamObserver` | | Log every value the hub emits | Subscribe a custom `IStreamObserver` | | Trigger external alerts on threshold crosses | Subscribe a custom `IStreamObserver` | | Filter/transform results and feed downstream chained indicators | Implement `IChainProvider` in addition to `IStreamObserver` (see below) | Subscribing does not modify the source hub. The hub keeps its cache, its rollback behavior, and its other subscribers. Your observer is a peer subscriber that receives the same notifications; if it throws, the hub isolates it so the other subscribers are unaffected (see [If an observer throws](#if-an-observer-throws)). > [!NOTE] > **Fully customizable stream hubs are not yet supported** > > Stream hub customization has not been implemented yet (see [#2097](https://github.com/facioquo/stock-indicators-dotnet/issues/2097)). Until then, this observer pattern is the supported, idiomatic way to thread custom processing into a streaming pipeline. ## `IStreamObserver` interface ```csharp public interface IStreamObserver { bool IsSubscribed { get; } void Unsubscribe(); void OnAdd(T item, bool notify, int? indexHint); void OnRebuild(DateTime fromTimestamp); void OnPrune(DateTime toTimestamp); void OnError(Exception exception); void OnCompleted(); } ``` The hub calls these methods on every subscribed observer in response to upstream activity: * **`OnAdd(item, notify, indexHint)`** โ€” A new result is being appended (or, for late-arriving data, an item is being inserted at `indexHint`). The `notify` flag indicates whether downstream cascading is desired; for most external integrations, ignore it and just process `item`. * **`OnRebuild(fromTimestamp)`** โ€” The hub is replaying its cache from `fromTimestamp` onward in response to a late arrival, a `Rebuild(...)` call, or a `RemoveAt(...)`. Observers that maintain derived state (e.g. a UI list view) typically clear their state from `fromTimestamp` and let subsequent `OnAdd` calls re-populate it. * **`OnPrune(toTimestamp)`** โ€” The hub's cache exceeded its `MaxCacheSize` and entries up to `toTimestamp` were dropped from the head. Observers persisting older results can use this signal to flush their own retention window. * **`OnError(exception)`** โ€” The hub entered a faulted state. Observers should surface this to the operator (log, alert, halt the pipeline) and decide whether to call `Unsubscribe()`. * **`OnCompleted()`** โ€” The provider declared it will send no more data. Finite streams only; live feeds typically never call this. ### If an observer throws The hub **isolates** a faulting subscriber. If your callback throws from `OnAdd`, `OnRebuild`, or `OnPrune`, the hub catches the exception, routes it to *your* observer's `OnError`, and keeps notifying the other subscribers โ€” one bad observer can neither starve its siblings nor surface as an exception out of the hub's `Add`. A throw from `OnError` itself is swallowed (it has nowhere left to go). Even with that safety net, design your callbacks to be robust: * Handle your own failures (I/O, parsing, downstream user callbacks) inside the method. Your `OnError` now fires for two reasons โ€” an upstream provider fault (above) *or* one of your own callbacks throwing and being isolated โ€” and both arrive as a plain `Exception`, so don't assume a single cause. * Keep the hand-off fast and non-blocking โ€” your callback runs inside the hub's notification path; do risky or slow work elsewhere (see [Thread-safety expectations](#thread-safety-expectations)). * Surface failures through your own channel โ€” log, alert, or a flag your writer checks. ## Minimal external observer Subscribe an observer to any hub's `Results` provider via `Subscribe(observer)`: ```csharp using FacioQuo.Stock.Indicators; // observer that pushes every EMA value into a UI dispatcher public sealed class EmaUiObserver : IStreamObserver, IDisposable { private readonly Action _dispatch; private IDisposable? _subscription; public EmaUiObserver(IStreamObservable source, Action dispatch) { _dispatch = dispatch; _subscription = source.Subscribe(this); } public bool IsSubscribed => _subscription is not null; public void OnAdd(EmaResult item, bool notify, int? indexHint) => _dispatch(item); public void OnRebuild(DateTime fromTimestamp) { /* clear UI from fromTimestamp */ } public void OnPrune(DateTime toTimestamp) { /* drop UI rows older than toTimestamp */ } public void OnError(Exception exception) { /* surface to operator */ } public void OnCompleted() { /* finalize UI (no more data expected) */ } public void Unsubscribe() { _subscription?.Dispose(); _subscription = null; } public void Dispose() => Unsubscribe(); } ``` Wire it up alongside the rest of the pipeline: ```csharp BarHub barHub = new(); EmaHub emaHub = barHub.ToEmaHub(20); using EmaUiObserver ui = new(emaHub, result => uiDispatcher.Post(result)); // call from your WebSocket or SSE message handler void OnBarReceived(Bar bar) => barHub.Add(bar); ``` Every bar published to `barHub` cascades through `emaHub.OnAdd(...)`; the EMA result then notifies `ui.OnAdd(...)`, which posts to the UI thread. Disposing `ui` unsubscribes cleanly. ## Observer as a chain provider If you want downstream indicators to chain off your observer's processed output (rather than the raw hub output), implement `IChainProvider` on the same type. The pattern is a thin box that re-emits each item through its own subscriber list: ```csharp public sealed class FilteringChainProvider : IStreamObserver, IChainProvider { private readonly List> _subscribers = []; private readonly List _results = []; public BinarySettings Properties { get; } = new(0b00000000, 0b11111111); public IReadOnlyList Results => _results; public int MaxCacheSize => 100_000; public int ObserverCount => _subscribers.Count; public bool HasObservers => _subscribers.Count > 0; public bool HasSubscriber(IStreamObserver observer) => _subscribers.Contains(observer); public IDisposable Subscribe(IStreamObserver observer) { _subscribers.Add(observer); return new Unsubscriber(_subscribers, observer); } public bool Unsubscribe(IStreamObserver observer) => _subscribers.Remove(observer); public void EndTransmission() { foreach (var s in _subscribers) s.OnCompleted(); _subscribers.Clear(); } public bool IsSubscribed { get; private set; } public void Unsubscribe() => IsSubscribed = false; public void OnAdd(EmaResult item, bool notify, int? indexHint) { // filter, transform, decorate โ€” then re-emit downstream if (item.Ema is null) return; var emitted = new TimeValue(item.Timestamp, item.Ema.Value); _results.Add(emitted); foreach (var s in _subscribers) s.OnAdd(emitted, notify, indexHint); } public void OnRebuild(DateTime fromTimestamp) { /* propagate */ } public void OnPrune(DateTime toTimestamp) { /* propagate */ } public void OnError(Exception exception) { /* propagate */ } public void OnCompleted() => EndTransmission(); private sealed record Unsubscriber(List> List, IStreamObserver Observer) : IDisposable { public void Dispose() => List.Remove(Observer); } } ``` This lets external code do: ```csharp RsiHub rsiOfFiltered = filteringProvider.ToRsiHub(14); ``` โ€” treating the custom observer as just another `IChainProvider` source. This is the pattern community contributors have used to thread custom processing into the standard chaining surface without subclassing a hub. ## Thread-safety expectations Source hubs hold their internal cache monitor for the duration of `OnAdd` / `OnRebuild` / `OnPrune` callbacks. That means: * **Your observer's callbacks run inside the source hub's lock.** Keep them fast and non-blocking. Posting to a UI dispatcher or enqueuing onto a background channel is fine; doing synchronous I/O (database writes, HTTP calls) is not. * **Do not re-enter the source hub from a callback.** Calling `source.Add(...)` from inside `OnAdd` will deadlock. * **Do not block the calling thread.** If you need to do heavy work, offload it to a `Task`, `Channel`, or background worker and return immediately. If you need to expose a `Results` collection from your observer (as in the `FilteringChainProvider` example above), apply your own synchronization or use a concurrent collection โ€” the source hub does not lock *your* state. ## Lifecycle and resource cleanup The `Subscribe(...)` method returns an `IDisposable`. Always hold the reference for the lifetime you want the subscription to last, and dispose it explicitly: ```csharp IDisposable subscription = emaHub.Subscribe(myObserver); // ... later ... subscription.Dispose(); // or equivalently: emaHub.Unsubscribe(myObserver) ``` Failing to unsubscribe keeps your observer rooted from the source hub's subscriber list, preventing GC of both the observer and any state it holds. ## See also * [Custom Series (batch) style indicators](/guide/customization.md) โ€” when you want to invent your own indicators * [Stream hubs](/guide/styles/stream.md) โ€” the source-side streaming guide * [Buffer lists](/guide/styles/buffer.md) โ€” alternative when you don't need observable propagation --- --- url: /utilities/bars.md description: >- Utilities for preparing and transforming historical price bars before using them with indicators. --- # Bar utilities Utilities for preparing and transforming historical price bars before using them with indicators. ## Use alternate price `bars.Use()` selects which price element to analyze before most indicator calls โ€” for example the median (`HL2`) or typical (`HLC3`) price instead of `Close`. It cannot be used with indicators that require the full OHLCV bar profile. ```csharp // median price (HL2) into an SMA var results = bars .Use(CandlePart.HL2) .ToSma(20); ``` > [!WARNING] > **๐Ÿšฉ Incompatible indicators** > > Some indicators require the full OHLCV bar profile and cannot be used with `.Use()`: > > * Indicators that explicitly use multiple price points (ATR, Stochastic, etc.) > * Volume-based indicators (OBV, CMF, etc.) unless using `CandlePart.Volume` > * Candlestick pattern indicators > ::: > > ### `candlePart` parameter > > The price element to use for calculations. > > #### `CandlePart` enum options > > | enum / value | description | > | ----- | ----------- | > | `CandlePart.Open`0 | `Open` price | > | `CandlePart.High`1 | `High` price | > | `CandlePart.Low`2 | `Low` price | > | `CandlePart.Close`3 | `Close` price | > | `CandlePart.Volume`4 | `Volume` | > | `CandlePart.HL2`5 | `(High+Low)/2` | > | `CandlePart.HLC3`6 | `(High+Low+Close)/3` | > | `CandlePart.OC2`7 | `(Open+Close)/2` | > | `CandlePart.OHL3`8 | `(Open+High+Low)/3` | > | `CandlePart.OHLC4`9 | `(Open+High+Low+Close)/4` | > > ### Incremental and streaming variants > > The bar-part selector also has incremental and live streaming variants that emit `TimeValue` results: > > ```csharp > // BufferList โ€” incremental, single-threaded > BarPartList parts = bars.ToBarPartList(CandlePart.HL2); > > // StreamHub โ€” chain into a live pipeline > BarHub barHub = new(); > BarPartHub partHub = barHub.ToBarPartHub(CandlePart.HLC3); > EmaHub emaHub = partHub.ToEmaHub(20); // chain downstream indicators > ``` > > See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. > > ## Sort bars > > `bars.ToSortedList()` sorts any collection of `TBar` (or `ISeries`) and returns an `IReadOnlyList` ordered by ascending `Timestamp`. Library indicators expect chronological bars, so use this when you cannot guarantee the sort order from your data source. > > ```csharp > // sort, then use inline with indicators > var results = bars > .ToSortedList() > .ToRsi(14); > ``` > > > [!NOTE] > > **Pre-sorted data** > > > > Most providers already return bars in chronological order. Sorting large datasets has a cost, so skip this step when the order is guaranteed. > > ## Validate bar history > > `bars.Validate()` is an advanced check of your `IReadOnlyList`. It detects duplicate timestamps and out-of-sequence (non-ascending) dates, throwing an `InvalidBarsException` if either is found. On success it returns the validated collection, so it can be used inline. > > ```csharp > try > { > var results = bars > .Validate() > .ToRsi(14); > } > catch (InvalidBarsException ex) > { > Console.WriteLine($"Invalid bars: {ex.Message}"); > } > ``` > > > [!NOTE] > > **When to use** > > > > `.Validate()` performs thorough checks and has a cost. Use it when data quality is uncertain โ€” importing from untrusted sources, production data-quality gates, or debugging unexpected results โ€” but avoid re-validating the same dataset repeatedly. To simply fix ordering, use [Sort bars](#sort-bars) instead. > > ## Resize bar history > > `bars.Aggregate()` combines intraday bars into larger timeframes โ€” minute to hourly, hourly to daily, or any custom `TimeSpan`. > > ```csharp > // using the BarInterval enum > IReadOnlyList hourlyBars = minuteBars.Aggregate(BarInterval.OneHour); > > // using a custom TimeSpan > IReadOnlyList customBars = minuteBars.Aggregate(TimeSpan.FromMinutes(45)); > ``` > > | param | description | > | ----- | ----------- | > | `newSize``BarInterval` | Target period size: `Month`, `Week`, `Day`, `FourHours`, `TwoHours`, `OneHour`, `ThirtyMinutes`, `FifteenMinutes`, `FiveMinutes`, `ThreeMinutes`, `TwoMinutes`, `OneMinute`. | > | `timeSpan``TimeSpan` | Any value greater than `TimeSpan.Zero`, for custom periods. | > > Each aggregated bar takes the first **Open**, highest **High**, lowest **Low**, last **Close**, summed **Volume**, and the period's starting **Timestamp**. > > Periods are fixed calendar buckets, not rolling windows, in the clock your timestamps already use; no time zone is applied. `Month` starts on the 1st of each month. `Week` starts Monday 00:00 and runs through Sunday. `Day` and shorter sizes start at midnight. A custom `TimeSpan` counts its buckets from `DateTime.MinValue`, so a size that does not divide 24 hours evenly, such as 7 minutes, does not restart at midnight. The streaming aggregator hubs use the same boundaries for every size except `Month`, which they do not support. > > > [!WARNING] > > **๐Ÿšฉ Partially populated periods** > > > > Partial period windows at the start, end, or market open/close can be misleading. For example, a lone 4:00pm minute bar aggregated into 15-minute bars yields a 4:00pm bar holding just one minute of data, while the prior 3:45pm bar holds the full 15 minutes. Filter out partial periods if they could skew your analysis. > > ### Streaming aggregator hubs > > For live feeds, the aggregator hubs convert small bars or raw ticks into larger period bars in real time. Both accept a `BarInterval` or custom `TimeSpan` (`BarInterval.Month` is not supported in streaming โ€” use the `TimeSpan` overload): > > ```csharp > // bar โ†’ bar (e.g. 1-minute bars to 5-minute bars) > BarHub barHub = new(); > BarAggregatorHub fiveMinHub = barHub.ToBarAggregatorHub(BarInterval.FiveMinutes); > > // tick โ†’ bar (raw trades to 1-minute OHLCV) > TradeTickHub tickHub = new(); > TradeTickAggregatorHub oneMinHub = tickHub.ToTradeTickAggregatorHub(BarInterval.OneMinute); > ``` > > Both aggregator hubs accept a `GapFillMode` to control how silent buckets โ€” periods with no upstream input โ€” are handled: > > | value | behavior | > | ----- | -------- | > | `None` (default) | Silent buckets are omitted from the output stream. | > | `ForwardFill` | Silent buckets are filled with zero-volume bars, carrying the prior bar's `Close` into `Open`, `High`, `Low`, and `Close`. | > | `Interpolate` | Silent buckets are filled with zero-volume bars whose `Open`, `High`, `Low`, and `Close` are linearly interpolated between the prior bar's `Close` and the next real bar's `Open` (the next real tick's price, for ticks), evenly spaced across the missing buckets. | > > ```csharp > // bar โ†’ bar > BarAggregatorHub fiveMinHub = barHub.ToBarAggregatorHub(BarInterval.FiveMinutes, GapFillMode.Interpolate); > > // tick โ†’ bar > TradeTickAggregatorHub oneMinHub = tickHub.ToTradeTickAggregatorHub(BarInterval.OneMinute, GapFillMode.Interpolate); > ``` > > An optional `fillGaps` boolean overload also remains for convenience: `fillGaps: false` (default) is equivalent to `GapFillMode.None`, and `fillGaps: true` is equivalent to `GapFillMode.ForwardFill`. Volume is always `0` on a synthesized bar, regardless of mode. `Interpolate` prices are not rounded; a quotient that does not terminate (e.g. two silent buckets, which divide the move into thirds) carries full `decimal` precision. > > `Interpolate` also looks ahead: each synthesized bar uses the next real bar's `Open`, so its value is not knowable at its own timestamp. A backtest that trades on interpolated bars acts on data it could not have had yet; use `ForwardFill` when look-ahead matters. When a late or revised input changes that next bar, the hub regenerates the affected gap bars, so revising the `Open` of the bar after a long gap costs time proportional to the gap length. > > ## Extended candle properties > > `bar.ToCandle()` and `bars.ToCandles()` convert a bar into an extended `CandleProperties` format with additional calculated candle measurements (body size, wicks, etc.). > > ```csharp > // single bar > CandleProperties candle = bar.ToCandle(); > > // collection of bars > IReadOnlyList candles = bars.ToCandles(); > > // access extended properties > decimal? bodySize = candle.Body; > decimal? upperWick = candle.UpperWick; > bool isBullish = candle.IsBullish; > ``` > > ### Return type > > #### `CandleProperties` > > The `CandleProperties` record class extends the basic `Bar` type with calculated properties. > > | property | description | > | ----- | ----------- | > | `Timestamp`*`DateTime`* | Close date | > | `Open`*`decimal`* | Open price | > | `High`*`decimal`* | High price | > | `Low`*`decimal`* | Low price | > | `Close`*`decimal`* | Close price | > | `Volume`*`decimal`* | Volume | > | `Size`*`decimal?`* | `High-Low` | > | `Body`*`decimal?`* | `\|Open-Close\|` | > | `UpperWick`*`decimal?`* | Upper wick size | > | `LowerWick`*`decimal?`* | Lower wick size | > | `BodyPct`*`double?`* | `Body/Size` | > | `UpperWickPct`*`double?`* | `UpperWick/Size` | > | `LowerWickPct`*`double?`* | `LowerWick/Size` | > | `IsBullish`*`bool`* | `Close>Open` direction | > | `IsBearish`*`bool`* | `Close > ## See also > > * [Result utilities](/utilities/results.md) โ€” work with indicator results after calculation > * [Additional helper utilities](/utilities/helpers.md) โ€” math and numerical methods for custom indicators > * [Indicator catalog](/utilities/catalog.md) โ€” discover indicator metadata programmatically > > --- >--- url: /utilities/results.md description: Utilities for working with indicator results after calculation and analysis. --- # Result utilities Utilities for working with indicator results after calculation and analysis. ## Condense `results.Condense()` removes non-essential results so only meaningful records remain. For example, on [candlestick patterns](/indicators/doji.md) it returns only the dates where a signal occurs. It is a lightweight filter โ€” it does not recalculate the indicator. ```csharp // only the dates with Marubozu signals IReadOnlyList results = bars.ToMarubozu().Condense(); ``` `.Condense()` removes results where the value is `null` or `NaN`; for candlestick patterns it removes records with no match (`Match.None`). Behavior varies by indicator type: | Indicator type | Condensed behavior | | -------------- | ------------------ | | Candlestick patterns | Returns only pattern matches | | Signal-based indicators | Returns only signal points | | Continuous indicators | Removes warmup-period nulls | > [!WARNING] > **๐Ÿšฉ Data reduction** > > Condensed results contain fewer records than the input and may have gaps in the timeline. This is intentional โ€” use it when you only care about specific events or signals, not the continuous time series. ## Find by date `results.Find(lookupDate)` looks up a single indicator result by date, using a binary search over the time-sorted series. It returns the matching result, or the default value (`null` for reference types) when no result has that exact timestamp. ```csharp IReadOnlyList results = bars.ToSma(20); SmaResult? result = results.Find(DateTime.Parse("2024-01-15")); if (result is not null) { Console.WriteLine($"SMA: {result.Sma}"); } ``` The comparison is exact, so a timestamp with a time component (e.g. `09:30:00`) will not match a date-only entry. > [!TIP] > **Date-only comparison** > > To match on date only (ignoring time), use LINQ instead: > > ```csharp > var target = DateTime.Parse("2024-01-15").Date; > var result = results.FirstOrDefault(r => r.Timestamp.Date == target); > ``` For range queries or filtering by value, use LINQ `.Where()` rather than repeated `.Find()` calls. ## Remove warmup periods `results.RemoveWarmupPeriods()` trims the initial warmup periods from indicator results. An overload `.RemoveWarmupPeriods(removePeriods)` lets you specify the exact amount. ```csharp // automatic โ€” uses the indicator's recommended amount IReadOnlyList auto = bars.ToAdx(14).RemoveWarmupPeriods(); // custom โ€” remove a specific quantity IReadOnlyList custom = bars.ToAdx(14).RemoveWarmupPeriods(114); ``` For most indicators, the automatic amount is every leading result with no calculated value. Indicators whose values converge gradually, such as EMA and RSI, also remove the periods they need to converge. See [individual indicator pages](/indicators.md) for each indicator's recommended pruning amount. Common values: | Indicator | Removed warmup | | --------- | -------------- | | SMA(n) | n โˆ’ 1 periods | | EMA(n) | n + 100 periods | | RSI(n) | 10ร—n periods | | ADX(n) | 2ร—n + 100 periods | | MACD(fast, slow, signal) | slow + signal + 250 periods | When every result is still warming up, the automatic form returns an empty list. > [!NOTE] > **Limited availability** > > The parameterless `.RemoveWarmupPeriods()` is available on the indicators whose pages list it. For the others, use the `.RemoveWarmupPeriods(removePeriods)` overload to prune a specific amount. > [!WARNING] > **๐Ÿšฉ Auto-pruning is unstable on chained indicators** > > Without a `removePeriods` value, the utility derives the pruning amount from the first calculated value. With unusual results or chained indicators, this can over-prune. Specify an explicit amount when chaining. > > ```csharp > // AVOID: auto-pruning on chained indicators may remove too much > bars.ToEma(20).ToRsi(14).RemoveWarmupPeriods(); > > // BETTER: predictable, explicit amount > bars.ToEma(20).ToRsi(14).RemoveWarmupPeriods(300); > ``` ## Sort results `results.ToSortedList()` sorts any collection of indicator results and returns an `IReadOnlyList` ordered by ascending `Timestamp`. Results from the built-in library indicators are already sorted, so you only need this when building [custom indicators](/guide/customization.md) or after manually manipulating timestamps. ```csharp // ensure chronological order before returning custom results return customResults.ToSortedList(); ``` > [!NOTE] > **When to use: custom indicators only** > > Built-in indicators preserve the chronological order of the input bars, so their results are already sorted. Reach for `.ToSortedList()` only in custom implementations or after merging or re-timestamping results. For raw price data, see [Sort bars](/utilities/bars.md#sort-bars). ## See also * [Bar utilities](/utilities/bars.md) โ€” prepare and transform price bars * [Additional helper utilities](/utilities/helpers.md) โ€” math and numerical methods for custom indicators * [Indicator catalog](/utilities/catalog.md) โ€” discover indicator metadata programmatically --- --- url: /utilities/helpers.md description: >- Numerical methods and math helpers (NullMath, DeMath) for custom indicator development. --- # Additional utilities Numerical and math helpers for building [custom indicators](/guide/customization.md). These are the same internal tools the library's built-in indicators use. ## Numerical methods The static `Numerical` class exposes slope and standard-deviation helpers. ### Slope `Numerical.Slope(x, y)` returns the slope of the linear regression (best-fit) line through paired `x` and `y` values, using the least-squares method. The arrays must be the same length, or an `ArgumentException` is thrown. ```csharp double[] x = [1, 2, 5, 4]; double[] y = [4, 7, 8, 1]; double slope = Numerical.Slope(x, y); // slope of the best-fit line ``` ### Standard deviation `StdDev()` is an extension on `double[]` that returns the **population** standard deviation of the values (it divides by `n`, not `nโˆ’1`). ```csharp double[] values = [1, 2, 3, 4]; double sd = values.StdDev(); ``` ## NullMath Null-safe mathematical operations that handle null values gracefully. `System.Math` does not accept `null`, so these wrappers let you skip the repetitive defensive null-checking โ€” they simply return `null` (or `NaN`) when the input is `null`. | Method | Example | Result | | ------ | ------- | ------ | | `Abs(double?)` | `NullMath.Abs(-25.1)``NullMath.Abs(null)` | `25.1``null` | | `Null2NaN(double?)` | `((double?)null).Null2NaN()` | `double.NaN` | | `Null2NaN(decimal?)` | `((decimal?)1.5m).Null2NaN()` | `1.5` (as `double`) | | `NaN2Null(double?)``NaN2Null(double)` | `double.NaN.NaN2Null()` | `null` | ```csharp double? x = NullMath.Abs(-25.1); // x โ†’ 25.1 double n = ((double?)null).Null2NaN(); // n โ†’ NaN double? v = double.NaN.NaN2Null(); // v โ†’ null ``` `Null2NaN` is useful when interfacing with systems โ€” such as charting libraries โ€” that expect `NaN` instead of `null`; `NaN2Null` does the reverse when ingesting external data. Both have overloads for the relevant `double`/`decimal` types. ## DeMath `DeMath` provides deterministic math operations that produce identical, bit-for-bit results across .NET platforms and operating systems. It eliminates platform-specific floating-point drift that would otherwise cause indicator values to differ slightly between Windows, Linux, and macOS. > [!NOTE] > **Internal API** > > `DeMath` is an `internal` class and is not directly accessible from external code. It is documented here for contributors and to explain the library's cross-platform precision strategy. `System.Math` delegates some operations (`Log`, `Exp`, `Atan`, etc.) to native platform implementations that can produce slightly different results on different operating systems. For financial indicators โ€” especially those that compound results over hundreds of periods โ€” these tiny differences accumulate into observable divergence. `DeMath` reimplements them with deterministic algorithms so the same calculation yields the same result everywhere. | Method | Description | | ------ | ----------- | | `Log(x)` | Natural logarithm | | `Log10(x)` | Base-10 logarithm | | `Exp(x)` | Exponential function (`e^x`) | | `Atan(x)` | Arctangent | | `Atan2(y, x)` | Two-argument arctangent (matches `Math.Atan2` quadrant conventions) | | `Atanh(x)` | Inverse hyperbolic tangent | If you are contributing a custom indicator that uses transcendental functions and cross-platform reproducibility matters, prefer `DeMath` over `System.Math` for those operations. See the [Customization guide](/guide/customization.md) for the full custom indicator pattern. ## See also * [Bar utilities](/utilities/bars.md) โ€” prepare and transform price bars * [Result utilities](/utilities/results.md) โ€” work with indicator results * [Indicator catalog](/utilities/catalog.md) โ€” discover indicator metadata programmatically * [Slope indicator](/indicators/slope.md) and [Standard deviation indicator](/indicators/std-dev.md) โ€” built-in equivalents --- --- url: /utilities/catalog.md description: >- Programmatic access to indicator metadata for building dynamic UIs or automation. --- # Indicator catalog (metadata) Use the indicator catalog to discover indicators, inspect their parameters and results, build pickers or configuration UIs, and execute a selected indicator without hard-coding its method call. * Discover indicators, parameters, and results at runtime * Build configuration UIs from indicator metadata, or export it to JSON or Markdown * Execute a selected indicator from its listing, without hard-coding the method call > [!WARNING] > **Non-idiomatic** > > *The Catalog* provides a programmatic way to interact with indicators and options; however, it is not the idiomatic .NET way to use this library. See the examples in [the Guide](/guide/getting-started.md) for normal usage. ## Browse and search the catalog All query methods are on the static `Catalog` class and return `IndicatorListing` metadata (no calculations are run). ```csharp using FacioQuo.Stock.Indicators; // all listings IReadOnlyList all = Catalog.Get(); // a single listing by ID + style (null if not found) IndicatorListing? emaSeries = Catalog.Get("EMA", Style.Series); // all styles available for one ID IReadOnlyList allEma = Catalog.Get("EMA"); // filter by style IReadOnlyList seriesOnly = Catalog.Get(Style.Series); // filter by category IReadOnlyList movingAverages = Catalog.Get(Category.MovingAverage); // partial-match search on ID or name (empty query returns all) IReadOnlyList matches = Catalog.Search("average"); ``` | Method | Returns | Description | | ------ | ------- | ----------- | | `Get()` | `IReadOnlyList` | All listings | | `Get(string id, Style style)` | `IndicatorListing?` | One listing, or `null` if not found | | `Get(string id)` | `IReadOnlyList` | All styles for an ID | | `Get(Style style)` | `IReadOnlyList` | Listings of a given style | | `Get(Category category)` | `IReadOnlyList` | Listings in a category | | `Search(string query)` | `IReadOnlyList` | Partial (case-insensitive) match on ID or name | ## Listing metadata Each `IndicatorListing` describes one indicator-and-style combination. This is the metadata you bind a UI to. | Property | Description | | ----- | ----------- | | `Uiid``string` | Unique indicator ID (e.g. `"EMA"`) | | `Name``string` | Display name | | `Style``Style` | `Series`, `Buffer`, or `Stream` | | `Category``Category` | Indicator category (see below) | | `Parameters``IReadOnlyList?` | Input parameters (`null` when there are none) | | `Results``IReadOnlyList` | Output fields the indicator produces | | `Panes``IReadOnlyList?` | Chart panes with a fixed axis range or reference lines (`null` when none) | | `ResultRecordType``string?` | Name of the result record the method returns (e.g. `"EmaResult"`), the same for all three styles โ€” the record type, not the method's literal return type (`EmaList` for Buffer, `EmaHub` for Stream). `null` when `MethodName` is unset. Derived when the catalog builds the listing, so a `with` copy that changes `MethodName` keeps the original value | | `MethodName``string?` | Method name, for automation use cases | | `LegendTemplate``string` | Legend template for charting | `GetMethodOverloads()` returns the public static overloads of `MethodName` as `IReadOnlyList`, for a caller that reads their parameters or invokes them directly. The list is empty when `MethodName` is unset or no catalog listing names the method. The methods survive trimming and NativeAOT publishing, so no assembly scan is needed. `IndicatorParam` (each input parameter): | Property | Description | | ----- | ----------- | | `DisplayName``string` | Human-friendly label | | `ParameterName``string` | Method parameter name (use this with `WithParamValue`) | | `DataType``string` | Parameter type name | | `Description``string?` | Optional description | | `IsRequired``bool` | Whether the caller must supply the parameter. `false` means omitting it yields `DefaultValue` | | `DefaultValue``object?` | Default value, if any | | `Minimum` / `Maximum``double?` | Recommended bounds, if any | | `EnumOptions``Dictionary?` | Allowed values for enum parameters (`null` otherwise) | `IndicatorResult` (each output field): | Property | Description | | ----- | ----------- | | `DisplayName``string` | Human-friendly label | | `DataName``string` | Result property name (e.g. `"Ema"`) | | `DataType``ResultType` | Charting/display hint | | `IsReusable``bool` | `true` for the field exposed as the chainable `IReusable.Value` | | `ChartPane``string?` | Chart pane the result draws in: `"Price"` (`IndicatorResult.PricePane`) for the price axis, another name for a separate pane, `null` when not charted | Results with the same `ChartPane` share units, so group a listing's results by it and draw each group as one chart. Bollinger Bands, for example, puts its bands on `"Price"` and `PercentB`, `ZScore` and `Width` each in a pane of its own. A `null` pane marks a flag or pattern code, such as a PSAR reversal, which has no axis to draw on. `IndicatorPane` (each pane with a fixed range or reference lines): | Property | Description | | ----- | ----------- | | `Name``string` | Pane name, matching the `ChartPane` of the results it draws | | `Minimum` / `Maximum``double?` | Fixed axis bounds; `null` means autoscale | | `Thresholds``IReadOnlyList?` | Reference line values, such as overbought and oversold levels, in drawing order | Nine indicators list a pane today: RSI, Stochastic, Stochastic RSI, MFI, Williams %R, Ultimate Oscillator, ConnorsRSI, ADX and Aroon. RSI, for example, draws on 0 to 100 with lines at 70 and 30. A pane that is not listed has no data here, which is not the same as autoscaling with no reference lines. Colours and line styles are left to the chart. Example โ€” inspect an indicator's inputs: ```csharp IndicatorListing? listing = Catalog.Get("EMA", Style.Series); foreach (IndicatorParam p in listing?.Parameters ?? []) { Console.WriteLine( $"{p.DisplayName} ({p.DataType}), required: {p.IsRequired}, default: {p.DefaultValue}"); } ``` ### Enums * **`Style`**: `Series`, `Buffer`, `Stream` โ€” the three [indicator styles](/guide/styles.md). * **`Category`**: `Undefined`, `CandlestickPattern`, `MovingAverage`, `Oscillator`, `PriceChannel`, `PriceCharacteristic`, `PricePattern`, `PriceTransform`, `PriceTrend`, `StopAndReverse`, `VolumeBased`. * **`ResultType`**: `Default`, `Centerline`, `Channel`, `Bar`, `BarStacked`, `Point` โ€” display hints for charting libraries. ## Export the catalog These extension methods serialize an `IReadOnlyList` (typically `Catalog.Get()`). Each accepts an optional file path; when provided, the content is written to that file and also returned. ```csharp IReadOnlyList catalog = Catalog.Get(); // JSON (indented, camelCase, string enums) string json = catalog.ToJson(); catalog.ToJson("catalog.json"); // also writes to file // Markdown checklist: "- [ ] {id}: {name} ({styles})" string checklist = catalog.ToMarkdownChecklist(); // Markdown table with ID, Name, Series, Buffer, Stream columns string table = catalog.ToMarkdownTable(); ``` | Method | Output | | ------ | ------ | | `ToJson(filePath?)` | JSON array of listings | | `ToMarkdownChecklist(filePath?)` | Markdown checklist, one row per indicator ID | | `ToMarkdownTable(filePath?)` | Markdown table with a column per style | ## Execute from a listing Look up a listing, then run it for strongly-typed results without calling the indicator's extension method directly: ```csharp IndicatorListing indicatorListing = Catalog .Get("EMA", Style.Series) ?? throw new InvalidOperationException("Indicator 'EMA' (Series) not found."); // run with the catalog's default parameters IReadOnlyList emaWithDefaults = indicatorListing .Execute(bars); // or override parameters with the fluent builder IReadOnlyList emaWithParams = indicatorListing .WithParamValue("lookbackPeriods", 10) .FromSource((IEnumerable)bars) // cast selects the bars overload .Execute(); ``` A Stream listing runs from bars the same way: the bars feed a `BarHub`, and `Execute` returns the indicator hub's results. The fluent `ListingExecutionBuilder` supports `WithParamValue(name, value)`, `WithParams(dictionary)`, `WithoutParam(name)`, `FromSource(bars)`, `FromSource(series, parameterName?)` for chaining off another indicator's results, and `Execute()`. Parameter values are type-checked against the listing's `IndicatorParam` metadata. A series value must be assignable to `IReadOnlyList`, so its elements are reference types; a list of `IReusable` structs is rejected. ### Leave a parameter out A few indicators have an overload that means "this argument was not given" rather than "this argument took its default". `ToPrs(sourceEval, sourceBase)` computes no `PrsPercent` at all โ€” no value in the parameter's advertised range (`Minimum: 1`, `Maximum: 250`) produces that, and the only override that does is the `int.MinValue` sentinel the overload forwards internally, which the catalog gives you no way to discover. `WithoutParam` drops the argument instead of supplying one: ```csharp IndicatorListing prs = Catalog .Get("PRS", Style.Series) ?? throw new InvalidOperationException("Indicator 'PRS' (Series) not found."); // declared default: lookbackPeriods = 20, PrsPercent computed IReadOnlyList withPercent = prs .WithParamValue("sourceBase", benchmarkBars) .FromSource((IEnumerable)bars) .Execute(); // no lookback argument at all: PrsPercent is null throughout IReadOnlyList ratioOnly = prs .WithParamValue("sourceBase", benchmarkBars) .WithoutParam("lookbackPeriods") .FromSource((IEnumerable)bars) .Execute(); ``` A shorter overload is selected where one exists; where none does, the method's own default for that parameter applies instead. Arguments bind positionally, so only a trailing run of parameters can be dropped โ€” omitting one while a later one is still supplied is rejected rather than silently shifting arguments into the wrong slots. Series parameters supply the indicator's data rather than a setting, so they cannot be omitted. Omissions are part of a configuration, not a transient builder detail: `IndicatorConfig` round-trips them through `OmittedParameters` alongside `Parameters`, so a saved configuration reloads as the same indicator. > [!TIP] > **โœจ Tip: disambiguate the bars overload** > > Because `Bar` implements both `IBar` and `IReusable`, a `bars` collection matches both `FromSource(bars)` and the `FromSource(series, โ€ฆ)` chaining overload. Cast to `(IEnumerable)` (as above) to select the bars overload. The simpler `listing.Execute(bars)` form needs no cast. ## Execute from a saved configuration `IndicatorConfig` is a serializable description of an indicator selection and its parameter overrides โ€” useful for persisting a user's choices (per strategy, dashboard, etc.) and replaying them later. ```csharp // build (or deserialize) a configuration IndicatorConfig config = new() { Id = "EMA", Style = Style.Series, Parameters = new Dictionary { ["lookbackPeriods"] = 20 } }; // run it against bars IReadOnlyList results = config.Execute(bars); ``` Use `config.ToBuilder()` to obtain a `ListingExecutionBuilder` for further fluent configuration, or `IndicatorConfig.FromBuilder(builder)` to capture an existing builder's settings back into a config for storage. ## Trimming and NativeAOT The package is marked trimmable, so a trimmed app keeps only the library members it uses. Catalog lookup and execution work in trimmed and NativeAOT apps; `ToJson` does not (see below). A listing's `MethodName` must name a catalog indicator method: a listing you build yourself, or a `with` copy, cannot name any other library method. > [!WARNING] > **Reflection-based helpers** > > `ToJson` serializes with reflection, so it carries `[RequiresUnreferencedCode]` and `[RequiresDynamicCode]` and raises IL2026 and IL3050 in a trimmed or NativeAOT build; export the catalog from an untrimmed app instead. `ToStringOut` and `ToConsole` require the public properties of `T` to be preserved, which an unannotated generic caller sees as IL2091; annotate the caller's own type parameter with `[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]`, or call them with a concrete result type. In a NativeAOT app they print no property descriptions: they read descriptions from the XML documentation file beside the assembly, and a native executable has no assembly file to sit beside. ## See also * [Bar utilities](/utilities/bars.md) โ€” prepare and transform price bars * [Result utilities](/utilities/results.md) โ€” work with indicator results after calculation * [Additional helper utilities](/utilities/helpers.md) โ€” math and numerical methods for custom indicators * [Indicators](/indicators.md) โ€” the full indicator reference behind the catalog metadata --- --- url: /examples.md description: >- The Stock Indicators for .NET library is a simple yet flexible tool to help you build your financial market systems. Here's a few complete working examples that you can download and try yourself. --- # Example usage code To help you get started, here are a few minimalist [.NET](https://dotnet.microsoft.com/download) C# projects that you can review. They are complete working examples. * `ConsoleApp` is a minimalist example of how to use the library (start here) * `Backtest` is a slightly more complicated example of how to analyze results * `CustomIndicatorsLibrary` shows how you can [create your own custom indicators](/guide/customization.md) * `CustomIndicatorsUsage` shows how you'd use custom indicators just like any other in the main library * `UseQuoteApi` shows how you'd get quotes from an API quote source, using the Alpaca SDK for .NET * `LiveSignals` is a live market desk: real crypto or US stock feeds, daily regimes and rotation across a basket, explained one-minute signals, and narrated insights in a browser or headless console (no key needed for crypto) For more information on how to use this library overall, see the [Guide](/guide.md). ## Getting started with our sample projects `LiveSignals` streams crypto from Kraken's public feed by default, with no key. Its US stock mode and the `UseQuoteApi` example use Alpaca market data instead; to run those, [get a free Alpaca API key and secret](https://docs.alpaca.markets/), then set your local environment variables. Run the following command line items to set, after replacing the `MY-ALPACA-KEY` and `MY-ALPACA-SECRET` values; then restart your IDE. ```bash # use your own keys setx ALPACA_KEY "MY-ALPACA-KEY" setx ALPACA_SECRET "MY-ALPACA-SECRET" ``` ## Get and run the sample projects 1. [Download the ZIP file](/FacioQuo.Stock.Indicators-Examples.zip) and extract contents 2. Open `Examples.sln` in [Visual Studio](https://visualstudio.microsoft.com) 3. Review the code in the `Program.cs` file(s) 4. Run the `ConsoleApp` or any other project with one of the following methods: * select (will be bold in Solution Explorer) and press the `CTRL+F5` key * execute `dotnet run` CLI command in the `ConsoleApp` project folder * clicking the play button ![how to execute the code](run.png) ## Backtest example ```csharp /* This is a basic 20-year backtest-style analysis of * Stochastic RSI. It will buy-to-open (BTO) one share * when the Stoch RSI (%K) is below 20 and crosses over the * Signal (%D). The reverse Sell-to-Close (STC) and * Sell-To-Open (STO) occurs when the Stoch RSI is above 80 and * crosses below the Signal. * * As a result, there will always be one open LONG or SHORT * position that is opened and closed at signal crossover * points in the overbought and oversold regions of the indicator. */ // fetch historical price bars from data provider IReadOnlyList bars = GetBarsFromFeed(); // calculate Stochastic RSI IReadOnlyList resultsList = bars.ToStochRsi(14, 14, 3, 1); // initialize decimal trdPrice = 0; decimal trdQty = 0; decimal rlzGain = 0; Console.WriteLine(" Date Close StRSI Signal Cross Net Gains"); Console.WriteLine("-------------------------------------------------------"); // roll through source values for (int i = 1; i < bars.Count; i++) { Bar q = bars[i]; StochRsiResult e = resultsList[i]; // evaluation period StochRsiResult l = resultsList[i - 1]; // last (prior) period string cross = string.Empty; // unrealized gain on open trade decimal trdGain = trdQty * (q.Close - trdPrice); // check for LONG event // condition: Stoch RSI was <= 20 and Stoch RSI crosses over Signal if (l.StochRsi <= 20 && l.StochRsi < l.Signal && e.StochRsi >= e.Signal && trdQty != 1) { // emulates BTC + BTO rlzGain += trdGain; trdQty = 1; trdPrice = q.Close; cross = "LONG"; } // check for SHORT event // condition: Stoch RSI was >= 80 and Stoch RSI crosses under Signal if (l.StochRsi >= 80 && l.StochRsi > l.Signal && e.StochRsi <= e.Signal && trdQty != -1) { // emulates STC + STO rlzGain += trdGain; trdQty = -1; trdPrice = q.Close; cross = "SHORT"; } if (cross != string.Empty) { Console.WriteLine( $"{q.Timestamp,10:yyyy-MM-dd} " + $"{q.Close,10:c2}" + $"{e.StochRsi,7:N1}" + $"{e.Signal,7:N1}" + $"{cross,7}" + $"{rlzGain + trdGain,13:c2}"); } } ``` ### Console output ```console Date Close StRSI Signal Cross Net Gains ------------------------------------------------------- 1999-11-01 $1,354.12 92.0 97.3 SHORT $0.00 1999-12-01 $1,397.72 14.6 4.9 LONG ($87.20) 1999-12-28 $1,457.66 96.9 97.6 SHORT $76.28 2000-01-06 $1,403.45 5.2 2.9 LONG $124.76 2000-02-09 $1,411.71 63.4 80.3 SHORT $87.07 2000-02-22 $1,352.17 9.3 6.0 LONG $197.89 2000-03-06 $1,391.28 77.8 92.6 SHORT $216.57 2000-04-06 $1,501.34 22.0 7.3 LONG ($42.66) 2000-05-17 $1,447.80 77.6 92.5 SHORT ($39.68) 2000-05-24 $1,399.05 33.8 19.8 LONG $111.36 2000-06-05 $1,467.63 89.9 96.1 SHORT $199.77 2000-06-26 $1,455.31 29.8 9.9 LONG $155.83 2000-07-14 $1,509.98 100.0 100.0 SHORT $252.85 2000-07-25 $1,474.47 18.9 14.3 LONG $269.20 2000-08-09 $1,472.87 82.5 90.9 SHORT $230.49 2000-09-07 $1,502.51 29.4 18.2 LONG $172.81 2000-10-23 $1,395.78 99.0 99.7 SHORT ($11.01) 2000-11-14 $1,382.95 45.2 15.1 LONG $121.38 2000-12-12 $1,371.18 89.0 94.1 SHORT $85.01 2000-12-18 $1,322.74 34.5 34.4 LONG $193.66 2001-01-05 $1,298.35 58.2 81.8 SHORT $96.44 2001-02-09 $1,314.76 0.0 0.0 LONG $88.01 2001-03-09 $1,233.42 46.1 74.1 SHORT ($58.26) 2001-03-13 $1,197.66 36.0 27.4 LONG $94.60 2001-03-28 $1,153.29 72.7 79.1 SHORT ($29.90) 2001-05-15 $1,249.44 12.0 10.9 LONG ($177.83) 2001-05-18 $1,291.96 100.0 100.0 SHORT $3.36 2001-05-31 $1,255.82 10.4 3.5 LONG $33.12 2001-07-05 $1,219.24 57.2 83.8 SHORT ($76.18) 2001-07-12 $1,208.14 71.5 23.8 LONG ($17.40) 2001-07-16 $1,202.45 62.9 74.1 SHORT ($39.88) 2001-07-25 $1,190.49 63.7 42.5 LONG ($10.27) 2001-08-02 $1,220.75 100.0 100.0 SHORT $38.29 2001-08-16 $1,181.66 13.5 9.3 LONG $86.21 2001-08-28 $1,161.51 42.8 75.9 SHORT $6.82 2001-08-31 $1,133.58 12.8 9.8 LONG $82.83 [...] 2020-01-08 $3,253.05 22.8 13.4 LONG $633.19 2020-01-21 $3,320.79 70.1 81.6 SHORT $797.71 2020-01-28 $3,276.24 24.6 8.2 LONG $819.07 2020-02-18 $3,370.29 86.4 92.8 SHORT $962.62 2020-02-26 $3,116.39 0.0 0.0 LONG $1,376.37 2020-03-27 $2,541.47 87.3 95.8 SHORT ($27.37) 2020-05-14 $2,852.50 23.5 11.3 LONG ($74.51) 2020-05-21 $2,948.51 78.6 83.2 SHORT $428.54 2020-06-15 $3,066.59 19.7 10.6 LONG $96.37 2020-07-07 $3,145.32 73.8 86.8 SHORT $371.91 2020-07-27 $3,239.41 32.7 25.2 LONG $105.00 2020-08-07 $3,351.28 100.0 100.0 SHORT $422.83 ``` ## More info * Tutorial: [Create a simple C# console app](https://docs.microsoft.com/en-us/visualstudio/get-started/csharp/tutorial-console) * These files can also be found in the [/docs/examples](https://github.com/facioquo/stock-indicators-dotnet/tree/main/docs/examples) GitHub repo folder --- --- url: /examples/custom-chart.md description: >- Render a candlestick + volume chart from your own quote data using OverlayChart, with a locally computed EMA(20) overlay - no API required. --- # Custom chart (bring your own data) Render a candlestick + volume chart **and** a technical indicator overlay directly from your own quote data - no API required. Useful for offline demos, internal dashboards, or static documentation sites. ## Live demo The chart above plots OHLC + volume from a hard-coded `Quote[]` array, with an EMA(20) line computed locally. Everything ships in the page bundle - no network calls; see [`docs/examples/StaticChart.vue`](https://github.com/facioquo/stock-indicators-dotnet/blob/main/docs/examples/StaticChart.vue) for the source. ## How it works [`OverlayChart`](https://github.com/facioquo/stock-charts/tree/main/libs/indy-charts) is a lower-level building block exported from `@facioquo/indy-charts` for rendering candlestick + volume charts directly from your own data, with no API required. `@facioquo/indy-charts` is maintained in the [facioquo/stock-charts](https://github.com/facioquo/stock-charts) repository, not here โ€” see its [README](https://github.com/facioquo/stock-charts/blob/main/libs/indy-charts/README.md) for the full usage guide and API reference. > [!TIP] > **โœจ Tip: direct lower-level use is opt-in** > > For most pages you should use the higher-level `` from `@facioquo/indy-charts/vue` (registered globally in `.vitepress/theme/index.ts`). It fetches quotes + indicators from the configured API, manages its own lifecycle, and respects the central indicator catalog. Drop down to `OverlayChart` only when you genuinely need to ship data inline. --- --- url: /indicators/price-trends.md description: >- Trend direction and strength indicators for identifying and following market trends. --- --- --- url: /indicators/adx.md description: >- Created by J. Welles Wilder, the Average Directional Index (ADX) is part of the Directional Movement system (commonly referred to as DMI). This system includes the Positive and Negative Directional Indicators (+DI and โˆ’DI), the Directional Index (DX), and ADX, and is used to measure the strength of price trends. --- # Average Directional Index (ADX) Created by J. Welles Wilder, the [Average Directional Movement Index](https://en.wikipedia.org/wiki/Average_directional_movement_index) (ADX) is part of the Directional Movement system (commonly referred to as DMI). This system includes the Positive and Negative Directional Indicators (+DI and โˆ’DI), the Directional Index (DX), and ADX, and is used to measure the strength of price trends. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/270 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAdx(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) to consider. Must be greater than 1. Default is 14. | ### Historical price bars requirements You must have at least `2ร—N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. We generally recommend you use at least `2ร—N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `2ร—N-1` periods will have `null` values for `Adx` since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `2ร—N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `AdxResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Pdi`*`double?`* | Plus Directional Index (+DI) | | `Mdi`*`double?`* | Minus Directional Index (-DI) | | `Dx`*`double?`* | Directional Index (DX) | | `Adx`*`double?`* | Average Directional Index (ADX) | | `Adxr`*`double?`* | Average Directional Index Rating (ADXR) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Adx` with additional chain-enabled indicators. ```csharp // example var results = bars .ToAdx(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations: ```csharp AdxList adxList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => adxList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = adxList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AdxHub observer = barHub.ToAdxHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/aroon.md description: >- Created by Tushar Chande, Aroon is a oscillator view of how long ago the new high or low price occurred. --- # Aroon Created by Tushar Chande, [Aroon (Up/Down)](https://www.google.com/search?q=Aroon+Up/Down+Indicator) is a oscillator view that tracks how recently each lookback window saw a new high (Up) and low (Down). [Aroon Oscillator](https://www.google.com/search?q=Aroon+Up/Down+Indicator) is the difference `Up โˆ’ Down`, presented as a single oscillator that crosses zero when the dominant trend flips. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/266 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAroon(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the lookback evaluation. Must be greater than 0. Default is 25. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values for `Aroon` since there's not enough data to calculate. ### `AroonResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `AroonUp`*`double?`* | Based on last High price | | `AroonDown`*`double?`* | Based on last Low price | | `Oscillator`*`double?`* | AroonUp - AroonDown | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Oscillator` with additional chain-enabled indicators. ```csharp // example var results = bars .ToAroon(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AroonList aroonList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => aroonList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = aroonList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AroonHub observer = barHub.ToAroonHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/atr-stop.md description: >- Created by Welles Wilder, the ATR Trailing Stop indicator attempts to determine the primary trend of financial market prices by using Average True Range (ATR) band thresholds. It can indicate a buy/sell signal or a trailing stop when the trend changes. --- # ATR Trailing Stop Created by Welles Wilder, the ATR Trailing Stop indicator attempts to determine the primary trend of Close prices by using [Average True Range (ATR)](/indicators/atr.md) band thresholds. It can indicate a buy/sell signal or a trailing stop when the trend changes. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/724 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAtrStop(lookbackPeriods, multiplier, endType); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the ATR evaluation. Must be greater than 1. Default is 21. | | `multiplier`*`double`* | Multiplier sets the ATR band width. Must be greater than 0 and is usually set around 2 to 3. Default is 3. | | `endType`*`EndType`* | Determines whether `Close` or `High/Low` is used as basis for stop offset. Default is `EndType.Close`. | ### Historical price bars requirements You must have at least `N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` periods prior to the intended usage date for optimal precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `EndType` enum options | enum / value | description | | ----- | ----------- | | `EndType.Close`0 | Threshold measured from bar `Close` price | | `EndType.HighLow`1 | Threshold measured from bar `High` and `Low` price | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` AtrStop values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > the line segment before the first reversal and the first `N+100` periods are unreliable due to an initial guess of trend direction and precision convergence for the underlying ATR values. ### `AtrStopResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `AtrStop`*`double?`* | ATR Trailing Stop line contains both Upper and Lower segments | | `BuyStop`*`double?`* | Upper band only (green) | | `SellStop`*`double?`* | Lower band only (red) | | `Atr`*`double?`* | Average True Range | `BuyStop` and `SellStop` values are provided to differentiate buy vs sell stop lines and to clearly demark trend reversal. `AtrStop` is the contiguous combination of both upper and lower line data. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AtrStopList atrStopList = new(lookbackPeriods, multiplier: 3.0, endType: EndType.Close); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => atrStopList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = atrStopList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AtrStopHub observer = barHub.ToAtrStopHub(lookbackPeriods, multiplier: 3.0, endType: EndType.Close); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/elder-ray.md description: >- Created by Alexander Elder, the Elder-ray Index, also known as Bull and Bear Power, is an oscillator that depicts buying and selling pressure. It compares current high/low prices against an Exponential Moving Average. --- # Elder-ray Index Created by Alexander Elder, the [Elder-ray Index](https://www.investopedia.com/terms/e/elderray.asp), also known as Bull and Bear Power, is an oscillator that depicts buying and selling pressure. It compares current high/low prices against an Exponential Moving Average. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/378 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToElderRay(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the underlying EMA evaluation. Must be greater than 0. Default is 13. | ### Historical price bars requirements You must have at least `2ร—N` or `N+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` indicator values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `ElderRayResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Ema`*`double?`* | Exponential moving average | | `BullPower`*`double?`* | Bull Power | | `BearPower`*`double?`* | Bear Power | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `(BullPower+BearPower)` with additional chain-enabled indicators. ```csharp // example var results = bars .ToElderRay(..) .ToEma(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ElderRayList elderRayList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => elderRayList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = elderRayList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ElderRayHub observer = barHub.ToElderRayHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/ichimoku.md description: >- Created by Goichi Hosoda (็ดฐ็”ฐๆ‚Ÿไธ€, Hosoda Goichi), Ichimoku Cloud, also known as Ichimoku Kinkล Hyล, is a collection of indicators that depict support and resistance, momentum, and trend direction. --- # Ichimoku Cloud Created by Goichi Hosoda (็ดฐ็”ฐๆ‚Ÿไธ€, Hosoda Goichi), [Ichimoku Cloud](https://en.wikipedia.org/wiki/Ichimoku_Kink%C5%8D_Hy%C5%8D), also known as Ichimoku Kinkล Hyล, is a collection of indicators that depict support and resistance, momentum, and trend direction. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/251 "Community discussion about this indicator") ```csharp // C# usage syntax (batch) IReadOnlyList results = bars.ToIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods); // usage with custom offset IReadOnlyList results = bars.ToIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods, offsetPeriods); // usage with different custom offsets IReadOnlyList results = bars.ToIchimoku(tenkanPeriods, kijunPeriods, senkouBPeriods, senkouOffset, chikouOffset); // buffered usage (incremental), with the same offset overloads IchimokuList buffer = bars.ToIchimokuList(tenkanPeriods, kijunPeriods, senkouBPeriods); IchimokuList buffer = bars.ToIchimokuList(tenkanPeriods, kijunPeriods, senkouBPeriods, offsetPeriods); IchimokuList buffer = bars.ToIchimokuList(tenkanPeriods, kijunPeriods, senkouBPeriods, senkouOffset, chikouOffset); IReadOnlyList results = buffer; // streaming usage (real-time) BarHub barHub = new(); IchimokuHub observer = barHub.ToIchimokuHub(tenkanPeriods, kijunPeriods, senkouBPeriods); IReadOnlyList results = observer.Results; ``` ## Parameters | param | description | | ----- | ----------- | | `tenkanPeriods`*`int`* | Number of periods (`T`) in the Tenkan-sen midpoint evaluation. Must be greater than 0. Default is 9. | | `kijunPeriods`*`int`* | Number of periods (`K`) in the shorter Kijun-sen midpoint evaluation. Must be greater than 0. Default is 26. | | `senkouBPeriods`*`int`* | Number of periods (`S`) in the longer Senkou leading span B midpoint evaluation. Must be greater than `K`. Default is 52. | | `offsetPeriods`*`int`* | Optional. Number of periods to offset both `Senkou` and `Chikou` spans. Must be non-negative. Default is `kijunPeriods`. | | `senkouOffset`*`int`* | Optional. Number of periods to offset the `Senkou` span. Must be non-negative. Default is `kijunPeriods`. | | `chikouOffset`*`int`* | Optional. Number of periods to offset the `Chikou` span. Must be non-negative. Default is `kijunPeriods`. | See overloads usage above to determine which parameters are relevant for each. If you are customizing offsets, all parameter arguments must be specified. ### Historical price bars requirements You must have at least the greater of `T`,`K`, `S`, and offset periods for `bars` to cover the warmup periods; though, given the leading and lagging nature, we recommend notably more. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `T-1`, `K-1`, and `S-1` periods will have various `null` values since there's not enough data to calculate. Custom offset periods may also increase `null` results for warmup periods. ### `IchimokuResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `TenkanSen`*`double?`* | Conversion / signal line | | `KijunSen`*`double?`* | Base line | | `SenkouSpanA`*`double?`* | Leading span A | | `SenkouSpanB`*`double?`* | Leading span B | | `ChikouSpan`*`double?`* | Lagging span | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be used for chaining in subsequent indicators when streaming. ```csharp // example: chain to another indicator (streaming) var emaHub = bars .ToIchimokuHub() .ToEmaHub(14); ``` Note: `TenkanSen` is the primary reusable value for chaining purposes. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp IchimokuList ichimokuList = new(tenkanPeriods, kijunPeriods, senkouBPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => ichimokuList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = ichimokuList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); IchimokuHub observer = barHub.ToIchimokuHub(tenkanPeriods, kijunPeriods, senkouBPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/macd.md description: >- Created by Gerald Appel, MACD is a simple oscillator view of two converging / diverging exponential moving averages and their differences. --- # Moving Average Convergence / Divergence (MACD) Created by Gerald Appel, [MACD](https://en.wikipedia.org/wiki/MACD) is a simple oscillator view of two converging / diverging exponential moving averages and their differences. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/248 "Community discussion about this indicator") ```csharp // C# usage syntax (with Close price) IReadOnlyList results = bars.ToMacd(fastPeriods, slowPeriods, signalPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `fastPeriods`*`int`* | Number of periods (`F`) for the faster moving average. Must be greater than 0. Default is 12. | | `slowPeriods`*`int`* | Number of periods (`S`) for the slower moving average. Must be greater than `fastPeriods`. Default is 26. | | `signalPeriods`*`int`* | Number of periods (`P`) for the moving average of MACD. Must be greater than 0. Default is 9. | ### Historical price bars requirements You must have at least `2ร—(S+P)` or `S+P+100` worth of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `S+P+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `S-1` slow periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `S+P+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `MacdResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Macd`*`double?`* | The MACD line is the difference between slow and fast moving averages (`MACD = FastEma - SlowEma`) | | `Signal`*`double?`* | Moving average of the `MACD` line | | `Histogram`*`double?`* | Gap between the `MACD` and `Signal` line | | `FastEma`*`double?`* | Fast Exponential Moving Average | | `SlowEma`*`double?`* | Slow Exponential Moving Average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToMacd(..); ``` Results can be further processed on `Macd` with additional chain-enabled indicators. ```csharp // example var results = bars .ToMacd(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp MacdList macdList = new(fastPeriods, slowPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => macdList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = macdList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); MacdHub observer = barHub.ToMacdHub(fastPeriods, slowPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/pivot-points.md description: >- Pivot Points depict classic support and resistance levels, based on prior calendar windows. You can specify window size (e.g. month, week, day, etc) and any of the traditional Floor Trading, Camarilla, Demark, Fibonacci, and Woodie variants. --- # Pivot Points [Pivot Points](https://en.wikipedia.org/wiki/Pivot_point_\(technical_analysis\)) depict support and resistance levels, based on prior calendar windows. You can specify window size (e.g. month, week, day, etc) and any of the traditional Floor Trading, Camarilla, Demark, Fibonacci, and Woodie variants. See [Rolling Pivot Points](/indicators/rolling-pivots.md) for lookback window variant. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/274 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToPivotPoints(windowSize, pointType); ``` ## Parameters | param | description | | ----- | ----------- | | `windowSize`*`BarInterval`* | Size of the lookback window. Default is `BarInterval.Month` | | `pointType`*`PivotPointType`* | Type of Pivot Point. Default is `PivotPointType.Standard` | ### Historical price bars requirements You must have at least `2` windows of `bars` to cover the warmup periods. For example, if you specify a `Week` window size, you need at least 14 calendar days of `bars`. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### BarInterval options (for windowSize) **`BarInterval.Month`** - Use the prior month's data to calculate current month's Pivot Points **`BarInterval.Week`** - Use the prior week's data to calculate current week's Pivot Points **`BarInterval.Day`** - Use the prior day's data to calculate current day's Pivot Points. Commonly used for intraday data. **`BarInterval.OneHour`** - Use the prior hour's data to calculate current hour's Pivot Points ### PivotPointType options **`PivotPointType.Standard`** - Floor Trading (default) **`PivotPointType.Camarilla`** - Camarilla **`PivotPointType.Demark`** - Demark **`PivotPointType.Fibonacci`** - Fibonacci **`PivotPointType.Woodie`** - Woodie ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first window will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ Incomplete first window** > > The second window may be inaccurate if the first window contains incomplete data. For example, this can occur if you specify a `Month` window size and only provide 45 calendar days (1.5 months) of `bars`. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > The last window is repainted when it does not contain a full window of data. ### `PivotPointsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `R4`*`double?`* | Resistance level 4 (Camarilla only) | | `R3`*`double?`* | Resistance level 3 | | `R2`*`double?`* | Resistance level 2 | | `R1`*`double?`* | Resistance level 1 | | `PP`*`double?`* | Pivot Point | | `S1`*`double?`* | Support level 1 | | `S2`*`double?`* | Support level 2 | | `S3`*`double?`* | Support level 3 | | `S4`*`double?`* | Support level 4 (Camarilla only) | ### Utilities * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp PivotPointsList pivotPointsList = new(windowSize, pointType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => pivotPointsList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = pivotPointsList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); PivotPointsHub observer = barHub.ToPivotPointsHub(windowSize, pointType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/roc-wb.md description: >- Rate of Change with Bands, created by Vitali Apirine, is a volatility banded variant of the basic Rate of Change (ROC) indicator. --- # Rate of Change with Bands Rate of Change (ROC) with Bands, created by Vitali Apirine, is a volatility banded variant of [Rate of Change (ROC)](/indicators/roc.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/242 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToRocWb(lookbackPeriods, emaPeriods, stdDevPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) to go back. Must be greater than 0. Typical values range from 10-20. Default is 20. | | `emaPeriods`*`int`* | Number of periods for the ROC EMA line. Must be greater than 0. Default is 5. | | `stdDevPeriods`*`int`* | Number of periods the standard deviation for upper/lower band lines. Must be greater than 0 and not more than `lookbackPeriods`. Default is 5. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` ### `RocWbResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Roc`*`double?`* | Rate of Change over `N` lookback periods (%, not decimal) | | `RocEma`*`double?`* | Exponential moving average (EMA) of `Roc` | | `UpperBand`*`double?`* | Upper band of ROC (overbought indicator) | | `LowerBand`*`double?`* | Lower band of ROC (oversold indicator) | ### Utilities See [Utilities and helpers](/utilities.md) for more information. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp RocWbList rocWbList = new(lookbackPeriods, emaPeriods, stdDevPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => rocWbList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = rocWbList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); RocWbHub observer = barHub.ToRocWbHub(lookbackPeriods, emaPeriods, stdDevPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToRocWb(..); ``` Results can be further processed on `Roc` with additional chain-enabled indicators. ```csharp // example var results = bars .ToRocWb(..) .ToEma(..); ``` See [Chaining indicators](/guide/chaining.md) for more. --- --- url: /indicators/rolling-pivots.md description: >- Rolling Pivot Points is a modern update to traditional fixed calendar window Pivot Points. It depicts support and resistance levels, based on a defined rolling window and offset. --- # Rolling Pivot Points Created by Dave Skender, Rolling Pivot Points is a modern update to traditional fixed calendar window Pivot Points. It depicts support and resistance levels, based on a defined *rolling* window and offset. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/274 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToRollingPivots(windowPeriods, offsetPeriods, pointType); ``` ## Parameters | param | description | | ----- | ----------- | | `windowPeriods`*`int`* | Number of periods (`W`) in the evaluation window. Must be greater than 0 to calculate; but is typically specified in the 5-20 range. Default is 20. | | `offsetPeriods`*`int`* | Number of periods (`F`) to offset the window from the current period. Must be greater than or equal to 0 and is typically less than or equal to `W`. Default is 0. | | `pointType`*`PivotPointType`* | Type of Pivot Point. Default is `PivotPointType.Standard` | For example, a window of 8 with an offset of 4 would evaluate bars like: `W W W W W W W W F F F F C`, where `W` is the window included in the Pivot Point calculation, and `F` is the distance from the current evaluation position `C`. A `bars` with daily bars using `W/F` values of `20/10` would most closely match the `month` variant of the traditional [Pivot Points](/indicators/pivot-points.md) indicator. ### Historical price bars requirements You must have at least `W+F` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### PivotPointType options **`PivotPointType.Standard`** - Floor Trading (default) **`PivotPointType.Camarilla`** - Camarilla **`PivotPointType.Demark`** - Demark **`PivotPointType.Fibonacci`** - Fibonacci **`PivotPointType.Woodie`** - Woodie ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `W+F-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > Historical results are a function of the rolling window position and will shift as new bars are added. Each new period causes the window to move forward, recalculating pivot points based on the new window data. ### `RollingPivotsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `R4`*`double?`* | Resistance level 4 (Camarilla only) | | `R3`*`double?`* | Resistance level 3 | | `R2`*`double?`* | Resistance level 2 | | `R1`*`double?`* | Resistance level 1 | | `PP`*`double?`* | Pivot Point | | `S1`*`double?`* | Support level 1 | | `S2`*`double?`* | Support level 2 | | `S3`*`double?`* | Support level 3 | | `S4`*`double?`* | Support level 4 (Camarilla only) | ### Utilities * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp RollingPivotsList rollingPivotsList = new(windowPeriods, offsetPeriods, pointType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => rollingPivotsList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = rollingPivotsList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); RollingPivotsHub observer = barHub.ToRollingPivotsHub(windowPeriods, offsetPeriods, pointType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/super-trend.md description: >- Created by Oliver Seban, the SuperTrend indicator attempts to determine the primary trend of financial market prices by using Average True Range (ATR) band thresholds around an HL2 midline. It can indicate a buy/sell signal or a trailing stop when the trend changes. --- # SuperTrend Created by Oliver Seban, the SuperTrend indicator attempts to determine the primary trend of prices by using [Average True Range (ATR)](/indicators/atr.md) band thresholds around an HL2 midline. It can indicate a buy/sell signal or a trailing stop when the trend changes. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/235 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToSuperTrend(lookbackPeriods, multiplier); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the ATR evaluation. Must be greater than 1 and is usually set between 7 and 14. Default is 10. | | `multiplier`*`double`* | Multiplier sets the ATR band width. Must be greater than 0 and is usually set around 2 to 3. Default is 3. | ### Historical price bars requirements You must have at least `N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` periods prior to the intended usage date for optimal precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` SuperTrend values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > the line segment before the first reversal and the first `N+100` periods are unreliable due to an initial guess of trend direction and precision convergence for the underlying ATR values. ### `SuperTrendResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `SuperTrend`*`decimal?`* | SuperTrend line contains both Upper and Lower segments | | `UpperBand`*`decimal?`* | Upper band only (bearish/red) | | `LowerBand`*`decimal?`* | Lower band only (bullish/green) | `UpperBand` and `LowerBand` values are provided to differentiate bullish vs bearish trends and to clearly demark trend reversal. `SuperTrend` is the contiguous combination of both upper and lower line data. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp SuperTrendList superTrendList = new(lookbackPeriods, multiplier); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => superTrendList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = superTrendList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); SuperTrendHub observer = barHub.ToSuperTrendHub(lookbackPeriods, multiplier); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/vortex.md description: >- Created by Etienne Botes and Douglas Siepman, the Vortex Indicator is a measure of price directional movement. It includes positive and negative indicators, and is often used to identify trends and reversals. --- # Vortex Indicator (VI) Created by Etienne Botes and Douglas Siepman, the [Vortex Indicator](https://en.wikipedia.org/wiki/Vortex_indicator) is a measure of price directional movement. It includes positive and negative indicators, and is often used to identify trends and reversals. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/339 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToVortex(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) to consider. Must be greater than 1 and is usually between 14 and 30. Default is 14. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values for VI since there's not enough data to calculate. ### `VortexResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Pvi`*`double?`* | Positive Vortex Indicator (VI+) | | `Nvi`*`double?`* | Negative Vortex Indicator (VI-) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp VortexList vortexList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => vortexList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = vortexList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); VortexHub observer = barHub.ToVortexHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/alligator.md description: >- Created by Bill Williams, Alligator is a depiction of three smoothed moving averages of median price, showing chart patterns that compared to an alligator's feeding habits when describing market movement. The moving averages are known as the Jaw, Teeth, and Lips, which are calculated using lookback and offset periods. The related Gator Oscillator depicts periods of eating and resting. --- # Williams Alligator Created by Bill Williams, Alligator is a depiction of three smoothed moving averages of median price, showing chart patterns that compared to an alligator's feeding habits when describing market movement. The moving averages are known as the Jaw, Teeth, and Lips, which are calculated using lookback and offset periods. See also the [Gator Oscillator](/indicators/gator.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/385 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAlligator(jawPeriods,jawOffset,teethPeriods,teethOffset,lipsPeriods,lipsOffset); ``` ## Parameters | param | description | | ----- | ----------- | | `jawPeriods`*`int`* | Number of periods (`JP`) for the Jaw moving average. Must be greater than `teethPeriods`. Default is 13. | | `jawOffset`*`int`* | Number of periods (`JO`) for the Jaw offset. Must be greater than 0. Default is 8. | | `teethPeriods`*`int`* | Number of periods (`TP`) for the Teeth moving average. Must be greater than `lipsPeriods`. Default is 8. | | `teethOffset`*`int`* | Number of periods (`TO`) for the Teeth offset. Must be greater than 0. Default is 5. | | `lipsPeriods`*`int`* | Number of periods (`LP`) for the Lips moving average. Must be greater than 0. Default is 5. | | `lipsOffset`*`int`* | Number of periods (`LO`) for the Lips offset. Must be greater than 0. Default is 3. | ### Historical price bars requirements You must have at least `JP+JO+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `JP+JO+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `JP+JO` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `JP+JO+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `AlligatorResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Jaw`*`double?`* | Alligator's Jaw | | `Teeth`*`double?`* | Alligator's Teeth | | `Lips`*`double?`* | Alligator's Lips | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToAlligator(); ``` Results **cannot** be further chained with additional transforms. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AlligatorList alligatorList = new(jawPeriods, jawOffset, teethPeriods, teethOffset, lipsPeriods, lipsOffset); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => alligatorList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = alligatorList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AlligatorHub observer = barHub.ToAlligatorHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/price-channels.md description: >- Envelope and band indicators that define price range boundaries around a trend. --- --- --- url: /indicators/bollinger-bands.md description: >- Created by John Bollinger, the Bollinger Bands price channels depict volatility as upper and lower lines set a number of standard deviations from a moving average of price. Bollinger Bands® is a registered trademark of John A. Bollinger. --- # Bollinger Bandsยฎ Created by John Bollinger, [Bollinger Bands](https://en.wikipedia.org/wiki/Bollinger_Bands) price channels depict volatility as upper and lower lines set a number of standard deviations from a moving average of price. Bollinger Bandsยฎ is a registered trademark of John A. Bollinger. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/267 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToBollingerBands(lookbackPeriods, standardDeviations); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the center line moving average. Must be greater than 1 to calculate; however we suggest a larger period for statistically appropriate sample size. Default is 20. | | `standardDeviations`*`double`* | Width of bands. Standard deviations (`D`) from the moving average. Must be greater than 0. Default is 2. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `BollingerBandsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sma`*`double?`* | Simple moving average (SMA) of price (center line) | | `UpperBand`*`double?`* | Upper line is `D` standard deviations above the SMA | | `LowerBand`*`double?`* | Lower line is `D` standard deviations below the SMA | | `PercentB`*`double?`* | `%B` is the location within the bands. `(Price-LowerBand)/(UpperBand-LowerBand)` | | `ZScore`*`double?`* | Z-score of current price (number of standard deviations from mean) | | `Width`*`double?`* | Width as percent of SMA price. `(UpperBand-LowerBand)/Sma` | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToBollingerBands(..); ``` Results can be further processed on `PercentB` with additional chain-enabled indicators. ```csharp // example var results = bars .ToBollingerBands(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp BollingerBandsList bbList = new(lookbackPeriods, standardDeviations); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => bbList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = bbList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); BollingerBandsHub observer = barHub.ToBollingerBandsHub(lookbackPeriods, standardDeviations); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/donchian.md description: >- Created by Richard Donchian, Donchian Channels, also called Price Channels, are price ranges derived from highest High and lowest Low values. --- # Donchian Channels Created by Richard Donchian, [Donchian Channels](https://en.wikipedia.org/wiki/Donchian_channel), also called Price Channels, are price ranges derived from highest High and lowest Low values. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/257 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToDonchian(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for lookback period. Must be greater than 0 to calculate; however we suggest a larger value for an appropriate sample size. Default is 20. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values since there's not enough data to calculate. ### `DonchianResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `UpperBand`*`double?`* | Upper line is the highest High over `N` periods | | `Centerline`*`double?`* | Simple average of Upper and Lower bands | | `LowerBand`*`double?`* | Lower line is the lowest Low over `N` periods | | `Width`*`double?`* | Width as percent of Centerline price. `(UpperBand-LowerBand)/Centerline` | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp DonchianList donchianList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => donchianList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = donchianList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); DonchianHub observer = barHub.ToDonchianHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/fcb.md description: >- Created by Edward William Dreiss, Fractal Chaos Bands outline high and low price channels to depict broad less-chaotic price movements. FCB is a channelized depiction of Williams Fractal. --- # Fractal Chaos Bands (FCB) Created by Edward William Dreiss, Fractal Chaos Bands outline high and low price channels to depict broad less-chaotic price movements. FCB is a channelized depiction of [Williams Fractal](/indicators/fractal.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/347 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToFcb(windowSpan); ``` ## Parameters | param | description | | ----- | ----------- | | `windowSpan`*`int`* | Fractal evaluation window span width (`S`). Must be at least 2. Default is 2. | The total evaluation window size is `2ร—S+1`, representing `ยฑS` from the evaluation date. See [Williams Fractal](/indicators/fractal.md) for more information about Fractals and `windowSpan`. ### Historical price bars requirements You must have at least `2ร—S+1` periods of `bars` to cover the warmup periods; however, more is typically provided since this is a chartable candlestick pattern. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The periods before the first fractal are `null` since they cannot be calculated. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > Fractal Chaos Bands are based on [Williams Fractal](/indicators/fractal.md), which uses future bars. This indicator will never identify bands in the last `S` periods of `bars` since fractals are retroactively identified. ### `FcbResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `UpperBand`*`decimal?`* | FCB upper band | | `LowerBand`*`decimal?`* | FCB lower band | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp FcbList fcbList = new(windowSpan); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => fcbList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = fcbList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); FcbHub observer = barHub.ToFcbHub(windowSpan); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/keltner.md description: >- Created by Chester W. Keltner, the Keltner Channels price range overlay is based on an EMA centerline and Average True Range (ATR) band widths. STARC Bands are the SMA centerline equivalent. --- # Keltner Channels Created by Chester W. Keltner, [Keltner Channels](https://en.wikipedia.org/wiki/Keltner_channel) are based on an EMA centerline and ATR band widths. See also [STARC Bands](/indicators/starc-bands.md) for an SMA centerline equivalent. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/249 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToKeltner(emaPeriods, multiplier, atrPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `emaPeriods`*`int`* | Number of lookback periods (`E`) for the center line moving average. Must be greater than 1 to calculate. Default is 20. | | `multiplier`*`double`* | ATR Multiplier. Must be greater than 0. Default is 2. | | `atrPeriods`*`int`* | Number of lookback periods (`A`) for the Average True Range. Must be greater than 1 to calculate. Default is 10. | ### Historical price bars requirements You must have at least `2ร—N` or `N+100` periods of `bars`, whichever is more, where `N` is the greater of `E` or `A` periods, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `KeltnerResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `UpperBand`*`double?`* | Upper band of Keltner Channel | | `Centerline`*`double?`* | EMA of price | | `LowerBand`*`double?`* | Lower band of Keltner Channel | | `Width`*`double?`* | Width as percent of Centerline price. `(UpperBand-LowerBand)/Centerline` | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Centerline` with other [chained indicators](/guide/chaining.md). This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. ```csharp // example var results = bars .ToKeltner(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp KeltnerList keltnerList = new(emaPeriods, multiplier, atrPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => keltnerList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = keltnerList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); KeltnerHub observer = barHub.ToKeltnerHub(emaPeriods, multiplier, atrPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/ma-envelopes.md description: >- Moving Average Envelopes is a price band channel overlay that is offset from the moving average of price. --- # Moving Average Envelopes [Moving Average Envelopes](https://en.wikipedia.org/wiki/Moving_average_envelope) is a price band channel overlay that is offset from the moving average of price. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/288 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToMaEnvelopes(lookbackPeriods, percentOffset, movingAverageType); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 1. | | `percentOffset`*`double`* | Percent offset for envelope width. Example: 3.5% would be entered as 3.5 (not 0.035). Must be greater than 0. Typical values range from 2 to 10. Default is 2.5. | | `movingAverageType`*`MaType`* | Type of moving average (e.g. SMA, EMA, HMA). See [`MaType` enum options](#matype-enum-options) below. Default is `MaType.SMA`. | ### Historical price bars requirements `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. Detailed `bars` requirements for each of the [`MaType` enum options](#matype-enum-options) types can be found at the links in the following table. ### `MaType` enum options These are the supported moving average types: | enum value | moving average | | ------------- | ----------------------------------------------------- | | `MaType.ALMA` | [Arnaud Legoux Moving Average](/indicators/alma.md) | | `MaType.DEMA` | [Double Exponential Moving Average](/indicators/dema.md) | | `MaType.EPMA` | [Endpoint Moving Average](/indicators/epma.md) | | `MaType.EMA` | [Exponential Moving Average](/indicators/ema.md) | | `MaType.HMA` | [Hull Moving Average](/indicators/hma.md) | | `MaType.SMA` | [Simple Moving Average](/indicators/sma.md) (default) | | `MaType.SMMA` | [Smoothed Moving Average](/indicators/smma.md) | | `MaType.TEMA` | [Triple Exponential Moving Average](/indicators/tema.md) | | `MaType.WMA` | [Weighted Moving Average](/indicators/wma.md) | > [!WARNING] > **๐Ÿšฉ ALMA parameters** > > For ALMA, default values are used for `offset` and `sigma`. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first periods will have `null` values since there's not enough data to calculate; the quantity will vary based on the `movingAverageType` specified. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > Some moving average variants have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. See the [`MaType` enum options](#matype-enum-options) section above for more information. ### `MaEnvelopeResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Centerline`*`double?`* | Moving average | | `UpperEnvelope`*`double?`* | Upper envelope band | | `LowerEnvelope`*`double?`* | Lower envelope band | The moving average `Centerline` is based on the `movingAverageType` type specified. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HLC3) .ToMaEnvelopes(..); ``` Results **cannot** be further chained with additional transforms. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp MaEnvelopesList maEnvList = new(lookbackPeriods, percentOffset, movingAverageType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => maEnvList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = maEnvList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); MaEnvelopesHub observer = barHub.ToMaEnvelopesHub(lookbackPeriods, percentOffset, movingAverageType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/starc-bands.md description: >- Created by Manning Stoller, the Stoller Average Range Channel (STARC) Bands are financial market price ranges based on an simple moving average centerline and Average True Range (ATR) band widths. Keltner Channels are the EMA centerline equivalent. --- # STARC Bands Created by Manning Stoller, the [Stoller Average Range Channel (STARC) Bands](https://www.investopedia.com/terms/s/starc.asp), are price ranges based on an SMA centerline and ATR band widths. See also [Keltner Channels](/indicators/keltner.md) for an EMA centerline equivalent. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/292 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToStarcBands(smaPeriods, multiplier, atrPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `smaPeriods`*`int`* | Number of lookback periods (`S`) for the center line moving average. Must be greater than 1 to calculate and is typically between 5 and 10. Default is 5. | | `multiplier`*`double`* | ATR Multiplier. Must be greater than 0. Default is 2. | | `atrPeriods`*`int`* | Number of lookback periods (`A`) for the Average True Range. Must be greater than 1 to calculate and is typically the same value as `smaPeriods`. Default is 10. | ### Historical price bars requirements You must have at least `S` or `A+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `A+150` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate, where `N` is the greater of `S` or `A`. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `A+150` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `StarcBandsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `UpperBand`*`double?`* | Upper STARC band | | `Centerline`*`double?`* | SMA of price | | `LowerBand`*`double?`* | Lower STARC band | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp StarcBandsList starcBandsList = new(smaPeriods, multiplier, atrPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => starcBandsList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = starcBandsList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); StarcBandsHub observer = barHub.ToStarcBandsHub(smaPeriods, multiplier, atrPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/std-dev-channels.md description: >- Standard Deviation Channels are price ranges based on an linear regression centerline and standard deviations band widths. --- # Standard Deviation Channels Standard Deviation Channels are prices ranges based on an linear regression centerline and standard deviations band widths. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/368 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToStdDevChannels(lookbackPeriods, stdDeviations); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Size (`N`) of the evaluation window. Must be `null` or greater than 1 to calculate. A `null` value will produce a full `bars` evaluation window ([see below](#alternative-depiction-for-full-bars-variant)). Default is 20. | | `stdDeviations`*`double`* | Width of bands. Standard deviations (`D`) from the regression line. Must be greater than 0. Default is 2. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * Up to `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > Historical results are a function of the current period window position and will fluctuate over time. Recommended for visualization; not recommended for backtesting. ### `StdDevChannelsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Centerline`*`double?`* | Linear regression line (center line) | | `UpperChannel`*`double?`* | Upper line is `D` standard deviations above the center line | | `LowerChannel`*`double?`* | Lower line is `D` standard deviations below the center line | | `BreakPoint`*`bool`* | Helper information. Indicates first point in new window. | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Alternative depiction for full bars variant If you specify `null` for the `lookbackPeriods`, you will get a regression line over the entire provided `bars`. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = barsEval .Use(CandlePart.HL2) .ToStdDevChannels(..); ``` Results **cannot** be further chained with additional transforms. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming **โš ๏ธ Streaming not supported**: Due to the reverse-window algorithm that recalculates the entire dataset on each new data point, Standard Deviation Channels is only available as a batch Series implementation. The computational cost grows quadratically (O(nยฒ)) as the dataset size increases, making it impractical for incremental streaming (StreamHub) or buffer (BufferList) scenarios. **Recommendation**: Use the Series implementation (`ToStdDevChannels()`) with periodic batch recalculation. For real-time scenarios, consider recalculating at appropriate intervals (e.g., end of period, every N bars) rather than on every tick. --- --- url: /indicators/oscillators.md description: >- Momentum and strength indicators that measure price velocity and market conditions. --- --- --- url: /indicators/awesome.md description: >- Created by Bill Williams, the Awesome Oscillator (AO), also known as Super AO, is a measure of the gap between a fast and slow period modified moving average. --- # Awesome Oscillator (AO) Created by Bill Williams, the Awesome Oscillator (aka Super AO) is a measure of the gap between a fast and slow period modified moving average. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/282 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAwesome(fastPeriods, slowPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `fastPeriods`*`int`* | Number of periods (`F`) for the faster moving average. Must be greater than 0. Default is 5. | | `slowPeriods`*`int`* | Number of periods (`S`) for the slower moving average. Must be greater than `fastPeriods`. Default is 34. | ### Historical price bars requirements You must have at least `S` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first period `S-1` periods will have `null` values since there's not enough data to calculate. ### `AwesomeResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Oscillator`*`double?`* | Awesome Oscillator | | `Normalized`*`double?`* | `100 ร— Oscillator รท (median price)` | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToAwesome(..); ``` Results can be further processed on `Oscillator` with additional chain-enabled indicators. ```csharp // example var results = bars .ToAwesome(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AwesomeList awesomeList = new(fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => awesomeList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = awesomeList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AwesomeHub observer = barHub.ToAwesomeHub(fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/cmo.md description: >- The Chande Momentum Oscillator is a momentum indicator depicting the weighted percent of higher prices in financial markets. --- # Chande Momentum Oscillator (CMO) Created by Tushar Chande, the [Chande Momentum Oscillator](https://www.investopedia.com/terms/c/chandemomentumoscillator.asp) is a weighted percent of higher prices over a lookback window. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/892 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToCmo(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback window. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values for CMO since there's not enough data to calculate. ### `CmoResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Cmo`*`double?`* | Chande Momentum Oscillator | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToCmo(..); ``` Results can be further processed on `Cmo` with additional chain-enabled indicators. ```csharp // example var results = bars .ToCmo(..) .ToEma(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp CmoList cmoList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => cmoList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = cmoList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); CmoHub observer = barHub.ToCmoHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See the [guide](/guide.md) for more information. See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/cci.md description: >- Created by Donald Lambert, the Commodity Channel Index is an oscillator depicting deviation from typical price range, often used to identify cyclical trends. --- # Commodity Channel Index (CCI) Created by Donald Lambert, the [Commodity Channel Index](https://en.wikipedia.org/wiki/Commodity_channel_index) is an oscillator depicting deviation from typical price range, often used to identify cyclical trends. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/265 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToCci(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. Default is 20. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `CciResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Cci`*`double?`* | Commodity Channel Index | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Cci` with additional chain-enabled indicators. ```csharp // example var results = bars .ToCci(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp CciList cciList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => cciList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = cciList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); CciHub observer = barHub.ToCciHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/connors-rsi.md description: >- Created by Laurence Connors, the ConnorsRSI is a composite oscillator that incorporates RSI, winning/losing streaks, and percentile gain metrics on scale of 0 to 100. --- # ConnorsRSI Created by Laurence Connors, the [ConnorsRSI](https://alvarezquanttrading.com/wp-content/uploads/2016/05/ConnorsRSIGuidebook.pdf) is a composite oscillator that incorporates RSI, winning/losing streaks, and percentile gain metrics on scale of 0 to 100. See [analysis](https://alvarezquanttrading.com/blog/connorsrsi-analysis). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/260 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToConnorsRsi(rsiPeriods, streakPeriods, rankPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `rsiPeriods`*`int`* | Lookback period (`R`) for the price RSI. Must be greater than 1. Default is 3. | | `streakPeriods`*`int`* | Lookback period (`S`) for the streak RSI. Must be greater than 1. Default is 2. | | `rankPeriods`*`int`* | Lookback period (`P`) for the Percentile Rank. Must be greater than 1. Default is 100. | ### Historical price bars requirements `N` is the greater of `R+100`, `S`, and `P+2`. You must have at least `N` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+150` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `MAX(R,S,P)+1` periods will have `null` `ConnorsRsi` values since there's not enough data to calculate all three component scores (RSI of close, RSI of streak, percent rank) and combine them. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `ConnorsRsiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Streak`*`double`* | Consecutive up (positive) or down (negative) periods in the price | | `Rsi`*`double?`* | `RSI(R)` of the price. | | `RsiStreak`*`double?`* | `RSI(S)` of the Streak. | | `PercentRank`*`double?`* | Percentile rank of the period gain value. | | `ConnorsRsi`*`double?`* | ConnorsRSI | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToConnorsRsi(..); ``` Results can be further processed on `ConnorsRsi` with additional chain-enabled indicators. ```csharp // example var results = bars .ToConnorsRsi(..) .ToSma(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ConnorsRsiList connorsRsiList = new(rsiPeriods, streakPeriods, rankPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => connorsRsiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = connorsRsiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ConnorsRsiHub observer = barHub.ToConnorsRsiHub(rsiPeriods, streakPeriods, rankPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/dpo.md description: >- Detrended Price Oscillator depicts the difference between price and an offset simple moving average. It is used to identify trend cycles and duration. --- # Detrended Price Oscillator (DPO) [Detrended Price Oscillator](https://en.wikipedia.org/wiki/Detrended_price_oscillator) depicts the difference between price and an offset simple moving average. It is used to identify trend cycles and duration. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/551 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToDpo(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N` historical price bars to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N/2-2` and last `N/2+1` periods will be `null` since they cannot be calculated. ### `DpoResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sma`*`double?`* | Simple moving average offset by `N/2+1` periods | | `Dpo`*`double?`* | Detrended Price Oscillator (DPO) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToDpo(..); ``` Results can be further processed on `Dpo` with additional chain-enabled indicators. ```csharp // example var results = bars .ToDpo(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp DpoList dpoList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => dpoList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = dpoList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); DpoHub observer = barHub.ToDpoHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` **Note:** DPO has a lookahead requirement (offset = N/2+1 periods), which means results are calculated when sufficient future data becomes available. This introduces a delay in real-time scenarios but maintains mathematical accuracy with the series implementation. See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/gator.md description: >- Created by Bill Williams, the Gator Oscillator is an expanded oscillator view of Williams Alligator's three moving averages. --- # Gator Oscillator Created by Bill Williams, the Gator Oscillator is an expanded oscillator view of [Williams Alligator](/indicators/alligator.md)'s three moving averages. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/385 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToGator(); // with custom Alligator configuration IReadOnlyList results = bars .ToAlligator([see Alligator docs]) .ToGator(); ``` ## Historical price bars requirements If using default settings, you must have at least 121 periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least 271 data points prior to the intended usage date for better precision. If using a custom Alligator configuration, see [Alligator documentation](/indicators/alligator.md#historical-price-bars-requirements) for Historical price bars requirements. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first 10-20 periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first 150 periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `GatorResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Upper`*`double?`* | Absolute value of Alligator `Jaw-Teeth` | | `Lower`*`double?`* | Absolute value of Alligator `Lips-Teeth` | | `UpperIsExpanding`*`bool?`* | Upper value is growing | | `LowerIsExpanding`*`bool?`* | Lower value is growing | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HLC3) .ToGator(); ``` Results **cannot** be further chained with additional transforms. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp GatorList gatorList = new(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => gatorList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = gatorList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); GatorHub observer = barHub.ToGatorHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` > [!NOTE] > **Compound hub** > > The Gator hub is based on the Alligator indicator. When the Gator hub is chained from an existing `AlligatorHub` instance it will reuse the existing Alligator hub values rather than creating its own internal Alligator calculations. **This is not a normal chaining model.** > > ```csharp > // creates an internal Alligator hub > var gatorHub = bars > .ToGatorHub(); > > // this is helpful in cases where you have an independent > // Alligator hub and do not want to create duplicate copies > > var alligatorHub = bars > .ToAlligatorHub(); > > // does not create 2nd internal huba separate internal Alligator hub > var gatorHub = alligatorHub > .ToGatorHub(); // does not create 2nd internal hub > > // โŒ Alligator โ†’ [ Alligator ] โ†’ Gator > // โœ… Alligator โ†’ Gator > ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/pmo.md description: >- Created by Carl Swenlin, the DecisionPoint Price Momentum Oscillator is double-smoothed momentum indicator, based on Rate of Change (ROC). --- # Price Momentum Oscillator (PMO) Created by Carl Swenlin, the DecisionPoint [Price Momentum Oscillator](https://www.google.com/search?q=DecisionPoint+Price+Momentum+Oscillator+\(PMO\)) is double-smoothed momentum indicator based on Rate of Change (ROC). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/244 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToPmo(timePeriods, smoothPeriods, signalPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `timePeriods`*`int`* | Number of periods (`T`) for first ROC smoothing. Must be greater than 1. Default is 35. | | `smoothPeriods`*`int`* | Number of periods (`S`) for second PMO smoothing. Must be greater than 0. Default is 20. | | `signalPeriods`*`int`* | Number of periods (`G`) for Signal line EMA. Must be greater than 0. Default is 10. | ### Historical price bars requirements You must have at least `N` periods of `bars`, where `N` is the greater of `T+S`, `2ร—T`, or `T+100` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses multiple smoothing operations, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `T+S-1` periods will have `null` values for PMO since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `T+S+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `PmoResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Pmo`*`double?`* | Price Momentum Oscillator | | `Signal`*`double?`* | Signal line is EMA of PMO | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToPmo(..); ``` Results can be further processed on `Pmo` with additional chain-enabled indicators. ```csharp // example var results = bars .ToPmo(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp PmoList pmoList = new(timePeriods, smoothPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => pmoList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = pmoList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); PmoHub observer = barHub.ToPmoHub(timePeriods, smoothPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/rsi.md description: >- Created by J. Welles Wilder, the Relative Strength Index is an oscillator that measures strength of the winning/losing price streak on a scale of 0 to 100, to depict overbought and oversold conditions. --- # Relative Strength Index (RSI) Created by J. Welles Wilder, the [Relative Strength Index](https://en.wikipedia.org/wiki/Relative_strength_index) is an oscillator that measures strength of the winning/losing streak over `N` lookback periods on a scale of 0 to 100, to depict overbought and oversold conditions. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/224 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToRsi(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback period. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `10ร—N` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `10ร—N` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `RsiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Rsi`*`double?`* | Relative Strength Index | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToRsi(..); ``` Results can be further processed on `Rsi` with additional chain-enabled indicators. ```csharp // example var results = bars .ToRsi(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp RsiList rsiList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => rsiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = rsiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); RsiHub observer = barHub.ToRsiHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/stc.md description: >- Created by Doug Schaff, the Schaff Trend Cycle is a stochastic oscillator view of two converging/diverging exponential moving averages. In other words, it's a Stochastic Oscillator of Moving Average Convergence / Divergence (MACD). --- # Schaff Trend Cycle Created by Doug Schaff, the [Schaff Trend Cycle](https://www.investopedia.com/articles/forex/10/schaff-trend-cycle-indicator.asp) is a stochastic oscillator view of two converging/diverging exponential moving averages. In other words, it's a Stochastic Oscillator of Moving Average Convergence / Divergence (MACD). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/570 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToStc(cyclePeriods, fastPeriods, slowPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `cyclePeriods`*`int`* | Number of periods (`C`) for the Trend Cycle. Must be greater than 0. Default is 10. | | `fastPeriods`*`int`* | Number of periods (`F`) for the faster moving average. Must be greater than 0. Default is 23. | | `slowPeriods`*`int`* | Number of periods (`S`) for the slower moving average. Must be greater than `fastPeriods`. Default is 50. | ### Historical price bars requirements You must have at least `2ร—(S+C)` or `S+C+100` worth of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `S+C+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `S+C` slow periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `S+C+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `StcResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Stc`*`double?`* | Schaff Trend Cycle | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToStc(..); ``` Results can be further processed on `Stc` with additional chain-enabled indicators. ```csharp // example var results = bars .ToStc(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp StcList stcList = new(cyclePeriods, fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => stcList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = stcList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); StcHub observer = barHub.ToStcHub(cyclePeriods, fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/smi.md description: >- Created by William Blau, the Stochastic Momentum Index (SMI) oscillator is a double-smoothed variant of the traditional Stochastic Oscillator, depicted on a scale from -100 to 100. --- # Stochastic Momentum Index (SMI) Created by William Blau, the Stochastic Momentum Index (SMI) oscillator is a double-smoothed variant of the [Stochastic Oscillator](/indicators/stoch.md), depicted on a scale from -100 to 100. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/625 "Community discussion about this indicator") ```csharp // C# usage syntax (standard) IReadOnlyList results = bars.ToSmi(lookbackPeriods, firstSmoothPeriods, secondSmoothPeriods, signalPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Lookback period (`N`) for the stochastic. Must be greater than 0. Default is 13. | | `firstSmoothPeriods`*`int`* | First smoothing factor lookback. Must be greater than 0. Default is 25. | | `secondSmoothPeriods`*`int`* | Second smoothing factor lookback. Must be greater than 0. Default is 2. | | `signalPeriods`*`int`* | EMA of SMI lookback periods. Must be greater than 0. Default is 3. | ### Historical price bars requirements You must have at least `N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` SMI values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `SmiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Smi`*`double?`* | Stochastic Momentum Index (SMI) | | `Signal`*`double?`* | Signal line: an Exponential Moving Average (EMA) of SMI | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Smi` with additional chain-enabled indicators. ```csharp // example var results = bars .ToSmi(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp SmiList smiList = new(lookbackPeriods, firstSmoothPeriods, secondSmoothPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => smiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = smiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); SmiHub observer = barHub.ToSmiHub(lookbackPeriods, firstSmoothPeriods, secondSmoothPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/stoch.md description: >- Created by George Lane, the Stochastic Oscillator, also known as KDJ Index, is a momentum oscillator that compares current financial market price with recent highs and lows and is presented on a scale of 0 to 100. %J is also included for the KDJ Index extension. --- # Stochastic Oscillator Created by George Lane, the [Stochastic Oscillator](https://en.wikipedia.org/wiki/Stochastic_oscillator), also known as KDJ Index, is a momentum oscillator that compares current price with recent highs and lows and is presented on a scale of 0 to 100. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/237 "Community discussion about this indicator") ```csharp // C# usage syntax (standard) IReadOnlyList results = bars.ToStoch(lookbackPeriods, signalPeriods, smoothPeriods); // advanced customization IReadOnlyList results = bars.ToStoch(lookbackPeriods, signalPeriods, smoothPeriods, kFactor, dFactor, movingAverageType); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Lookback period (`N`) for the oscillator (%K). Must be greater than 0. Default is 14. | | `signalPeriods`*`int`* | Smoothing period for the signal (%D). Must be greater than 0. Default is 3. | | `smoothPeriods`*`int`* | Smoothing period (`S`) for the Oscillator (%K). "Slow" stochastic uses 3, "Fast" stochastic uses 1. Must be greater than 0. Default is 3. | | `kFactor`*`double`* | Optional. Weight of %K in the %J calculation. Must be greater than 0. Default is 3. | | `dFactor`*`double`* | Optional. Weight of %D in the %J calculation. Must be greater than 0. Default is 2. | | `movingAverageType`*`MaType`* | Optional. Type of moving average (SMA or SMMA) used for smoothing. See [`MaType` enum options](#matype-enum-options) below. Default is `MaType.SMA`. | ### Historical price bars requirements You must have at least `N+S` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `MaType` enum options These are the supported moving average types: | enum value | moving average | | ------------- | ----------------------------------------------------- | | `MaType.SMA` | [Simple Moving Average](/indicators/sma.md) (default) | | `MaType.SMMA` | [Smoothed Moving Average](/indicators/smma.md) | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N+S-2` periods will have `null` Oscillator values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods when using `MaType.SMMA`. Standard use of `MaType.SMA` does not have convergence-related precision errors. ### `StochResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Oscillator` or `K`*`double?`* | %K Oscillator | | `Signal` or `D`*`double?`* | %D Simple moving average of Oscillator | | `PercentJ` or `J`*`double?`* | %J is the weighted divergence of %K and %D: `%J = kFactor ร— %K - dFactor ร— %D` | Note: aliases of `K`, `D`, and `J` are also provided. They can be used interchangeably with the standard outputs. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Oscillator` with additional chain-enabled indicators. ```csharp // example var results = bars .ToStoch(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp StochList stochList = new(lookbackPeriods, signalPeriods, smoothPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => stochList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = stochList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); StochHub observer = barHub.ToStochHub(lookbackPeriods, signalPeriods, smoothPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/stoch-rsi.md description: >- Created by Tushar Chande and Stanley Kroll, Stochastic RSI is a Stochastic Oscillator interpretation of the Relative Strength Index. It is different from, and often confused with the more traditional Stochastic Oscillator. --- # Stochastic RSI Created by Tushar Chande and Stanley Kroll, [Stochastic RSI](https://chartschool.stockcharts.com/table-of-contents/technical-indicators-and-overlays/technical-indicators/stochrsi) is a Stochastic interpretation of the Relative Strength Index. It is different from, and often confused with the more traditional [Stochastic Oscillator](/indicators/stoch.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/236 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToStochRsi(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `rsiPeriods`*`int`* | Number of periods (`R`) in the lookback period. Must be greater than 0. Default is 14. | | `stochPeriods`*`int`* | Number of periods (`S`) in the lookback period. Must be greater than 0. Typically the same value as `rsiPeriods`. Default is 14. | | `signalPeriods`*`int`* | Number of periods (`G`) in the signal line (SMA of the StochRSI). Must be greater than 0. Typically 3-5. Default is 3. | | `smoothPeriods`*`int`* | Smoothing periods (`M`) for the Stochastic. Must be greater than 0. Default is 1 (Fast variant). | The original Stochastic RSI formula uses a the Fast variant of the Stochastic calculation (`smoothPeriods=1`). For a standard period of 14, the original formula would be `bars.ToStochRSI(14,14,3,1)`. The "3" here is just for the Signal (%D), which is not present in the original formula, but useful for additional smoothing and analysis. ### Historical price bars requirements You must have at least `N` periods of `bars`, where `N` is the greater of `R+S+M` and `R+100` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique in the underlying RSI value, we recommend you use at least `10ร—R` periods prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `R+S+M` periods will have `null` values for `StochRsi` since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `10ร—R` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. We recommend pruning at least `R+S+M+100` initial values. ### `StochRsiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `StochRsi`*`double?`* | %K Oscillator = Stochastic RSI = Stoch(`S`,`G`,`M`) of RSI(`R`) of price | | `Signal`*`double?`* | %D Signal Line = Simple moving average of %K based on `G` periods | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToStochRsi(..); ``` Results can be further processed on `StochRsi` with additional chain-enabled indicators. ```csharp // example var results = bars .ToStochRsi(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp StochRsiList stochRsiList = new(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => stochRsiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = stochRsiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); StochRsiHub observer = barHub.ToStochRsiHub(rsiPeriods, stochPeriods, signalPeriods, smoothPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` > [!NOTE] > **Compound hub** > > The StochRSI hub is based on the RSI indicator. When the StochRSI hub is chained from an existing `RsiHub` instance it will reuse the existing RSI hub values rather than creating its own internal RSI calculations. **This is not a normal chaining model.** > > ```csharp > // creates a new internal RSI hub > var stochRsiHub = bars > .ToStochRsiHub(); > ``` > > As an option, if you have an existing RSI hub you may reuse it: > > ```csharp > > // existing hub > var rsiHub = bars > .ToRsiHub(); > > // does not create a 2nd internal hub > var stochRsiHub = rsiHub > .ToStochRsiHub(); > > // โŒ RSI โ†’ [ RSI ] โ†’ StochRSI > // โœ… RSI โ†’ StochRSI > ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/trix.md description: >- Created by Jack Hutson, TRIX is a rolling rate of change for a 3 EMA smoothing of the price over a lookback window. TRIX is often confused with Triple EMA (TEMA). --- # Triple EMA Oscillator (TRIX) Created by Jack Hutson, [TRIX](https://en.wikipedia.org/wiki/Trix_\(technical_analysis\)) is the rate of change for a 3 EMA smoothing of the price over a lookback window. TRIX is often confused with [TEMA](/indicators/tema.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/234 "Community discussion about this indicator") ```csharp // C# usage syntax for Trix IReadOnlyList results = bars.ToTrix(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in each of the exponential moving averages. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `4ร—N` or `3ร—N+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `3ร—N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `3ร—N-3` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `3ร—N+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `TrixResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Ema3`*`double?`* | 3 EMAs of the price | | `Trix`*`double?`* | Rate of Change of 3 EMAs | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToTrix(..); ``` Results can be further processed on `Trix` with additional chain-enabled indicators. ```csharp // example var results = bars .ToTrix(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp TrixList trixList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => trixList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = trixList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); TrixHub observer = barHub.ToTrixHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/tsi.md description: >- Created by William Blau, the True Strength Index is a momentum oscillator that uses a series of exponential moving averages to depicts trends in price changes. --- # True Strength Index (TSI) Created by William Blau, the [True Strength Index](https://en.wikipedia.org/wiki/True_strength_index) is a momentum oscillator that uses a series of exponential moving averages to depicts trends in price changes. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/300 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToTsi(lookbackPeriods, smoothPeriods, signalPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the first EMA. Must be greater than 0. Default is 25. | | `smoothPeriods`*`int`* | Number of periods (`M`) for the second smoothing. Must be greater than 0. Default is 13. | | `signalPeriods`*`int`* | Number of periods (`S`) in the TSI moving average. Must be greater than or equal to 0. Default is 7. | ### Historical price bars requirements You must have at least `N+M+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a two-stage EMA smoothing technique, we recommend you use at least `N+M+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N+M-1` periods will have `null` values since there's not enough data to calculate. * `Signal` will be `null` for all periods if `signalPeriods=0`. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+M+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `TsiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Tsi`*`double?`* | True Strength Index | | `Signal`*`double?`* | Signal line (EMA of TSI) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp TsiList tsiList = new(lookbackPeriods, smoothPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => tsiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = tsiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); TsiHub observer = barHub.ToTsiHub(lookbackPeriods, smoothPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToTsi(..); ``` Results can be further processed on `Tsi` with additional chain-enabled indicators. ```csharp // example var results = bars .ToTsi(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. --- --- url: /indicators/ultimate.md description: >- Created by Larry Williams, the Ultimate Oscillator uses several moving averages to weigh buying power against true range price to produce an oversold / overbought oscillator. --- # Ultimate Oscillator Created by Larry Williams, the [Ultimate Oscillator](https://en.wikipedia.org/wiki/Ultimate_oscillator) uses several moving averages to weigh buying power against true range price to produce an oversold / overbought oscillator. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/231 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToUltimate(shortPeriods, middlePeriods, longPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `shortPeriods`*`int`* | Number of periods (`S`) in the short lookback. Must be greater than 0. Default is 7. | | `middlePeriods`*`int`* | Number of periods (`M`) in the middle lookback. Must be greater than `S`. Default is 14. | | `longPeriods`*`int`* | Number of periods (`L`) in the long lookback. Must be greater than `M`. Default is 28. | ### Historical price bars requirements You must have at least `L+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `L-1` periods will have `null` Ultimate values since there's not enough data to calculate. ### `UltimateResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Ultimate`*`double?`* | Ultimate Oscillator | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Ultimate` with additional chain-enabled indicators. ```csharp // example var results = bars .ToUltimate(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp UltimateList ultimateList = new(shortPeriods, middlePeriods, longPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => ultimateList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = ultimateList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); UltimateHub observer = barHub.ToUltimateHub(shortPeriods, middlePeriods, longPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/williams-r.md description: >- Created by Larry Williams, the Williams %R momentum oscillator compares current price with recent highs and lows and is presented on scale of -100 to 0. It is exactly the same as the fast variant of Stochastic Oscillator, but with a different scaling. --- # Williams %R Created by Larry Williams, the [Williams %R](https://en.wikipedia.org/wiki/Williams_%25R) momentum oscillator compares current price with recent highs and lows and is presented on scale of -100 to 0. It is exactly the same as the fast variant of [Stochastic Oscillator](/indicators/stoch.md), but with a different scaling. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/229 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToWilliamsR(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback period. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` Oscillator values since there's not enough data to calculate. ### `WilliamsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `WilliamsR`*`double?`* | Oscillator over prior `N` lookback periods | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `WilliamsR` with additional chain-enabled indicators. ```csharp // example var results = bars .ToWilliamsR(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp WilliamsRList williamsRList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => williamsRList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = williamsRList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); WilliamsRHub observer = barHub.ToWilliamsRHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/stop-and-reverse.md description: Indicators that signal trend reversals and define dynamic exit or stop levels. --- --- --- url: /indicators/chandelier.md description: >- Created by Charles Le Beau, the Chandelier Exit is an adjusted Average True Range (ATR) offset from price that is typically used for stop-loss and can be computed for both long or short types. --- # Chandelier Exit Created by Charles Le Beau, the [Chandelier Exit](https://www.google.com/search?q=Chandelier+Exit+indicator) is an adjusted Average True Range (ATR) offset from price that is typically used for stop-loss and can be computed for both long or short types. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/263 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToChandelier(lookbackPeriods, multiplier, type); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the lookback evaluation. Must be greater than 1. Default is 22. | | `multiplier`*`double`* | Multiplier number must be a positive value. Default is 3. | | `type`*`Direction`* | Direction of exit. Default is `Direction.Long`. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `Direction` enum options *The direction of a held [trade position](https://en.wikipedia.org/wiki/Position_\(finance\))* | enum / value | description | | ----- | ----------- | | `Direction.Long`0 | Long trade positions | | `Direction.Short`1 | Short trade positions | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` Chandelier values since there's not enough data to calculate. ### `ChandelierResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `ChandelierExit`*`double?`* | Exit line | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `ChandelierExit` with additional chain-enabled indicators. ```csharp // example var results = bars .ToChandelier(..) .ToEma(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ChandelierList chandelierList = new(lookbackPeriods, multiplier, type); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => chandelierList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = chandelierList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ChandelierHub observer = barHub.ToChandelierHub(lookbackPeriods, multiplier, type); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/parabolic-sar.md description: >- Created by J. Welles Wilder, Parabolic SAR (stop and reverse) is a price-time based indicator used to determine trend direction and reversals. It can be used to identify trend direction, reversals, and stop-loss signals. --- # Parabolic SAR Created by J. Welles Wilder, [Parabolic SAR](https://en.wikipedia.org/wiki/Parabolic_SAR) (stop and reverse) is a price-time based indicator used to determine trend direction and reversals. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/245 "Community discussion about this indicator") ```csharp // C# usage syntax (standard) IReadOnlyList results = bars.ToParabolicSar(accelerationStep, maxAccelerationFactor); // alternate usage with custom initial Factor IReadOnlyList results = bars.ToParabolicSar(accelerationStep, maxAccelerationFactor, initialFactor); ``` ## Parameters | param | description | | ----- | ----------- | | `accelerationStep`*`double`* | Incremental step size for the Acceleration Factor. Must be greater than 0. Default is 0.02 | | `maxAccelerationFactor`*`double`* | Maximum factor limit. Must be greater than `accelerationStep`. Default is 0.2 | | `initialFactor`*`double`* | Optional. Initial Acceleration Factor. Must be greater than 0 and not larger than `maxAccelerationFactor`. Default is `accelerationStep`. | ### Historical price bars requirements You must have at least two historical price bars to cover the warmup periods; however, we recommend at least 100 data points. Initial Parabolic SAR values prior to the first reversal are not accurate and are excluded from the results. Therefore, provide sufficient bars to capture prior trend reversals, before your intended usage period. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first trend will have `null` values since it is not accurate and based on an initial guess. ### `ParabolicSarResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sar`*`double?`* | Stop and Reverse value | | `IsReversal`*`bool?`* | Indicates a trend reversal | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Sar` with additional chain-enabled indicators. ```csharp // example var results = bars .ToParabolicSar(..) .ToEma(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ParabolicSarList psarList = new(accelerationStep, maxAccelerationFactor); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => psarList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = psarList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ParabolicSarHub observer = barHub.ToParabolicSarHub(accelerationStep, maxAccelerationFactor); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/volatility-stop.md description: >- Created by J. Welles Wilder, Volatility Stop, also known his Volatility System, is an ATR based indicator used to determine trend direction, stops, and reversals. It is similar to Wilder's Parabolic SAR, SuperTrend, and more contemporary ATR Trailing Stop. --- # Volatility Stop Created by J. Welles Wilder, [Volatility Stop](https://archive.org/details/newconceptsintec00wild), also known his Volatility System, is an [ATR](/indicators/atr.md) based indicator used to determine trend direction, stops, and reversals. It is similar to Wilder's [Parabolic SAR](/indicators/parabolic-sar.md) and [SuperTrend](/indicators/super-trend.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/564 "Community discussion about this indicator") ```csharp // C# usage syntax (Series) IReadOnlyList results = bars.ToVolatilityStop(lookbackPeriods, multiplier); // Usage with BarHub (streaming) BarHub barHub = new(); VolatilityStopHub observer = barHub.ToVolatilityStopHub(lookbackPeriods, multiplier); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) ATR lookback window. Must be greater than 1. Default is 7. | | `multiplier`*`double`* | ATR multiplier for the offset. Must be greater than 0. Default is 3.0. | ### Historical price bars requirements You must have at least `N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since the underlying ATR uses a smoothing technique, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. Initial values prior to the first reversal are not accurate and are excluded from the results. Therefore, provide sufficient bars to capture prior trend reversals. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first trend will have `null` values since it is not accurate and based on an initial guess. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `VolatilityStopResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sar`*`double?`* | Stop and Reverse value contains both Upper and Lower segments | | `IsStop`*`bool?`* | Indicates a trend reversal | | `UpperBand`*`double?`* | Upper band only (bearish/red) | | `LowerBand`*`double?`* | Lower band only (bullish/green) | `UpperBand` and `LowerBand` values are provided to differentiate bullish vs bearish trends and to clearly demark trend reversal. `Sar` is the contiguous combination of both upper and lower line data. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Sar` with additional chain-enabled indicators. ```csharp // example var results = bars .ToVolatilityStop(..) .ToEma(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp VolatilityStopList volatilityStopList = new(lookbackPeriods, multiplier); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => volatilityStopList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = volatilityStopList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); VolatilityStopHub observer = barHub.ToVolatilityStopHub(lookbackPeriods, multiplier); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/candlestick-patterns.md description: Chart patterns derived from candle shapes and price formations. --- ## About candlestick patterns Candlestick patterns uniquely share a common `IReadOnlyList` response type. ### `CandleResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Price`*`decimal?`* | Price of the most relevant OHLC candle element when a signal is present | | `Match`*`Match`* | Indicates a [matching signal type](#match) for this candlestick pattern | | `Candle`*`CandleProperties`* | Characteristics of the candle body and wicks | #### `Match` When a candlestick pattern is recognized, it produces a matching ***signal***. In some cases, an intrinsic *confirmation* is also available after the ***signal***. In cases where previous bars were used to identify a pattern, they are indicated as the ***basis*** for the ***signal***. This `enum` can also be referenced as an `int` value. Documentation for each [candlestick pattern](/indicators/candlestick-patterns.md) will indicate whether confirmation and/or ***basis*** information is produced. | enum / value | description | | ----- | ----------- | | `Match.BullConfirmed`200 | Confirmation of a prior bull signal | | `Match.BullSignal`100 | Bullish signal | | `Match.BullBasis`10 | Bars supporting a bullish signal | | `Match.Neutral`1 | Signal for non-directional patterns | | `Match.None`0 | No match | | `Match.BearBasis`-10 | Bars supporting a bearish signal | | `Match.BearSignal`-100 | Bearish signal | | `Match.BearConfirmed`-200 | Confirmation of a prior bear signal | #### `CandleProperties` The `CandleProperties` record class extends the basic `Bar` type with calculated properties. | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Close date | | `Open`*`decimal`* | Open price | | `High`*`decimal`* | High price | | `Low`*`decimal`* | Low price | | `Close`*`decimal`* | Close price | | `Volume`*`decimal`* | Volume | | `Size`*`decimal?`* | `High-Low` | | `Body`*`decimal?`* | `\|Open-Close\|` | | `UpperWick`*`decimal?`* | Upper wick size | | `LowerWick`*`decimal?`* | Lower wick size | | `BodyPct`*`double?`* | `Body/Size` | | `UpperWickPct`*`double?`* | `UpperWick/Size` | | `LowerWickPct`*`double?`* | `LowerWick/Size` | | `IsBullish`*`bool`* | `Close>Open` direction | | `IsBearish`*`bool`* | `Close- Doji is a single-bar candlestick pattern where open and close price are virtually identical, representing market indecision. --- # Doji [Doji](https://en.wikipedia.org/wiki/Doji) is a single-bar candlestick pattern where open and close price are virtually identical, representing market indecision. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/734 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToDoji(maxPriceChangePercent); ``` ## Parameters | param | description | | ----- | ----------- | | `maxPriceChangePercent`*`double`* | Optional. Maximum absolute percent difference in open and close price. Example: 0.3% would be entered as 0.3 (not 0.003). Must be between 0 and 0.5 percent, if specified. Default is 0.1 (0.1%). | ### Historical price bars requirements You must have at least one historical bar; however, more is typically provided since this is a chartable candlestick pattern. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The candlestick pattern is indicated on dates where `Match` is `Match.Neutral`. * `Price` is `Close` price; however, all OHLCV elements are included in `CandleProperties`. * There is no intrinsic basis or confirmation signal provided for this pattern. ### `CandleResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Price`*`decimal?`* | Price of the most relevant OHLC candle element when a signal is present | | `Match`*`Match`* | Indicates a [matching signal type](#match) for this candlestick pattern | | `Candle`*`CandleProperties`* | Characteristics of the candle body and wicks | #### `Match` When a candlestick pattern is recognized, it produces a matching ***signal***. In some cases, an intrinsic *confirmation* is also available after the ***signal***. In cases where previous bars were used to identify a pattern, they are indicated as the ***basis*** for the ***signal***. This `enum` can also be referenced as an `int` value. Documentation for each [candlestick pattern](/indicators/candlestick-patterns.md) will indicate whether confirmation and/or ***basis*** information is produced. | enum / value | description | | ----- | ----------- | | `Match.BullConfirmed`200 | Confirmation of a prior bull signal | | `Match.BullSignal`100 | Bullish signal | | `Match.BullBasis`10 | Bars supporting a bullish signal | | `Match.Neutral`1 | Signal for non-directional patterns | | `Match.None`0 | No match | | `Match.BearBasis`-10 | Bars supporting a bearish signal | | `Match.BearSignal`-100 | Bearish signal | | `Match.BearConfirmed`-200 | Confirmation of a prior bear signal | #### `CandleProperties` The `CandleProperties` record class extends the basic `Bar` type with calculated properties. | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Close date | | `Open`*`decimal`* | Open price | | `High`*`decimal`* | High price | | `Low`*`decimal`* | Low price | | `Close`*`decimal`* | Close price | | `Volume`*`decimal`* | Volume | | `Size`*`decimal?`* | `High-Low` | | `Body`*`decimal?`* | `\|Open-Close\|` | | `UpperWick`*`decimal?`* | Upper wick size | | `LowerWick`*`decimal?`* | Lower wick size | | `BodyPct`*`double?`* | `Body/Size` | | `UpperWickPct`*`double?`* | `UpperWick/Size` | | `LowerWickPct`*`double?`* | `LowerWick/Size` | | `IsBullish`*`bool`* | `Close>Open` direction | | `IsBearish`*`bool`* | `Close` when you need incremental calculations without a hub: ```csharp DojiList dojiList = new(maxPriceChangePercent); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => dojiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = dojiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); DojiHub observer = barHub.ToDojiHub(maxPriceChangePercent); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/marubozu.md description: >- Marubozu is a single-bar candlestick pattern that has no wicks, representing consistent directional movement. --- # Marubozu [Marubozu](https://en.wikipedia.org/wiki/Marubozu) is a single-bar candlestick pattern that has no wicks, representing consistent directional movement. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/512 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToMarubozu(minBodyPercent); ``` ## Parameters | param | description | | ----- | ----------- | | `minBodyPercent`*`double`* | Optional. Minimum body size as a percent of total candle size. Example: 85% would be entered as 85 (not 0.85). Must be between 80 and 100, if specified. Default is 95 (95%). | ### Historical price bars requirements You must have at least one historical bar; however, more is typically provided since this is a chartable candlestick pattern. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The candlestick pattern is indicated on dates where `Match` is `Match.BullSignal` or `Match.BearSignal`. * `Price` is `Close` price; however, all OHLCV elements are included in `CandleProperties`. * There is no intrinsic basis or confirmation signal provided for this pattern. ### `CandleResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Price`*`decimal?`* | Price of the most relevant OHLC candle element when a signal is present | | `Match`*`Match`* | Indicates a [matching signal type](#match) for this candlestick pattern | | `Candle`*`CandleProperties`* | Characteristics of the candle body and wicks | #### `Match` When a candlestick pattern is recognized, it produces a matching ***signal***. In some cases, an intrinsic *confirmation* is also available after the ***signal***. In cases where previous bars were used to identify a pattern, they are indicated as the ***basis*** for the ***signal***. This `enum` can also be referenced as an `int` value. Documentation for each [candlestick pattern](/indicators/candlestick-patterns.md) will indicate whether confirmation and/or ***basis*** information is produced. | enum / value | description | | ----- | ----------- | | `Match.BullConfirmed`200 | Confirmation of a prior bull signal | | `Match.BullSignal`100 | Bullish signal | | `Match.BullBasis`10 | Bars supporting a bullish signal | | `Match.Neutral`1 | Signal for non-directional patterns | | `Match.None`0 | No match | | `Match.BearBasis`-10 | Bars supporting a bearish signal | | `Match.BearSignal`-100 | Bearish signal | | `Match.BearConfirmed`-200 | Confirmation of a prior bear signal | #### `CandleProperties` The `CandleProperties` record class extends the basic `Bar` type with calculated properties. | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Close date | | `Open`*`decimal`* | Open price | | `High`*`decimal`* | High price | | `Low`*`decimal`* | Low price | | `Close`*`decimal`* | Close price | | `Volume`*`decimal`* | Volume | | `Size`*`decimal?`* | `High-Low` | | `Body`*`decimal?`* | `\|Open-Close\|` | | `UpperWick`*`decimal?`* | Upper wick size | | `LowerWick`*`decimal?`* | Lower wick size | | `BodyPct`*`double?`* | `Body/Size` | | `UpperWickPct`*`double?`* | `UpperWick/Size` | | `LowerWickPct`*`double?`* | `LowerWick/Size` | | `IsBullish`*`bool`* | `Close>Open` direction | | `IsBearish`*`bool`* | `Close` when you need incremental calculations without a hub: ```csharp MarubozuList marubozuList = new(minBodyPercent); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => marubozuList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = marubozuList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); MarubozuHub observer = barHub.ToMarubozuHub(minBodyPercent); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/pivots.md description: >- Pivots is an extended customizable version of Williams Fractal that includes identification of Higher High, Lower Low, Higher Low, and Lower Low trends between pivots in a lookback window. --- # Pivots Pivots is an extended customizable version of [Williams Fractal](/indicators/fractal.md) that includes identification of Higher High, Lower Low, Higher Low, and Lower Low trends between pivots in a lookback window. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/436 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToPivots(leftSpan, rightSpan, maxTrendPeriods, endType); ``` ## Parameters | param | description | | ----- | ----------- | | `leftSpan`*`int`* | Left evaluation window span width (`L`). Must be at least 2. Default is 2. | | `rightSpan`*`int`* | Right evaluation window span width (`R`). Must be at least 2. Default is 2. | | `maxTrendPeriods`*`int`* | Maximum lookback periods (`N`) for drawing trend lines between pivot points. When pivot points are further apart than this value, the trend line tracking resets. Must be greater than `leftSpan`. Default is 20. | | `endType`*`EndType`* | Determines whether `Close` or `High/Low` are used to find end points. Default is `EndType.HighLow`. | The total evaluation window size is `L+R+1`. > [!NOTE] > The `maxTrendPeriods` parameter controls the lookback window for trend line calculations, not the number of results returned. ### Historical price bars requirements You must have at least `L+R+1` periods of `bars` to cover the warmup periods; however, more is typically provided since this is a chartable candlestick pattern. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `EndType` enum options | enum / value | description | | ----- | ----------- | | `EndType.Close`0 | Threshold measured from bar `Close` price | | `EndType.HighLow`1 | Threshold measured from bar `High` and `Low` price | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `L` and last `R` periods in `bars` are unable to be calculated since there's not enough prior/following data. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > This price pattern looks forward and backward in the historical price bars so it will never identify a pivot in the last `R` periods of `bars`. Pivots are retroactively identified. ### `PivotsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `HighPoint`*`decimal?`* | Value indicates a **high** point; otherwise `null` is returned. | | `LowPoint`*`decimal?`* | Value indicates a **low** point; otherwise `null` is returned. | | `HighLine`*`decimal?`* | Drawn line between two high points in the `maxTrendPeriods` | | `LowLine`*`decimal?`* | Drawn line between two low points in the `maxTrendPeriods` | | `HighTrend`*`PivotTrend?`* | Enum that represents higher high or lower high. See [PivotTrend values](#pivottrend-values) below. | | `LowTrend`*`PivotTrend?`* | Enum that represents higher low or lower low. See [PivotTrend values](#pivottrend-values) below. | #### PivotTrend values **`PivotTrend.Hh`** - Higher high **`PivotTrend.Lh`** - Lower high **`PivotTrend.Hl`** - Higher low **`PivotTrend.Ll`** - Lower low #### Filtering results Since this method returns one result per input bar (with `null` values where no pivot exists), you'll often want to filter results for specific use cases: ```csharp // get only records with pivot points var pivotsOnly = results.Condense(); // get only records with trend lines var trendsOnly = results .Where(x => x.HighTrend != null || x.LowTrend != null); // get only recent N periods var recentPivots = results.TakeLast(period); // get only high pivot points with Higher High trend var higherHighs = results .Where(x => x.HighPoint != null && x.HighTrend == PivotTrend.Hh); // combine filters: recent periods with trends var recentTrends = results .Where(x => x.HighTrend != null || x.LowTrend != null) .TakeLast(period); ``` ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp PivotsList pivotsList = new(leftSpan, rightSpan, maxTrendPeriods, endType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => pivotsList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = pivotsList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); PivotsHub observer = barHub.ToPivotsHub(leftSpan, rightSpan, maxTrendPeriods, endType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/fractal.md description: >- Created by Bill Williams, Fractal is a retrospective price pattern that identifies a central high or low point chevron. --- # Williams Fractal Created by Bill Williams in *Trading Chaos* (1995), [Fractal](https://www.investopedia.com/terms/f/fractal.asp) is a retrospective price pattern that identifies a central high or low point chevron. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/255 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToFractal(windowSpan); ``` ## Parameters | param | description | | ----- | ----------- | | `windowSpan`*`int`* | Evaluation window span width (`S`). Must be at least 2. Default is 2. | | `endType`*`EndType`* | Determines whether `Close` or `High/Low` are used to find end points. Default is `EndType.HighLow`. | The total evaluation window size is `2ร—S+1`, representing `ยฑS` from the evaluation date. An overload takes `leftSpan` and `rightSpan` in place of `windowSpan` to evaluate an asymmetric window: `bars.ToFractal(leftSpan, rightSpan, endType)`. Each span must be at least 2, and the total evaluation window size is `leftSpan+rightSpan+1`. ### Historical price bars requirements You must have at least `2ร—S+1` periods of `bars` (`leftSpan+rightSpan+1` for the overload) to cover the warmup periods; however, more is typically provided since this is a chartable candlestick pattern. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `EndType` enum options | enum / value | description | | ----- | ----------- | | `EndType.Close`0 | Threshold measured from bar `Close` price | | `EndType.HighLow`1 | Threshold measured from bar `High` and `Low` price | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first and last `S` periods in `bars` are unable to be calculated since there's not enough prior/following data. * Per Williams' definition the middle bar must be strictly beyond both wings, so when two or more bars tie for the extreme value, no fractal is identified. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > This price pattern uses future bars and will never identify a `fractal` in the last `S` periods of `bars`. Fractals are retroactively identified. ### `FractalResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `FractalBear`*`decimal?`* | Value indicates a **high** point; otherwise `null` is returned. | | `FractalBull`*`decimal?`* | Value indicates a **low** point; otherwise `null` is returned. | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator is not chain-enabled and must be generated from `bars`. It **cannot** be used for further processing by other chain-enabled indicators. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp FractalList fractalList = new(windowSpan); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => fractalList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = fractalList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); FractalHub observer = barHub.ToFractalHub(windowSpan); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/volume-based.md description: >- Volume-weighted indicators that incorporate trading activity into price analysis. --- --- --- url: /indicators/adl.md description: >- Created by Marc Chaikin, the Accumulation / Distribution Line is a rolling accumulation of Chaikin Money Flow Volume. It can be a leading momentum indicator for financial market price movements. --- # Accumulation / Distribution Line (ADL) Created by Marc Chaikin, the [Accumulation/Distribution Line/Index](https://en.wikipedia.org/wiki/Accumulation/distribution_index) is a rolling accumulation of Chaikin Money Flow Volume. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/271 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAdl(); ``` ## Historical price bars requirements You must have at least two historical price bars to cover the warmup periods; however, since this is a trendline, more is recommended. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. ### `AdlResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `MoneyFlowMultiplier`*`double?`* | Money Flow Multiplier | | `MoneyFlowVolume`*`double?`* | Money Flow Volume | | `Adl`*`double`* | Accumulation Distribution Line (ADL) | > [!WARNING] > **๐Ÿšฉ Relative values only** > > Absolute values in ADL and MFV are somewhat meaningless. Use with caution. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Adl` with additional chain-enabled indicators. ```csharp // example var results = bars .ToAdl() .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AdlList adlList = new(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => adlList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = adlList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AdlHub observer = barHub.ToAdlHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/cmf.md description: >- Created by Marc Chaikin, Chaikin Money Flow is the simple moving average of the directional Money Flow Volume. --- # Chaikin Money Flow (CMF) Created by Marc Chaikin, [Chaikin Money Flow](https://en.wikipedia.org/wiki/Chaikin_Analytics#Chaikin_Money_Flow) is the simple moving average of the directional Money Flow Volume. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/261 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToCmf(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. Default is 20. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `CmfResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `MoneyFlowMultiplier`*`double?`* | Money Flow Multiplier | | `MoneyFlowVolume`*`double?`* | Money Flow Volume | | `Cmf`*`double?`* | Chaikin Money Flow = SMA of MFV | > [!WARNING] > **๐Ÿšฉ Relative values only** > > Absolute values in MFV and CMF are somewhat meaningless. Use with caution. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Cmf` with additional chain-enabled indicators. ```csharp // example var results = bars .ToCmf(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp CmfList cmfList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => cmfList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = cmfList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); CmfHub observer = barHub.ToCmfHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/chaikin-osc.md description: >- Created by Marc Chaikin, the Chaikin Oscillator is the difference between fast and slow Exponential Moving Averages (EMA) of an Accumulation / Distribution Line (ADL). --- # Chaikin Oscillator Created by Marc Chaikin, the [Chaikin Oscillator](https://en.wikipedia.org/wiki/Chaikin_Analytics#Chaikin_Oscillator) is the difference between fast and slow Exponential Moving Averages (EMA) of the [Accumulation/Distribution Line](/indicators/adl.md) (ADL). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/264 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToChaikinOsc(fastPeriods, slowPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `fastPeriods`*`int`* | Number of periods (`F`) in the ADL fast EMA. Must be greater than 0 and smaller than `S`. Default is 3. | | `slowPeriods`*`int`* | Number of periods (`S`) in the ADL slow EMA. Must be greater than `F`. Default is 10. | ### Historical price bars requirements You must have at least `2ร—S` or `S+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `S+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `S-1` periods will have `null` values for `Oscillator` since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `S+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `ChaikinOscResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `MoneyFlowMultiplier`*`double?`* | Money Flow Multiplier | | `MoneyFlowVolume`*`double?`* | Money Flow Volume | | `Adl`*`double?`* | Accumulation Distribution Line (ADL) | | `Oscillator`*`double?`* | Chaikin Oscillator | | `FastEma`*`double?`* | Fast EMA of the ADL | | `SlowEma`*`double?`* | Slow EMA of the ADL | > [!WARNING] > **๐Ÿšฉ Relative values only** > > Absolute values in MFV, ADL, FastEma, SlowEma, and Oscillator are somewhat meaningless. Use with caution. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Oscillator` with additional chain-enabled indicators. ```csharp // example var results = bars .ToChaikinOsc(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ChaikinOscList chaikinOscList = new(fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => chaikinOscList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = chaikinOscList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ChaikinOscHub observer = barHub.ToChaikinOscHub(fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/force-index.md description: >- Created by Alexander Elder, the Force Index depicts volume-based buying and selling pressure based on the change in price. --- # Force Index Created by Alexander Elder, the [Force Index](https://en.wikipedia.org/wiki/Force_index) depicts volume-based buying and selling pressure based on the change in price. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/382 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToForceIndex(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Lookback window (`N`) for the EMA of Force Index. Must be greater than 0 and is commonly 2 or 13 (shorter/longer view). Default is 2. | ### Historical price bars requirements You must have at least `N+100` for `2ร—N` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique for EMA, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will be `null` since they cannot be calculated. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `ForceIndexResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `ForceIndex`*`double?`* | Force Index | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `ForceIndex` with additional chain-enabled indicators. ```csharp // example var results = bars .ToForceIndex(..) .ToEma(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ForceIndexList forceIndexList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => forceIndexList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = forceIndexList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ForceIndexHub observer = barHub.ToForceIndexHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/kvo.md description: >- Created by Stephen Klinger, the Klinger Volume Oscillator depicts volume-based trend reversal and divergence between short and long-term money flow. --- # Klinger Volume Oscillator Created by Stephen Klinger, the [Klinger Volume Oscillator](https://www.investopedia.com/terms/k/klingeroscillator.asp) depicts volume-based trend reversal and divergence between short and long-term money flow. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/446 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToKvo(fastPeriods, slowPeriods, signalPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `fastPeriods`*`int`* | Number of lookback periods (`F`) for the short-term EMA. Must be greater than 2. Default is 34. | | `slowPeriods`*`int`* | Number of lookback periods (`L`) for the long-term EMA. Must be greater than `F`. Default is 55. | | `signalPeriods`*`int`* | Number of lookback periods for the signal line. Must be greater than 0. Default is 13. | ### Historical price bars requirements You must have at least `L+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `L+150` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `L+1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `L+150` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `KvoResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Oscillator`*`double?`* | Klinger Oscillator | | `Signal`*`double?`* | EMA of Klinger Oscillator (signal line) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Kvo` with additional chain-enabled indicators. ```csharp // example var results = bars .ToKvo(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp KvoList kvoList = new(34, 55, 13); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => kvoList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = kvoList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); KvoHub observer = barHub.ToKvoHub(34, 55, 13); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/mfi.md description: >- Created by Quong and Soudack, the Money Flow Index is a price-volume oscillator that shows buying and selling momentum. --- # Money Flow Index (MFI) Created by Quong and Soudack, the [Money Flow Index](https://en.wikipedia.org/wiki/Money_flow_index) is a price-volume oscillator that shows buying and selling momentum. Values outside of the 80 / 20 thresholds are considered overbought / oversold. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/247 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToMfi(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback period. Must be greater than 1. Default is 14. | ### Historical price bars requirements You must have at least `N+1` historical price bars to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` MFI values since they cannot be calculated. ### `MfiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Mfi`*`double?`* | Money Flow Index | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp MfiList mfiList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => mfiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = mfiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); MfiHub observer = barHub.ToMfiHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. ## Chaining Results can be further processed on `Mfi` with additional chain-enabled indicators. ```csharp // example var results = bars .ToMfi(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. --- --- url: /indicators/obv.md description: >- Popularized by Joseph Granville, On-balance Volume is a rolling accumulation of volume based on Close price direction. --- # On-Balance Volume (OBV) Popularized by Joseph Granville, [On-balance Volume](https://en.wikipedia.org/wiki/On-balance_volume) is a rolling accumulation of volume based on Close price direction. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/246 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToObv(); ``` ## Historical price bars requirements You must have at least two historical price bars to cover the warmup periods; however, since this is a trendline, more is recommended. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first period OBV will have a `0` value since there's not enough data to calculate. ### `ObvResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Obv`*`double`* | On-balance Volume | > [!WARNING] > **๐Ÿšฉ Relative values only** > > Absolute values in OBV are somewhat meaningless. Use with caution. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Obv` with additional chain-enabled indicators. ```csharp // example var results = bars .ToObv(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ObvList obvList = new(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => obvList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = obvList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ObvHub observer = barHub.ToObvHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/pvo.md description: >- The Percentage Volume Oscillator is a simple oscillator view of the rate of change between two converging / diverging exponential moving averages of Volume. It is presented similarly to MACD. --- # Percentage Volume Oscillator (PVO) The [Percentage Volume Oscillator](https://www.google.com/search?q=Percentage+Volume+Oscillator+\(PVO\)) is a simple oscillator view of the rate of change between two converging / diverging exponential moving averages of Volume. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/305 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToPvo(fastPeriods, slowPeriods, signalPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `fastPeriods`*`int`* | Number of periods (`F`) for the faster moving average. Must be greater than 0. Default is 12. | | `slowPeriods`*`int`* | Number of periods (`S`) for the slower moving average. Must be greater than `fastPeriods`. Default is 26. | | `signalPeriods`*`int`* | Number of periods (`P`) for the moving average of PVO. Must be greater than 0. Default is 9. | ### Historical price bars requirements You must have at least `2ร—(S+P)` or `S+P+100` worth of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `S+P+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `S-1` slow periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `S+P+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `PvoResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Pvo`*`double?`* | Normalized difference between two Volume moving averages | | `Signal`*`double?`* | Moving average of the `Pvo` line | | `Histogram`*`double?`* | Gap between the `Pvo` and `Signal` line | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Pvo` with additional chain-enabled indicators. ```csharp // example var results = bars .ToPvo(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp PvoList pvoList = new(fastPeriods, slowPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => pvoList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = pvoList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); PvoHub observer = barHub.ToPvoHub(fastPeriods, slowPeriods, signalPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/vwap.md description: >- The Volume Weighted Average Price is a volume weighted average of price, typically used on intraday data. Trading above or below the VWAP line can assist in finding favorable short-term trading windows. --- # Volume Weighted Average Price (VWAP) The [Volume Weighted Average Price](https://en.wikipedia.org/wiki/Volume-weighted_average_price) is a Volume weighted average of price, typically used on intraday data. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/310 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToVwap(); // usage with optional anchored start date IReadOnlyList results = bars.ToVwap(startDate); ``` ## Parameters | param | description | | ----- | ----------- | | `startDate`*`DateTime`* | Optional. The anchor date used to start the VWAP accumulation. The earliest date in `bars` is used when not provided. | ### Historical price bars requirements You must have at least one historical bar to calculate; however, more is often needed to be useful. Historical price bars are typically provided for a single day using minute-based intraday periods. Since this is an accumulated weighted average price, different start dates will produce different results. The accumulation starts at the first period in the provided `bars`, unless it is specified in the optional `startDate` parameter. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first period or the `startDate` will have a `Vwap = Close` value since it is the initial starting point. * `Vwap` values before `startDate`, if specified, will be `null`. ### `VwapResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Vwap`*`double?`* | Volume Weighted Average Price | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Vwap` with additional chain-enabled indicators. ```csharp // example var results = bars .ToVwap(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp VwapList vwapList = new(startDate); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => vwapList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = vwapList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); VwapHub observer = barHub.ToVwapHub(startDate); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/vwma.md description: >- Volume Weighted Moving Average is the volume adjusted average price over a lookback window. --- # Volume Weighted Moving Average (VWMA) Volume Weighted Moving Average is the volume adjusted average price over a lookback window. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/657 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToVwma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values for `Vwma` since there's not enough data to calculate. ### `VwmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Vwma`*`double?`* | Volume Weighted Moving Average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Vwma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToVwma(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp VwmaList vwmaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => vwmaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = vwmaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); VwmaHub observer = barHub.ToVwmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` ### Additional buffering methods For volume-weighted calculations, VWMA also supports direct price and volume input: ```csharp VwmaList vwmaList = new(lookbackPeriods); // Add individual price and volume data vwmaList.Add(DateTime.Now, price: 100.50, volume: 1000); ``` **Note:** VWMA requires both price and volume data, so it only supports methods that accept `IBar` or direct price/volume parameters. See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/moving-averages.md description: Price smoothing indicators that reduce noise and reveal underlying trends. --- See also: [Moving Average Envelopes](/indicators/ma-envelopes.md) --- --- url: /indicators/alma.md description: >- Created by Arnaud Legoux and Dimitrios Kouzis-Loukas, ALMA is a normal Gaussian distribution weighted moving average of price. --- # Arnaud Legoux Moving Average (ALMA) Created by Arnaud Legoux and Dimitrios Kouzis-Loukas, [ALMA](https://github.com/facioquo/stock-indicators-dotnet/files/5654531/ALMA-Arnaud-Legoux-Moving-Average.pdf) is a normal Gaussian distribution weighted moving average of price. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/209 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAlma(lookbackPeriods, offset, sigma); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 1, but is typically in the 5-20 range. Default is 9. | | `offset`*`double`* | Adjusts smoothness versus responsiveness on a scale from 0 to 1; where 1 is max responsiveness. Default is 0.85. | | `sigma`*`double`* | Defines the width of the Gaussian [normal distribution](https://en.wikipedia.org/wiki/Normal_distribution). Must be greater than 0. Default is 6. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `AlmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Alma`*`double?`* | Arnaud Legoux Moving Average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToAlma(..); ``` Results can be further processed on `Alma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToAlma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AlmaList almaList = new(lookbackPeriods, offset, sigma); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => almaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = almaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AlmaHub observer = barHub.ToAlmaHub(lookbackPeriods, offset, sigma); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/dema.md description: >- Created by Patrick G. Mulloy, the Double Exponential Moving Average is a faster smoothed EMA of financial market prices. --- # Double Exponential Moving Average (DEMA) Created by Patrick G. Mulloy, the [Double exponential moving average](https://en.wikipedia.org/wiki/Double_exponential_moving_average) is a faster smoothed EMA of the price over a lookback window. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/807 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToDema(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `3ร—N` or `2ร—N+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `2ร—N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `2ร—N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `DemaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Dema`*`double?`* | Double exponential moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToDema(..); ``` Results can be further processed on `Dema` with additional chain-enabled indicators. ```csharp // example var results = bars .ToDema(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp DemaList demaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => demaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = demaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); DemaHub observer = barHub.ToDemaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/epma.md description: >- Endpoint Moving Average (EPMA), also known as Least Squares Moving Average (LSMA), plots the projected last point of a defined retrospective linear regression. --- # Endpoint Moving Average (EPMA) Endpoint Moving Average (EPMA), also known as Least Squares Moving Average (LSMA), plots the projected last point of a defined retrospective linear regression. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/371 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToEpma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 1. Default is 10. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `EpmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Epma`*`double?`* | Endpoint moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToEpma(..); ``` Results can be further processed on `Epma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToEpma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp EpmaList epmaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => epmaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = epmaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); EpmaHub observer = barHub.ToEpmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/ema.md description: >- Exponentially [weighted] Moving Average is a rolling moving average that puts more weight on current price. --- # Exponential Moving Average (EMA) [Exponentially weighted moving average](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) is a rolling moving average that puts more weight on current price. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/256 "Community discussion about this indicator") ```csharp // C# usage syntax (with Close price) IReadOnlyList results = bars.ToEma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. | ### Historical price bars requirements You must have at least `2ร—N` or `N+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `EmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Ema`*`double?`* | Exponential moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToEma(..); ``` Results can be further processed on `Ema` with additional chain-enabled indicators. ```csharp // example var results = bars .ToEma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp EmaList emaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => emaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = emaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); EmaHub observer = barHub.ToEmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/ht-trendline.md description: >- Created by John Ehlers, the Hilbert Transform Instantaneous Trendline is a 5-period trendline of high/low price that that uses classic electrical radio-frequency signal processing algorithms reduce noise. --- # Hilbert Transform Instantaneous Trendline Created by John Ehlers, the Hilbert Transform Instantaneous Trendline is a 5-period trendline of high/low price that that uses classic electrical radio-frequency signal processing algorithms reduce noise. Dominant Cycle Periods information is also provided. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/363 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToHtTrendline(); ``` ## Historical price bars requirements You must have at least `100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `6` periods will have `null` values for `SmoothPrice` since there's not enough data to calculate. * The first `7` periods will have `null` values for `DcPeriods` since there is not enough data to calculate; and are generally unreliable for the first ~25 periods. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `HtlResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `DcPeriods`*`int?`* | Dominant cycle periods (smoothed) | | `Trendline`*`double?`* | HT Trendline | | `SmoothPrice`*`double?`* | Weighted moving average of `(H+L)/2` price | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Streaming ### Real-time streaming Use the streaming hub for real-time incremental calculations: ```csharp BarHub barHub = new(); HtTrendlineHub observer = barHub.ToHtTrendlineHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` ### Buffer-style streaming Use the buffer-style `List` when you need incremental calculations: ```csharp HtTrendlineList htlList = new(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => htlList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = htlList; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HLC3) .ToHtTrendline(..); ``` Results can be further processed on `Trendline` with additional chain-enabled indicators. ```csharp // example var results = bars .ToHtTrendline(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. --- --- url: /indicators/hma.md description: >- Created by Alan Hull, the Hull Moving Average is a modified weighted average of price that reduces lag. --- # Hull Moving Average (HMA) Created by Alan Hull, the [Hull Moving Average](https://alanhull.com/hull-moving-average) is a modified weighted average of price that reduces lag. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/252 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToHma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 1. Default is 14. | ### Historical price bars requirements You must have at least `N+(integer of SQRT(N))-1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N+(integer of SQRT(N))-1` periods will have `null` values since there's not enough data to calculate. ### `HmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Hma`*`double?`* | Hull moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToHma(..); ``` Results can be further processed on `Hma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToHma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp HmaList hmaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => hmaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = hmaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); HmaHub observer = barHub.ToHmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/kama.md description: >- Created by Perry Kaufman, KAMA is an volatility adaptive (adjusted) moving average of price over configurable lookback periods. --- # Kaufman's Adaptive Moving Average (KAMA) Created by Perry Kaufman, [KAMA](https://www.google.com/search?q=Kaufman+Adaptive+Moving+Average+\(KAMA\)) is an volatility adaptive moving average of price over configurable lookback periods. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/210 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToKama(erPeriods, fastPeriods, slowPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `erPeriods`*`int`* | Number of Efficiency Ratio (volatility) periods (`E`). Must be greater than 0. Default is 10. | | `fastPeriods`*`int`* | Number of Fast EMA periods. Must be greater than 0. Default is 2. | | `slowPeriods`*`int`* | Number of Slow EMA periods. Must be greater than `fastPeriods`. Default is 30. | ### Historical price bars requirements You must have at least `6ร—E` or `E+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `10ร—E` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `E-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `10ร—E` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `KamaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Er`*`double?`* | Efficiency Ratio is the fractal efficiency of price changes | | `Kama`*`double?`* | Kaufman's adaptive moving average | More about Efficiency Ratio: ER fluctuates between 0 and 1, but these extremes are the exception, not the norm. ER would be 1 if prices moved up or down consistently over the `erPeriods` window. ER would be zero if prices are unchanged over the `erPeriods` window. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToKama(..); ``` Results can be further processed on `Kama` with additional chain-enabled indicators. ```csharp // example var results = bars .ToKama(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp KamaList kamaList = new(erPeriods, fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => kamaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = kamaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); KamaHub observer = barHub.ToKamaHub(erPeriods, fastPeriods, slowPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/mama.md description: >- Created by John Ehlers, the MAMA indicator is a 5-period adaptive moving average of high/low price that uses classic electrical radio-frequency signal processing algorithms to reduce noise. --- # MESA Adaptive Moving Average (MAMA) Created by John Ehlers, the [MAMA](https://mesasoftware.com/papers/MAMA.pdf) indicator is a 5-period adaptive moving average of high/low price that uses classic electrical radio-frequency signal processing algorithms to reduce noise. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/211 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToMama(fastLimit, slowLimit); ``` ## Parameters | param | description | | ----- | ----------- | | `fastLimit`*`double`* | Fast limit threshold. Must be greater than `slowLimit` and less than 1. Default is 0.5. | | `slowLimit`*`double`* | Slow limit threshold. Must be greater than 0. Default is 0.05. | ### Historical price bars requirements You must have at least `50` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `5` periods will have `null` values for `Mama` since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `50` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `MamaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Mama`*`double?`* | MESA adaptive moving average (MAMA) | | `Fama`*`double?`* | Following adaptive moving average (FAMA) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToMama(..); ``` Results can be further processed on `Mama` with additional chain-enabled indicators. ```csharp // example var results = bars .ToMama(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp MamaList mamaList = new(fastLimit, slowLimit); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => mamaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = mamaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); MamaHub observer = barHub.ToMamaHub(fastLimit, slowLimit); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/dynamic.md description: >- Created by John R. McGinley, the McGinley Dynamic is a more responsive variant of exponential moving average. --- # McGinley Dynamic Created by John R. McGinley, the [McGinley Dynamic](https://www.investopedia.com/terms/m/mcginley-dynamic.asp) is a more responsive variant of exponential moving average. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/866 "Community discussion about this indicator") ```csharp // C# usage syntax (with Close price) IReadOnlyList results = bars.ToDynamic(lookbackPeriods, kFactor); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. | | `kFactor`*`double`* | Optional. Range adjustment factor (`K`). Must be greater than 0. Default is 0.6 | ### Historical price bars requirements You must have at least `2` periods of `bars`, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `4ร—N` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### Pro tips > Use a `kFactor` value of `1` if you do not want to adjust the `N` value. > > McGinley suggests that using a `K` value of 60% (0.6) allows you to use a `N` equivalent to other moving averages. For example, DYNAMIC(20,0.6) is comparable to EMA(20); conversely, DYNAMIC(20,1) uses the raw 1:1 `N` value and is not equivalent. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first period will have a `null` value since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `4ร—N` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `DynamicResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Dynamic`*`double?`* | McGinley Dynamic | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToDynamic(..); ``` Results can be further processed on `Dynamic` with additional chain-enabled indicators. ```csharp // example var results = bars .ToDynamic(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp DynamicList dynamicList = new(lookbackPeriods, kFactor); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => dynamicList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = dynamicList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); DynamicHub observer = barHub.ToDynamicHub(lookbackPeriods, kFactor); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/smma.md description: >- Smoothed Moving Average (SMMA), Modified Moving Average (MMA), Running Moving Average (RMA) are all the same simple rolling moving average of financial market prices. New values are calculated based on the last known value only, making a more efficient but less accurate method for computing an average. --- # Smoothed Moving Average (SMMA) [Smoothed Moving Average](https://en.wikipedia.org/wiki/Moving_average) is the average of price over a lookback window using a smoothing method. SMMA is also known as modified moving average (MMA) and running moving average (RMA). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/375 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToSmma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. | ### Historical price bars requirements You must have at least `2ร—N` or `N+100` periods of `bars`, whichever is more, to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `SmmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Smma`*`double?`* | Smoothed moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToSmma(..); ``` Results can be further processed on `Smma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToSmma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp SmmaList smmaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => smmaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = smmaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); SmmaHub observer = barHub.ToSmmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/sma.md description: >- Simple moving average. Extended to include mean absolute deviation, mean square error, and mean absolute percentage error --- # Simple Moving Average (SMA) [Simple Moving Average](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average) is the average price over a lookback window. An [extended SMA analysis](/indicators/sma-analysis.md) option includes mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/240 "Community discussion about this indicator") ```csharp // C# usage syntax (with Close price) IReadOnlyList results = bars.ToSma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback window. Must be greater than 0. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `SmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sma`*`double?`* | Simple moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Analysis This indicator has an extended version with more analysis. See [SMA with extended analysis](/indicators/sma-analysis.md) for the full documentation including streaming support. ```csharp // C# usage syntax IReadOnlyList analysis = bars.ToSmaAnalysis(lookbackPeriods); ``` ### `SmaAnalysisResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sma`*`double?`* | Simple moving average | | `Mad`*`double?`* | Mean absolute deviation | | `Mse`*`double?`* | Mean square error | | `Mape`*`double?`* | Mean absolute percentage error | ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.Volume) .ToSma(..); ``` Results can be further processed on `Sma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToSma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp SmaList smaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => smaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = smaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); SmaHub observer = barHub.ToSmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/t3.md description: >- Created by Tim Tillson, the T3 indicator is a smooth moving average that reduces both lag and overshooting. --- # Tillson T3 Moving Average Created by Tim Tillson, the [T3](https://forex-station.com/download/file.php?id=3407929) indicator is a smooth moving average that reduces both lag and overshooting. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/332 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToT3(lookbackPeriods, volumeFactor); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the EMA smoothing. Must be greater than 0 and is usually less than 63. Default is 5. | | `volumeFactor`*`double`* | Size of the Volume Factor. Must be greater than 0 and is usually less than 2. Default is 0.7 | ### Historical price bars requirements You must have at least `6ร—(N-1)+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `6ร—(N-1)+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `6ร—(N-1)+250` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### T3Result | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `T3`*`double?`* | T3 Moving Average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToT3(..); ``` Results can be further processed on `T3` with additional chain-enabled indicators. ```csharp // example var results = bars .ToT3(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp T3List t3List = new(lookbackPeriods, volumeFactor); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => t3List.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = t3List; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); T3Hub observer = barHub.ToT3Hub(lookbackPeriods, volumeFactor); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/tema.md description: >- Created by Patrick G. Mulloy, the Triple Exponential Moving Average is a faster multi-smoothed moving average. TEMA is often confused with the alternative TRIX oscillator. --- # Triple Exponential Moving Average (TEMA) Created by Patrick G. Mulloy, the [Triple exponential moving average](https://en.wikipedia.org/wiki/Triple_exponential_moving_average) is a faster multi-smoothed EMA of the price over a lookback window. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/808 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToTema(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the moving average. Must be greater than 0. Default is 20. | ### Historical price bars requirements You must have at least `N` periods of `bars` to produce any TEMA values. However, due to the nature of the smoothing technique, we recommend you use at least `3ร—N+250` data points prior to the intended usage date for better precision. See [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) guidance for more information. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. Also note that we are using the proper [weighted variant](https://en.wikipedia.org/wiki/Triple_exponential_moving_average) for TEMA. If you prefer the unweighted raw 3 EMAs value, please use the `Ema3` output from the [TRIX](/indicators/trix.md) oscillator instead. **Example for TEMA(20)**: ```text Period 1-19: null values (incalculable) Period 20: first TEMA value (may have convergence issues) Period 160+: fully converged, reliable values ``` > โ“˜ **Incalculable periods**: The first `N-1` periods will have `null` values since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `3ร—N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. Use the `.RemoveWarmupPeriods()` method to remove these potentially unreliable values. ### `TemaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Tema`*`double?`* | Triple exponential moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToTema(..); ``` Results can be further processed on `Tema` with additional chain-enabled indicators. ```csharp // example var results = bars .ToTema(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp TemaList temaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => temaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = temaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); TemaHub observer = barHub.ToTemaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/wma.md description: >- Weighted Moving Average is the linear weighted average of financial market prices over a lookback window. This also called Linear Weighted Moving Average (LWMA). --- # Weighted Moving Average (WMA) [Weighted Moving Average](https://en.wikipedia.org/wiki/Moving_average#Weighted_moving_average) is the linear weighted average of price over a lookback window. This also called Linear Weighted Moving Average (LWMA). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/227 "Community discussion about this indicator") ```csharp // C# usage syntax (with Close price) IReadOnlyList results = bars.ToWma(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback window. Must be greater than 0. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `WmaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Wma`*`double?`* | Weighted moving average | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToWma(..); ``` Results can be further processed on `Wma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToWma(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp WmaList wmaList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => wmaList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = wmaList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); WmaHub observer = barHub.ToWmaHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/price-transforms.md description: Indicators that convert or reshape price data into alternative chart forms. --- --- --- url: /indicators/bar-part.md description: >- Basic price bar transforms (e.g. HL2, OHL3, etc.) and isolation of individual price bar candle parts from a full OHLCV bar. --- # Basic price bar transforms Returns a reusable (chainable) basic bar transform (e.g. HL2, OHL3, etc.) by isolating a single component part value or calculated value from the full OHLCV bar candle parts. ```csharp // C# usage syntax IReadOnlyList results = bars.Use(candlePart); // alternate syntax IReadOnlyList results = bars.ToBarPart(candlePart); ``` ## Parameters | param | description | | ----- | ----------- | | `candlePart`*`CandlePart`* | The [OHLCV](/guide/getting-started.md#historical-bars) element or simple price transform | ### Historical price bars requirements You must have at least 1 period of `bars`. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. #### `CandlePart` enum options | enum / value | description | | ----- | ----------- | | `CandlePart.Open`0 | `Open` price | | `CandlePart.High`1 | `High` price | | `CandlePart.Low`2 | `Low` price | | `CandlePart.Close`3 | `Close` price | | `CandlePart.Volume`4 | `Volume` | | `CandlePart.HL2`5 | `(High+Low)/2` | | `CandlePart.HLC3`6 | `(High+Low+Close)/3` | | `CandlePart.OC2`7 | `(Open+Close)/2` | | `CandlePart.OHL3`8 | `(Open+High+Low)/3` | | `CandlePart.OHLC4`9 | `(Open+High+Low+Close)/4` | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. ### `TimeValue` type | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Value`*`double`* | Price of `CandlePart` option | ### Utilities * [.Find(lookupDate)](/utilities/results.md#find-by-date) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Value` with additional chain-enabled indicators. ```csharp // example var results = bars .Use(CandlePart.OHLC4) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Subscribe to a `BarHub` for streaming scenarios: ```csharp BarHub barHub = new(); BarPartHub observer = barHub.ToBarPartHub(CandlePart.HL2); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. ## Buffering Use a `BarPartList` for incremental buffering scenarios: ```csharp BarPartList buffer = new(CandlePart.Close); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => buffer.Add(bar); // results fill as the handler runs IReadOnlyList results = buffer; ``` --- --- url: /indicators/fisher-transform.md description: >- Created by John Ehlers, the Fisher Transform converts financial market prices into a Gaussian normal distribution. --- # Ehlers Fisher Transform Created by John Ehlers, the [Fisher Transform](https://www.investopedia.com/terms/f/fisher-transform.asp) converts prices into a Gaussian normal distribution. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/409 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToFisherTransform(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback window. Must be greater than 0. Default is 10. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+15` warmup periods will have unusable decreasing magnitude, convergence-related precision errors that can be as high as ~25% deviation in earlier indicator values. ### `FisherTransformResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Fisher`*`double`* | Fisher Transform | | `Trigger`*`double?`* | FT offset by one period | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) For pruning of warmup periods, we recommend using the following guidelines: ```csharp bars.ToFisherTransform(lookbackPeriods) .RemoveWarmupPeriods(lookbackPeriods+15); ``` See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToFisherTransform(..); ``` Results can be further processed on `Alma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToFisherTransform(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp FisherTransformList fisherList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => fisherList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = fisherList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); FisherTransformHub observer = barHub.ToFisherTransformHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/heikin-ashi.md description: >- Created by Munehisa Homma, [Heikin-Ashi](https://en.wikipedia.org/wiki/Candlestick_chart#Heikin-Ashi_candlesticks) is a modified candlestick pattern that transforms prices based on prior period prices for smoothing. --- # Heikin-Ashi Created by Munehisa Homma, [Heikin-Ashi](https://en.wikipedia.org/wiki/Candlestick_chart#Heikin-Ashi_candlesticks) is a modified candlestick pattern that transforms prices based on prior period prices for smoothing. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/254 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToHeikinAshi(); ``` ## Historical price bars requirements You must have at least two periods of `bars` to cover the warmup periods; however, more is typically provided since this is a chartable candlestick pattern. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * `HeikinAshiResult` is based on `IBar`, so it can be used as a direct replacement for `bars`. ### `HeikinAshiResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Open`*`decimal`* | Modified open price | | `High`*`decimal`* | Modified high price | | `Low`*`decimal`* | Modified low price | | `Close`*`decimal`* | Modified close price | | `Volume`*`decimal`* | Volume (same as `bars`) | ### Utilities * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) * .ToBars() to convert to a `Bar` collection. Example: ```csharp IReadOnlyList results = bars .ToHeikinAshi() .ToBars(); ``` See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results are based in `IBar` and can be further used in any indicator. ```csharp // example var results = bars .ToHeikinAshi(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp HeikinAshiList heikinAshiList = new(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => heikinAshiList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = heikinAshiList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); HeikinAshiHub observer = barHub.ToHeikinAshiHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/renko.md description: >- The Renko Chart is a Japanese price transformed candlestick pattern that uses "bricks" to show a defined increment of change over a non-linear time series. Transitions can use either Close or High/Low price values. An Average True Range (ATR) variant is also provided where brick size is determined by current Average True Range values. --- # Renko Chart The [Renko Chart](https://en.m.wikipedia.org/wiki/Renko_chart) is a Japanese price transformed candlestick pattern that uses "bricks" to show a defined increment of change over a non-linear time series. Transitions can use either `Close` or `High/Low` price values. An ATR variant is also provided where brick size is determined by current Average True Range values. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/478 "Community discussion about this indicator") ```csharp // C# usage syntax (fixed brick size) IReadOnlyList results = bars.ToRenko(brickSize, endType); // C# usage syntax (ATR-derived brick size โ€” Series only) IReadOnlyList results = bars.ToRenkoAtr(atrPeriods, endType); ``` ## Parameters | param | description | | ----- | ----------- | | `endType`*`EndType`* | Price threshold used to spawn new bricks, on both variants. Default is `EndType.Close` | ### Fixed brick size | param | description | | ----- | ----------- | | `brickSize`*`decimal`* | Brick size. Must be greater than 0. | ### ATR-derived brick size | param | description | | ----- | ----------- | | `atrPeriods`*`int`* | Number of lookback periods (`A`) for ATR evaluation. Must be greater than 1. Default is 14. | ### Historical price bars requirements **Fixed brick size**: You must have at least two periods of `bars` to cover the warmup periods; however, more is typically provided since this is a chartable candlestick pattern. **ATR-derived brick size**: You must have at least `A+100` periods of `bars`. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `EndType` enum options | enum / value | description | | ----- | ----------- | | `EndType.Close`0 | Threshold measured from bar `Close` price | | `EndType.HighLow`1 | Threshold measured from bar `High` and `Low` price | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It does not return a single incremental indicator value. * `RenkoResult` is based on `IBar`, so it can be used as a direct replacement for `bars`. * Each result record represents one Renko brick. > [!WARNING] > **๐Ÿšฉ Fewer results than bars** > > Unlike most indicators in this library, this indicator DOES NOT return the same number of elements as there are in the historical price bars. Renko bricks are added to the results once the `brickSize` change is achieved. For example, if it takes 3 days for a $2.50 price change to occur an entry is made on the third day while the first two are skipped. If a period change occurs at multiples of `brickSize`, multiple bricks are drawn with the same `Timestamp`. See [online documentation](https://www.investopedia.com/terms/r/renkochart.asp) for more information. > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning *(ATR variant)*** > > When using the `ToRenkoAtr()` variant, the last [Average True Range (ATR)](/indicators/atr.md) value is used to set `brickSize`. Since the ATR changes over time, historical bricks will be repainted as new periods are added or updated in `bars`. ### `RenkoResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Formation date of brick(s) | | `Open`*`decimal`* | Brick open price | | `High`*`decimal`* | Highest high during elapsed `bars` periods | | `Low`*`decimal`* | Lowest low during elapsed `bars` periods | | `Close`*`decimal`* | Brick close price | | `Volume`*`decimal`* | Sum of Volume over elapsed `bars` periods | | `IsUp`*`bool`* | Direction of brick (true=up,false=down) | > [!WARNING] > **๐Ÿšฉ Multiple bricks per period** > > When multiple bricks are drawn from a single `bar` period, the extra information about `High` and `Low` wicks and `Volume` is potentially confusing to interpret. `High` and `Low` wicks will be the same across the multiple bricks; and `Volume` is portioning evenly across the number of bricks. For example, if within one `bar` period 3 bricks are drawn, the `Volume` for each brick will be `(sum of bars Volume since last brick) / 3`. ### Utilities * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results are based in `IBar` and can be further used in any indicator. ```csharp // example var results = bars .ToRenko(..) .ToRsi(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming **Fixed brick size only** โ€” Streaming implementations are available for the fixed brick size variant only. Use a `BufferList` for incremental processing: ```csharp RenkoList buffer = new(brickSize, endType); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => buffer.Add(bar); // results fill as the handler runs IReadOnlyList results = buffer; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); RenkoHub observer = barHub.ToRenkoHub(brickSize); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` > [!WARNING] > **๐Ÿšฉ No streaming support** > > `ToRenkoAtr()` does not support streaming. The ATR brick size is derived from the full dataset and changes as new bars are added, making incremental output undefined. Use the Series implementation with periodic recalculation instead. See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/zig-zag.md description: >- Zig Zag is a financial market price chart overlay that simplifies the up and down movements and transitions based on a percent change smoothing threshold. --- # Zig Zag [Zig Zag](https://www.google.com/search?q=Zig+Zag+\(ZIGZAG\)+indicator) is a price chart overlay that simplifies the up and down movements and transitions based on a percent change smoothing threshold. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/226 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToZigZag(endType, percentChange); ``` ## Parameters | param | description | | ----- | ----------- | | `endType`*`EndType`* | Determines whether `Close` or `High/Low` are used for percent change threshold. Default is `EndType.Close`. | | `percentChange`*`decimal`* | Percent change required to establish a line endpoint. **Example: 3.5% would be entered as 3.5 (not 0.035)**. Must be greater than 0. Typical values range from 3 to 10. Default is 5. | > [!TIP] > **โœจ Tip: adjust `percentChange` for bar interval sizes** > > Use a percent change threshold that is appropriate for your price bar interval size. For example, `5%` works well for `Day`-sized price bar aggregates, but `Hourly` or `Minute`-sized bars typically need smaller values (e.g., `1%` or `0.1%`), depending on the asset class and typical price volatility. ### Historical price bars requirements You must have at least two periods of `bars` to cover the warmup periods, but notably more is needed to be useful. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `EndType` enum options | enum / value | description | | ----- | ----------- | | `EndType.Close`0 | Threshold measured from bar `Close` price | | `EndType.HighLow`1 | Threshold measured from bar `High` and `Low` price | ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * If you do not supply enough points to cover the percent change, there will be no Zig Zag points or lines. * The first line segment starts after the first confirmed point; ZigZag values before the first confirmed point will be `null`. * The last line segment is an approximation as the direction is indeterminate. > [!WARNING] > **๐Ÿšฉ Zero prices** > > Depending on the specified `endType`, the indicator cannot be initialized if the first `Bar` in `bars` has a `High`,`Low`, or `Close` value of 0 (zero). > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > The last line segment will always be redrawn back to the last known pivot. Do not attempt to calculate incremental values since previous values may change based on newer bars. ### `ZigZagResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `ZigZag`*`decimal?`* | Zig Zag line for `percentChange` | | `PointType`*`string?`* | Zig Zag endpoint type (`H` for high point, `L` for low point) | | `RetraceHigh`*`decimal?`* | Retrace line for high points | | `RetraceLow`*`decimal?`* | Retrace line for low points | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `ZigZag` with additional chain-enabled indicators. ```csharp // example var results = bars .ToZigZag(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Streaming is not supported for this indicator. This indicator requires lookahead to confirm reversal points; output repaints as new data arrives, making incremental results undefined. Use the Series (batch) implementation with periodic recalculation instead. --- --- url: /indicators/price-characteristics.md description: >- Indicators that measure volatility, momentum, and other intrinsic price properties. --- ## Additional [numerical analysis](/indicators/numerical-analysis.md) * [Beta coefficient (ฮฒ)](/indicators/beta.md) * [Coefficient of determination](/indicators/correlation.md) * [Correlation coefficient](/indicators/correlation.md) * [Linear regression (best-fit line)](/indicators/slope.md) * [Mean absolute deviation (MAD)](/indicators/sma-analysis.md) * [Mean absolute percentage error (MAPE)](/indicators/sma-analysis.md) * [Mean square error (MSE)](/indicators/sma-analysis.md) * [R-squared (Rยฒ)](/indicators/correlation.md) * [Slope (gradient)](/indicators/correlation.md) * [Standard deviation (ฯƒ)](/indicators/std-dev.md) * [Z-score (standard score)](/indicators/std-dev.md) --- --- url: /indicators/atr.md description: >- Created by J. Welles Wilder, True Range and Average True Range is a measure of volatility that captures gaps and limits between periods. --- # Average True Range (ATR) / True Range (TR) Created by J. Welles Wilder, True Range and [Average True Range](https://en.wikipedia.org/wiki/Average_true_range) are measures of volatility that capture gaps and limits between periods. See [True Range (TR)](/indicators/tr.md) for dedicated TR documentation, including streaming support. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/269 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToAtr(lookbackPeriods); // ATR with custom moving average IReadOnlyList results = bars.ToTr().ToSmma(lookbackPeriods); // raw True Range (TR) only IReadOnlyList results = bar.ToTr(); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) to consider. Must be greater than 1. Default is 14. | ### Historical price bars requirements You must have at least `N+100` periods of `bars` to cover the [warmup and convergence](https://github.com/facioquo/stock-indicators-dotnet/discussions/688) periods. Since this uses a smoothing technique, we recommend you use at least `N+250` data points prior to the intended usage date for better precision. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values for ATR since there's not enough data to calculate. > [!WARNING] > **๐Ÿšฉ โšž Convergence warning** > > The first `N+100` periods will have decreasing magnitude, convergence-related precision errors that can be as high as ~5% deviation in indicator values for earlier periods. ### `AtrResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Tr`*`double?`* | True Range for current period | | `Atr`*`double?`* | Average True Range | | `Atrp`*`double?`* | Average True Range Percent is `(ATR/Price)*100`. This normalizes so it can be compared to other stocks. | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Atrp` with additional chain-enabled indicators. ```csharp // example var results = bars .ToAtr(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp AtrList atrList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => atrList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = atrList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); AtrHub observer = barHub.ToAtrHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/bop.md description: >- Created by Igor Levshin, the Balance of Power (aka Balance of Market Power) is a momentum oscillator that depicts the strength of buying and selling pressure. --- # Balance of Power (BOP) Created by Igor Levshin, the [Balance of Power](https://www.google.com/search?q=Balance+of+Power+\(BOP\)+indicator) (aka Balance of Market Power) is a momentum oscillator that depicts the strength of buying and selling pressure. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/302 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToBop(smoothPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `smoothPeriods`*`int`* | Number of periods (`N`) for smoothing. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `BopResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Bop`*`double?`* | Balance of Power | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Bop` with additional chain-enabled indicators. ```csharp // example var results = bars .ToBop(..) .ToSlope(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp BopList bopList = new(smoothPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => bopList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = bopList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); BopHub observer = barHub.ToBopHub(smoothPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/chop.md description: >- Created by E.W. Dreiss, the Choppiness Index measures the trendiness or choppiness on a scale of 0 to 100, to depict steady trends versus conditions of choppiness. --- # Choppiness Index Created by E.W. Dreiss, the Choppiness Index measures the trendiness or choppiness on a scale of 0 to 100, to depict steady trends versus conditions of choppiness. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/357 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToChop(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the lookback evaluation. Must be greater than 1. Default is 14. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values since there's not enough data to calculate. ### `ChopResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Chop`*`double?`* | Choppiness Index | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Chop` with additional chain-enabled indicators. ```csharp // example var results = bars .ToChop(..) .ToEma(..); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp ChopList chopList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => chopList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = chopList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); ChopHub observer = barHub.ToChopHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/std-dev.md description: >- Standard deviation represents the volatility of historical financial market prices. It is also known as Historical Volatility (HV), includes Z-score. --- # Standard deviation (ฯƒ, volatility) [Standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) of price over a rolling lookback window. Also known as Historical Volatility (HV), includes Z-score. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/239 "Community discussion about this indicator") ```csharp // C# usage syntax (series) IReadOnlyList results = bars.ToStdDev(lookbackPeriods); // usage with streaming bars BarHub barHub = new(); StdDevHub observer = barHub.ToStdDevHub(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback period. Must be greater than 1 to calculate; however we suggest a larger period for statistically appropriate sample size. Default is 14. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `StdDevResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `StdDev`*`double?`* | Standard deviation of price | | `Mean`*`double?`* | Mean value of price | | `ZScore`*`double?`* | Z-score of current price (number of standard deviations from mean) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToStdDev(..); ``` Results can be further processed on `StdDev` with additional chain-enabled indicators. ```csharp // example var results = bars .ToStdDev(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp StdDevList stdDevList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => stdDevList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = stdDevList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); StdDevHub observer = barHub.ToStdDevHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/hurst.md description: >- Hurst Exponent (H) with Rescaled Range Analysis is a random-walk path analysis that measures trending and mean-reverting tendencies of incremental return values. When H is greater than 0.5 it depicts trending. When H is less than 0.5 it is is more likely to revert to the mean. When H is around 0.5 it represents a random walk. --- # Hurst Exponent The [Hurst Exponent](https://en.wikipedia.org/wiki/Hurst_exponent) (`H`) is part of a Rescaled Range Analysis, a [random-walk](https://en.wikipedia.org/wiki/Random_walk) path analysis that measures trending and mean-reverting tendencies of incremental return values. When `H` is greater than 0.5 it depicts trending. When `H` is less than 0.5 it is is more likely to revert to the mean. When `H` is around 0.5 it represents a random walk. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/477 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToHurst(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the Hurst Analysis. Must be at least 20. Default is 100. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values since there's not enough data to calculate. ### `HurstResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `HurstExponent`*`double?`* | Hurst Exponent (`H`) from raw rescaled range (R/S) analysis | | `HurstExponentAL`*`double?`* | [Anis-Lloyd corrected](https://en.wikipedia.org/wiki/Hurst_exponent#Rescaled_range_\(R/S\)_analysis) Hurst Exponent (`H`). Removes finite-sample bias from the raw R/S estimate. | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HLC3) .ToHurst(..); ``` Results can be further processed on `HurstExponent` with additional chain-enabled indicators. ```csharp // example var results = bars .ToHurst(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## References * Inputs are log returns `ln(c_t / c_{t-1})`, which provide additive, scale-consistent increments. * `HurstExponent` (raw `H`) is the slope of `log(R/S)` against `log(n)` across chunk sizes, following the rescaled-range analysis of Hurst (1951) and Mandelbrot & Wallis (1969). * `HurstExponentAL` applies the Anis & Lloyd (1976) finite-sample expectation `E[R/S]_n = ฮ“((n-1)/2) / (โˆšฯ€ ยท ฮ“(n/2)) ยท ฮฃ_{r=1}^{n-1} โˆš((n-r)/r)`, then corrects each observed R/S as `R/S โˆ’ E[R/S]_n + โˆš(ฯ€ยทn/2)` before regressing. The expected value uses an exact `LogGamma` evaluation for `n โ‰ค 340` and Peters' (1994) Stirling approximation `โˆš(2 / (ฯ€ยท(n-1)))` for larger `n`. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp HurstList hurstList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => hurstList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = hurstList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); HurstHub observer = barHub.ToHurstHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/roc.md description: >- Rate of Change, also known as Momentum Oscillator, is the percent change of price over a lookback window. Momentum is the raw price change equivalent. --- # Rate of Change (ROC) [Rate of Change](https://en.wikipedia.org/wiki/Momentum_\(technical_analysis\)), also known as Momentum Oscillator, is the percent change of price over a lookback window. Momentum is the raw price change equivalent. A [Rate of Change with Bands](/indicators/roc-wb.md) variant, created by Vitali Apirine, is also available. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/242 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToRoc(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) to go back. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N+1` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N` periods will have `null` values for ROC since there's not enough data to calculate. ### `RocResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Momentum`*`double?`* | Raw change in price over `N` periods | | `Roc`*`double?`* | Percent change in price (%, not decimal) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToRoc(..); ``` Results can be further processed on `Roc` with additional chain-enabled indicators. ```csharp // example var results = bars .ToRoc(..) .ToEma(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp RocList rocList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => rocList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = rocList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); RocHub observer = barHub.ToRocHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/prs.md description: >- Price Relative Strength, also called Comparative Relative Strength, shows the ratio of two bar histories, based on price. It is often used to compare against a market index or sector ETF. When using the optional lookback window, this also returns relative percent change over the specified periods. This is not the same as the more prevalent Relative Strength Index (RSI). --- # Price Relative Strength (PRS) [Price Relative Strength (PRS)](https://en.wikipedia.org/wiki/Relative_strength), also called Comparative Relative Strength, shows the ratio of two bar histories, based on price. It is often used to compare against a market index or sector ETF. When using the optional `lookbackPeriods`, this also returns relative percent change over the specified periods. This is not the same as the more prevalent [Relative Strength Index (RSI)](/indicators/rsi.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/243 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = barsEval.ToPrs(barsBase); ``` ## Parameters | param | description | | ----- | ----------- | | `sourceEval`*`IReadOnlyList`* | [Historical price bars](/guide/getting-started.md#historical-bars) used as the evaluation subject. You must have the same number of periods as `sourceBase`. | | `sourceBase`*`IReadOnlyList`* | [Historical price bars](/guide/getting-started.md#historical-bars) used as the benchmark basis for comparison. This is usually market index data. You must have the same number of periods as `sourceEval`. | | `lookbackPeriods`*`int`* | Optional. Number of periods (`N`) to lookback to compute % ROC difference. Must be greater than 0 if specified; omit it for no lookback. | ### Historical price bars requirements You must have at least `N+1` periods of price bars to calculate `PrsPercent` if `lookbackPeriods` is specified; otherwise, you must specify at least `2` periods. More than the minimum is typically specified. `sourceEval` and `sourceBase` must have consistent frequency (day, hour, minute, etc). Mismatch histories will throw `InvalidBarsException`. See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The `N` periods will have `null` values for `PrsPercent` since there's not enough data to calculate. ### `PrsResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Prs`*`double?`* | Price Relative Strength is the ratio of `Eval` / `Base` price values | | `PrsPercent`*`double?`* | Percent change difference between `Eval` and `Base` over `N` periods | > [!NOTE] > **Example** > > At period `i`, `Prs` is `Eval[i] / Base[i]` and `PrsPercent` is the difference in `Eval` percent change and `Base` percent change over the prior `N` periods. Example: if `Eval` gains 10% and `Base` gains 3%, `PrsPercent` depicts +7% relative outperformance. ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = barsEval .Use(CandlePart.HL2) .ToPrs(barsBase, ..); ``` > [!WARNING] > Both `sourceEval` and `sourceBase` arguments must contain the same number of elements and be the results of a chainable indicator or `.Use()` method. Results can be further processed on `Beta` with additional chain-enabled indicators. ```csharp // example var results = barsEval .ToPrs(barsBase, ..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Streaming is not supported for this indicator. This indicator requires a second synchronized bar series, which cannot be expressed in the single-series streaming model. Use the Series (batch) implementation with periodic recalculation instead. --- --- url: /indicators/tr.md description: >- Created by J. Welles Wilder, True Range is a measure of volatility that captures gaps and limits between periods. It is the foundation for Average True Range (ATR). --- # True Range (TR) Created by J. Welles Wilder, [True Range](https://en.wikipedia.org/wiki/Average_true_range) is a measure of volatility that captures gaps and limits between periods. It is the building block for [Average True Range](/indicators/atr.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/269 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToTr(); ``` ## Historical price bars requirements You must have at least 2 periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first period will have a `null` value since there is no prior period close. ### `TrResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Tr`*`double?`* | True Range | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining Results can be further processed on `Tr` with additional chain-enabled indicators. ```csharp // example: ATR using a custom moving average var results = bars .ToTr() .ToSmma(lookbackPeriods); ``` This indicator must be generated from `bars` and **cannot** be generated from results of another chain-enabled indicator or method. See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp TrList trList = new(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => trList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = trList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); TrHub observer = barHub.ToTrHub(); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/ulcer-index.md description: >- Created by Peter Martin, the Ulcer Index is a measure of downside price volatility. Often called the "heart attack" score, it measures the amount of pain seen from drawdowns in financial market prices and portfolio value. --- # Ulcer Index (UI) Created by Peter Martin, the [Ulcer Index](https://en.wikipedia.org/wiki/Ulcer_index) is a measure of downside price volatility over a lookback window. Often called the "heart attack" score, it measures the amount of pain seen from drawdowns in financial market prices and portfolio value. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/232 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToUlcerIndex(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for review. Must be greater than 0. Default is 14. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `UlcerIndexResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `UlcerIndex`*`double?`* | Ulcer Index | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToUlcerIndex(..); ``` Results can be further processed on `UlcerIndex` with additional chain-enabled indicators. ```csharp // example var results = bars .ToUlcerIndex(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp UlcerIndexList ulcerIndexList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => ulcerIndexList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = ulcerIndexList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); UlcerIndexHub observer = barHub.ToUlcerIndexHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/numerical-analysis.md description: Fundamental mathematical calculations for financial analysis --- ## Additional [price characteristics](/indicators/price-characteristics.md) * [Average True Range (ATR)](/indicators/atr.md) * [Balance of Power (BOP)](/indicators/bop.md) * [Bull and Bear Power](/indicators/elder-ray.md) * [Choppiness Index (CHOP)](/indicators/chop.md) * [Comparative Relative Strength](/indicators/prs.md) * [Dominant Cycle Period](/indicators/ht-trendline.md) * [Historical Volatility (HV)](/indicators/std-dev.md) * [Hurst Exponent](/indicators/hurst.md) * [Momentum Oscillator (MO)](/indicators/roc.md) * [Normalized Average True Range](/indicators/atr.md) * [Price Momentum Oscillator (PMO)](/indicators/pmo.md) * [Price Relative Strength (PRS)](/indicators/prs.md) * [Rate of Change (ROC)](/indicators/roc.md) * [Rate of Change with Bands](/indicators/roc-wb.md) * [Rescaled Range Analysis](/indicators/hurst.md) * [True Range (TR)](/indicators/tr.md) * [True Strength Index (TSI)](/indicators/tsi.md) * [Ulcer Index (UI)](/indicators/ulcer-index.md) --- --- url: /indicators/beta.md description: >- Beta coefficient with Beta+/Beta- shows how strongly one asset's price responds to systemic volatility of the entire market. Upside Beta (Beta+) and Downside Beta (Beta-), popularized by Harry M. Markowitz, are also included. --- # Beta coefficient (ฮฒ) [Beta](https://en.wikipedia.org/wiki/Beta_\(finance\)) shows how strongly one asset's price responds to systemic volatility of the entire market. Beta measures an asset's non-diversifiable systematic risk โ€” its market exposure โ€” not its idiosyncratic, asset-specific risk. [Upside Beta](https://en.wikipedia.org/wiki/Upside_beta) (Beta+) and [Downside Beta](https://en.wikipedia.org/wiki/Downside_beta) (Beta-), [popularized by Harry M. Markowitz](https://www.jstor.org/stable/j.ctt1bh4c8h), are also included. Beta+ and Beta- capture asymmetric sensitivity during rising and falling markets. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/268 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = barsEval .ToBeta(barsMarket, lookbackPeriods, type); ``` ## Parameters | param | description | | ----- | ----------- | | `sourceEval`*`IReadOnlyList`* | [Historical price bars](/guide/getting-started.md#historical-bars) used as the evaluation subject. You must have the same number of periods as `sourceMrkt`. | | `sourceMrkt`*`IReadOnlyList`* | [Historical price bars](/guide/getting-started.md#historical-bars) used as the benchmark basis for comparison. This is usually market index data. You must have the same number of periods as `sourceEval`. | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback window. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size and especially when using Beta +/-. | | `type`*`BetaType`* | Type of Beta to calculate. Default is `BetaType.Standard`. See [`BetaType` options](#betatype-options) below. | ### Historical price bars requirements You must have at least `N` periods of `sourceEval` and `sourceMrkt` to cover the warmup periods. More than the minimum is typically provided, since a larger sample improves statistical quality โ€” especially when using Beta +/-. `sourceEval` and `sourceMrkt` must have consistent frequency (day, hour, minute, etc). Mismatch histories will throw `InvalidBarsException`. See [the Guide](/guide/getting-started.md#historical-bars) for more information. ### `BetaType` options | type | description | | ------------------- | ------------------------------------------------------------------------------------------ | | `BetaType.Standard` | Standard Beta only. Uses all historical price bars. (default) | | `BetaType.Up` | Upside Beta only. Uses market up bars only. | | `BetaType.Down` | Downside Beta only. Uses market down bars only. | | `BetaType.All` | Returns all of the above. Required for `Ratio` and `Convexity` values. Note: 3ร— slower. | > [!TIP] > **โœจ Pro tip** > > Financial institutions often depict a single number for Beta on their sites. To get that same long-term Beta value, use 5 years of monthly bars for `bars` and a value of 60 for `lookbackPeriods`. If you only have smaller bars, use the [Aggregate()](/utilities/bars.md#resize-bar-history) utility to convert it. > > [Alpha](https://en.wikipedia.org/wiki/Alpha_\(finance\)) is calculated as `R โ€“ Rf โ€“ Beta (Rm - Rf)`, where `Rf` is the risk-free rate. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `BetaResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Beta`*`double?`* | Beta coefficient based | | `BetaUp`*`double?`* | Beta+ (Up Beta) | | `BetaDown`*`double?`* | Beta- (Down Beta) | | `Ratio`*`double?`* | Beta ratio is `BetaUp/BetaDown` | | `Convexity`*`double?`* | Beta convexity is (BetaUp-BetaDown)2 | | `ReturnsEval`*`double?`* | Returns of evaluated bars (`R`) | | `ReturnsMrkt`*`double?`* | Returns of market bars (`Rm`) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = barsEval .Use(CandlePart.HL2) .ToBeta(barsMarket.Use(CandlePart.HL2), ..); ``` > [!WARNING] > **๐Ÿšฉ Matching inputs required** > > Both eval and market arguments must contain the same number of elements and be the results of a chainable indicator or `.Use()` method. Results can be further processed on `Beta` with additional chain-enabled indicators. ```csharp // example var results = barsEval .ToBeta(barsMarket, ..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Streaming is not supported for this indicator. This indicator requires a second synchronized bar series, which cannot be expressed in the single-series streaming model. Use the Series (batch) implementation with periodic recalculation instead. --- --- url: /indicators/correlation.md description: >- Created by Karl Pearson, the correlation coefficient depicts the linear statistical correlation between two price bar histories; includes R-squared (Rยฒ) / coefficient of determination, variance, and covariance. --- # Correlation coefficient Created by Karl Pearson, the [Correlation coefficient](https://en.wikipedia.org/wiki/Correlation_coefficient) depicts the linear statistical correlation between two price bar histories; includes R-squared (Rยฒ) / coefficient of determination, variance, and covariance. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/259 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = sourceA.ToCorrelation(sourceB, lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `sourceA`*`IReadOnlyList`* | First source series (A), such as an indicator's results or `.Use()` output. It must have at least the same matching date elements of `sourceB`. | | `sourceB`*`IReadOnlyList`* | Second source series (B), such as an indicator's results or `.Use()` output. It must have at least the same matching date elements of `sourceA`. | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback period. Must be greater than 0 to calculate; however we suggest a larger period for statistically appropriate sample size. | ### Historical price bars requirements You must have at least `N` periods for both `sourceA` and `sourceB` to cover the warmup periods. More than the minimum is typically specified. `sourceA` and `sourceB` must have consistent frequency (day, hour, minute, etc). Mismatch histories will throw `InvalidBarsException`. See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `sourceA` and `sourceB` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `CorrResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `VarianceA`*`double?`* | Variance of A | | `VarianceB`*`double?`* | Variance of B | | `Covariance`*`double?`* | Covariance of A+B | | `Correlation`*`double?`* | Correlation `R` | | `RSquared`*`double?`* | R-squared (`Rยฒ`), aka Coefficient of determination | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToCorrelation(barsMarket.Use(CandlePart.HL2),20); ``` > [!WARNING] > **๐Ÿšฉ Matching inputs required** > > Both `sourceA` and `sourceB` arguments must contain the same number of elements and be the results of a chainable indicator or `.Use()` method. Results can be further processed on `Correlation` with additional chain-enabled indicators. ```csharp // example var results = bars .ToCorrelation(..) .ToSlope(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Streaming is not supported for this indicator. This indicator requires a second synchronized bar series, which cannot be expressed in the single-series streaming model. Use the Series (batch) implementation with periodic recalculation instead. --- --- url: /indicators/slope.md description: >- Slope of the best fit line is determined by an ordinary least-squares simple linear regression on price. It can be used to help identify trend strength and direction. This indicator can be used to produce both a rolling slope value and a straight line through a specified lookback window. --- # Slope and linear regression [Slope of the best fit line](https://www.google.com/search?q=Slope+linear+regression+indicator) is determined by an [ordinary least-squares simple linear regression](https://en.wikipedia.org/wiki/Simple_linear_regression) on price. It can be used to help identify trend strength and direction. [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/241 "Community discussion about this indicator") ```csharp // C# usage syntax IReadOnlyList results = bars.ToSlope(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) for the linear regression. Must be greater than 1. Default is 14. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values for `Slope` since there's not enough data to calculate. * `Line` values are only provided for the last `N` periods of your bar history > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > The `Line` is continuously repainted since it is based on the last bar and lookback period. ### `SlopeResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Slope`*`double?`* | Slope `๐‘š` of the best-fit line of price | | `Intercept`*`double?`* | Y-intercept `๐‘` of the best-fit line | | `StdDev`*`double?`* | Standard deviation of price over `N` lookback periods | | `RSquared`*`double?`* | R-squared (Rยฒ), aka Coefficient of determination | | `Line`*`decimal?`* | Best-fit line `๐‘ฆ` over the last `N` periods (i.e. `๐‘ฆ=๐‘š๐‘ฅ+๐‘` using last period values) | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .ToEma(..) .ToSlope(..); ``` Results can be further processed on `Slope` with additional chain-enabled indicators. ```csharp // example var results = bars .ToSlope(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp SlopeList slopeList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => slopeList.Add(bar); // based on `ICollection`; fills as the handler runs IReadOnlyList results = slopeList; ``` Subscribe to a `BarHub` for advanced streaming scenarios: ```csharp BarHub barHub = new(); SlopeHub observer = barHub.ToSlopeHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` > [!WARNING] > **๐Ÿ–Œ๏ธ Repaint warning** > > The streaming implementation exhibits the same repaint behavior as the series version. `Line` values are recalculated for the last `N` periods as new data arrives, matching the series implementation's behavior. See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides. --- --- url: /indicators/sma-analysis.md description: >- Mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE). --- # SMA with extended analysis [Simple Moving Average](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average) with extended statistical analysis including mean absolute deviation (MAD), mean square error (MSE), and mean absolute percentage error (MAPE). See also [Simple Moving Average](/indicators/sma.md). [\[Discuss\] ๐Ÿ’ฌ](https://github.com/facioquo/stock-indicators-dotnet/discussions/240 "Community discussion about this indicator") Each error metric charts on its own scale โ€” MAD is in price units, MSE in price units squared, and MAPE is a fraction โ€” so they are depicted separately. ```csharp // C# usage syntax IReadOnlyList results = bars.ToSmaAnalysis(lookbackPeriods); ``` ## Parameters | param | description | | ----- | ----------- | | `lookbackPeriods`*`int`* | Number of periods (`N`) in the lookback window. Must be greater than 0. | ### Historical price bars requirements You must have at least `N` periods of `bars` to cover the warmup periods. `bars` is a collection of generic `TBar` historical price bars. It should have a consistent frequency (day, hour, minute, etc). See [the Guide](/guide/getting-started.md#historical-bars) for more information. ## Response ```csharp IReadOnlyList ``` * This method returns a time series of all available indicator values for the `bars` provided. * It always returns the same number of elements as there are in the historical price bars. * It does not return a single incremental indicator value. * The first `N-1` periods will have `null` values since there's not enough data to calculate. ### `SmaAnalysisResult` | property | description | | ----- | ----------- | | `Timestamp`*`DateTime`* | Date from evaluated `TBar` | | `Sma`*`double?`* | Simple moving average | | `Mad`*`double?`* | Mean absolute deviation | | `Mse`*`double?`* | Mean square error | | `Mape`*`double?`* | Mean absolute percentage error | ### Utilities * [.Condense()](/utilities/results.md#condense) * [.Find(lookupDate)](/utilities/results.md#find-by-date) * [.RemoveWarmupPeriods()](/utilities/results.md#remove-warmup-periods) * [.RemoveWarmupPeriods(removePeriods)](/utilities/results.md#remove-warmup-periods) See [Utilities and helpers](/utilities.md) for more information. ## Chaining This indicator may be generated from any chain-enabled indicator or method. ```csharp // example var results = bars .Use(CandlePart.HL2) .ToSmaAnalysis(..); ``` Results can be further processed on `Sma` with additional chain-enabled indicators. ```csharp // example var results = bars .ToSmaAnalysis(..) .ToRsi(..); ``` See [Chaining indicators](/guide/chaining.md) for more. ## Streaming Use the buffer-style `List` when you need incremental calculations without a hub: ```csharp SmaAnalysisList smaAnalysisList = new(lookbackPeriods); // call from your WebSocket or SSE message handler void OnValueReceived(IReusable value) => smaAnalysisList.Add(value); // based on `ICollection`; fills as the handler runs IReadOnlyList results = smaAnalysisList; ``` Subscribe to a chain-enabled hub for advanced streaming scenarios: ```csharp BarHub barHub = new(); SmaAnalysisHub observer = barHub.ToSmaAnalysisHub(lookbackPeriods); // call from your WebSocket or SSE message handler void OnBarReceived(IBar bar) => barHub.Add(bar); // results fill as the handler runs IReadOnlyList results = observer.Results; ``` See [Buffer lists](/guide/styles/buffer.md) and [Stream hubs](/guide/styles/stream.md) for full usage guides.