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
<PackageReference Include="OoplesFinance.StockIndicators" Version="1.1.0" />
<PackageVersion Include="OoplesFinance.StockIndicators" Version="1.1.0" />
<PackageReference Include="OoplesFinance.StockIndicators" />
paket add OoplesFinance.StockIndicators --version 1.1.0
#r "nuget: OoplesFinance.StockIndicators, 1.1.0"
#:package OoplesFinance.StockIndicators@1.1.0
#addin nuget:?package=OoplesFinance.StockIndicators&version=1.1.0
#tool nuget:?package=OoplesFinance.StockIndicators&version=1.1.0
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.Strictrequires all series to share the sameEndTime. WithIncludeUpdates = 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>usesArrayPool<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() |
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 librarytests/unit testsbenchmarks/BenchmarkDotNet suiteexamples/developer console and examples
Additional docs
INDICATORS.mdlist of indicatorsOPTIMIZATIONS.mdoptimization backlog and notesMODERNIZATION_PLAN.mdroadmap for refactors and performance work
License
Apache 2.0. See LICENSE.txt.
| Product | Versions 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. |
-
.NETFramework 4.6.1
- Alpaca.Markets (>= 7.2.0)
- Alpaca.Markets.Extensions (>= 7.2.0)
- LinqOptimizer.CSharp (>= 0.7.0)
- MathNet.Numerics (>= 5.0.0)
- System.Buffers (>= 4.6.1)
- System.Memory (>= 4.6.3)
- System.Net.Http (>= 4.3.4)
- System.Text.Json (>= 10.0.12)
-
net10.0
- Alpaca.Markets (>= 7.2.0)
- Alpaca.Markets.Extensions (>= 7.2.0)
- LinqOptimizer.CSharp (>= 0.7.0)
- MathNet.Numerics (>= 5.0.0)
-
net8.0
- Alpaca.Markets (>= 7.2.0)
- Alpaca.Markets.Extensions (>= 7.2.0)
- LinqOptimizer.CSharp (>= 0.7.0)
- MathNet.Numerics (>= 5.0.0)
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 |