@alphacharts/indicators 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -2
  3. package/dist/all-_xzUPrL7.js +21 -0
  4. package/dist/all-_xzUPrL7.js.map +1 -0
  5. package/dist/all.js +10 -0
  6. package/dist/all.js.map +1 -0
  7. package/dist/define-XUgQMhSl.js +687 -0
  8. package/dist/define-XUgQMhSl.js.map +1 -0
  9. package/dist/index.js +744 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/registry-DdYYyZN_.js +53 -0
  12. package/dist/registry-DdYYyZN_.js.map +1 -0
  13. package/dist/runner-B53M0IsJ.js +237 -0
  14. package/dist/runner-B53M0IsJ.js.map +1 -0
  15. package/dist/studies.js +1 -0
  16. package/dist/tv-volume-BYSbfrVk.js +3454 -0
  17. package/dist/tv-volume-BYSbfrVk.js.map +1 -0
  18. package/dist/types/all.d.ts +3 -0
  19. package/dist/types/chart/marker-floor.d.ts +9 -0
  20. package/dist/types/chart/studies.d.ts +192 -0
  21. package/dist/types/define.d.ts +46 -0
  22. package/dist/types/engine/feed.d.ts +88 -0
  23. package/dist/types/engine/graph.d.ts +136 -0
  24. package/dist/types/engine/kernels.d.ts +9 -0
  25. package/dist/types/engine/study.d.ts +40 -0
  26. package/dist/types/index.d.ts +15 -0
  27. package/dist/types/registry.d.ts +7 -0
  28. package/dist/types/runner.d.ts +91 -0
  29. package/dist/types/studies/all.d.ts +2 -0
  30. package/dist/types/studies/cycle.d.ts +7 -0
  31. package/dist/types/studies/index.d.ts +13 -0
  32. package/dist/types/studies/ma.d.ts +26 -0
  33. package/dist/types/studies/math.d.ts +28 -0
  34. package/dist/types/studies/momentum.d.ts +30 -0
  35. package/dist/types/studies/overlap.d.ts +14 -0
  36. package/dist/types/studies/patterns.d.ts +62 -0
  37. package/dist/types/studies/price.d.ts +6 -0
  38. package/dist/types/studies/sar.d.ts +2 -0
  39. package/dist/types/studies/statistic.d.ts +9 -0
  40. package/dist/types/studies/tv-osc.d.ts +18 -0
  41. package/dist/types/studies/tv-trend.d.ts +20 -0
  42. package/dist/types/studies/tv-volume.d.ts +15 -0
  43. package/dist/types/studies/volatility.d.ts +3 -0
  44. package/dist/types/studies/volume.d.ts +3 -0
  45. package/dist/types/types.d.ts +169 -0
  46. package/dist/types/wasm.d.ts +2 -0
  47. package/dist/types/worker.d.ts +1 -0
  48. package/dist/worker.js +8 -0
  49. package/dist/worker.js.map +1 -0
  50. package/package.json +72 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alphabench
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,107 @@
1
- # Temporary Holding Version
1
+ # @alphacharts/indicators
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Technical studies for alphacharts: a typed study model, an incremental engine over the core's
4
+ columnar data, a Web Worker runner, chart integration through the core plugin API, and
5
+ 211 studies (TA-Lib's catalogue plus TradingView's built-ins).
6
+
7
+ ## Use
8
+
9
+ ```ts
10
+ import { addStudy } from "@alphacharts/indicators";
11
+ import { rsi, macd, bbands } from "@alphacharts/indicators/studies"; // import only what you use
12
+
13
+ const bb = addStudy(chart, candles, bbands, { length: 20 }); // overlay on the candles' pane
14
+ const r = addStudy(chart, candles, rsi, { length: 14 }); // own pane
15
+ addStudy(chart, candles, "obv", {}, { volume: volumeSeries }); // by id: needs registerAllStudies()
16
+ r.setInputs({ length: 21 }); r.setVisible(false); r.remove(); // re-parameterise, hide, remove
17
+ chart.saveState(); chart.restoreState(saved); // studies are in the layout (installStudies(chart) first on a fresh chart)
18
+ ```
19
+
20
+ `addStudy` throws on an unknown input, a value outside min/max/step type, or a source that lacks what the
21
+ study needs (volume, OHLC). `await study.ready()` resolves when the last computation is on the chart.
22
+ Subpaths: `.` (engine, runner, plugin), `./studies` (every study as a named export), `./all`
23
+ (`ALL_STUDIES`, `registerAllStudies`), `./worker` (worker entry). Studies are plain objects in group modules
24
+ (`src/studies/*.ts`), so a bundler keeps only those imported (one module per family rather than per study).
25
+
26
+ ## How it works
27
+
28
+ - **Model** (`src/types.ts`): `StudyDef { id, name, family, inputs (int/float/bool/source/select with default, min, max,
29
+ step, options), needs, pane: overlay|new, columns, plots (line, histogram, area, band, fill, hline, markers, zigzag),
30
+ build }`. `build(graph, params)` declares nodes and returns the output columns.
31
+ - **Engine** (`src/engine/graph.ts`): every value lives in a Float64Array column; a node writes only bar `i` and reads
32
+ `<= i` of its inputs and `<= i-1` of its own columns. So one code path is the batch (`run(0, n)`), an appended bar
33
+ (`run(n, n+1)`) and a tick revision (re-`run(n-1, n)`): per-bar cost does not depend on the series length
34
+ (O(1) for recursive and running-sum nodes, O(period) for window scans), and a revision recomputes from committed
35
+ state. Warm-up is NaN; absent values never become 0 (except where TA-Lib defines 0, noted in the study's reference).
36
+ setData/prepend on the source recompute fully.
37
+ - **Runner** (`src/runner.ts`): `createRunner(ids, factory)` gives a `WorkerRunner` on the worker `factory` makes (the host's bundler spells the worker URL out; the package never guesses it) (arrays transferred, chunks of 65,536
38
+ bars so a `cancel` is serviced within a chunk; protocol documented in the file header) or the `InlineRunner`
39
+ when no factory is given or the worker would not start. The worker returns every graph column; the main thread adopts them and keeps
40
+ streaming ticks. Studies not built in (defined at runtime) run inline (`canRun`).
41
+ - **Chart integration** (`src/chart/studies.ts`): one core series per drawn plot (pane = the source's for overlays,
42
+ the lowest free pane otherwise), the rest drawn on the overlay (fills, markers, ZigZag, lines shifted back in time
43
+ or joined across gaps; these do not take part in autoscale). Layout JSON is keyed by study instance id.
44
+ - **Outside series** (`src/engine/feed.ts`, `Study.setFeed` / `pushFeedBar`): a study can read bars the host gives it (another
45
+ instrument, another timeframe). Each is a graph of its own on its own bars; the study's graph reads it through a join by time
46
+ (`Graph.joined` / `external`): `"same"` is the bar with exactly the study bar's time, else NaN, `"closed"` the latest bar whose
47
+ open + length is not after the study bar's start (no value contains data from after its bar, and the live bar sees the last finished one).
48
+ Lookups walk forward from the last answer, so a run and a tick are O(1) amortised; `StudyEngine.pushFeed` re-evaluates the last bar only
49
+ when it sees a different row. Runners: `RunRequest.feeds`, `StudyRunner.feedBar`.
50
+ - **Bar colours, background shading, per-point line colour, left axis** (`src/chart/studies.ts`): `barcolor` plots colour the source's candles
51
+ (core `Series.setBarColors`; several studies compose on one source, the latest wins, removal restores exactly), `bgcolor` plots draw through core's
52
+ `BarShading` primitive (no part in autoscale), a line plot's `colorBy: { col, color }` colours its segments per bar, `StudyDef.scale: "left"`
53
+ puts the series on the left axis.
54
+ - **WASM** (`src/engine/kernels.ts`, `enableWasm()` from `src/wasm.ts`): optional batch kernels from `@alphacharts/wasm`
55
+ for running sum, variance/stddev, rolling highest/lowest (+ bars) and linear regression, the kernels that won at 1M
56
+ and 10M bars in its benchmark. They are bit-identical to the JS loops (test/wasm.test.ts runs every study both
57
+ ways). Not used: EMA/RMA/WMA (no win), chunked runs (worker), series under 4,096 bars.
58
+
59
+ ## Verification
60
+
61
+ Run `pnpm -C packages/indicators test`.
62
+ - **TA-Lib** (`test/talib.test.ts`; `atr` and `natr` are not here, see below): fixtures generated offline by `scripts/gen_fixtures.py` (TA-Lib 0.8.2 in a
63
+ Python venv, see below) from `fixtures/cases/*.json` on a seeded 400-bar OHLCV set (`fixtures/dataset.json`;
64
+ 3,000 bars of mixed candle shapes for the patterns). Compared at 1e-9 relative (+1e-12 absolute floor), NaN positions
65
+ must be identical; patterns are integers compared exactly. Case flags: `lead` (our study has valid values earlier
66
+ than TA-Lib's first output, e.g. MACD's line before its signal), `abs` (a value that is numerical noise around 0).
67
+ - **TradingView-only** studies: hand-computed fixtures with the arithmetic in the test comments, plus invariants
68
+ (`test/tv-*.test.ts`).
69
+ - **Incremental == batch** (`test/incremental.test.ts`): every study, seeded random appends with random last-bar
70
+ revisions, exact equality.
71
+ - Runner protocol, worker/inline parity and cancellation: `test/runner.test.ts`. Browser: `bench/tests/indicators.spec.ts`.
72
+
73
+ Where a study is not TA-Lib's arithmetic it says so in its `reference` field. Known differences:
74
+ `atr`/`natr` (and every ATR band: keltner, supertrend, chande-kroll-stop, median) are Wilder's: bar 0's true range is high - low and the first value is at bar `length - 1`, TA-Lib's skips bar 0 and starts one bar later (hand case and an independent loop in `test/atr.test.ts`; `trange` stays TA-Lib's); `rsi` (and `stochrsi`) report 100 where RS is 0/0, the first `length` changes all zero (TA-Lib: 0; charting packages: 100, `test/rsi-flat.test.ts`);
75
+ MACD/APO/PPO seed like TA-Lib (fast EMA at the slow EMA's first bar), TradingView seeds at bar 0 (gap < 1e-9 after
76
+ ~150 bars); `cmo` defaults to TradingView's window-sum form (`method: "wilder"` is TA-Lib's); the linear regression
77
+ studies are TA-Lib's (checked against numpy polyfit); `ht-trendmode` reports TA-Lib's 0 inside its warm-up;
78
+ `mavp` (TA-Lib's moving average with a per-bar period series) is not implemented: it needs a second input series.
79
+ Not checked against TradingView itself (no access): the TradingView-only studies follow the definitions cited in their
80
+ `reference` field; pivot formulas, Klinger, Schaff, the Relative Volatility form and Fractals tie rules were written
81
+ from memory of TradingView's scripts and are verified only by the hand-computed tests.
82
+
83
+ ## Studies (211)
84
+
85
+ - **Overlap (TA-Lib + MAs, bands, SAR)** (17): `sma`, `ema`, `wma`, `dema`, `tema`, `trima`, `kama`, `t3`, `ma`, `bbands`, `bbpb`, `bbw`, `midpoint`, `midprice`, `mama`, `sar`, `sarext`
86
+ - **Momentum (TA-Lib)** (30): `rsi`, `cmo`, `mom`, `roc`, `rocp`, `rocr`, `rocr100`, `trix`, `apo`, `ppo`, `macd`, `macdfix`, `macdext`, `stoch`, `stochf`, `stochrsi`, `willr`, `cci`, `bop`, `mfi`, `ultosc`, `aroon`, `aroonosc`, `adx`, `adxr`, `dx`, `plus-di`, `minus-di`, `plus-dm`, `minus-dm`
87
+ - **Volume (TA-Lib)** (3): `ad`, `adosc`, `obv`
88
+ - **Volatility** (3): `trange` (TA-Lib's), `atr`, `natr` (Wilder's, not TA-Lib's)
89
+ - **Price transforms (TA-Lib)** (4): `avgprice`, `medprice`, `typprice`, `wclprice`
90
+ - **Statistics (TA-Lib)** (9): `linearreg`, `linearreg-slope`, `linearreg-intercept`, `linearreg-angle`, `tsf`, `stddev`, `var`, `correl`, `beta`
91
+ - **Math transforms and operators (TA-Lib)** (26): `acos`, `asin`, `atan`, `ceil`, `cos`, `cosh`, `exp`, `floor`, `ln`, `log10`, `sin`, `sinh`, `sqrt`, `tan`, `tanh`, `add`, `sub`, `mult`, `div`, `max`, `min`, `sum`, `maxindex`, `minindex`, `minmax`, `minmaxindex`
92
+ - **Cycle / Hilbert (TA-Lib)** (6): `ht-dcperiod`, `ht-dcphase`, `ht-phasor`, `ht-sine`, `ht-trendline`, `ht-trendmode`
93
+ - **TradingView-only** (52): `smma`, `vwma`, `hma`, `alma`, `lsma`, `mcginley`, `ma-ribbon`, `ma-cross`, `envelopes`, `keltner`, `donchian`, `price-channel`, `chande-kroll-stop`, `linreg-channel`, `median`, `ichimoku`, `supertrend`, `alligator`, `fractals`, `zigzag`, `fisher`, `kst`, `tsi`, `vortex`, `coppock`, `connors-rsi`, `choppiness`, `rvi-vigor`, `rvi-volatility`, `smi-ergodic`, `rank-corr`, `bull-bear-power`, `awesome`, `accelerator`, `mass-index`, `dpo`, `schaff-tc`, `stoch-tv`, `volume`, `volume-osc`, `pvt`, `net-volume`, `elder-force`, `eom`, `chaikin-mf`, `klinger`, `vwap` (Session VWAP), `anchored-vwap`, `pivot-points`, `hist-vol`, `adr`, `bars-per-year`
94
+ - **Candlestick patterns (TA-Lib CDL*)** (61): `cdl-<name>` for every TA-Lib pattern, e.g. `cdl-2crows`, `cdl-3blackcrows`, `cdl-3inside`, `cdl-3linestrike`, `cdl-3outside`, ...
95
+
96
+ ## Regenerating the TA-Lib fixtures
97
+
98
+ Only needed when a case changes. From `packages/indicators`:
99
+
100
+ ```
101
+ python -m venv .venv && . .venv/bin/activate
102
+ pip install TA-Lib numpy # TA-Lib needs the C library (brew install ta-lib)
103
+ pnpm fixtures # python scripts/gen_fixtures.py [group ...]
104
+ python scripts/gen_cdl_dataset.py # only to rebuild fixtures/dataset-cdl.json
105
+ ```
106
+
107
+ The fixtures are TA-Lib's output (BSD licence), see `NOTICE` at the repository root.
@@ -0,0 +1,21 @@
1
+ import { $ as e, N as t, Nn as n, Nr as r, dn as i, er as a, f as o, fr as s, gr as c, ln as l, or as u, pn as d, ri as f } from "./tv-volume-BYSbfrVk.js";
2
+ //#region src/studies/all.ts
3
+ var p = (e) => typeof e == "object" && !!e && "build" in e && "columns" in e && "plots" in e, m = [
4
+ f,
5
+ r,
6
+ c,
7
+ s,
8
+ u,
9
+ a,
10
+ n,
11
+ d,
12
+ i,
13
+ l,
14
+ e,
15
+ t,
16
+ o
17
+ ].flatMap((e) => Object.values(e).filter(p));
18
+ //#endregion
19
+ export { m as t };
20
+
21
+ //# sourceMappingURL=all-_xzUPrL7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"all-_xzUPrL7.js","names":["overlap","momentum","volume","volatility","price","statistic","math","cycle","sar","patterns","tvTrend","tvOsc","tvVolume","tv_trend_exports","tv_osc_exports","math_exports","momentum_exports","sar_exports","statistic_exports","tv_volume_exports","volatility_exports","volume_exports","patterns_exports","price_exports","cycle_exports","overlap_exports","isStudy","x","ALL_STUDIES","m"],"sources":["../src/studies/all.ts"],"sourcesContent":["// Every built-in study, collected from the group modules. Importing this pulls\n// them all in; import single studies from their group module to stay small.\n\nimport type { StudyDef } from \"../types.js\";\nimport * as overlap from \"./overlap.js\";\nimport * as momentum from \"./momentum.js\";\nimport * as volume from \"./volume.js\";\nimport * as volatility from \"./volatility.js\";\nimport * as price from \"./price.js\";\nimport * as statistic from \"./statistic.js\";\nimport * as math from \"./math.js\";\nimport * as cycle from \"./cycle.js\";\nimport * as sar from \"./sar.js\";\nimport * as patterns from \"./patterns.js\";\nimport * as tvTrend from \"./tv-trend.js\";\nimport * as tvOsc from \"./tv-osc.js\";\nimport * as tvVolume from \"./tv-volume.js\";\n\nconst isStudy = (x: unknown): x is StudyDef => typeof x === \"object\" && x !== null && \"build\" in x && \"columns\" in x && \"plots\" in x;\n\nexport const ALL_STUDIES: readonly StudyDef[] = [overlap, momentum, volume, volatility, price, statistic, math, cycle, sar, patterns, tvTrend, tvOsc, tvVolume].flatMap((m) =>\n Object.values(m).filter(isStudy),\n);\n"],"mappings":";;AAkBA,IAAM0B,KAAW,MAA8B,OAAOC,KAAM,cAAYA,KAAc,WAAWA,KAAK,aAAaA,KAAK,WAAWA,GAEtHC,IAAmC;CAAC5B;CAASC;CAAUC;CAAQC;CAAYC;CAAOC;CAAWC;CAAMC;CAAOC;CAAKC;CAAUC;CAASC;CAAOC;AAAQ,CAAC,CAAC,SAAS,MACvK,OAAO,OAAOiB,CAAC,CAAC,CAAC,OAAOH,CAAO,CACjC"}
package/dist/all.js ADDED
@@ -0,0 +1,10 @@
1
+ import { r as e } from "./registry-DdYYyZN_.js";
2
+ import { t } from "./all-_xzUPrL7.js";
3
+ //#region src/all.ts
4
+ function n() {
5
+ e(t);
6
+ }
7
+ //#endregion
8
+ export { t as ALL_STUDIES, n as registerAllStudies };
9
+
10
+ //# sourceMappingURL=all.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"all.js","names":["registerStudies","registerAllStudies","ALL_STUDIES"],"sources":["../src/all.ts"],"sourcesContent":["// Everything: the study list and a registrar. Importing this module pulls every\n// study in; import single studies from \"@alphacharts/indicators/studies\" to stay small.\nimport { registerStudies } from \"./registry.js\";\nimport { ALL_STUDIES } from \"./studies/all.js\";\n\nexport { ALL_STUDIES };\nexport function registerAllStudies(): void {\n registerStudies(ALL_STUDIES);\n}\n"],"mappings":";;;AAMA,SAAgBC,IAA2B;CACzC,EAAgBC,CAAW;AAC7B"}