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 +146 -87
- package/dist/candles.d.ts +15 -0
- package/dist/candles.js +36 -0
- package/dist/core/math.d.ts +1 -0
- package/dist/core/math.js +8 -1
- package/dist/core/momentum.js +2 -4
- package/dist/core/trend.js +2 -4
- package/dist/core/volume.js +3 -7
- package/dist/crypto.d.ts +2 -0
- package/dist/crypto.js +2 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +7 -1
- package/dist/indicators.d.ts +6 -0
- package/dist/indicators.js +6 -0
- package/dist/stateful.d.ts +13 -0
- package/dist/stateful.js +83 -0
- package/package.json +27 -2
package/README.md
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# ta-crypto
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/ta-crypto)
|
|
4
4
|
[](https://github.com/TDamiao/ta-crypto/actions/workflows/ci.yml)
|
|
5
5
|
|
|
6
|
-
Technical analysis indicators for crypto markets in Node.js.
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
71
|
-
-
|
|
72
|
-
- `
|
|
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
|
-
##
|
|
140
|
+
## Hero Features (crypto edge)
|
|
98
141
|
|
|
99
|
-
Session VWAP
|
|
100
|
-
|
|
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
|
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
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
|
-
|
|
107
|
-
- `volatilityRegime` computes realized volatility and returns -1, 0, or 1 based on z-score thresholds.
|
|
152
|
+
## Candle Contracts
|
|
108
153
|
|
|
109
|
-
|
|
110
|
-
- `signedVolume`, `volumeDelta`, and `orderflowImbalance` infer buy/sell pressure from candle direction.
|
|
154
|
+
Use typed candles plus helpers:
|
|
111
155
|
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
121
|
-
|
|
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
|
-
|
|
176
|
+
## Bench (internal baseline, 10k candles)
|
|
125
177
|
|
|
126
|
-
```
|
|
127
|
-
|
|
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
|
-
|
|
188
|
+
Run locally:
|
|
131
189
|
|
|
132
190
|
```bash
|
|
133
|
-
npm run
|
|
191
|
+
npm run bench
|
|
134
192
|
```
|
|
135
193
|
|
|
136
|
-
|
|
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
|
-
|
|
203
|
+
gh auth login
|
|
204
|
+
gh workflow run ci.yml
|
|
205
|
+
gh run list --workflow ci.yml --limit 1
|
|
140
206
|
```
|
|
141
207
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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;
|
package/dist/candles.js
ADDED
|
@@ -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
|
+
}
|
package/dist/core/math.d.ts
CHANGED
|
@@ -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(
|
|
17
|
+
throw new Error(`All series must have the same length (expected ${len}, got ${s.length})`);
|
|
11
18
|
}
|
|
12
19
|
}
|
|
13
20
|
}
|
package/dist/core/momentum.js
CHANGED
|
@@ -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++) {
|
package/dist/core/trend.js
CHANGED
|
@@ -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++) {
|
package/dist/core/volume.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/crypto.d.ts
ADDED
package/dist/crypto.js
ADDED
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>;
|
package/dist/stateful.js
ADDED
|
@@ -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.
|
|
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
|
-
"
|
|
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": [
|