ohlcv-indicators 3.5.77 → 3.5.79

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 (42) hide show
  1. package/AGENTS.md +105 -0
  2. package/README.md +415 -0
  3. package/dist/ohlcv-indicators.min.js +2 -1
  4. package/dist/ohlcv-indicators.min.js.LICENSE.txt +23 -0
  5. package/index.js +96 -127
  6. package/package.json +1 -1
  7. package/src/core-functions/initializeColumns.js +40 -0
  8. package/src/core-functions/mainLoop.js +49 -26
  9. package/src/core-functions/pushToMain.js +1 -3
  10. package/src/core-indicators/README.md +61 -0
  11. package/src/core-indicators/composites.js +136 -0
  12. package/src/core-indicators/index.js +5 -0
  13. package/src/core-indicators/indicator.js +82 -0
  14. package/src/core-indicators/movingAverages.js +88 -0
  15. package/src/core-indicators/rsi.js +41 -0
  16. package/src/core-indicators/volatility.js +107 -0
  17. package/src/machine-learning/ml-utilities.js +0 -9
  18. package/src/moving-averages/bollingerBands.js +11 -26
  19. package/src/moving-averages/donchianChannel.js +30 -29
  20. package/src/moving-averages/heikenAshi.js +23 -33
  21. package/src/moving-averages/macd.js +10 -26
  22. package/src/moving-averages/movingAverages.js +7 -23
  23. package/src/moving-averages/relativeVolume.js +23 -21
  24. package/src/oscillators/rsi.js +7 -26
  25. package/src/oscillators/stochastic.js +6 -19
  26. package/src/oscillators/volumeDelta.js +36 -28
  27. package/src/oscillators/volumeOscillator.js +29 -31
  28. package/src/studies/{priceFeatures.js → candleFeatures.js} +15 -23
  29. package/src/studies/dateTime.js +22 -27
  30. package/src/studies/findCrosses.js +16 -7
  31. package/src/studies/lag.js +26 -13
  32. package/src/studies/mapCols.js +14 -15
  33. package/src/utilities/assignTypes.js +1 -1
  34. package/src/utilities/config.js +64 -0
  35. package/src/utilities/numberUtilities.js +2 -0
  36. package/src/utilities/validators.js +37 -21
  37. package/src/utilities/verticalToHorizontal.js +21 -16
  38. package/src/volatility/adx.js +7 -20
  39. package/src/volatility/atr.js +10 -23
  40. package/test/test.js +533 -0
  41. package/src/machine-learning/scaler.js +0 -165
  42. package/src/moving-averages/vidya.js +0 -181
package/AGENTS.md ADDED
@@ -0,0 +1,105 @@
1
+ # OHLCV Indicators Agent Guide
2
+
3
+ ## Project
4
+
5
+ `ohlcv-indicators` is an ESM JavaScript library for computing stateful indicators over OHLCV rows. The public API is chainable and row-oriented, while the runtime stores data column-wise in typed arrays for backtesting and feature generation.
6
+
7
+ Input rows normally contain `open`, `high`, `low`, `close`, and `volume`; `date` is optional. Most studies require the full OHLC set even though the constructor currently enforces only `close`.
8
+
9
+ ## Core design
10
+
11
+ 1. `index.js` owns the `OHLCV_INDICATORS` class. The constructor accepts `{ input, ticker?, chunkProcess?, config? }`. Public indicator methods validate arguments and register immutable `{ key, order?, params }` declarations through `_registerIndicator()`; they do not calculate results immediately.
12
+ 2. `compute()`, `getData()`, or `getLastValues()` starts the one-shot calculation. Do not allow indicators to be registered after computation.
13
+ 3. `compute()` copies declarations into `main.executionParams` and sorts only that runtime queue. `src/core-functions/mainLoop.js` creates input columns in `main.verticalOhlcv`, walks rows in chronological order, writes the raw row and `mid_price`, and invokes every queued handler from `mainFunctions`.
14
+ 4. Indicator handlers initialize state and output columns when `index === 0`, keep rolling state in `main.instances`, update once per row, and write through `main.pushToMain()`.
15
+ 5. Output columns start as `NaN` during warm-up or after an unexpected value. `areKeyValuesValid()` records the last invalid row in `main.invalidValueIndex`; `getData({ skipNull: true })` starts after that row.
16
+ 6. `verticalToHorizontal()` converts the column store back to row objects. Temporary columns in `verticalOhlcvTempCols` are not returned.
17
+
18
+ ### Indicator contract
19
+
20
+ - Add or change the public method in `index.js`, then import and register its runtime handler in `mainFunctions` in `src/core-functions/mainLoop.js`.
21
+ - At `index === 0`, validate required source columns, create a unique instance key, and declare outputs through `initializeColumns(main, columns, { lag })`. Each declaration has a `key` and optional `type`, `fill`, `enabled`, `priceBased`, and `includeInLag`; numeric outputs default to `Float64Array` filled with `NaN`.
22
+ - Preserve `NaN` for warm-up and invalid output. Never substitute zero for unavailable numeric data.
23
+ - Update each stateful instance exactly once per row. Reject duplicate configurations when they would share an instance or output key.
24
+ - Let `initializeColumns` append generated lag jobs only to `main.executionParams` after allocation, using only enabled outputs. Never call the public `main.lag()` during computation. Set `includeInLag: false` for explicit exclusions. Numeric lag columns use `Float64Array` to preserve missing values as `NaN`; object/one-hot lag columns use `Array` with `null`.
25
+ - Add an output to `main.priceBased` only when it remains in scaled price units. Ratios, percentages, return logs, counters, dates, volume, and one-hot vectors are not price-based.
26
+ - `retLogs` outputs use natural log ratios through `mathLog(a, b)` where applicable. Logged values are dimensionless.
27
+ - Keep output names deterministic. When multiple configurations are supported, include enough parameters and target information to prevent collisions.
28
+ - `src/core-indicators/` supplies compatible numeric EMA, SMA, WSMA, RSI, MACD, Bollinger Bands, stochastic, ATR, and ADX implementations. Use cheap `isStable` checks before `getResult()`; do not use exceptions for routine warm-up. The installed `trading-signals@5.0.4` remains a test oracle, not a production import.
29
+ - Preserve reference arithmetic order, warm-up, zero/NaN behavior, and update/replacement sequencing in core classes. SMA/Bollinger use bounded chronological rings rather than rolling sums that change rounding. RSI history and MACD counters must stay bounded; WSMA releases its seed window. Runtime state must never enter exported config.
30
+
31
+ ## Configuration export and replay guards
32
+
33
+ - `config` contains `schemaVersion` (currently `1`), `precision`, `useFullNames`, `inputParams`, `dateFormat`, `skipNull`, and `timeZone`.
34
+ - Omitting `config.inputParams` (or setting it to `null`) creates a chainable instance. Supplying an array, including `[]`, imports independent copies and computes immediately.
35
+ - `exportConfig()` returns a new JSON-safe snapshot. Export before and after `compute()`, `getData()`, or `getLastValues()` must produce equivalent configuration. Never include generated lag jobs, indicator instances, buffers, cached column references, or resolved callback functions in that snapshot.
36
+ - `this.inputParams` is persistent configuration, not a work queue. Keep declarations deeply copied and frozen. Register public methods through `_registerIndicator()`; never sort, push into, or edit existing declarations directly. Runtime caching and parameter mutation belong only in `executionParams` or `instances`.
37
+ - Sorting and generated jobs must affect only `executionParams`. Import must never mutate the caller's config, even when the same saved object is replayed repeatedly. Exports must not share nested arrays or objects with the instance or the imported config.
38
+ - Every public indicator method must reject registration during or after computation. Preserve the `isComputing` reentrancy guard and the `isComputed` guard, including for `stochastic()`.
39
+ - `mapCols` accepts a callback function or a registered string name. Register portable callbacks with `OHLCV_INDICATORS.registerMapCallback('name.v1', callback)` before loading configs. The built-in callback uses the reserved name `default`. Never overwrite a registered name with different code; use a new versioned name.
40
+ - Callback arguments belong in `mapCols` options as JSON-safe `callbackParams`; callbacks receive them as `params` in `{ index, main, params }`. Resolve callbacks and copy their arguments only into the runtime queue. Registered callbacks must derive results from supplied input/params, not changing closure state, clocks, randomness, or external services.
41
+ - Unregistered callbacks may execute locally, but export must throw explicitly. Do not serialize function source, evaluate stored code, or silently let JSON replace functions, undefined, or non-finite numbers with null. Reject unsupported config versions, circular references, and non-JSON objects.
42
+ - `dateFormat` and `skipNull` in config are output defaults. Per-call getter overrides must not modify exported settings. `timeZone` is stored explicitly (defaults to the resolved runtime timezone) and controls `dateTime` calendar features through a per-instance formatter.
43
+ - Cross-runtime replay requires identical inputs, callback implementations, and library versions. Use unambiguous input dates (valid Date objects, supported timestamps, or ISO strings with `Z`/offsets). Timezone-less date strings and local `dateFormat: 'string'/'toString'` output still depend on runtime date parsing/formatting; prefer `iso`, `milliseconds`, or `seconds` for portable output.
44
+ - Verify JSON export/import both before and after computation, repeated reuse of one frozen saved config, nested-copy isolation, generated and explicit lags, callback resolution, constructor/output defaults, and calendar features across runtime timezones.
45
+
46
+ ## Indicator files
47
+
48
+ ### Moving averages and channels
49
+
50
+ - `src/moving-averages/movingAverages.js` — shared runtime for public `ema()` and `sma()`.
51
+ - `src/moving-averages/bollingerBands.js` — Bollinger upper, middle, and lower bands.
52
+ - `src/moving-averages/donchianChannel.js` — rolling Donchian upper, basis, and lower channels with optional offset.
53
+ - `src/moving-averages/heikenAshi.js` — optional pre/post-smoothed Heiken-Ashi structure returns and trend counter.
54
+ - `src/moving-averages/macd.js` — MACD difference, signal/DEA, and histogram for a selectable target.
55
+ - `src/moving-averages/relativeVolume.js` — current volume divided by the previous completed volume-SMA window.
56
+
57
+ ### Oscillators and volume
58
+
59
+ - `src/oscillators/rsi.js` — RSI plus an SMA of RSI, optionally expressed as log ratios to 50.
60
+ - `src/oscillators/stochastic.js` — stochastic K and D, optionally expressed as log ratios to 50.
61
+ - `src/oscillators/volumeDelta.js` — signed per-bar volume and consecutive buy/sell direction counter.
62
+ - `src/oscillators/volumeOscillator.js` — percentage difference and optional log ratio between fast and slow volume EMAs.
63
+
64
+ ### Volatility
65
+
66
+ - `src/volatility/atr.js` — ATR or its log ratio to close.
67
+ - `src/volatility/adx.js` — ADX or its log ratio to the neutral reference value.
68
+
69
+ ### Studies and feature utilities
70
+
71
+ - `src/studies/candleFeatures.js` — candle change, gap, body, wick, range, mid-price, and selected price-target returns.
72
+ - `src/studies/dateTime.js` — numeric or one-hot calendar features.
73
+ - `src/studies/findCrosses.js` — signed intervals since a cross, optional clipping, and optional one-hot encoding.
74
+ - `src/studies/lag.js` — lag columns for existing vertical columns.
75
+ - `src/studies/mapCols.js` — custom user-defined columns produced by a callback.
76
+
77
+ ## Minimum required helpers
78
+
79
+ - `src/core-functions/mainLoop.js` — handler registry and chronological execution engine.
80
+ - `src/core-indicators/index.js` — compatible `Faster*` class exports; `src/core-indicators/README.md` records storage choices and reference-compatibility constraints.
81
+ - `src/core-functions/initializeColumns.js` — output allocation using `main.len`, explicit type/fill overrides, price metadata, collision checks, and lag registration from the same declarations.
82
+ - `src/core-functions/pushToMain.js` — column writes and per-row validity checks.
83
+ - `src/utilities/assignTypes.js` — input type inference, typed-array allocation, and array-type lookup used by lagging.
84
+ - `src/utilities/validators.js` — public API and queued-parameter validation.
85
+ - `src/utilities/config.js` — strict configuration copying/freezing, schema version, named callback registration/resolution, and JSON-safe parameter export.
86
+ - `src/utilities/verticalToHorizontal.js` — output materialization, invalid-prefix skipping, temporary-column filtering, and precision restoration.
87
+ - `src/utilities/numberUtilities.js` — input/output numeric formatters, numeric classification, and volume-number predicates.
88
+ - `src/utilities/precisionMultiplier.js` — fixed-point conversion for exact price-string processing when `precision: true`.
89
+ - `src/utilities/math.js` — finite-number checks and safe natural log ratios.
90
+ - `src/utilities/dateUtilities.js` — date input detection/conversion and requested output formatting.
91
+ - `src/machine-learning/ml-utilities.js` — one-hot vector creation used by date and cross studies.
92
+
93
+ ## Precision and storage rules
94
+
95
+ - With `precision: false`, price inputs use normal floating-point values.
96
+ - With `precision: true`, OHLC price strings are converted to fixed-point numbers using one shared multiplier. Price-based indicator outputs stay in those units internally and are restored only during output conversion.
97
+ - Volume is currently stored in an `Int32Array`; do not assume that integer typed arrays preserve `NaN`.
98
+ - `Array` columns are used for objects such as dates and one-hot vectors; numeric indicator outputs normally use `Float64Array`.
99
+
100
+ ## Changes and verification
101
+
102
+ - Preserve unrelated working-tree changes; indicator files are frequently edited together.
103
+ - Do not edit `dist/ohlcv-indicators.min.js` manually. Run `npm run build` after source changes that should ship in the browser bundle.
104
+ - Use deterministic local OHLCV arrays to test warm-up boundaries, zero/invalid inputs, lagged columns, multiple configurations, `retLogs`, `precision: true`, and both `getData()` modes.
105
+ - Run `git diff --check`, `npm test`, and `npm run build`. `test/test.js` runs deterministic local regression checks by default; `npm test -- --live` runs the optional integration demo against its configured market-data service.
package/README.md ADDED
@@ -0,0 +1,415 @@
1
+ # OHLCV Indicators
2
+
3
+ Compute technical indicators and features from an array of market candles. Configure the calculations with a chain of methods, then get the results as ordinary JavaScript objects.
4
+
5
+ **OHLCV** means **open, high, low, close, and volume**. Each input row represents one candle. The library is useful for preparing backtest data, building feature datasets, and calculating indicators for trading applications; it does not fetch market data or execute trades.
6
+
7
+ This README describes the API in this repository. Keep the same library version when reproducing stored configurations.
8
+
9
+ ## Contents
10
+
11
+ - [Install and quick start](#install-and-quick-start)
12
+ - [Input requirements](#input-requirements)
13
+ - [How calculation and output work](#how-calculation-and-output-work)
14
+ - [Constructor options](#constructor-options)
15
+ - [Indicator reference](#indicator-reference)
16
+ - [Advanced: combine indicators and lagged features](#advanced-combine-indicators-and-lagged-features)
17
+ - [Save and reuse a configuration](#save-and-reuse-a-configuration)
18
+ - [Advanced: custom columns with portable callbacks](#advanced-custom-columns-with-portable-callbacks)
19
+ - [Precision mode](#precision-mode)
20
+ - [Current limitations and troubleshooting](#current-limitations-and-troubleshooting)
21
+ - [Notes for developers and LLMs](#notes-for-developers-and-llms)
22
+
23
+ ## Install and quick start
24
+
25
+ ```sh
26
+ npm install ohlcv-indicators
27
+ ```
28
+
29
+ Use ES modules: save your script as `.mjs`, or set `"type": "module"` in your application's `package.json`.
30
+
31
+ ### Calculate a simple moving average
32
+
33
+ A moving average smooths prices over a window. `sma(3)` averages the latest three closing prices.
34
+
35
+ ```js
36
+ import OHLCV_INDICATORS from 'ohlcv-indicators';
37
+
38
+ const input = [
39
+ { date: '2024-01-01T00:00:00Z', open: 100, high: 103, low: 99, close: 102, volume: 1000 },
40
+ { date: '2024-01-02T00:00:00Z', open: 102, high: 105, low: 101, close: 104, volume: 1200 },
41
+ { date: '2024-01-03T00:00:00Z', open: 104, high: 105, low: 100, close: 102, volume: 900 },
42
+ { date: '2024-01-04T00:00:00Z', open: 102, high: 107, low: 101, close: 106, volume: 1500 },
43
+ { date: '2024-01-05T00:00:00Z', open: 106, high: 109, low: 105, close: 108, volume: 1300 },
44
+ ];
45
+
46
+ const study = new OHLCV_INDICATORS({
47
+ input,
48
+ config: { timeZone: 'UTC', dateFormat: 'iso' },
49
+ }).sma(3);
50
+
51
+ const rows = study.getData();
52
+ console.table(rows);
53
+
54
+ const latest = study.getLastValues();
55
+ console.log(latest.close); // 108
56
+ console.log(latest.sma_3); // 105.33333333333333
57
+ ```
58
+
59
+ `rows` contains the last three candles: the first two do not yet have enough history for the average. Each result includes the original columns, `mid_price` (`(open + close) / 2`), and `sma_3`.
60
+
61
+ To keep the warm-up rows too:
62
+
63
+ ```js
64
+ // Reuses the already computed study; it does not calculate the indicators again.
65
+ const allRows = study.getData({ skipNull: false });
66
+ console.log(allRows.length); // 5
67
+ console.log(allRows[0].sma_3); // NaN: not enough history yet
68
+ ```
69
+
70
+ Later examples reuse `input` and the import above unless they define their own data.
71
+
72
+ ### Browser bundle
73
+
74
+ Serve the repository's `dist/ohlcv-indicators.min.js` with your application. The current bundle exposes the class through `OHLCV_INDICATORS.default`:
75
+
76
+ ```html
77
+ <script src="./dist/ohlcv-indicators.min.js"></script>
78
+ <script>
79
+ const Indicators = OHLCV_INDICATORS.default;
80
+ const candles = [
81
+ { open: 10, high: 12, low: 9, close: 11, volume: 100 },
82
+ { open: 11, high: 13, low: 10, close: 12, volume: 120 },
83
+ { open: 12, high: 14, low: 11, close: 13, volume: 110 },
84
+ ];
85
+ console.log(new Indicators({ input: candles }).sma(2).getLastValues());
86
+ </script>
87
+ ```
88
+
89
+ Use a modern JavaScript runtime with typed arrays and `Intl.DateTimeFormat` support. Browser bundling does not imply that every host environment has been tested.
90
+
91
+ ## Input requirements
92
+
93
+ Provide a nonempty array ordered **oldest to newest**, with consistent columns and value types. The library does not sort candles, fill missing timestamps, or resample their intervals.
94
+
95
+ | Field | Recommended input |
96
+ | --- | --- |
97
+ | `open`, `high`, `low`, `close` | Finite numbers greater than zero. Whole-number prices are valid too. |
98
+ | `volume` | Integer greater than or equal to zero; currently limited to `0`–`2147483647` by `Int32Array` storage. |
99
+ | `date` | Optional. Prefer a valid `Date` or an ISO string with `Z` or an explicit timezone offset. Required by `dateTime()`. |
100
+ | Extra columns | Supported; columns and storage types are inferred from the first row. |
101
+
102
+ Use the full OHLCV shape shown above. The constructor checks for `close`, but the calculation loop also uses `open` for `mid_price`; different indicators require other columns.
103
+
104
+ Clean numeric strings such as `'102.50'` are also accepted. Use a consistent numeric representation for all OHLC fields. With `precision: true`, OHLC values **must** be numeric strings; see [precision mode](#precision-mode).
105
+
106
+ Required indicator inputs are checked at row zero. Subsequent rows are not comprehensively validated for performance, so validate your dataset before passing it in. In particular, integer storage can truncate fractional volume or wrap oversized values before a handler sees them. Do not rely on the library to reject every malformed row.
107
+
108
+ ## How calculation and output work
109
+
110
+ 1. Construct an instance with input candles.
111
+ 2. Chain all indicators and features you want.
112
+ 3. Call `compute()`, `getData()`, or `getLastValues()` to calculate once.
113
+
114
+ | Method | Result |
115
+ | --- | --- |
116
+ | `compute()` | Calculates and returns the instance, without building output row objects. |
117
+ | `getData({ skipNull?, dateFormat? })` | Calculates if needed and returns an array of rows. |
118
+ | `getLastValues({ dateFormat? })` | Calculates if needed and returns one final row, even if it contains unavailable values. |
119
+ | `exportConfig()` | Returns a fresh JSON-safe configuration without triggering calculation. |
120
+
121
+ Register everything before calculation. You cannot append indicators or new candles to a computed instance. Create another instance to change the setup or process another dataset. `getLastValues()` still computes the full input history; it is not a streaming update API.
122
+
123
+ ### Warm-up and missing values
124
+
125
+ Numeric outputs generally start as `NaN` until sufficient history exists. Combined indicators may need more rows than their period alone suggests. For example, RSI also produces an SMA of RSI, which needs additional history.
126
+
127
+ `skipNull: true` is the default. It returns only the suffix **after the last row containing any `NaN`, `null`, or `undefined` column value**. It does not just remove individual bad rows. A late missing value can therefore discard many earlier valid rows; an invalid final row can make the result `[]`. This check is not a general finite-number check: `Infinity` is not filtered by it.
128
+
129
+ Use `getData({ skipNull: false })` when debugging or when you need one output row for every input row. Missing values remain missing; they are not replaced with zero.
130
+
131
+ `relativeVolume()`, `volumeDelta()`, and `volumeOscillator()` deliberately skip zero-volume candles without updating their indicator state. Their output on those candles stays `NaN`, and their warm-up can take more calendar rows.
132
+
133
+ ## Constructor options
134
+
135
+ ```js
136
+ const configured = new OHLCV_INDICATORS({
137
+ input,
138
+ ticker: 'EXAMPLE',
139
+ chunkProcess: 2000,
140
+ config: {
141
+ schemaVersion: 1,
142
+ precision: false,
143
+ useFullNames: true,
144
+ inputParams: null,
145
+ dateFormat: 'iso',
146
+ skipNull: false,
147
+ timeZone: 'UTC',
148
+ },
149
+ }).ema(3);
150
+ ```
151
+
152
+ Only `input` is required. Put calculation and output settings inside `config`, not at the constructor's top level.
153
+
154
+ | Option | Default | Meaning |
155
+ | --- | --- | --- |
156
+ | `ticker` | `null` | Optional label used in some validation messages; does not load data. |
157
+ | `chunkProcess` | `2000` | Loop chunk size, integer `100`–`50000`. Processing remains synchronous and retains full output columns. |
158
+ | `config.schemaVersion` | `1` | Configuration format version, not the package version. |
159
+ | `config.precision` | `false` | Enables fixed-point input conversion from price strings. |
160
+ | `config.useFullNames` | `false` | Includes parameter suffixes in indicators with optional compact names. |
161
+ | `config.inputParams` | `null` | Saved indicator declarations. An array, even `[]`, triggers calculation during construction. |
162
+ | `config.dateFormat` | `'milliseconds'` | Default date representation in returned rows. |
163
+ | `config.skipNull` | `true` | Default invalid-prefix handling for `getData()`. |
164
+ | `config.timeZone` | Runtime timezone | Explicit timezone for `dateTime()` calendar features; saved in exports. |
165
+
166
+ Supported output date formats: `milliseconds`, `seconds`, `iso` / `toISOString`, `object` (a `Date`), `string` / `toString`, and `toUTCString`. Getter overrides do not change exported defaults. Prefer `iso`, `milliseconds`, or `seconds` for portable output; local string formatting depends on the runtime.
167
+
168
+ ## Indicator reference
169
+
170
+ All methods below return the instance for chaining. Periods and lookbacks are measured in rows, not minutes or days. Periods must be positive integers within the input length; allow extra history for warm-up. Avoid repeating configurations that produce the same output names.
171
+
172
+ Defaults are shown below. `target` means the numeric source column; `lag: n` creates output copies from each of the previous `1` through `n` rows. `retLogs` means natural-log ratios, not application logging.
173
+
174
+ | Method | Options and behavior |
175
+ | --- | --- |
176
+ | `ema(size = 5, options = {})` | Exponential moving average. `{ target: 'close', lag: 0 }`. |
177
+ | `sma(size = 5, options = {})` | Simple moving average. `{ target: 'close', lag: 0 }`. |
178
+ | `bollingerBands(size = 20, stdDev = 2, options = {})` | Upper, middle, and lower close-price bands. `{ lag: 0 }`. |
179
+ | `donchianChannels(size = 20, offset = 0, options = {})` | Rolling high/low channel and midpoint. `{ lag: 0 }`; positive offset shifts the window into the past. |
180
+ | `macd(fast = 12, slow = 26, signal = 9, options = {})` | Difference, signal (`dea`), and histogram. `{ target: 'close', lag: 0 }`. |
181
+ | `rsi(size = 14, options = {})` | RSI and a same-period SMA of RSI. `{ target: 'close', lag: 0, retLogs: false }`. |
182
+ | `stochastic(kPeriod = 14, kSlowingPeriod = 3, dPeriod = 3, options = {})` | K and D outputs. `{ lag: 0, retLogs }`; explicitly supply `retLogs: false` or `true` in this version. |
183
+ | `atr(size = 14, options = {})` | Average true range. `{ lag: 0, retLogs: false }`. |
184
+ | `adx(size = 14, options = {})` | Average directional index. `{ lag: 0, retLogs: false }`. |
185
+ | `heikenAshi(smoothLength = null, afterSmoothLength = null, options = {})` | Heiken-Ashi body/wick/range returns and direction counter, not raw HA OHLC columns. `{ lag: 0, retLogs: false }`; use either two `null`s or two positive smoothing periods. |
186
+ | `relativeVolume(size = 10, options = {})` | Current volume / previous completed volume-SMA window, excluding the current sample from the denominator. `{ lag: 0 }`. |
187
+ | `volumeDelta(options = {})` | Candle-direction-based signed volume, delta high/low/close, and direction counter; not trade-by-trade bid/ask delta. `{ lag: 0 }`. |
188
+ | `volumeOscillator(fast = 5, slow = 10, options = {})` | `100 * (fastVolumeEMA - slowVolumeEMA) / slowVolumeEMA`. `{ lag: 0, retLogs: false }`. |
189
+ | `candleFeatures(options = {})` | Change, gap, body, wick, and range returns. `{ lag: 0, colKeys: [], retLogs: false }`. Extra `colKeys` compare current close to each selected price column, not that column's previous value. |
190
+ | `dateTime(options = {})` | Calendar features from `date`. `{ lag: 0, oneHot: false }`. |
191
+ | `crossPairs(pairs = [], options = {})` | Signed direction/run counters for `{ fast, slow }` pairs. `{ limit: null, oneHot: false }`; `slow` can be a column name or numeric constant. Call once with all pairs. |
192
+ | `mapCols(newCols = ['change'], callback = null, options = {})` | Custom columns. `{ lag: 0, isPriceBased: false, callbackParams: {} }`. See the callback example below. |
193
+ | `lag(colKeys = ['close'], lookback = 1)` | Copies existing columns into `<column>_lag_1`, ..., `<column>_lag_<lookback>`. |
194
+
195
+ ### Output names and units
196
+
197
+ Examples of names: `ema_5`, `sma_5_open`, `rsi_14`, `rsi_sma_14`, `atr_14`, `relative_volume_10`, and `volume_oscillator_5_10`.
198
+
199
+ A single Bollinger configuration normally uses `bollinger_bands_upper`, `bollinger_bands_middle`, and `bollinger_bands_lower`. With `useFullNames: true`, `.bollingerBands(20, 2)` adds `_20_2`. MACD and Donchian also expand their names when multiple configurations are registered. Use full names for persistent downstream schemas, and inspect `Object.keys(instance.getLastValues())` rather than guessing names.
200
+
201
+ For `retLogs: true`, RSI and stochastic use `ln(value / 50)`, ADX uses `ln(value / 20)`, and ATR uses `ln(ATR / close)`. Their logged columns replace the corresponding raw outputs. Volume oscillator is different: it keeps its percentage column and **adds** `ret_log_volume_oscillator_<fast>_<slow>` containing `ln(fastEMA / slowEMA)`.
202
+
203
+ Non-log candle and Heiken-Ashi returns are fractions, not percentages: `0.01` means 1%. Unavailable indicator values can remain `NaN`. The shared `mathLog()` helper substitutes `0.001` for a zero numerator or denominator and throws for incompatible signs or a non-finite result; its zero handling is not literal `ln(0)`. Candle and Heiken-Ashi features have their own log calculations.
204
+
205
+ ## Advanced: combine indicators and lagged features
206
+
207
+ This runnable example generates deterministic demonstration candles. Replace them with your own chronological dataset in a real application.
208
+
209
+ ```js
210
+ const history = Array.from({ length: 160 }, (_, i) => {
211
+ const open = 100 + i * 0.1 + Math.sin(i / 5);
212
+ const close = open + Math.cos(i / 3) * 0.8;
213
+ return {
214
+ date: new Date(Date.UTC(2024, 0, 1) + i * 3600000).toISOString(),
215
+ open,
216
+ high: Math.max(open, close) + 1,
217
+ low: Math.min(open, close) - 1,
218
+ close,
219
+ volume: 1000 + (i % 20) * 25,
220
+ };
221
+ });
222
+
223
+ const features = new OHLCV_INDICATORS({
224
+ input: history,
225
+ config: { useFullNames: true, timeZone: 'UTC', dateFormat: 'iso' },
226
+ })
227
+ .ema(5, { lag: 2 })
228
+ .ema(20)
229
+ .rsi(14)
230
+ .macd(12, 26, 9)
231
+ .bollingerBands(20, 2)
232
+ .donchianChannels(20, 1)
233
+ .stochastic(14, 3, 3, { retLogs: false })
234
+ .atr(14)
235
+ .adx(14)
236
+ .relativeVolume(10)
237
+ .volumeOscillator(5, 10, { retLogs: true })
238
+ .candleFeatures({ retLogs: true })
239
+ .dateTime({ oneHot: true })
240
+ .crossPairs([{ fast: 'close', slow: 'open' }], { limit: 5, oneHot: true })
241
+ .lag(['close', 'volume'], 2);
242
+
243
+ const featureRows = features.getData();
244
+ const lastFeature = features.getLastValues();
245
+ console.log(featureRows.length);
246
+ console.log(lastFeature.ema_5_lag_1); // EMA value from the previous row
247
+ console.log(lastFeature.macd_histogram_12_26_9);
248
+ console.log(lastFeature.one_hot_hour); // Uint8Array of length 24
249
+ ```
250
+
251
+ Register producers before consumers: for example, create a custom column before asking `lag()` to read it. Do not create the same lag twice using both an indicator's `lag` option and an explicit `.lag()` call.
252
+
253
+ A lag always refers to an earlier row, not an earlier nonzero-volume row. Features on the current candle can depend on its final high, low, close, and volume; account for candle completion when using them in a backtest.
254
+
255
+ Numeric `dateTime()` weekdays use Monday = 1 through Sunday = 7. One-hot weekdays instead use Sunday at index 0 through Saturday at index 6. One-hot values are `Uint8Array` vectors; convert with `Array.from(vector)` if another tool needs plain arrays. One-hot calendar output omits the year.
256
+
257
+ ## Save and reuse a configuration
258
+
259
+ `exportConfig()` saves **the recipe**, not the candles or computed values. It includes indicator declarations, precision/naming settings, date output defaults, and timezone. It excludes `input`, `ticker`, `chunkProcess`, buffers, and indicator state.
260
+
261
+ The following Node.js example writes the recipe to a file, loads it, and reproduces the result. It reuses the quick-start `input`.
262
+
263
+ ```js
264
+ import { readFile, writeFile } from 'node:fs/promises';
265
+ import assert from 'node:assert/strict';
266
+
267
+ const original = new OHLCV_INDICATORS({
268
+ input,
269
+ config: { timeZone: 'UTC', dateFormat: 'iso', useFullNames: true },
270
+ })
271
+ .sma(3, { lag: 1 })
272
+ .volumeOscillator(2, 3, { retLogs: true });
273
+
274
+ // Export before computing. Exporting afterward gives the same recipe.
275
+ const storedConfig = original.exportConfig();
276
+ await writeFile('./indicator-config.json', JSON.stringify(storedConfig, null, 2));
277
+
278
+ const expectedRows = original.getData();
279
+ assert.deepStrictEqual(original.exportConfig(), storedConfig);
280
+
281
+ // Later, or in another process using the same library version:
282
+ const loadedConfig = JSON.parse(await readFile('./indicator-config.json', 'utf8'));
283
+ const restored = new OHLCV_INDICATORS({ input, config: loadedConfig });
284
+ assert.deepStrictEqual(restored.getData(), expectedRows);
285
+
286
+ // Reuse the exact same saved object again; the constructor does not mutate it.
287
+ const restoredAgain = new OHLCV_INDICATORS({ input, config: loadedConfig });
288
+ assert.deepStrictEqual(restoredAgain.getData(), expectedRows);
289
+
290
+ // Apply the same recipe to a different dataset. Results depend on that data.
291
+ const onOtherHistory = new OHLCV_INDICATORS({ input: history, config: loadedConfig });
292
+ console.log(onOtherHistory.getLastValues());
293
+ ```
294
+
295
+ The last two lines use `history` from the advanced example. In a browser, store the same JSON text in your chosen storage instead of using `node:fs/promises`.
296
+
297
+ Important replay rules:
298
+
299
+ - Passing `config.inputParams` as an array computes immediately, even if it is empty. Do **not** chain more indicators onto a restored instance.
300
+ - Preserve the whole exported config; saving only `inputParams` loses settings that affect the result.
301
+ - For identical results, reuse identical ordered input, the same library version, the same callback implementations, and unambiguous dates. The config schema version alone does not pin library behavior.
302
+ - Exported objects are independent copies. Runtime-generated lag jobs are not added to the saved declarations; replay regenerates them from their options.
303
+ - Custom callbacks must be registered in each process before importing their configs. Function source and closures are not serialized.
304
+ - Configuration values must be JSON-safe. Functions without registered names, `undefined`, `NaN`, `Infinity`, circular references, and non-plain objects cannot be exported as configuration.
305
+
306
+ JSON safety applies to exported **configuration**, not arbitrary result rows. Serializing results converts `NaN` to `null`, and typed-array vectors are not serialized as ordinary arrays automatically.
307
+
308
+ ## Advanced: custom columns with portable callbacks
309
+
310
+ `mapCols()` runs a callback once per input row. It receives `{ index, main, params }`; input and previously calculated columns are available at `main.verticalOhlcv[column][index]`.
311
+
312
+ Register a versioned callback name so that its configuration can be saved and replayed. This example reuses `input` from the quick start and the `assert` import from the save-and-reuse example:
313
+
314
+ ```js
315
+ const bodyPercent = ({ index, main, params }) => {
316
+ const { open, close } = main.verticalOhlcv;
317
+ return {
318
+ body_percent: ((close[index] - open[index]) / open[index]) * params.scale,
319
+ };
320
+ };
321
+
322
+ OHLCV_INDICATORS.registerMapCallback('body-percent.v1', bodyPercent);
323
+
324
+ const custom = new OHLCV_INDICATORS({
325
+ input,
326
+ config: { timeZone: 'UTC', dateFormat: 'iso' },
327
+ }).mapCols(['body_percent'], 'body-percent.v1', {
328
+ callbackParams: { scale: 100 },
329
+ lag: 1,
330
+ });
331
+
332
+ const customConfig = JSON.parse(JSON.stringify(custom.exportConfig()));
333
+ const customRows = custom.getData();
334
+ const customReplay = new OHLCV_INDICATORS({ input, config: customConfig });
335
+ assert.deepStrictEqual(customReplay.getData(), customRows);
336
+ console.log(customReplay.getLastValues().body_percent);
337
+ ```
338
+
339
+ In a new process, define and register `body-percent.v1` before constructing `customReplay`. Registration is runtime-local. Do not replace an existing callback name with different code; register `body-percent.v2` instead.
340
+
341
+ Callback guidelines:
342
+
343
+ - Declare every output name in `newCols`, return only those keys, and do not overwrite existing columns.
344
+ - Return `null` or `undefined` to leave that row's custom outputs unavailable.
345
+ - Put configurable values in JSON-safe `callbackParams`. Do not depend on changing closure state, randomness, clocks, or external services if you need reproducible replay.
346
+ - Treat `main` as read-only apart from returning your declared values. Do not mutate input, internal queues, configuration, or other columns.
347
+ - With precision mode, price columns in `verticalOhlcv` are scaled internal values. Ratios such as the example above cancel that scale. Use `isPriceBased: true` only for custom outputs that remain in scaled price units; that option requires `precision: true`.
348
+
349
+ You may pass an unregistered function directly for local use, but `exportConfig()` will throw until it has a registered name. The built-in callback name `default` is reserved. With no callback, `.mapCols()` creates `change = 100 * (close[i] - open[i - 1]) / open[i - 1]`, not the usual close-to-close return; use an explicit callback when you want another formula.
350
+
351
+ ## Precision mode
352
+
353
+ Use numeric strings for all OHLC prices when enabling precision:
354
+
355
+ ```js
356
+ const stringPrices = input.map(row => ({
357
+ ...row,
358
+ open: row.open.toFixed(4),
359
+ high: row.high.toFixed(4),
360
+ low: row.low.toFixed(4),
361
+ close: row.close.toFixed(4),
362
+ }));
363
+
364
+ const fixedPoint = new OHLCV_INDICATORS({
365
+ input: stringPrices,
366
+ config: { precision: true, timeZone: 'UTC', dateFormat: 'iso' },
367
+ }).sma(3);
368
+
369
+ console.log(fixedPoint.getLastValues().sma_3); // 105.3333
370
+ ```
371
+
372
+ Prices are scaled internally using a shared multiplier derived from the first row, then price-based outputs are restored to ordinary numbers. This is **not arbitrary-precision decimal arithmetic**: calculations still use JavaScript numbers. Extra decimal places beyond the chosen scale are truncated, including fractional scaled indicator results during output conversion. Keep scaled prices within JavaScript's safe-integer range and use a consistent decimal scale throughout your dataset.
373
+
374
+ ## Current limitations and troubleshooting
375
+
376
+ - **“Already computed” error:** register all methods before calling any getter or `compute()`. Imported configs already computed during construction.
377
+ - **Empty output:** inspect `getData({ skipNull: false })`. You may need more history, or a late missing/zero-volume row may have moved the `skipNull` cutoff.
378
+ - **Stochastic options error:** explicitly call, for example, `.stochastic(14, 3, 3, { retLogs: false })`; the current implementation does not supply that boolean's default.
379
+ - **A derived target fails at row zero:** source validation requires positive finite values at the first row for non-volume targets. This restricts using warming-up outputs (such as an EMA) or signed/zero-valued custom columns as inputs to another indicator or `crossPairs()`. Registering the producer first does not bypass that validation.
380
+ - **Date errors:** use `Date` objects or timezone-qualified ISO strings consistently. Numeric millisecond input currently has a formatter-name mismatch; wrap it with `new Date(timestampMs)` first. Millisecond *output* is supported. Local/timezone-less strings can parse differently across runtimes.
381
+ - **Unexpected volume:** storage is signed 32-bit integer. Normalize data externally; larger counts and fractional volumes are not preserved.
382
+ - **Logged ATR with precision:** the current handler marks even logged ATR as price-based, so `precision: true` rescales that dimensionless output incorrectly. Avoid that combination until corrected; use `precision: false` for logged ATR.
383
+ - **MACD on a non-price target:** its outputs are always marked price-based. With `precision: true`, targets such as volume or a custom ratio are therefore rescaled incorrectly; use `precision: false` for those targets.
384
+ - **Comparing raw and logged variants:** use separate instances for ADX, stochastic, or Heiken-Ashi at identical periods/smoothing settings. Their raw/log variants can share internal state despite having different output names.
385
+ - **Cross one-hot output:** provide a finite integer `limit` of at least 2 (within the input length) when `oneHot: true` so the vector has a meaningful fixed size.
386
+
387
+ These are descriptions of the current implementation, not promises that invalid inputs will always throw. No data cleaning or strategy decisions are performed for you.
388
+
389
+ ## Notes for developers and LLMs
390
+
391
+ Use this section as a compact implementation contract, alongside [AGENTS.md](./AGENTS.md).
392
+
393
+ - Public entry point: default ESM export `OHLCV_INDICATORS` from `index.js`. Constructor shape: `{ input, ticker?, chunkProcess?, config? }`. There are no public `scaler()` or `vidya()` methods.
394
+ - Registration is separate from execution. `inputParams` contains immutable declarations; `executionParams` is the mutable runtime queue. Never serialize runtime instances, buffers, generated jobs, or resolved callback functions.
395
+ - Handlers execute chronologically, allocate columns on `index === 0`, and keep rolling state in `main.instances`. The runtime is column-oriented even though input and returned data are row-oriented.
396
+ - `initializeColumns()` owns output allocation, naming collision checks, price metadata, and generated lags. Numeric outputs normally use `Float64Array` with `NaN`; object/one-hot columns use arrays. Volume input uses `Int32Array`.
397
+ - `src/core-indicators/` contains the local numeric indicator classes used at runtime. `trading-signals` is retained as a regression reference, not a production indicator import. Preserve warm-up, arithmetic order, and intentional zero/NaN behavior when changing these classes.
398
+ - Full output storage grows with both row count and column count. Each `Float64Array` column uses approximately `8 * input.length` bytes, excluding other overhead. Lags add full columns; `getData()` allocates row objects on each call. Request only needed features, or use `getLastValues()` when you only need the final row.
399
+ - `chunkProcess` does not turn the API into asynchronous, streaming, or bounded-memory processing. Create a new instance for each independent run and retain the input separately if you need replay.
400
+ - Read the implementation for exact output naming and formulas; do not assume every third-party indicator convention or parameter combination is supported.
401
+
402
+ ### Development checks
403
+
404
+ From a checkout with dependencies installed:
405
+
406
+ ```sh
407
+ npm test
408
+ npm test -- --benchmark --rows 20000 --repeats 9
409
+ npm run build
410
+ git diff --check
411
+ ```
412
+
413
+ `test/test.js` runs deterministic local regressions by default, including indicator behavior, configuration replay, and core-class comparisons. `npm test -- --live` is an optional integration demo requiring its configured market-data service. Build the browser bundle with `npm run build`; do not edit the minified file manually.
414
+
415
+ The package declares the ISC license. Adapted core indicator code retains its upstream MIT notice; preserve that notice when redistributing it and the generated bundle's accompanying license file.