@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
@@ -0,0 +1,3 @@
1
+ import { ALL_STUDIES } from "./studies/all.js";
2
+ export { ALL_STUDIES };
3
+ export declare function registerAllStudies(): void;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The first bar index whose mark is kept, over bars `[0, n)` of `col`: marks at
3
+ * or after it are drawn, those before are not. 0 when the plot asks for no limit.
4
+ */
5
+ export declare function markerFloor(col: ArrayLike<number>, n: number, o: {
6
+ last?: number;
7
+ within?: number;
8
+ text?: boolean;
9
+ }): number;
@@ -0,0 +1,192 @@
1
+ import { type Chart, type ChartPlugin, type DataChange, type PluginApi, type PluginPaneView, type Series, type SeriesType } from "@alphacharts/core";
2
+ import { type FeedBar, type FeedBars, type FeedConfig } from "../engine/feed.js";
3
+ import { type StudyRunner } from "../runner.js";
4
+ import type { InputValue, Params, StudyDef } from "../types.js";
5
+ export declare const PLUGIN_ID = "alphacharts.studies";
6
+ type AnySeries = Series<SeriesType>;
7
+ type SourceSeries = Series<"candles" | "bars" | "line" | "area" | "histogram">;
8
+ export interface AddStudyOptions {
9
+ /** Volume series (histogram or line) for studies that need volume. */
10
+ volume?: Series<"histogram" | "line" | "area">;
11
+ /** Instance id; default `s<n>`. Layout state is keyed by it. */
12
+ id?: string;
13
+ /** Batch evaluation off the UI thread; ticks always run inline. */
14
+ runner?: StudyRunner;
15
+ /** Called once when the study is dropped or disposed (removed, source removed, layout restored over it). */
16
+ onDispose?: () => void;
17
+ /**
18
+ * Whether `StudiesPlugin.toJSON` saves this study (default true). A study
19
+ * that something else rebuilds on restore (a script, through
20
+ * `restoreScript`) sets it false: the plugin could not recreate it, and a
21
+ * layout holding it would be refused.
22
+ */
23
+ persist?: boolean;
24
+ }
25
+ export interface PlotStyle {
26
+ color?: string;
27
+ width?: number;
28
+ }
29
+ export declare class Study {
30
+ private readonly host;
31
+ readonly id: string;
32
+ private readonly source;
33
+ private readonly volume;
34
+ private readonly ownRunner?;
35
+ private readonly onDispose?;
36
+ readonly def: StudyDef;
37
+ private params;
38
+ private engine;
39
+ private outs;
40
+ private lines;
41
+ /** Background shading: one primitive per bgcolor plot, attached to the series of the pane it shades. */
42
+ private shades;
43
+ /** Outside series the host gave this study, by key. */
44
+ private feeds;
45
+ private sameScale;
46
+ private styles;
47
+ private shown;
48
+ private pane;
49
+ private scaleId;
50
+ private version;
51
+ private job;
52
+ /** A tick arrived while a job was pending (caught up on adoption). */
53
+ private dirty;
54
+ /** A replace/prepend arrived while a job was pending: the job's data is stale. */
55
+ private structural;
56
+ /** Set when a computation failed: the study stops following the source and says why. */
57
+ error: Error | null;
58
+ private ready_;
59
+ disposed: boolean;
60
+ /** Decimals a separate-pane study's axis and labels show when its def does not set `minMove`: two, more when the values are small. */
61
+ private decimals;
62
+ /** Whether the plugin saves this study (see `AddStudyOptions.persist`). */
63
+ persist: boolean;
64
+ inputs(): Params;
65
+ get visible(): boolean;
66
+ /** Resolves when the last requested computation has been applied to the chart. */
67
+ ready(): Promise<void>;
68
+ /**
69
+ * Redraw from the engine's columns after they were written from outside (a
70
+ * computation that arrives late, e.g. a script worker): the last bar when
71
+ * `onlyLast`, else every bar.
72
+ */
73
+ refresh(onlyLast?: boolean): void;
74
+ /** Output series, in plot order. */
75
+ series(): readonly AnySeries[];
76
+ /**
77
+ * Change inputs; unspecified ones keep their value, and the keys in `reset` go back to their defaults (a preset that
78
+ * has no value for an input the study has). Validated before anything changes. Outside series the study was given stay.
79
+ */
80
+ setInputs(partial: Readonly<Record<string, InputValue>>, reset?: readonly string[]): void;
81
+ setVisible(visible: boolean): void;
82
+ setStyle(plotKey: string, style: PlotStyle): void;
83
+ remove(): void;
84
+ /** The last computed value of each column (undefined while in warm-up), by column name. */
85
+ lastValues(): Record<string, number | undefined>;
86
+ /** Output column `name` over the source bars (a view: copy it to keep it). */
87
+ column(name: string): Float64Array;
88
+ /** Bar times of the columns. */
89
+ times(): Float64Array;
90
+ /** The study as saved: its id, definition, inputs, source series ids, visibility and styles. `StudiesPlugin.toJSON` is a list of these. */
91
+ state(): {
92
+ visible: boolean;
93
+ styles: Record<string, PlotStyle>;
94
+ volume?: string;
95
+ id: string;
96
+ study: string;
97
+ inputs: Record<string, InputValue>;
98
+ source: string;
99
+ };
100
+ /**
101
+ * Give the study outside series `key` (see engine/feed.ts for what a feed is and how it is joined), replacing the
102
+ * previous bars of that key, and compute everything again. The study keeps its own copy.
103
+ */
104
+ setFeed(key: string, bars: FeedBars, cfg: FeedConfig): void;
105
+ /** Keys of the outside series given so far. */
106
+ feedKeys(): string[];
107
+ /**
108
+ * A live bar of outside series `key` (same time as its last bar revises it, a later time appends). Only the study's
109
+ * last bar is evaluated again, and only when it sees a different feed bar through the join; otherwise nothing is
110
+ * recomputed or redrawn.
111
+ */
112
+ pushFeedBar(key: string, bar: FeedBar): void;
113
+ get isShown(): boolean;
114
+ private get ohlc();
115
+ private checkSource;
116
+ /** Whether `d` is the store of one of this study's inputs. */
117
+ private watches;
118
+ private bars;
119
+ private barAt;
120
+ /** Recompute everything from the source's bars. */
121
+ private recompute;
122
+ /** Bring an adopted engine up to the source's current last bars (ticks that arrived during the job). */
123
+ private catchUp;
124
+ private fullLoad;
125
+ private title;
126
+ private retitle;
127
+ private color;
128
+ private seriesOptions;
129
+ private createSeries;
130
+ private syncAll;
131
+ /** Background shades and bar colours, from the engine's columns (every bar). */
132
+ private syncOverlays;
133
+ private writeSeries;
134
+ /** Show enough decimals for values below 1 (three significant digits); only ever grows. A def's own `minMove` wins; an overlay reads like its source. */
135
+ private widen;
136
+ /** The output series that draws `column`, or undefined when no plot does (a column that is only an input to others). */
137
+ seriesOfColumn(column: string): AnySeries | undefined;
138
+ /** After one appended or revised bar: one point per output series. */
139
+ private tickSync;
140
+ private drawMarkers;
141
+ }
142
+ export declare class StudiesPlugin implements ChartPlugin {
143
+ readonly chart: Chart;
144
+ readonly runner: StudyRunner | undefined;
145
+ private readonly resolve;
146
+ readonly id = "alphacharts.studies";
147
+ api: PluginApi | null;
148
+ private readonly studies;
149
+ private counter;
150
+ /** Sources whose bars the plugin has coloured: the plugin owns their colour column while a barcolor study exists. */
151
+ private readonly coloured;
152
+ constructor(chart: Chart, runner: StudyRunner | undefined, resolve: (id: string) => StudyDef);
153
+ install(api: PluginApi): void;
154
+ uninstall(): void;
155
+ dataChanged(c: DataChange): void;
156
+ drawPane(ctx: CanvasRenderingContext2D, view: PluginPaneView): void;
157
+ list(): Study[];
158
+ get(id: string): Study | undefined;
159
+ /** Lowest pane index >= 1 holding no series (reuses a gap), else a new pane. */
160
+ freePane(): number;
161
+ add(def: StudyDef, inputs: Readonly<Record<string, InputValue>>, source: SourceSeries, o?: AddStudyOptions, saved?: {
162
+ visible?: boolean;
163
+ styles?: Record<string, PlotStyle>;
164
+ }): Study;
165
+ removeStudy(id: string): void;
166
+ /** Drop studies whose source series was removed from the chart. */
167
+ private reap;
168
+ /** Never hand out an id at or below `n` again: ids of removed studies stay retired, so an alert that still names one cannot land on a different study. */
169
+ reserveIds(n: number): void;
170
+ /** The highest `s<n>` number handed out or restored so far. */
171
+ idHighWater(): number;
172
+ private nextId;
173
+ toJSON(): unknown;
174
+ /** Throws what `fromJSON(state)` would, changing nothing: the layout is refused whole when a study cannot be rebuilt. */
175
+ validateState(state: unknown, ctx: {
176
+ seriesIds: readonly string[];
177
+ }): void;
178
+ /** Called after the chart restored its series: rebuild each study over the restored series. */
179
+ fromJSON(state: unknown): void;
180
+ }
181
+ /** Install the studies plugin on a chart (idempotent). Restoring a layout that holds studies needs this first. */
182
+ export declare function installStudies(chart: Chart, opts?: {
183
+ runner?: StudyRunner;
184
+ resolve?: (id: string) => StudyDef;
185
+ }): StudiesPlugin;
186
+ /**
187
+ * Add a study over `source`. `study` is a definition (tree-shakable) or the id of
188
+ * a registered one. Throws on unknown inputs, out-of-range values or a source
189
+ * that lacks what the study needs.
190
+ */
191
+ export declare function addStudy(chart: Chart, source: SourceSeries, study: StudyDef | string, inputs?: Readonly<Record<string, InputValue>>, opts?: AddStudyOptions): Study;
192
+ export {};
@@ -0,0 +1,46 @@
1
+ import type { InputDef, LineStyleName, MarkerShape, PlotDef, StudyDef } from "./types.js";
2
+ export declare const int: (key: string, def: number, min?: number, max?: number, label?: string) => InputDef;
3
+ export declare const float: (key: string, def: number, min?: number, max?: number, step?: number, label?: string) => InputDef;
4
+ export declare const bool: (key: string, def: boolean, label?: string) => InputDef;
5
+ export declare const select: (key: string, def: string, options: readonly string[], label?: string) => InputDef;
6
+ export declare const source: (def?: string, key?: string) => InputDef;
7
+ export declare const COLORS: {
8
+ readonly blue: "#2962FF";
9
+ readonly orange: "#FF6D00";
10
+ readonly red: "#F23645";
11
+ readonly green: "#089981";
12
+ readonly purple: "#9C27B0";
13
+ readonly teal: "#00897B";
14
+ readonly yellow: "#FBC02D";
15
+ readonly pink: "#E91E63";
16
+ readonly gray: "#787B86";
17
+ readonly lime: "#8BC34A";
18
+ readonly cyan: "#00BCD4";
19
+ readonly brown: "#795548";
20
+ };
21
+ export declare const line: (col: string, color: string, o?: {
22
+ title?: string;
23
+ key?: string;
24
+ width?: number;
25
+ style?: LineStyleName;
26
+ offset?: number;
27
+ connect?: boolean;
28
+ }) => PlotDef;
29
+ export declare const hist: (col: string, color: string, colorDown?: string, o?: {
30
+ title?: string;
31
+ key?: string;
32
+ }) => PlotDef;
33
+ export declare const areaPlot: (col: string, color: string, o?: {
34
+ title?: string;
35
+ key?: string;
36
+ }) => PlotDef;
37
+ export declare const band: (key: string, upper: string, lower: string, color: string, fill: string, title?: string) => PlotDef;
38
+ export declare const fillBetween: (key: string, a: string, b: string, color: string, title?: string) => PlotDef;
39
+ export declare const hline: (key: string, value: number | ((p: import("./types.js").Params) => number), color?: string, style?: LineStyleName) => PlotDef;
40
+ export declare const markers: (col: string, shape: MarkerShape, position: "value" | "above" | "below" | "sign", color: string, o?: {
41
+ title?: string;
42
+ key?: string;
43
+ offset?: number;
44
+ text?: boolean;
45
+ }) => PlotDef;
46
+ export declare function defineStudy(def: StudyDef): StudyDef;
@@ -0,0 +1,88 @@
1
+ import { Graph } from "./graph.js";
2
+ export type FeedField = "open" | "high" | "low" | "close" | "volume";
3
+ export declare const FEED_FIELDS: readonly FeedField[];
4
+ export type FeedAlign = "same" | "closed";
5
+ export interface FeedConfig {
6
+ align: FeedAlign;
7
+ /** Nominal length of one feed bar, ms. Used by "closed" (> 0); ignored by "same". */
8
+ spanMs: number;
9
+ }
10
+ /** Columnar bars of a feed, in the feed's own time. */
11
+ export interface FeedBars {
12
+ time: Float64Array;
13
+ open: Float64Array;
14
+ high: Float64Array;
15
+ low: Float64Array;
16
+ close: Float64Array;
17
+ volume: Float64Array;
18
+ }
19
+ export interface FeedBar {
20
+ time: number;
21
+ open: number;
22
+ high: number;
23
+ low: number;
24
+ close: number;
25
+ volume: number;
26
+ }
27
+ /** A feed as sent to a runner: its bars and how to join them. */
28
+ export interface FeedData extends FeedConfig {
29
+ bars: FeedBars;
30
+ }
31
+ export type FeedChange = "appended" | "revised";
32
+ /**
33
+ * A feed is a graph of its own whose bars are the other series' bars: its base columns (time, open, high, low, close,
34
+ * volume) are the series itself, and whatever is computed from it (a script's `ta.rsi(outside(...), 14)`) is computed
35
+ * on those bars, so a window of 14 is 14 of the series' bars whatever the chart's timeframe. The chart's graph reads
36
+ * the result through the join (`Graph.joined`). Loading or ticking the feed evaluates its graph at once.
37
+ */
38
+ export declare class Feed {
39
+ readonly graph: Graph;
40
+ align: FeedAlign;
41
+ spanMs: number;
42
+ private cur;
43
+ get n(): number;
44
+ /** Replace the bars (copied) and the join. Throws, naming `what`, on bars that are not a series. */
45
+ load(bars: FeedBars, cfg: FeedConfig, what?: string): void;
46
+ /** Change how the bars are joined, keeping them. */
47
+ configure(cfg: FeedConfig, what?: string): void;
48
+ /** A live bar: the same time as the last bar replaces it, a later time appends; an earlier time throws. */
49
+ apply(b: FeedBar, what?: string): FeedChange;
50
+ /** The bars as a runner takes them (copies, length n). */
51
+ snapshot(): FeedBars;
52
+ /** Index of the feed bar the study bar at time `t` sees, or -1 when it sees none. */
53
+ lookup(t: number): number;
54
+ value(field: FeedField, i: number): number;
55
+ }
56
+ /** Row `field` of every bar in `feed` joined onto `times`, as a plain array: the join in one call (for tests and tools). */
57
+ export declare function joinFeed(feed: Feed, field: FeedField, times: ArrayLike<number>): Float64Array;
58
+ export type JoinReason =
59
+ /** Every chart bar sees a feed bar. */
60
+ "complete"
61
+ /** Some chart bars see a feed bar and some do not (holes in the feed, or the feed starts later or ends earlier). */
62
+ | "partial"
63
+ /** The join finds nothing: the chart has no bars, or the feed has none. */
64
+ | "empty"
65
+ /** Same-length join and the feed's bar times never equal the chart's: the two sit on different clocks (offsetMs). */
66
+ | "different-clock"
67
+ /** Longer-timeframe join and no feed bar has finished by any chart bar (the feed starts after the chart ends, or is too short). */
68
+ | "no-finished-bar";
69
+ export interface JoinReport {
70
+ align: FeedAlign;
71
+ chartBars: number;
72
+ /** Chart bars that see a feed bar. A chart bar that sees none is blank (NaN) in everything read from the feed. */
73
+ matched: number;
74
+ reason: JoinReason;
75
+ /**
76
+ * For "different-clock": how far the feed's bars sit from the chart's, in ms, in (-spanMs/2, spanMs/2]: the first feed bar's
77
+ * time minus the first chart bar's, modulo the bar length. NSE hourly bars open at 09:15 and MCX's at 09:00, so a chart on one
78
+ * reading the other gives +900000 or -900000. Otherwise null.
79
+ */
80
+ offsetMs: number | null;
81
+ /** One sentence a host can show where it would otherwise show "blank". */
82
+ message: string;
83
+ }
84
+ /**
85
+ * Why a join gives what it gives, for a host to explain a blank outside series: how many chart bars `cfg` joins to a bar of `feedTimes`
86
+ * and, when none, the reason. A pure function of the two time columns and the config handed to `setFeed`; it reads no value.
87
+ */
88
+ export declare function describeJoin(chartTimes: ArrayLike<number>, feedTimes: ArrayLike<number>, cfg: FeedConfig): JoinReport;
@@ -0,0 +1,136 @@
1
+ import { Feed, type FeedField } from "./feed.js";
2
+ export type Ref = Col | number;
3
+ export declare class Col {
4
+ v: Float64Array;
5
+ constructor(cap: number);
6
+ }
7
+ export declare class Node extends Col {
8
+ /** Evaluate bars [from, to). */
9
+ run: (from: number, to: number) => void;
10
+ }
11
+ export type SourceName = "open" | "high" | "low" | "close" | "hl2" | "hlc3" | "ohlc4" | "hlcc4";
12
+ export declare const SOURCE_NAMES: readonly SourceName[];
13
+ export declare class Graph {
14
+ /** Bars currently loaded. */
15
+ n: number;
16
+ private cap;
17
+ /** Every column (inputs, aux, nodes) in creation order: the unit of transfer between threads. */
18
+ readonly cols: Col[];
19
+ private readonly nodes;
20
+ readonly time: Col;
21
+ readonly open: Col;
22
+ readonly high: Col;
23
+ readonly low: Col;
24
+ readonly close: Col;
25
+ readonly volume: Col;
26
+ private readonly sources;
27
+ /** Outside series by key: bars given by the host, joined onto this graph's bars by `external`. */
28
+ readonly feeds: Map<string, Feed>;
29
+ constructor(cap?: number);
30
+ /** An auxiliary column (state a node keeps per bar). */
31
+ col(): Col;
32
+ /** Make room for `n` bars; existing values are kept. */
33
+ reserve(n: number): void;
34
+ /** Evaluate bars [from, to) on every node. `to` bars must already be loaded. */
35
+ run(from: number, to: number): void;
36
+ /** Forget all values (new data is loaded from bar 0). */
37
+ clear(): void;
38
+ /** Replace every column by transferred buffers (same builder, same order). */
39
+ adopt(buffers: readonly Float64Array[], n: number): void;
40
+ /** A node whose evaluation is `body`; the body reads other columns' `.v` when it runs. */
41
+ node(body: (self: Node, from: number, to: number) => void): Node;
42
+ /** Per-bar function of the index; it may read any column at <= i (own columns at <= i-1). */
43
+ calc(fn: (i: number) => number): Node;
44
+ /** A constant as a column-compatible value. */
45
+ private val;
46
+ src(name: SourceName): Node;
47
+ /** The feed `key` (created empty on first use: its bars arrive from the host, and until they do every join is absent). */
48
+ feed(key: string): Feed;
49
+ /** Field `field` of feed `key` joined onto this graph's bars (see feed.ts for the two joins). NaN where the join finds no bar. */
50
+ external(key: string, field: FeedField): Node;
51
+ /**
52
+ * A column of feed `key`'s own graph (its base columns by name, or anything computed on its bars) read through the
53
+ * join: at each bar of this graph, the value at the feed row the join finds, NaN when it finds none.
54
+ */
55
+ joined(key: string, col: Col | FeedField): Node;
56
+ map(x: Col, f: (x: number) => number): Node;
57
+ map2(x: Ref, y: Ref, f: (x: number, y: number) => number): Node;
58
+ add(x: Ref, y: Ref): Node;
59
+ sub(x: Ref, y: Ref): Node;
60
+ mul(x: Ref, y: Ref): Node;
61
+ /** x / y; a zero divisor gives NaN (absent), never Infinity. */
62
+ div(x: Ref, y: Ref): Node;
63
+ max2(x: Ref, y: Ref): Node;
64
+ min2(x: Ref, y: Ref): Node;
65
+ abs(x: Col): Node;
66
+ neg(x: Col): Node;
67
+ lag(x: Col, k: number): Node;
68
+ change(x: Col, k?: number): Node;
69
+ /** Consecutive valid (non-NaN) values of x ending at each bar. */
70
+ validRun(x: Col): Col;
71
+ /** Sum over the last p bars (NaN until p valid bars). */
72
+ sum(x: Col, p: number): Node;
73
+ sma(x: Col, p: number): Node;
74
+ /**
75
+ * Recursive smoothing y = y' + alpha (x - y'), seeded with the simple mean of
76
+ * the first p valid values (TA-Lib's EMA/Wilder seeding; also Pine's).
77
+ */
78
+ smooth(x: Col, p: number, alpha: number): Node;
79
+ ema(x: Col, p: number): Node;
80
+ /** Wilder's smoothing (RMA / SMMA). */
81
+ rma(x: Col, p: number): Node;
82
+ /** Linearly weighted mean, newest bar weighted p. */
83
+ wma(x: Col, p: number): Node;
84
+ /** Highest value over the last p bars. */
85
+ highest(x: Col, p: number): Node;
86
+ lowest(x: Col, p: number): Node;
87
+ /** Bars since the highest/lowest value in the window (0 = this bar); ties pick the most recent. */
88
+ highestBars(x: Col, p: number, oldest?: boolean): Node;
89
+ lowestBars(x: Col, p: number, oldest?: boolean): Node;
90
+ /** `oldest`: ties pick the oldest bar (TA-Lib's MAXINDEX/MININDEX) instead of the newest (Aroon). */
91
+ private extreme;
92
+ /**
93
+ * Population variance over p bars (TA-Lib VAR): mean-centred two-pass over the
94
+ * window, because running sums of squares lose ~1e-8 relative on price series.
95
+ * `sample` divides by p - 1 instead (the unbiased estimator, the one a Sharpe or volatility estimate from a sample uses): the
96
+ * population value scaled by p / (p - 1), undefined for p = 1.
97
+ */
98
+ variance(x: Col, p: number, sample?: boolean): Node;
99
+ stdev(x: Col, p: number, sample?: boolean): Node;
100
+ /**
101
+ * Least-squares line over the last p bars against x = 0..p-1 (oldest..newest).
102
+ * `value` is the line at the newest bar; `slope` per bar; `intercept` at the oldest bar.
103
+ */
104
+ linreg(x: Col, p: number): {
105
+ value: Node;
106
+ slope: Node;
107
+ intercept: Node;
108
+ };
109
+ /** A node that mirrors an aux column (so it can be returned as an output). */
110
+ alias(c: Col): Node;
111
+ /** True range: max(h-l, |h-c'|, |l-c'|); the first bar has no previous close, so is absent (TA-Lib TRANGE). */
112
+ tr(): Node;
113
+ /** True range with the first bar's range as high - low (Wilder; Pine's `ta.tr(true)`). */
114
+ trFirst(): Node;
115
+ /**
116
+ * Average true range as Wilder defined it: the first true range is high - low, the seed is the
117
+ * mean of true ranges 0..p-1 (first value at bar p-1), then Wilder's smoothing. Not TA-Lib's ATR (TA-Lib skips the first
118
+ * true range, so it starts one bar later and differs by a different seed).
119
+ */
120
+ atr(p: number): Node;
121
+ /**
122
+ * Bars per year, read from the bars of this graph and from nothing after the bar it is written on (a causal annualisation). Intraday bars: the median number of bars per exchange-clock day over the
123
+ * last {@link BPY_SESSIONS} finished days, times 252 sessions (nights and weekends add no bars). A day or longer between bars: 252 a
124
+ * year for daily bars that leave weekends out, otherwise calendar time (365 days over the median spacing: weekly 52.14,
125
+ * monthly 12.17), the median over the last {@link BPY_COARSE} bars. Constant within a day, so a finished bar's value never changes
126
+ * when later bars arrive or the last bar is revised; absent before the first day has finished (and, for a day or
127
+ * longer, before six bars). The day is cut at `offsetMin` minutes east of UTC: 0 suits every session inside one UTC day
128
+ * (NSE, BSE, MCX, NYSE, LSE).
129
+ */
130
+ barsPerYear(offsetMin?: number): Node;
131
+ }
132
+ /** Finished days whose bar counts the median is taken over (barsPerYear). */
133
+ export declare const BPY_SESSIONS = 60;
134
+ /** Bars the spacing and the weekend test are read from when bars are a day or longer (barsPerYear). */
135
+ export declare const BPY_COARSE = 120;
136
+ export declare function checkPeriod(p: number): void;
@@ -0,0 +1,9 @@
1
+ import type { Kernels } from "@alphacharts/wasm";
2
+ export type BatchKernels = Pick<Kernels, "sumState" | "variance" | "highest" | "lowest" | "highestBars" | "lowestBars" | "linreg">;
3
+ /** Route eligible batch runs through `k`; null restores the JS loops. */
4
+ export declare function setKernels(k: BatchKernels | null): void;
5
+ export declare function kernels(): BatchKernels | null;
6
+ /** Below this many bars the call overhead beats the win. */
7
+ export declare const KERNEL_MIN_BARS = 4096;
8
+ /** A kernel run needs the whole series from bar 0 and no absent values. */
9
+ export declare function eligible(x: Float64Array, from: number, to: number): boolean;
@@ -0,0 +1,40 @@
1
+ import { Graph, type Col } from "./graph.js";
2
+ import type { BarColumns, BarInput, InputValue, Params, StudyDef } from "../types.js";
3
+ import type { FeedBar, FeedConfig, FeedBars } from "./feed.js";
4
+ /** Validate `given` against the study's inputs and fill defaults. Throws on anything unknown or out of range. */
5
+ export declare function resolveParams(def: StudyDef, given?: Readonly<Record<string, InputValue>>): Params;
6
+ /** A study bound to parameters and a graph: appends, revises and batch-loads bars. */
7
+ export declare class StudyEngine {
8
+ readonly def: StudyDef;
9
+ readonly graph: Graph;
10
+ readonly outputs: readonly Col[];
11
+ readonly params: Params;
12
+ constructor(def: StudyDef, params?: Readonly<Record<string, InputValue>>);
13
+ get length(): number;
14
+ /** Replace all bars and compute. */
15
+ load(d: BarColumns): void;
16
+ /**
17
+ * Like `load`, but evaluates in chunks of `chunk` bars and awaits `pause()`
18
+ * between them, so a runner can service cancel messages. Stops quietly and
19
+ * returns false when `cancelled()` turns true.
20
+ */
21
+ loadChunked(d: BarColumns, chunk: number, pause: () => Promise<void>, cancelled: () => boolean): Promise<boolean>;
22
+ /** Give the study outside series `key`: bars in their own time and how to join them (copied). Run the study again afterwards. */
23
+ setFeed(key: string, bars: FeedBars, cfg: FeedConfig): void;
24
+ /**
25
+ * A live bar of outside series `key`: the feed's last bar is replaced or one is appended, and what the study computed
26
+ * on the feed's bars follows. When the study's last bar now sees a different feed row (or the same one revised), that
27
+ * bar is evaluated again and `true` is returned; otherwise nothing of the study is recomputed. O(1) amortised.
28
+ */
29
+ pushFeed(key: string, bar: FeedBar): boolean;
30
+ /** Append a bar (time later than the last). */
31
+ push(b: BarInput): void;
32
+ /** Replace the last bar (a tick revision). */
33
+ reviseLast(b: BarInput): void;
34
+ /** Output column `k` as a view over the bars loaded (valid until the next push that grows the graph). */
35
+ column(k: number): Float64Array;
36
+ private write;
37
+ private requireColumns;
38
+ }
39
+ /** One-shot: run a study over columns and return its output columns (copies, length n). */
40
+ export declare function runStudy(def: StudyDef, params: Readonly<Record<string, InputValue>>, d: BarColumns): Float64Array[];
@@ -0,0 +1,15 @@
1
+ export * from "./types.js";
2
+ export { Graph, Col, Node, SOURCE_NAMES } from "./engine/graph.js";
3
+ export type { Ref, SourceName } from "./engine/graph.js";
4
+ export { StudyEngine, resolveParams, runStudy } from "./engine/study.js";
5
+ export { Feed, FEED_FIELDS, describeJoin, joinFeed } from "./engine/feed.js";
6
+ export type { FeedAlign, FeedBar, FeedBars, FeedChange, FeedConfig, FeedData, FeedField, JoinReason, JoinReport } from "./engine/feed.js";
7
+ export { registerStudy, registerStudies, getStudy, listStudies, validateDef } from "./registry.js";
8
+ export { defineStudy } from "./define.js";
9
+ export { addStudy, installStudies, StudiesPlugin, Study, PLUGIN_ID } from "./chart/studies.js";
10
+ export type { AddStudyOptions, PlotStyle } from "./chart/studies.js";
11
+ export { markerFloor } from "./chart/marker-floor.js";
12
+ export { InlineRunner, WorkerRunner, createRunner, serveWorker, CancelledError, CHUNK } from "./runner.js";
13
+ export type { StudyRunner, RunRequest, RunResult, WorkerLike } from "./runner.js";
14
+ export { enableWasm, disableWasm } from "./wasm.js";
15
+ export { setKernels } from "./engine/kernels.js";
@@ -0,0 +1,7 @@
1
+ import type { StudyDef } from "./types.js";
2
+ export declare function registerStudy(def: StudyDef): void;
3
+ export declare function registerStudies(defs: readonly StudyDef[]): void;
4
+ export declare function getStudy(id: string): StudyDef;
5
+ export declare function listStudies(): StudyDef[];
6
+ /** Catch an inconsistent definition when it is registered, not when it is drawn. */
7
+ export declare function validateDef(def: StudyDef): void;
@@ -0,0 +1,91 @@
1
+ import type { FeedBar, FeedData } from "./engine/feed.js";
2
+ import type { BarColumns, InputValue, StudyDef } from "./types.js";
3
+ export declare const CHUNK: number;
4
+ export interface RunRequest {
5
+ studyId: string;
6
+ params: Readonly<Record<string, InputValue>>;
7
+ /** Transferred to the runner when it is a worker: do not use afterwards. */
8
+ bars: BarColumns;
9
+ /** Outside series by key, each a copy the runner may keep or transfer. Only a runner that can run a study that reads them (a script) uses them. */
10
+ feeds?: Record<string, FeedData>;
11
+ }
12
+ export interface RunResult {
13
+ n: number;
14
+ /** All graph columns (inputs, state, outputs), in registration order. */
15
+ buffers: Float64Array[];
16
+ }
17
+ export declare class CancelledError extends Error {
18
+ constructor();
19
+ }
20
+ export interface StudyRunner {
21
+ /** Whether this runner can evaluate `def` (a worker only knows built-ins). */
22
+ canRun(def: StudyDef): boolean;
23
+ /** Rejects with CancelledError when `cancel(handle)` is called first. */
24
+ run(def: StudyDef, req: RunRequest): {
25
+ promise: Promise<RunResult>;
26
+ cancel(): void;
27
+ };
28
+ /**
29
+ * A live bar of outside series `key` for a study this runner holds state for; the runner answers through the channel
30
+ * its ticks use. Optional: a runner that keeps no per-study state (every batch starts afresh) does not have it.
31
+ */
32
+ feedBar?(def: StudyDef, key: string, bar: FeedBar): void;
33
+ dispose(): void;
34
+ }
35
+ /** Main-thread fallback: same chunked evaluation, same cancellation. */
36
+ export declare class InlineRunner implements StudyRunner {
37
+ canRun(): boolean;
38
+ run(def: StudyDef, req: RunRequest): {
39
+ promise: Promise<RunResult>;
40
+ cancel: () => undefined;
41
+ };
42
+ dispose(): void;
43
+ }
44
+ export interface WorkerLike {
45
+ postMessage(msg: unknown, transfer?: Transferable[]): void;
46
+ addEventListener(type: "message", fn: (e: {
47
+ data: unknown;
48
+ }) => void): void;
49
+ addEventListener(type: "error", fn: (e: {
50
+ message?: string;
51
+ }) => void): void;
52
+ terminate(): void;
53
+ }
54
+ type WorkerReply = {
55
+ type: "result";
56
+ id: number;
57
+ n: number;
58
+ buffers: Float64Array[];
59
+ } | {
60
+ type: "cancelled";
61
+ id: number;
62
+ } | {
63
+ type: "error";
64
+ id: number;
65
+ message: string;
66
+ };
67
+ /** Runs built-in studies in a Web Worker. `isBuiltin` says which ids the worker knows. */
68
+ export declare class WorkerRunner implements StudyRunner {
69
+ private readonly worker;
70
+ private readonly isBuiltin;
71
+ private nextId;
72
+ /** Set once the worker has failed: later runs reject at once instead of waiting on a dead worker. */
73
+ private dead;
74
+ private readonly pending;
75
+ constructor(worker: WorkerLike, isBuiltin: (def: StudyDef) => boolean);
76
+ canRun(def: StudyDef): boolean;
77
+ run(def: StudyDef, req: RunRequest): {
78
+ promise: Promise<RunResult>;
79
+ cancel: () => void;
80
+ };
81
+ dispose(): void;
82
+ }
83
+ /** Worker-side message handler: wire it to `self.onmessage` (see worker.ts). */
84
+ export declare function serveWorker(post: (msg: WorkerReply, transfer?: Transferable[]) => void, find: (id: string) => StudyDef | undefined): (data: unknown) => void;
85
+ /**
86
+ * A worker runner on the worker `factory` creates, otherwise (no factory, or the worker would not start) the inline fallback.
87
+ * The package never guesses where its worker lives: the host's bundler spells the URL out,
88
+ * `() => new Worker(new URL("@alphacharts/indicators/worker", import.meta.url), { type: "module" })`.
89
+ */
90
+ export declare function createRunner(builtinIds: ReadonlySet<string>, factory?: () => WorkerLike): StudyRunner;
91
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { StudyDef } from "../types.js";
2
+ export declare const ALL_STUDIES: readonly StudyDef[];
@@ -0,0 +1,7 @@
1
+ export declare const htDcPeriod: import("../types.js").StudyDef;
2
+ export declare const htDcPhase: import("../types.js").StudyDef;
3
+ export declare const htPhasor: import("../types.js").StudyDef;
4
+ export declare const htSine: import("../types.js").StudyDef;
5
+ export declare const htTrendline: import("../types.js").StudyDef;
6
+ export declare const htTrendMode: import("../types.js").StudyDef;
7
+ export declare const mama: import("../types.js").StudyDef;