Skip to content

feat: add benchmarks, real-time and backtesting samples, and the v1.0 docs - #104

Merged
phmatray merged 3 commits into
devfrom
feat/v1-launch-benchmarks-samples-docs
Jul 27, 2026
Merged

feat: add benchmarks, real-time and backtesting samples, and the v1.0 docs#104
phmatray merged 3 commits into
devfrom
feat/v1-launch-benchmarks-samples-docs

Conversation

@phmatray

Copy link
Copy Markdown
Owner

Delivers the technical track of the launch checklist in #63. The marketing and monetization items are out of scope here.

Build 0 errors, 129 warnings against a 132 baseline (3 fewer)
Tests 1118 passing, 0 failed, 0 skipped
Change set 115 files, +22,218 / −34
New packages none — only BenchmarkDotNet and Microsoft.AspNetCore.SignalR.Client, both already registered

Performance benchmarks vs TA-Lib C

119 BenchmarkDotNet methods over 1k/10k/100k-bar series. Every indicator is measured twice — once through TAFunc with caller-supplied buffers (algorithm cost, ~0 B allocated) and once through TAMath (algorithm plus per-call allocation) — so the ergonomic API's cost is visible rather than blended in. Candlestick patterns run over double, float and decimal to price the generic-math design.

The comparison against native TA-Lib is opt-in and never required. A DllImportResolver probes 19 candidate names and paths; when nothing loads, the suite prints a banner and removes NativeComparisonBenchmarks from the runnable set. Where it does run, [GlobalSetup] asserts managed and native agree to 1e-9 before anything is timed — a faster result can never come from computing the wrong thing.

Real-time streaming sample

Deterministic tick feed → bar aggregator → rolling indicator engine, exposed over both a SignalR hub and a raw WebSocket endpoint, with a dependency-free browser dashboard (no CDN, fully inlined) and a console SignalR client.

Backtesting sample

Bar-driven engine with commission/slippage in basis points, five strategies plus a buy-and-hold baseline, and metrics covering CAGR, max drawdown, Sharpe, Sortino, Calmar, profit factor and expectancy. 220 unit tests.

On alignment

Both samples route every TA-Lib result through a single alignment-owning type. Output element k describes bar BegIdx + k; treating the two index spaces as interchangeable silently shifts every signal in time. The backtester goes further and clamps BegIdx/NBElement to the visible window, so a strategy cannot read a future bar even through the metadata — no-look-ahead is structural, not conventional.

Documentation

Indicator catalogue generated from source by tools/generate-indicator-catalog.py (98 function entry points, 61 candlestick patterns — the "200+" figure counts overloads), plus getting-started, TradingView integration, benchmarks, real-time and backtesting guides. The generator has a --check mode that exits 1 when the catalogue is stale; it is not yet wired into CI.

Published benchmark numbers were reproduced independently: the guide's --job Short table re-ran at 94.07 s vs the documented 94.37 s, with every row matching.


⚠️ Found while validating: TAFunc.Atr is broken in the shipping library

Not introduced by this PR — pre-existing, and it affects a released package.

src/TechnicalAnalysis.Functions/Atr/TAFunc.cs, main output loop:

prevATR *= optInTimePeriod - 1;
prevATR += tempBuffer[today];
today++;
outReal[outIdx] = prevATR / optInTimePeriod;   // divides the OUTPUT, never the STATE

prevATR is never divided, so it compounds by (period - 1) every bar. The same file's own warm-up loop does it correctly, and sibling Natr does it correctly.

Measured on a series whose true range is a constant 4.0, where ATR(14) must be exactly 4.0 forever:

k=0  atr=4.2857     k=2  atr=57.59      k=4  atr=9737.02
k=1  atr=4.4082     k=3  atr=748.98     ...ratio exactly 13.0 = period - 1

On a 1500-bar random walk, 1209 of 1486 outputs are +Infinity. Natr on the same data is stable. AtrTests.cs misses it because it asserts only RetCode == Success, never a value.

Consequences visible in this PR: the real-time dashboard publishes atr values like 6.27e+63, and the backtester's MACD + 3xATR row is effectively plain MACD because the trailing stop is inert once ATR overflows. The samples report faithfully what the library returns, and say so in their guides.

The fix is one line, but it belongs in its own PR with a value-asserting regression test rather than buried here.

Closes nothing on its own — #63 stays open for the remaining checklist items.

🤖 Generated with Claude Code

phmatray and others added 3 commits July 27, 2026 13:43
Adds the empty project skeletons, central package versions and solution
entries for the v1.0 launch work tracked in #63. Implementations land in
follow-up commits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The push trigger and the pre-release branch still named `develop`, a branch
this repository does not have, so no push to the default branch has ever run
CI. #101 fixed the pull_request trigger only.

Also carries over the .gitignore entries from the abandoned
WIP-feature-backup-20260218 branch before that branch is deleted.

Refs #63

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… docs

Delivers the technical track of the launch checklist in #63.

Benchmarks (benchmarks/TechnicalAnalysis.Benchmarks)
119 BenchmarkDotNet methods over series of 1k/10k/100k bars, every indicator
measured twice: once through TAFunc with caller-supplied buffers (algorithm
cost) and once through TAMath (algorithm plus allocation). Candlestick
patterns are exercised over double, float and decimal to price the
generic-math design. The managed-versus-C comparison is opt-in: a
DllImportResolver probes 19 candidate names and paths for the native
TA-Lib, and when it finds nothing the suite prints a banner and removes
NativeComparisonBenchmarks from the runnable set, so the whole thing works
offline with no native dependency. Where the comparison does run, both
implementations are asserted equivalent in [GlobalSetup] before anything is
timed, so a faster result can never come from computing the wrong thing.

Real-time sample (samples/TechnicalAnalysis.Samples.RealTime[.Client])
Deterministic tick feed, bar aggregator, and a rolling indicator engine
behind both a SignalR hub and a raw WebSocket endpoint, plus a
dependency-free browser dashboard and a console SignalR client.

Backtesting sample (samples/TechnicalAnalysis.Samples.Backtesting)
Bar-driven engine with commission and slippage, five strategies and a
buy-and-hold baseline, and a metrics suite covering CAGR, drawdown, Sharpe,
Sortino, Calmar, profit factor and expectancy. 220 unit tests.

Both samples route every TA-Lib result through a single alignment-owning
type, because output element k describes bar BegIdx + k and treating the two
index spaces as interchangeable silently shifts every signal in time. The
backtester goes further and clamps that metadata to the visible window, so a
strategy cannot read a future bar even through BegIdx and NBElement.

Docs
Complete indicator catalogue generated from source by
tools/generate-indicator-catalog.py (98 function entry points, 61
candlestick patterns), plus getting-started, TradingView integration,
benchmarks, real-time and backtesting guides.

0 errors, 1118 tests passing, and 129 warnings against a 132 baseline.

Refs #63

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@phmatray
phmatray merged commit 25f4818 into dev Jul 27, 2026
8 of 9 checks passed
@phmatray
phmatray deleted the feat/v1-launch-benchmarks-samples-docs branch July 27, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant