OoplesFinance.StockIndicators 1.1.0

Prefix Reserved
dotnet add package OoplesFinance.StockIndicators --version 1.1.0
                    
NuGet\Install-Package OoplesFinance.StockIndicators -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OoplesFinance.StockIndicators" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OoplesFinance.StockIndicators" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="OoplesFinance.StockIndicators" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OoplesFinance.StockIndicators --version 1.1.0
                    
#r "nuget: OoplesFinance.StockIndicators, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OoplesFinance.StockIndicators@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OoplesFinance.StockIndicators&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=OoplesFinance.StockIndicators&version=1.1.0
                    
Install as a Cake Tool

OoplesFinance.StockIndicators (High-Precision Fork)

High-precision technical indicators with a growing streaming and performance-focused toolchain. This fork removes rounding, restores mathematical constants, and adds modern streaming, stateful indicators, and benchmark coverage while preserving the familiar API.

Highlights

  • Precision first: no Math.Round, real constants (Math.PI, Math.Sqrt(2)), and full double precision output.
  • Streaming-ready: trade/quote/bar ingestion, timeframes, and stateful indicators that update incrementally.
  • Multi-series streaming: register indicators that consume multiple symbols/timeframes with alignment policies.
  • Performance focus: ongoing algorithmic and allocation optimizations with benchmarks to validate changes.
  • Targets: net461, net10.0.

Indicators

See the full list in INDICATORS.md.

Quick start (batch)

using OoplesFinance.StockIndicators.Models;
using OoplesFinance.StockIndicators;

var data = new List<TickerData>
{
    new() { Date = DateTime.UtcNow, Open = 100, High = 101, Low = 99, Close = 100.5, Volume = 1000 },
    new() { Date = DateTime.UtcNow.AddMinutes(1), Open = 100.5, High = 102, Low = 100, Close = 101.8, Volume = 900 }
};

var stockData = new StockData(data);
var sma = stockData.CalculateSimpleMovingAverage(20).CustomValuesList;

Streaming quick start (single-series)

using OoplesFinance.StockIndicators.Streaming;

var engine = new StreamingIndicatorEngine(new StreamingIndicatorEngineOptions
{
    EmitUpdates = false
});

engine.RegisterStatefulIndicator(
    "AAPL",
    BarTimeframe.Tick,
    new SimpleMovingAverageState(5),
    update => Console.WriteLine($"SMA(5) = {update.Value:F4}"),
    new IndicatorSubscriptionOptions { IncludeUpdates = false });

engine.OnTrade(new StreamTrade("AAPL", DateTime.UtcNow, 100, 1));
engine.OnTrade(new StreamTrade("AAPL", DateTime.UtcNow.AddSeconds(1), 101, 1));

Streaming quick start (multi-series)

using OoplesFinance.StockIndicators.Enums;
using OoplesFinance.StockIndicators.Streaming;

var engine = new StreamingIndicatorEngine(new StreamingIndicatorEngineOptions
{
    EmitUpdates = false
});

var primary = new SeriesKey("AAPL", BarTimeframe.Minutes(1));
var secondary = new SeriesKey("MSFT", BarTimeframe.Minutes(1));

engine.RegisterMultiSeriesIndicator(
    primary,
    new[] { secondary },
    new SpreadState(primary, secondary),
    update => Console.WriteLine($"Spread = {update.Value:F4}"),
    new IndicatorSubscriptionOptions
    {
        IncludeUpdates = false,
        SeriesAlignmentPolicy = SeriesAlignmentPolicy.Strict
    });

// Custom multi-series example state
sealed class SpreadState : IMultiSeriesIndicatorState
{
    private readonly SeriesKey _left;
    private readonly SeriesKey _right;

    public SpreadState(SeriesKey left, SeriesKey right)
    {
        _left = left;
        _right = right;
    }

    public IndicatorName Name => IndicatorName.None;

    public void Reset() { }

    public MultiSeriesIndicatorStateResult Update(MultiSeriesContext context, SeriesKey series, OhlcvBar bar,
        bool isFinal, bool includeOutputs)
    {
        if (!context.TryGetLatest(_left, out var left) || !context.TryGetLatest(_right, out var right))
        {
            return new MultiSeriesIndicatorStateResult(false, 0d, null);
        }

        var value = left.Close - right.Close;
        return new MultiSeriesIndicatorStateResult(true, value, null);
    }
}

Notes:

  • Default alignment is SeriesAlignmentPolicy.LastKnown (emit using most recent bars).
  • SeriesAlignmentPolicy.Strict requires all series to share the same EndTime. With IncludeUpdates = false, alignment uses final bars only.

v2.0 Builder API (New)

The v2.0 builder API provides zero-allocation indicator computation with fluent configuration:

using OoplesFinance.StockIndicators.Builder;

// Create data source
var stockData = new StockData(opens, highs, lows, closes, volumes, dates);
var source = IndicatorDataSource.FromBatch(stockData);

// Declare handles at outer scope for cross-lambda access
SeriesHandle sma = default, rsi = default;
SignalHandle overboughtSignal = default;

// Configure indicators with the fluent builder
var builder = new StockIndicatorBuilder(source)
    .ConfigureIndicators(indicators =>
    {
        sma = indicators.Sma(20);
        rsi = indicators.Rsi(14);
        var bb = indicators.BollingerBands(20, 2);
        var macd = indicators.Macd(12, 26, 9);
    })
    .ConfigureSignals(signals =>
    {
        // Define trading signals
        overboughtSignal = signals.When(rsi).CrossesAbove(70).Emit("overbought");
        signals.When(rsi).CrossesBelow(30).Emit("oversold");

        // Group conditions
        signals.Group(
            SignalCondition.Above(rsi, 50),
            SignalCondition.Above(sma, 100))
            .All()
            .ForBars(3)
            .Emit("bullish");
    })
    .ConfigureNotifications(notify =>
    {
        notify.Console();
        notify.Email(new EmailOptions { To = "alerts@example.com" });
        notify.Telegram(new TelegramOptions { ChatId = "123456" });
    })
    .ConfigureAutoTrading(trade =>
    {
        trade.Alpaca(new AlpacaOptions { UsePaper = true })
            .OnSignal(overboughtSignal)
            .MarketSell();
    });

// Build and use
using var runtime = builder.Build();
var smaBuffer = runtime.GetSeries(sma);
var values = smaBuffer.AsSpan(); // Zero-allocation access

Features

  • 750+ Indicators: Access all indicators via indicators.Calculate(IndicatorName, params) or typed methods
  • Zero Allocations: IndicatorBuffer<T> uses ArrayPool<T> for zero-allocation hot paths
  • SIMD Optimized: Math operations use loop unrolling for improved performance
  • Lazy Evaluation: Only compute indicators referenced by signals
  • Multi-Symbol Support: indicators.For(symbol, timeframe) for cross-symbol analysis

Notification Adapters

Adapter Configuration
Console notify.Console()
Email notify.Email(new EmailOptions { SmtpHost, To })
SMS notify.Sms(new SmsOptions { AccountSid, ToNumber })
Webhook notify.Webhook(new WebhookOptions { Url })
Telegram notify.Telegram(new TelegramOptions { BotToken, ChatId })
Discord notify.Discord(new DiscordOptions { WebhookUrl })

Trading Adapters

Adapter Configuration
Console (dry-run) trade.ConsoleAdapter()
Alpaca trade.Alpaca(new AlpacaOptions { UsePaper = true })

For migration from v1.x, see MIGRATION.md.

Why v2.0?

The v2.0 Builder API represents a fundamental shift in how you work with indicators. Here's why you should consider upgrading:

Performance: Streaming is 45% Faster

When processing real-time data with incremental updates, v2.0's streaming engine significantly outperforms v1.0's batch recomputation approach:

Scenario v1.0 (us) v2.0 (us) Improvement
Streaming (10 updates, 3 indicators) 27,136 14,987 45% faster

Benchmark: 10,000 data points, SMA(14) + RSI(14) + Bollinger Bands(20,2). AMD Ryzen 9 3950X, .NET 10.0.

Why v2.0 wins at streaming: v1.0 must recompute the entire history for each new bar. v2.0's streaming engine processes data incrementally, only computing the new values.

When v1.0 is faster: For one-time batch calculations on static data, v1.0's direct Calculate* methods have less overhead since there's no builder setup cost. If you're doing a single calculation and never updating, v1.0 remains efficient.

Unified Batch and Streaming API

Write your indicator logic once, use it for both historical analysis and live trading:

// Same builder configuration works for both modes
var builder = new StockIndicatorBuilder(source)
    .ConfigureIndicators(ind => { sma = ind.Sma(20); rsi = ind.Rsi(14); })
    .ConfigureSignals(sig => sig.When(rsi).CrossesAbove(70).Emit("overbought"));

// Batch mode: source = IndicatorDataSource.FromBatch(stockData)
// Streaming mode: source = IndicatorDataSource.FromStreaming(options)

Natural Indicator Chaining

Compose indicators intuitively without manual data extraction:

// v1.0: Manual and error-prone
var smaResult = stockData.CalculateSimpleMovingAverage(20);
// Now manually extract values and feed to RSI... complex!

// v2.0: Natural composition
indicators.Then(sma).Rsi(14);  // RSI of SMA - automatic!

Built-in Signal Generation

Detect trading signals declaratively:

.ConfigureSignals(signals =>
{
    // Crossover detection
    signals.When(fastMa).CrossesAbove(slowMa).Emit("golden_cross");
    signals.When(rsi).CrossesBelow(30).Emit("oversold");

    // Multi-condition groups
    signals.Group(
        SignalCondition.Above(rsi, 50),
        SignalCondition.Above(price, sma200))
        .All()
        .ForBars(3)
        .Emit("bullish_confirmation");
})

Integrated Notifications

Get alerted when signals fire:

.ConfigureNotifications(notify =>
{
    notify.Console();                                    // Debug output
    notify.Email(new EmailOptions { To = "..." });       // SMTP email
    notify.Sms(new SmsOptions { ToNumber = "..." });     // Twilio SMS
    notify.Telegram(new TelegramOptions { ChatId = "..." }); // Telegram bot
    notify.Discord(new DiscordOptions { WebhookUrl = "..." }); // Discord
    notify.Webhook(new WebhookOptions { Url = "..." });  // Custom webhook
})

Auto-Trading Integration

Execute trades automatically when signals fire:

.ConfigureAutoTrading(trade =>
{
    trade.Alpaca(new AlpacaOptions { UsePaper = true })
        .OnSignal(buySignal)
        .MarketBuy(quantity: 10);
})

When to Use Each API

Use Case Recommended API
One-time batch calculation v1.0 Calculate* methods
Real-time streaming data v2.0 Builder API
Multiple indicators together v2.0 Builder API
Signal detection v2.0 Builder API
Notifications/alerts v2.0 Builder API
Auto-trading v2.0 Builder API
Indicator chaining (RSI of SMA) v2.0 Builder API

v1.0 vs v2.0 Benchmark Details

Full benchmark results comparing v1.0 and v2.0 APIs (10,000 data points):

Category v1.0 (us) v2.0 (us) Notes
SMA single 326 8,896 v2.0 has builder setup overhead
EMA single 306 9,958 v2.0 has builder setup overhead
RSI single 555 9,380 v2.0 has builder setup overhead
Bollinger single 1,091 12,699 v2.0 has builder setup overhead
Streaming (10 updates) 27,136 14,987 v2.0 45% faster
Multi-indicator (4 ind.) 3,258 15,491 Setup overhead amortizes with more indicators

The v2.0 benchmarks create a new builder for each call to measure full setup cost. In real applications, you create the builder once and reuse it, eliminating repeated setup overhead.

Dev Console

A developer console is available to run batch, streaming, and multi-series examples locally.

dotnet run --project examples/OoplesFinance.StockIndicators.DevConsole/OoplesFinance.StockIndicators.DevConsole.csproj

Non-interactive:

dotnet run --project examples/OoplesFinance.StockIndicators.DevConsole/OoplesFinance.StockIndicators.DevConsole.csproj -- --run-all --no-pause

The developer console also includes an optional FXMacroData consumer example. It converts authenticated daily FX rows into the library's existing TickerData/StockData contract and calculates SMA(20); no data-provider code is added to the indicator package itself. Keep the API key in the process environment:

FXMD_API_KEY=your_api_key dotnet run --project examples/OoplesFinance.StockIndicators.DevConsole/OoplesFinance.StockIndicators.DevConsole.csproj -- --fxmacrodata EUR USD 2024-01-01 2024-12-31

On PowerShell, set $env:FXMD_API_KEY first and run the same dotnet command without placing the key on the command line.

This consumer example deliberately follows the indicator library's OHLC input contract instead of presenting a second general-purpose data client:

FXMacroData capability Example status Reason
Discovery/catalogue Not supported The command accepts one explicit FX pair.
Macro history Not supported Economic observations are not OHLC bars.
Release calendar Not supported Scheduled events do not map to TickerData.
Predictions Not supported Forecasts require a separate feature model.
Macro news Not supported The library has no text/news input contract.
FX spot history Native Daily rows map to TickerData and StockData.
Market sessions Not supported Session intervals cannot be represented by daily bars.
COT positioning Not supported Weekly positioning is not an OHLC price series.
Commodities Not supported This focused command accepts FX pairs only.
Seasonality Not supported Derived features need an explicit indicator design.

Performance chart

Sample BenchmarkDotNet results (Count=10000, net10.0). Optimized = this fork, Baseline = original library.

Length = 14

Indicator Optimized (us) Baseline (us) Speedup
SMA 319.6 13636.9 42.7x
EMA 323.0 814.4 2.5x
RSI 889.0 18767.6 21.1x
MACD 549.2 8872.7 16.2x
Bollinger Bands 1292.2 33604.7 26.0x
ATR 412.9 13023.5 31.5x
Chande CMO 679.5 27632.6 40.7x
Ulcer Index 2620.4 30663.0 11.7x

Length = 50 | Indicator | Optimized (us) | Baseline (us) | Speedup | | --- | --- | --- | --- | | SMA | 321.8 | 28647.1 | 89.0x | | EMA | 304.9 | 783.4 | 2.6x | | RSI | 879.0 | 18738.7 | 21.3x | | MACD | 551.2 | 8919.8 | 16.2x | | Bollinger Bands | 1299.6 | 23400.2 | 18.0x | | ATR | 375.1 | 13073.6 | 34.9x | | Chande CMO | 644.5 | 11835.8 | 18.4x | | Ulcer Index | 2756.0 | 14288.4 | 5.2x |

Length = 200 | Indicator | Optimized (us) | Baseline (us) | Speedup | | --- | --- | --- | --- | | SMA | 307.8 | 14493.6 | 47.1x | | EMA | 304.2 | 950.9 | 3.1x | | RSI | 925.7 | 18540.0 | 20.0x | | MACD | 543.8 | 8948.7 | 16.5x | | Bollinger Bands | 1820.9 | 67349.6 | 37.0x | | ATR | 394.2 | 13098.8 | 33.2x | | Chande CMO | 630.4 | 33713.7 | 53.5x | | Ulcer Index | 2749.9 | 72270.8 | 26.3x |

Streaming fanout (100k ticks, 5 timeframes): | Scenario | Indicators | Outputs | Throughput (ticks/sec) | p95 latency (ms) | Updates | | --- | --- | --- | --- | --- | --- | | Core | 10 | off | 207,346 | 0.01 | 6,001,190 | | Core + outputs | 10 | on | 109,301 | 0.02 | 6,001,190 | | Extended | 15 | off | 140,680 | 0.01 | 9,001,785 |

Streaming scaling (100k ticks, outputs off): | Timeframes | Indicators | Throughput (ticks/sec) | p95 latency (ms) | Updates | | --- | --- | --- | --- | --- | | 1 | 5 | 616,430 | 0.00 | 1,000,000 | | 3 | 5 | 404,420 | 0.00 | 2,000,590 | | 5 | 5 | 263,733 | 0.01 | 3,000,595 | | 5 | 10 | 207,346 | 0.01 | 6,001,190 | | 5 | 15 | 140,680 | 0.01 | 9,001,785 |

Full reports: BenchmarkDotNet.Artifacts/results/OoplesFinance.StockIndicators.Benchmarks.IndicatorBenchmarks-report-github.md

Benchmarks

Benchmarks live in benchmarks/ and include batch and streaming performance harnesses.

dotnet run --project benchmarks/OoplesFinance.StockIndicators.Benchmarks/OoplesFinance.StockIndicators.Benchmarks.csproj -c Release

Run streaming fanout throughput/latency:

dotnet run --project benchmarks/OoplesFinance.StockIndicators.Benchmarks/OoplesFinance.StockIndicators.Benchmarks.csproj -c Release -- --streaming-perf --ticks 100000

Compare optimized vs baseline:

.\benchmarks\setup-baseline.ps1 -Ref master
dotnet build -c Release -p:AssemblyName=OoplesFinance.StockIndicators.Original -p:TargetFramework=net10.0 benchmarks/.baseline/src/OoplesFinance.StockIndicators.csproj
dotnet run --project benchmarks/OoplesFinance.StockIndicators.Benchmarks/OoplesFinance.StockIndicators.Benchmarks.csproj -c Release -- --filter *IndicatorBenchmarks*

Results are written to BenchmarkDotNet.Artifacts/.

Project layout

  • src/ core library
  • tests/ unit tests
  • benchmarks/ BenchmarkDotNet suite
  • examples/ developer console and examples

Additional docs

  • INDICATORS.md list of indicators
  • OPTIMIZATIONS.md optimization backlog and notes
  • MODERNIZATION_PLAN.md roadmap for refactors and performance work

License

Apache 2.0. See LICENSE.txt.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on OoplesFinance.StockIndicators:

Repository Stars
alpacahq/alpaca-trade-api-csharp
C# SDK for Alpaca Trade API https://docs.alpaca.markets/
Version Downloads Last Updated
1.1.0 179 9/9/2026
1.0.53 6,818 8/8/2023
1.0.52 352 8/8/2023
1.0.51 440 8/2/2023
1.0.50 1,518 1/2/2023
1.0.49 582 12/19/2022
1.0.48 581 12/7/2022
1.0.47 777 10/10/2022
1.0.46 945 7/26/2022
1.0.45 713 7/19/2022
1.0.44 1,229 4/21/2022
1.0.43 801 4/3/2022
1.0.42 820 3/17/2022
1.0.41 691 3/17/2022
1.0.40 814 3/3/2022
1.0.39 670 2/26/2022
1.0.38 695 2/23/2022
1.0.37 685 2/23/2022
1.0.36 751 2/19/2022
1.0.35 681 2/19/2022
Loading failed