ta-crypto 0.1.4 → 0.2.3

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.
package/README.md CHANGED
@@ -1,15 +1,9 @@
1
- # ta-crypto
1
+ # ta-crypto
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/ta-crypto.svg)](https://www.npmjs.com/package/ta-crypto)
4
4
  [![CI](https://github.com/TDamiao/ta-crypto/actions/workflows/ci.yml/badge.svg)](https://github.com/TDamiao/ta-crypto/actions/workflows/ci.yml)
5
5
 
6
- Technical analysis indicators for crypto markets in Node.js. Inspired by pandas-ta, but focused on crypto use-cases and Node-friendly APIs.
7
-
8
- ## Features
9
-
10
- - Typed, lightweight indicators for OHLCV arrays
11
- - Crypto-specific extras like session VWAP, funding rate helpers, volatility regimes, and orderflow proxies
12
- - Functions return arrays aligned to input length with `null` for insufficient data
6
+ Technical analysis indicators for crypto markets in Node.js.
13
7
 
14
8
  ## Install
15
9
 
@@ -20,56 +14,105 @@ npm i ta-crypto
20
14
  ## Quick Start
21
15
 
22
16
  ```ts
23
- import {
24
- ta,
25
- sma,
26
- rsi,
27
- macd,
28
- bbands,
29
- atr,
30
- vwap,
31
- vwapSession,
32
- realizedVolatility,
33
- fundingRateCumulative,
34
- volatilityRegime,
35
- orderflowImbalance,
36
- obv,
37
- mfi,
38
- stoch,
39
- adx
40
- } from "ta-crypto";
41
-
42
- const close = [101, 102, 99, 105, 110, 108, 111];
43
- const open = [100, 101, 100, 103, 108, 109, 110];
44
- const high = [102, 103, 101, 106, 112, 110, 112];
45
- const low = [ 99, 100, 98, 102, 107, 106, 109];
46
- const volume =[ 10, 12, 11, 15, 17, 14, 18];
47
- const session=[ 1, 1, 1, 2, 2, 2, 3];
48
-
49
- const s = sma(close, 3);
17
+ import { sma, rsi, macd, bbands, atr, vwapSession, toOHLCV } from "ta-crypto";
18
+
19
+ const candles = [
20
+ { open: 100, high: 102, low: 99, close: 101, volume: 10, time: 1 },
21
+ { open: 101, high: 103, low: 100, close: 102, volume: 12, time: 2 }
22
+ ];
23
+
24
+ const { high, low, close, volume } = toOHLCV(candles, 0);
25
+ const s = sma(close, 14);
50
26
  const r = rsi(close, 14);
51
27
  const m = macd(close);
52
28
  const b = bbands(close, 20, 2);
53
29
  const a = atr(high, low, close, 14);
54
- const v = vwap(high, low, close, volume);
55
- const vs = vwapSession(high, low, close, volume, session);
56
- const rv = realizedVolatility(close, 30, 365);
57
- const fr = fundingRateCumulative([0.0001, -0.0002, 0.00005]);
58
- const vr = volatilityRegime(close, 30, 365);
59
- const ofi = orderflowImbalance(open, close, volume, 20);
60
- const o = obv(close, volume);
61
- const moneyFlow = mfi(high, low, close, volume, 14);
62
- const st = stoch(high, low, close, 14, 3);
63
- const dx = adx(high, low, close, 14);
64
-
65
- const s2 = ta.sma(close, 3);
30
+ const vs = vwapSession(high, low, close, volume, [1, 1]);
66
31
  ```
67
32
 
68
- ## API Conventions
33
+ ## Stateful API (streaming)
34
+
35
+ ```ts
36
+ import { createRSI, createVWAPSession } from "ta-crypto";
37
+
38
+ const rsi14 = createRSI(14);
39
+ const nextRsi = rsi14.next(101.25);
40
+
41
+ const vwap = createVWAPSession();
42
+ const nextVwap = vwap.next({
43
+ high: 102,
44
+ low: 99,
45
+ close: 101,
46
+ volume: 10,
47
+ sessionId: "2026-02-10-asia"
48
+ });
49
+ ```
50
+
51
+ ## Compatibility
52
+
53
+ `ta-crypto` now ships golden tests (`test/fixtures/golden.json`) to lock behavior across releases.
54
+
55
+ Classic indicators:
56
+
57
+ | Indicator | Reference | Tolerance |
58
+ | --- | --- | --- |
59
+ | SMA, EMA, RSI | Golden baseline | 1e-10 |
60
+ | MACD, BBANDS | Golden baseline | 1e-10 |
61
+ | ATR, ADX | Golden baseline | 1e-10 |
62
+
63
+ Crypto indicators:
64
+
65
+ | Indicator | Reference | Tolerance |
66
+ | --- | --- | --- |
67
+ | VWAP Session | Golden baseline | 1e-10 |
68
+ | Stateful VWAP Session parity | Batch VWAP Session | 1e-10 |
69
+
70
+ Streaming parity:
71
+
72
+ | Indicator | Reference | Tolerance |
73
+ | --- | --- | --- |
74
+ | Stateful RSI(14) | Batch RSI(14) | 1e-10 |
75
+
76
+ External parity:
77
+
78
+ | Indicator set | Reference libs | Tolerance |
79
+ | --- | --- | --- |
80
+ | SMA/EMA/RSI/MACD/BBANDS/ATR/ADX | TA-Lib, pandas-ta, technicalindicators | 1e-8 |
81
+
82
+ Generate/review vectors:
83
+
84
+ ```bash
85
+ npm run generate:golden
86
+ npm run test:golden
87
+ npm run generate:compat
88
+ npm run test:compat:technicalindicators
89
+ # python deps: pip install -r scripts/requirements-compat.txt
90
+ npm run test:compat:python
91
+ ```
92
+
93
+ Note:
94
+ - CI uses Linux + Python 3.12 for full TA-Lib/pandas-ta coverage.
95
+ - On Windows, `pandas-ta` may be unavailable depending on upstream wheels; the script reports explicit skip in that case.
96
+ - `TA-Lib` and `technicalindicators` are strict release gates; `pandas-ta` runs as compatibility telemetry and reports warnings when it diverges by environment.
97
+
98
+ ## Crypto Playbooks
99
+
100
+ Session VWAP reset:
101
+ - Provide one `sessionId` per candle.
102
+ - VWAP resets exactly when `sessionId` changes.
103
+ - Use exchange session boundaries (UTC day, funding window, or custom market session).
104
+
105
+ Funding APR:
106
+ - `fundingRateAPR(values, periodsPerYear)` annualizes periodic funding.
107
+ - `periodsPerYear` presets:
108
+ - `3/day` funding: `1095`
109
+ - `1/hour` funding: `8760`
110
+ - `1/8h` funding: `1095`
69
111
 
70
- - Input arrays must be the same length.
71
- - Outputs are aligned to input length.
72
- - `null` marks periods with insufficient data.
112
+ Volatility regime:
113
+ - `volatilityRegime(values, length, periodsPerYear, lowZ, highZ)` returns `-1`, `0`, `1`.
114
+ - Default thresholds: `lowZ = -0.5`, `highZ = 0.5`.
115
+ - Calibration approach: increase absolute thresholds for noisier low-timeframe pairs, reduce for smoother higher timeframes.
73
116
 
74
117
  ## Indicators
75
118
 
@@ -94,64 +137,80 @@ Trend:
94
137
  Crypto:
95
138
  - `vwapSession`, `fundingRateCumulative`, `fundingRateAPR`, `volatilityRegime`, `signedVolume`, `volumeDelta`, `orderflowImbalance`
96
139
 
97
- ## Crypto-Specific Notes
140
+ ## Hero Features (crypto edge)
98
141
 
99
- Session VWAP:
100
- - Provide a `session` array with an id per candle. VWAP resets on each new session id.
142
+ 1. Session-aware VWAP (`vwapSession`, `createVWAPSession`)
143
+ 2. Funding analytics (`fundingRateCumulative`, `fundingRateAPR`)
144
+ 3. Volatility regime labeling (`volatilityRegime`)
145
+ 4. Orderflow proxies (`signedVolume`, `volumeDelta`, `orderflowImbalance`)
146
+ 5. Streaming/stateful indicators for low-allocation pipelines
101
147
 
102
- Funding rate:
103
- - `fundingRateCumulative` sums periodic rates over time.
104
- - `fundingRateAPR` annualizes a periodic rate using `periodsPerYear`.
148
+ Limitations:
149
+ - Orderflow proxies infer pressure from candle direction and volume; they are not a replacement for L2/L3 order book data.
150
+ - Different libraries use different warmup conventions; comparisons use overlapping non-null windows.
105
151
 
106
- Volatility regimes:
107
- - `volatilityRegime` computes realized volatility and returns -1, 0, or 1 based on z-score thresholds.
152
+ ## Candle Contracts
108
153
 
109
- Orderflow proxies:
110
- - `signedVolume`, `volumeDelta`, and `orderflowImbalance` infer buy/sell pressure from candle direction.
154
+ Use typed candles plus helpers:
111
155
 
112
- ## Release
156
+ ```ts
157
+ import { pluckClose, toOHLCV } from "ta-crypto";
158
+
159
+ const close = pluckClose(candles);
160
+ const { open, high, low, close: c, volume } = toOHLCV(candles, 0);
161
+ ```
113
162
 
114
- Automated npm publish runs on tag push. Steps:
163
+ Validation:
164
+ - Multi-series indicators enforce equal lengths (`assertSameLength`).
165
+ - Candle helpers validate finite numeric fields with index-specific error messages.
115
166
 
116
- 1. Ensure `NPM_TOKEN` is set in GitHub Actions secrets.
117
- - For GitHub Packages, set `GITHUB_TOKEN` (already provided) or a PAT if needed.
118
- 2. Update changelog (recommended):
167
+ ## Module Imports
119
168
 
120
- ```bash
121
- npm run changelog
169
+ ```ts
170
+ import { sma } from "ta-crypto/indicators";
171
+ import { vwapSession } from "ta-crypto/crypto";
172
+ import { toOHLCV } from "ta-crypto/candles";
173
+ import { createRSI } from "ta-crypto/stateful";
122
174
  ```
123
175
 
124
- 3. Bump version (optional):
176
+ ## Bench (internal baseline, 10k candles)
125
177
 
126
- ```bash
127
- npm run version:patch
178
+ ```text
179
+ sma(14): 0.619 ms/run
180
+ ema(14): 0.549 ms/run
181
+ rsi(14): 0.647 ms/run
182
+ macd: 2.887 ms/run
183
+ bbands: 2.159 ms/run
184
+ atr(14): 1.356 ms/run
185
+ adx(14): 5.371 ms/run
128
186
  ```
129
187
 
130
- 4. Run:
188
+ Run locally:
131
189
 
132
190
  ```bash
133
- npm run release
191
+ npm run bench
134
192
  ```
135
193
 
136
- Or do everything in one step:
194
+ ## Release
195
+
196
+ Publications are gated by GitHub Actions:
197
+ - `CI` runs build/tests + compatibility checks.
198
+ - `Release` workflows run tests/compat again before publish.
199
+
200
+ Recommended with GitHub CLI:
137
201
 
138
202
  ```bash
139
- npm run release:patch
203
+ gh auth login
204
+ gh workflow run ci.yml
205
+ gh run list --workflow ci.yml --limit 1
140
206
  ```
141
207
 
142
- GitHub Packages:
143
- - A separate workflow publishes `@TDamiao/ta-crypto` to GitHub Packages on the same tag.
144
-
145
- This creates and pushes a tag like `v0.1.1`, triggering the release workflow.
208
+ ```bash
209
+ npm run changelog
210
+ npm run version:patch
211
+ npm run release
212
+ ```
146
213
 
147
214
  ## License
148
215
 
149
216
  MIT
150
-
151
- ## GitHub Packages
152
-
153
- Install from GitHub Packages:
154
-
155
- ```bash
156
- npm i @TDamiao/ta-crypto
157
- ```
@@ -0,0 +1,15 @@
1
+ import type { Candle } from "./types.js";
2
+ export type OHLCV = {
3
+ open: number[];
4
+ high: number[];
5
+ low: number[];
6
+ close: number[];
7
+ volume: number[];
8
+ time: Array<number | string | Date | undefined>;
9
+ };
10
+ export declare function pluckOpen(candles: Candle[]): number[];
11
+ export declare function pluckHigh(candles: Candle[]): number[];
12
+ export declare function pluckLow(candles: Candle[]): number[];
13
+ export declare function pluckClose(candles: Candle[]): number[];
14
+ export declare function pluckVolume(candles: Candle[], fallback?: number): number[];
15
+ export declare function toOHLCV(candles: Candle[], volumeFallback?: number): OHLCV;
@@ -0,0 +1,36 @@
1
+ import { assertFiniteSeries } from "./core/math.js";
2
+ export function pluckOpen(candles) {
3
+ const out = candles.map(c => c.open);
4
+ assertFiniteSeries("open", out);
5
+ return out;
6
+ }
7
+ export function pluckHigh(candles) {
8
+ const out = candles.map(c => c.high);
9
+ assertFiniteSeries("high", out);
10
+ return out;
11
+ }
12
+ export function pluckLow(candles) {
13
+ const out = candles.map(c => c.low);
14
+ assertFiniteSeries("low", out);
15
+ return out;
16
+ }
17
+ export function pluckClose(candles) {
18
+ const out = candles.map(c => c.close);
19
+ assertFiniteSeries("close", out);
20
+ return out;
21
+ }
22
+ export function pluckVolume(candles, fallback = 0) {
23
+ const out = candles.map(c => (c.volume === undefined ? fallback : c.volume));
24
+ assertFiniteSeries("volume", out);
25
+ return out;
26
+ }
27
+ export function toOHLCV(candles, volumeFallback = 0) {
28
+ return {
29
+ open: pluckOpen(candles),
30
+ high: pluckHigh(candles),
31
+ low: pluckLow(candles),
32
+ close: pluckClose(candles),
33
+ volume: pluckVolume(candles, volumeFallback),
34
+ time: candles.map(c => c.time)
35
+ };
36
+ }
@@ -1,4 +1,5 @@
1
1
  export declare function isNum(value: unknown): value is number;
2
+ export declare function assertFiniteSeries(name: string, values: number[]): void;
2
3
  export declare function assertSameLength(...series: number[][]): void;
3
4
  export declare function makeSeries(length: number): Array<number | null>;
4
5
  export declare function sum(values: number[], start: number, end: number): number;
package/dist/core/math.js CHANGED
@@ -1,13 +1,20 @@
1
1
  export function isNum(value) {
2
2
  return typeof value === "number" && Number.isFinite(value);
3
3
  }
4
+ export function assertFiniteSeries(name, values) {
5
+ for (let i = 0; i < values.length; i++) {
6
+ if (!isNum(values[i])) {
7
+ throw new Error(`${name}[${i}] must be a finite number`);
8
+ }
9
+ }
10
+ }
4
11
  export function assertSameLength(...series) {
5
12
  if (series.length === 0)
6
13
  return;
7
14
  const len = series[0].length;
8
15
  for (const s of series) {
9
16
  if (s.length !== len) {
10
- throw new Error("All series must have the same length");
17
+ throw new Error(`All series must have the same length (expected ${len}, got ${s.length})`);
11
18
  }
12
19
  }
13
20
  }
@@ -1,4 +1,4 @@
1
- import { makeSeries } from "./math.js";
1
+ import { assertSameLength, makeSeries } from "./math.js";
2
2
  import { ema } from "./overlap.js";
3
3
  export function macd(values, fast = 12, slow = 26, signal = 9) {
4
4
  const fastEma = ema(values, fast);
@@ -47,10 +47,8 @@ export function rsi(values, length = 14) {
47
47
  return out;
48
48
  }
49
49
  export function stoch(high, low, close, kLength = 14, dLength = 3) {
50
+ assertSameLength(high, low, close);
50
51
  const len = close.length;
51
- if (high.length !== len || low.length !== len) {
52
- throw new Error("All series must have the same length");
53
- }
54
52
  const k = makeSeries(len);
55
53
  const d = makeSeries(len);
56
54
  for (let i = kLength - 1; i < len; i++) {
@@ -1,11 +1,9 @@
1
- import { makeSeries } from "./math.js";
1
+ import { assertSameLength, makeSeries } from "./math.js";
2
2
  import { rma } from "./overlap.js";
3
3
  import { trueRange } from "./volatility.js";
4
4
  export function adx(high, low, close, length = 14) {
5
+ assertSameLength(high, low, close);
5
6
  const len = close.length;
6
- if (high.length !== len || low.length !== len) {
7
- throw new Error("All series must have the same length");
8
- }
9
7
  const plusDM = new Array(len).fill(0);
10
8
  const minusDM = new Array(len).fill(0);
11
9
  for (let i = 1; i < len; i++) {
@@ -1,8 +1,6 @@
1
- import { makeSeries } from "./math.js";
1
+ import { assertSameLength, makeSeries } from "./math.js";
2
2
  export function obv(close, volume) {
3
- if (close.length !== volume.length) {
4
- throw new Error("All series must have the same length");
5
- }
3
+ assertSameLength(close, volume);
6
4
  const out = makeSeries(close.length);
7
5
  let acc = 0;
8
6
  for (let i = 0; i < close.length; i++) {
@@ -19,10 +17,8 @@ export function obv(close, volume) {
19
17
  return out;
20
18
  }
21
19
  export function mfi(high, low, close, volume, length = 14) {
20
+ assertSameLength(high, low, close, volume);
22
21
  const len = close.length;
23
- if (high.length !== len || low.length !== len || volume.length !== len) {
24
- throw new Error("All series must have the same length");
25
- }
26
22
  const out = makeSeries(len);
27
23
  if (length <= 0)
28
24
  return out;
@@ -0,0 +1,2 @@
1
+ export { vwapSession, fundingRateCumulative, fundingRateAPR, volatilityRegime, signedVolume, volumeDelta, orderflowImbalance } from "./core/crypto.js";
2
+ export { createVWAPSession } from "./stateful.js";
package/dist/crypto.js ADDED
@@ -0,0 +1,2 @@
1
+ export { vwapSession, fundingRateCumulative, fundingRateAPR, volatilityRegime, signedVolume, volumeDelta, orderflowImbalance } from "./core/crypto.js";
2
+ export { createVWAPSession } from "./stateful.js";
package/dist/index.d.ts CHANGED
@@ -5,8 +5,20 @@ export { logReturn, percentReturn, realizedVolatility } from "./core/performance
5
5
  export { obv, mfi } from "./core/volume.js";
6
6
  export { adx } from "./core/trend.js";
7
7
  export { vwapSession, fundingRateCumulative, fundingRateAPR, volatilityRegime, signedVolume, volumeDelta, orderflowImbalance } from "./core/crypto.js";
8
+ export { pluckOpen, pluckHigh, pluckLow, pluckClose, pluckVolume, toOHLCV } from "./candles.js";
9
+ export { createRSI, createVWAPSession } from "./stateful.js";
8
10
  export * from "./types.js";
11
+ import * as candles from "./candles.js";
12
+ import * as stateful from "./stateful.js";
9
13
  export declare const ta: {
14
+ createRSI(period?: number): stateful.StatefulIndicator<number, number | null>;
15
+ createVWAPSession(): stateful.StatefulIndicator<stateful.VWAPSessionInput, number | null>;
16
+ pluckOpen(candles: import("./types.js").Candle[]): number[];
17
+ pluckHigh(candles: import("./types.js").Candle[]): number[];
18
+ pluckLow(candles: import("./types.js").Candle[]): number[];
19
+ pluckClose(candles: import("./types.js").Candle[]): number[];
20
+ pluckVolume(candles: import("./types.js").Candle[], fallback?: number): number[];
21
+ toOHLCV(candles: import("./types.js").Candle[], volumeFallback?: number): candles.OHLCV;
10
22
  vwapSession(high: number[], low: number[], close: number[], volume: number[], session: Array<string | number>): Array<number | null>;
11
23
  fundingRateCumulative(values: number[]): Array<number | null>;
12
24
  fundingRateAPR(values: number[], periodsPerYear?: number): Array<number | null>;
package/dist/index.js CHANGED
@@ -5,6 +5,8 @@ export { logReturn, percentReturn, realizedVolatility } from "./core/performance
5
5
  export { obv, mfi } from "./core/volume.js";
6
6
  export { adx } from "./core/trend.js";
7
7
  export { vwapSession, fundingRateCumulative, fundingRateAPR, volatilityRegime, signedVolume, volumeDelta, orderflowImbalance } from "./core/crypto.js";
8
+ export { pluckOpen, pluckHigh, pluckLow, pluckClose, pluckVolume, toOHLCV } from "./candles.js";
9
+ export { createRSI, createVWAPSession } from "./stateful.js";
8
10
  export * from "./types.js";
9
11
  import * as overlap from "./core/overlap.js";
10
12
  import * as momentum from "./core/momentum.js";
@@ -13,6 +15,8 @@ import * as performance from "./core/performance.js";
13
15
  import * as volume from "./core/volume.js";
14
16
  import * as trend from "./core/trend.js";
15
17
  import * as crypto from "./core/crypto.js";
18
+ import * as candles from "./candles.js";
19
+ import * as stateful from "./stateful.js";
16
20
  export const ta = {
17
21
  ...overlap,
18
22
  ...momentum,
@@ -20,5 +24,7 @@ export const ta = {
20
24
  ...performance,
21
25
  ...volume,
22
26
  ...trend,
23
- ...crypto
27
+ ...crypto,
28
+ ...candles,
29
+ ...stateful
24
30
  };
@@ -0,0 +1,6 @@
1
+ export { sma, ema, rma, hl2, hlc3, ohlc4, vwap, bbands } from "./core/overlap.js";
2
+ export { rsi, macd, stoch } from "./core/momentum.js";
3
+ export { trueRange, atr, natr } from "./core/volatility.js";
4
+ export { logReturn, percentReturn, realizedVolatility } from "./core/performance.js";
5
+ export { obv, mfi } from "./core/volume.js";
6
+ export { adx } from "./core/trend.js";
@@ -0,0 +1,6 @@
1
+ export { sma, ema, rma, hl2, hlc3, ohlc4, vwap, bbands } from "./core/overlap.js";
2
+ export { rsi, macd, stoch } from "./core/momentum.js";
3
+ export { trueRange, atr, natr } from "./core/volatility.js";
4
+ export { logReturn, percentReturn, realizedVolatility } from "./core/performance.js";
5
+ export { obv, mfi } from "./core/volume.js";
6
+ export { adx } from "./core/trend.js";
@@ -0,0 +1,13 @@
1
+ export type StatefulIndicator<TIn, TOut> = {
2
+ next(value: TIn): TOut;
3
+ reset(): void;
4
+ };
5
+ export declare function createRSI(period?: number): StatefulIndicator<number, number | null>;
6
+ export type VWAPSessionInput = {
7
+ high: number;
8
+ low: number;
9
+ close: number;
10
+ volume: number;
11
+ sessionId: string | number;
12
+ };
13
+ export declare function createVWAPSession(): StatefulIndicator<VWAPSessionInput, number | null>;
@@ -0,0 +1,83 @@
1
+ export function createRSI(period = 14) {
2
+ if (period <= 0) {
3
+ throw new Error("period must be > 0");
4
+ }
5
+ let prevPrice = null;
6
+ let avgGain = 0;
7
+ let avgLoss = 0;
8
+ let seedGain = 0;
9
+ let seedLoss = 0;
10
+ let seedCount = 0;
11
+ let count = 0;
12
+ return {
13
+ next(price) {
14
+ if (!Number.isFinite(price)) {
15
+ throw new Error("price must be a finite number");
16
+ }
17
+ count += 1;
18
+ if (prevPrice === null) {
19
+ prevPrice = price;
20
+ return null;
21
+ }
22
+ const diff = price - prevPrice;
23
+ prevPrice = price;
24
+ const gain = diff > 0 ? diff : 0;
25
+ const loss = diff < 0 ? -diff : 0;
26
+ if (seedCount < period) {
27
+ seedGain += gain;
28
+ seedLoss += loss;
29
+ seedCount += 1;
30
+ if (seedCount < period)
31
+ return null;
32
+ avgGain = seedGain / period;
33
+ avgLoss = seedLoss / period;
34
+ }
35
+ else {
36
+ avgGain = (avgGain * (period - 1) + gain) / period;
37
+ avgLoss = (avgLoss * (period - 1) + loss) / period;
38
+ }
39
+ if (count < period + 1)
40
+ return null;
41
+ if (avgLoss === 0)
42
+ return 100;
43
+ const rs = avgGain / avgLoss;
44
+ return 100 - 100 / (1 + rs);
45
+ },
46
+ reset() {
47
+ prevPrice = null;
48
+ avgGain = 0;
49
+ avgLoss = 0;
50
+ seedGain = 0;
51
+ seedLoss = 0;
52
+ seedCount = 0;
53
+ count = 0;
54
+ }
55
+ };
56
+ }
57
+ export function createVWAPSession() {
58
+ let cumPV = 0;
59
+ let cumV = 0;
60
+ let lastSession;
61
+ return {
62
+ next(candle) {
63
+ const { high, low, close, volume, sessionId } = candle;
64
+ if (!Number.isFinite(high) || !Number.isFinite(low) || !Number.isFinite(close) || !Number.isFinite(volume)) {
65
+ throw new Error("high, low, close and volume must be finite numbers");
66
+ }
67
+ if (lastSession !== sessionId) {
68
+ cumPV = 0;
69
+ cumV = 0;
70
+ lastSession = sessionId;
71
+ }
72
+ const typical = (high + low + close) / 3;
73
+ cumPV += typical * volume;
74
+ cumV += volume;
75
+ return cumV === 0 ? null : cumPV / cumV;
76
+ },
77
+ reset() {
78
+ cumPV = 0;
79
+ cumV = 0;
80
+ lastSession = undefined;
81
+ }
82
+ };
83
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ta-crypto",
3
- "version": "0.1.4",
3
+ "version": "0.2.3",
4
4
  "description": "Technical analysis for crypto markets in Node.js",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -9,8 +9,25 @@
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js"
12
+ },
13
+ "./indicators": {
14
+ "types": "./dist/indicators.d.ts",
15
+ "import": "./dist/indicators.js"
16
+ },
17
+ "./crypto": {
18
+ "types": "./dist/crypto.d.ts",
19
+ "import": "./dist/crypto.js"
20
+ },
21
+ "./candles": {
22
+ "types": "./dist/candles.d.ts",
23
+ "import": "./dist/candles.js"
24
+ },
25
+ "./stateful": {
26
+ "types": "./dist/stateful.d.ts",
27
+ "import": "./dist/stateful.js"
12
28
  }
13
29
  },
30
+ "sideEffects": false,
14
31
  "types": "dist/index.d.ts",
15
32
  "main": "dist/index.js",
16
33
  "files": [
@@ -30,10 +47,18 @@
30
47
  "version:minor": "npm version minor",
31
48
  "version:major": "npm version major",
32
49
  "changelog": "node ./scripts/changelog.js",
33
- "test": "node -e \"console.log('no tests yet')\""
50
+ "generate:golden": "npm run build && node ./scripts/generate-golden.js",
51
+ "generate:compat": "npm run build && node ./scripts/export-compat-vectors.js",
52
+ "bench": "npm run build && node ./scripts/bench.js",
53
+ "test": "npm run build && node --test test/*.test.mjs",
54
+ "test:golden": "npm run build && node --test test/golden.test.mjs",
55
+ "test:compat:technicalindicators": "npm run generate:compat && node ./scripts/compare-technicalindicators.js",
56
+ "test:compat:python": "npm run generate:compat && python ./scripts/compare-python-refs.py",
57
+ "test:compat": "npm run test:compat:technicalindicators && npm run test:compat:python"
34
58
  },
35
59
  "devDependencies": {
36
60
  "rimraf": "^5.0.5",
61
+ "technicalindicators": "^3.1.0",
37
62
  "typescript": "^5.4.5"
38
63
  },
39
64
  "keywords": [