ta-crypto 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # pandas_btc
2
+
3
+ [![npm version](https://img.shields.io/npm/v/ta-crypto.svg)](https://www.npmjs.com/package/ta-crypto)
4
+ [![CI](https://github.com/tdamiao/pandas-btc/actions/workflows/ci.yml/badge.svg)](https://github.com/tdamiao/pandas-btc/actions/workflows/ci.yml)
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
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm i ta-crypto
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```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);
50
+ const r = rsi(close, 14);
51
+ const m = macd(close);
52
+ const b = bbands(close, 20, 2);
53
+ 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);
66
+ ```
67
+
68
+ ## API Conventions
69
+
70
+ - Input arrays must be the same length.
71
+ - Outputs are aligned to input length.
72
+ - `null` marks periods with insufficient data.
73
+
74
+ ## Indicators
75
+
76
+ Overlap:
77
+ - `sma`, `ema`, `rma`, `hl2`, `hlc3`, `ohlc4`, `vwap`, `bbands`
78
+
79
+ Momentum:
80
+ - `rsi`, `macd`, `stoch`
81
+
82
+ Volatility:
83
+ - `trueRange`, `atr`, `natr`, `realizedVolatility`
84
+
85
+ Performance:
86
+ - `logReturn`, `percentReturn`
87
+
88
+ Volume:
89
+ - `obv`, `mfi`
90
+
91
+ Trend:
92
+ - `adx`
93
+
94
+ Crypto:
95
+ - `vwapSession`, `fundingRateCumulative`, `fundingRateAPR`, `volatilityRegime`, `signedVolume`, `volumeDelta`, `orderflowImbalance`
96
+
97
+ ## Crypto-Specific Notes
98
+
99
+ Session VWAP:
100
+ - Provide a `session` array with an id per candle. VWAP resets on each new session id.
101
+
102
+ Funding rate:
103
+ - `fundingRateCumulative` sums periodic rates over time.
104
+ - `fundingRateAPR` annualizes a periodic rate using `periodsPerYear`.
105
+
106
+ Volatility regimes:
107
+ - `volatilityRegime` computes realized volatility and returns -1, 0, or 1 based on z-score thresholds.
108
+
109
+ Orderflow proxies:
110
+ - `signedVolume`, `volumeDelta`, and `orderflowImbalance` infer buy/sell pressure from candle direction.
111
+
112
+ ## License
113
+
114
+ MIT
@@ -0,0 +1,7 @@
1
+ export declare function vwapSession(high: number[], low: number[], close: number[], volume: number[], session: Array<string | number>): Array<number | null>;
2
+ export declare function fundingRateCumulative(values: number[]): Array<number | null>;
3
+ export declare function fundingRateAPR(values: number[], periodsPerYear?: number): Array<number | null>;
4
+ export declare function volatilityRegime(values: number[], length?: number, periodsPerYear?: number, lowZ?: number, highZ?: number): Array<number | null>;
5
+ export declare function signedVolume(open: number[], close: number[], volume: number[]): Array<number | null>;
6
+ export declare function volumeDelta(open: number[], close: number[], volume: number[], length?: number): Array<number | null>;
7
+ export declare function orderflowImbalance(open: number[], close: number[], volume: number[], length?: number): Array<number | null>;
@@ -0,0 +1,98 @@
1
+ import { assertSameLength, makeSeries, mean, stdev } from "./math";
2
+ import { hlc3 } from "./overlap";
3
+ import { realizedVolatility } from "./performance";
4
+ export function vwapSession(high, low, close, volume, session) {
5
+ assertSameLength(high, low, close, volume);
6
+ if (session.length !== high.length) {
7
+ throw new Error("All series must have the same length");
8
+ }
9
+ const out = makeSeries(high.length);
10
+ const typical = hlc3(high, low, close).map(v => (v === null ? 0 : v));
11
+ let cumPV = 0;
12
+ let cumV = 0;
13
+ let lastSession = undefined;
14
+ for (let i = 0; i < high.length; i++) {
15
+ if (lastSession !== session[i]) {
16
+ cumPV = 0;
17
+ cumV = 0;
18
+ lastSession = session[i];
19
+ }
20
+ cumPV += typical[i] * volume[i];
21
+ cumV += volume[i];
22
+ out[i] = cumV === 0 ? null : cumPV / cumV;
23
+ }
24
+ return out;
25
+ }
26
+ export function fundingRateCumulative(values) {
27
+ const out = makeSeries(values.length);
28
+ let acc = 0;
29
+ for (let i = 0; i < values.length; i++) {
30
+ acc += values[i];
31
+ out[i] = acc;
32
+ }
33
+ return out;
34
+ }
35
+ export function fundingRateAPR(values, periodsPerYear = 365 * 3) {
36
+ const out = makeSeries(values.length);
37
+ for (let i = 0; i < values.length; i++) {
38
+ out[i] = values[i] * periodsPerYear * 100;
39
+ }
40
+ return out;
41
+ }
42
+ export function volatilityRegime(values, length = 30, periodsPerYear = 365, lowZ = -0.5, highZ = 0.5) {
43
+ const vol = realizedVolatility(values, length, periodsPerYear);
44
+ const out = makeSeries(values.length);
45
+ for (let i = length * 2; i < values.length; i++) {
46
+ const windowStart = i - length + 1;
47
+ const windowEnd = i;
48
+ const window = vol.slice(windowStart, windowEnd + 1).map(v => (v === null ? 0 : v));
49
+ const m = mean(window, 0, window.length - 1);
50
+ const s = stdev(window, 0, window.length - 1);
51
+ if (s === 0) {
52
+ out[i] = 0;
53
+ continue;
54
+ }
55
+ const z = (vol[i] - m) / s;
56
+ out[i] = z > highZ ? 1 : z < lowZ ? -1 : 0;
57
+ }
58
+ return out;
59
+ }
60
+ export function signedVolume(open, close, volume) {
61
+ assertSameLength(open, close, volume);
62
+ const out = makeSeries(close.length);
63
+ for (let i = 0; i < close.length; i++) {
64
+ const diff = close[i] - open[i];
65
+ out[i] = diff > 0 ? volume[i] : diff < 0 ? -volume[i] : 0;
66
+ }
67
+ return out;
68
+ }
69
+ export function volumeDelta(open, close, volume, length = 14) {
70
+ const sv = signedVolume(open, close, volume).map(v => (v === null ? 0 : v));
71
+ const out = makeSeries(close.length);
72
+ if (length <= 0)
73
+ return out;
74
+ for (let i = length - 1; i < close.length; i++) {
75
+ let acc = 0;
76
+ for (let j = i - length + 1; j <= i; j++)
77
+ acc += sv[j];
78
+ out[i] = acc;
79
+ }
80
+ return out;
81
+ }
82
+ export function orderflowImbalance(open, close, volume, length = 14) {
83
+ assertSameLength(open, close, volume);
84
+ const out = makeSeries(close.length);
85
+ if (length <= 0)
86
+ return out;
87
+ for (let i = length - 1; i < close.length; i++) {
88
+ let signed = 0;
89
+ let total = 0;
90
+ for (let j = i - length + 1; j <= i; j++) {
91
+ const diff = close[j] - open[j];
92
+ signed += diff > 0 ? volume[j] : diff < 0 ? -volume[j] : 0;
93
+ total += volume[j];
94
+ }
95
+ out[i] = total === 0 ? null : signed / total;
96
+ }
97
+ return out;
98
+ }
@@ -0,0 +1,7 @@
1
+ export declare function isNum(value: unknown): value is number;
2
+ export declare function assertSameLength(...series: number[][]): void;
3
+ export declare function makeSeries(length: number): Array<number | null>;
4
+ export declare function sum(values: number[], start: number, end: number): number;
5
+ export declare function mean(values: number[], start: number, end: number): number;
6
+ export declare function variance(values: number[], start: number, end: number): number;
7
+ export declare function stdev(values: number[], start: number, end: number): number;
@@ -0,0 +1,37 @@
1
+ export function isNum(value) {
2
+ return typeof value === "number" && Number.isFinite(value);
3
+ }
4
+ export function assertSameLength(...series) {
5
+ if (series.length === 0)
6
+ return;
7
+ const len = series[0].length;
8
+ for (const s of series) {
9
+ if (s.length !== len) {
10
+ throw new Error("All series must have the same length");
11
+ }
12
+ }
13
+ }
14
+ export function makeSeries(length) {
15
+ return Array.from({ length }, () => null);
16
+ }
17
+ export function sum(values, start, end) {
18
+ let acc = 0;
19
+ for (let i = start; i <= end; i++)
20
+ acc += values[i];
21
+ return acc;
22
+ }
23
+ export function mean(values, start, end) {
24
+ return sum(values, start, end) / (end - start + 1);
25
+ }
26
+ export function variance(values, start, end) {
27
+ const m = mean(values, start, end);
28
+ let acc = 0;
29
+ for (let i = start; i <= end; i++) {
30
+ const d = values[i] - m;
31
+ acc += d * d;
32
+ }
33
+ return acc / (end - start + 1);
34
+ }
35
+ export function stdev(values, start, end) {
36
+ return Math.sqrt(variance(values, start, end));
37
+ }
@@ -0,0 +1,10 @@
1
+ export declare function macd(values: number[], fast?: number, slow?: number, signal?: number): {
2
+ macd: (number | null)[];
3
+ signal: (number | null)[];
4
+ histogram: (number | null)[];
5
+ };
6
+ export declare function rsi(values: number[], length?: number): Array<number | null>;
7
+ export declare function stoch(high: number[], low: number[], close: number[], kLength?: number, dLength?: number): {
8
+ k: (number | null)[];
9
+ d: (number | null)[];
10
+ };
@@ -0,0 +1,81 @@
1
+ import { makeSeries } from "./math";
2
+ import { ema } from "./overlap";
3
+ export function macd(values, fast = 12, slow = 26, signal = 9) {
4
+ const fastEma = ema(values, fast);
5
+ const slowEma = ema(values, slow);
6
+ const macdLine = makeSeries(values.length);
7
+ for (let i = 0; i < values.length; i++) {
8
+ if (fastEma[i] === null || slowEma[i] === null)
9
+ continue;
10
+ macdLine[i] = fastEma[i] - slowEma[i];
11
+ }
12
+ const signalLine = ema(macdLine.map(v => (v === null ? 0 : v)), signal);
13
+ const histogram = makeSeries(values.length);
14
+ for (let i = 0; i < values.length; i++) {
15
+ if (macdLine[i] === null || signalLine[i] === null)
16
+ continue;
17
+ histogram[i] = macdLine[i] - signalLine[i];
18
+ }
19
+ return { macd: macdLine, signal: signalLine, histogram };
20
+ }
21
+ export function rsi(values, length = 14) {
22
+ const out = makeSeries(values.length);
23
+ if (length <= 0 || values.length < length + 1)
24
+ return out;
25
+ let gain = 0;
26
+ let loss = 0;
27
+ for (let i = 1; i <= length; i++) {
28
+ const diff = values[i] - values[i - 1];
29
+ if (diff >= 0)
30
+ gain += diff;
31
+ else
32
+ loss -= diff;
33
+ }
34
+ gain /= length;
35
+ loss /= length;
36
+ const rs = loss === 0 ? 0 : gain / loss;
37
+ out[length] = loss === 0 ? 100 : 100 - 100 / (1 + rs);
38
+ for (let i = length + 1; i < values.length; i++) {
39
+ const diff = values[i] - values[i - 1];
40
+ const g = diff > 0 ? diff : 0;
41
+ const l = diff < 0 ? -diff : 0;
42
+ gain = (gain * (length - 1) + g) / length;
43
+ loss = (loss * (length - 1) + l) / length;
44
+ const r = loss === 0 ? 0 : gain / loss;
45
+ out[i] = loss === 0 ? 100 : 100 - 100 / (1 + r);
46
+ }
47
+ return out;
48
+ }
49
+ export function stoch(high, low, close, kLength = 14, dLength = 3) {
50
+ 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
+ const k = makeSeries(len);
55
+ const d = makeSeries(len);
56
+ for (let i = kLength - 1; i < len; i++) {
57
+ let hh = -Infinity;
58
+ let ll = Infinity;
59
+ for (let j = i - kLength + 1; j <= i; j++) {
60
+ if (high[j] > hh)
61
+ hh = high[j];
62
+ if (low[j] < ll)
63
+ ll = low[j];
64
+ }
65
+ const range = hh - ll;
66
+ k[i] = range === 0 ? 0 : ((close[i] - ll) / range) * 100;
67
+ }
68
+ for (let i = kLength - 1 + dLength - 1; i < len; i++) {
69
+ let acc = 0;
70
+ let ok = true;
71
+ for (let j = i - dLength + 1; j <= i; j++) {
72
+ if (k[j] === null) {
73
+ ok = false;
74
+ break;
75
+ }
76
+ acc += k[j];
77
+ }
78
+ d[i] = ok ? acc / dLength : null;
79
+ }
80
+ return { k, d };
81
+ }
@@ -0,0 +1,12 @@
1
+ export declare function sma(values: number[], length?: number): Array<number | null>;
2
+ export declare function ema(values: number[], length?: number): Array<number | null>;
3
+ export declare function rma(values: number[], length?: number): Array<number | null>;
4
+ export declare function hl2(high: number[], low: number[]): Array<number | null>;
5
+ export declare function hlc3(high: number[], low: number[], close: number[]): Array<number | null>;
6
+ export declare function ohlc4(open: number[], high: number[], low: number[], close: number[]): Array<number | null>;
7
+ export declare function vwap(high: number[], low: number[], close: number[], volume: number[], length?: number): Array<number | null>;
8
+ export declare function bbands(values: number[], length?: number, std?: number): {
9
+ basis: (number | null)[];
10
+ upper: (number | null)[];
11
+ lower: (number | null)[];
12
+ };
@@ -0,0 +1,102 @@
1
+ import { assertSameLength, isNum, makeSeries, mean, stdev } from "./math";
2
+ export function sma(values, length = 14) {
3
+ const out = makeSeries(values.length);
4
+ if (length <= 0)
5
+ return out;
6
+ for (let i = length - 1; i < values.length; i++) {
7
+ out[i] = mean(values, i - length + 1, i);
8
+ }
9
+ return out;
10
+ }
11
+ export function ema(values, length = 14) {
12
+ const out = makeSeries(values.length);
13
+ if (length <= 0)
14
+ return out;
15
+ const k = 2 / (length + 1);
16
+ let prev = 0;
17
+ for (let i = 0; i < values.length; i++) {
18
+ if (i === length - 1) {
19
+ prev = mean(values, 0, i);
20
+ out[i] = prev;
21
+ }
22
+ else if (i >= length) {
23
+ prev = (values[i] - prev) * k + prev;
24
+ out[i] = prev;
25
+ }
26
+ }
27
+ return out;
28
+ }
29
+ export function rma(values, length = 14) {
30
+ const out = makeSeries(values.length);
31
+ if (length <= 0)
32
+ return out;
33
+ let prev = 0;
34
+ for (let i = 0; i < values.length; i++) {
35
+ if (i === length - 1) {
36
+ prev = mean(values, 0, i);
37
+ out[i] = prev;
38
+ }
39
+ else if (i >= length) {
40
+ prev = (prev * (length - 1) + values[i]) / length;
41
+ out[i] = prev;
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ export function hl2(high, low) {
47
+ assertSameLength(high, low);
48
+ const out = makeSeries(high.length);
49
+ for (let i = 0; i < high.length; i++)
50
+ out[i] = (high[i] + low[i]) / 2;
51
+ return out;
52
+ }
53
+ export function hlc3(high, low, close) {
54
+ assertSameLength(high, low, close);
55
+ const out = makeSeries(high.length);
56
+ for (let i = 0; i < high.length; i++)
57
+ out[i] = (high[i] + low[i] + close[i]) / 3;
58
+ return out;
59
+ }
60
+ export function ohlc4(open, high, low, close) {
61
+ assertSameLength(open, high, low, close);
62
+ const out = makeSeries(open.length);
63
+ for (let i = 0; i < open.length; i++)
64
+ out[i] = (open[i] + high[i] + low[i] + close[i]) / 4;
65
+ return out;
66
+ }
67
+ export function vwap(high, low, close, volume, length) {
68
+ assertSameLength(high, low, close, volume);
69
+ const typical = hlc3(high, low, close).map(v => (isNum(v) ? v : 0));
70
+ const out = makeSeries(high.length);
71
+ if (!length || length <= 0) {
72
+ let cumPV = 0;
73
+ let cumV = 0;
74
+ for (let i = 0; i < high.length; i++) {
75
+ cumPV += typical[i] * volume[i];
76
+ cumV += volume[i];
77
+ out[i] = cumV === 0 ? null : cumPV / cumV;
78
+ }
79
+ return out;
80
+ }
81
+ for (let i = length - 1; i < high.length; i++) {
82
+ let pv = 0;
83
+ let v = 0;
84
+ for (let j = i - length + 1; j <= i; j++) {
85
+ pv += typical[j] * volume[j];
86
+ v += volume[j];
87
+ }
88
+ out[i] = v === 0 ? null : pv / v;
89
+ }
90
+ return out;
91
+ }
92
+ export function bbands(values, length = 20, std = 2) {
93
+ const basis = sma(values, length);
94
+ const upper = makeSeries(values.length);
95
+ const lower = makeSeries(values.length);
96
+ for (let i = length - 1; i < values.length; i++) {
97
+ const s = stdev(values, i - length + 1, i);
98
+ upper[i] = basis[i] + std * s;
99
+ lower[i] = basis[i] - std * s;
100
+ }
101
+ return { basis, upper, lower };
102
+ }
@@ -0,0 +1,3 @@
1
+ export declare function logReturn(values: number[], cumulative?: boolean): Array<number | null>;
2
+ export declare function percentReturn(values: number[], cumulative?: boolean): Array<number | null>;
3
+ export declare function realizedVolatility(values: number[], length?: number, periodsPerYear?: number): Array<number | null>;
@@ -0,0 +1,40 @@
1
+ import { makeSeries, stdev } from "./math";
2
+ export function logReturn(values, cumulative = false) {
3
+ const out = makeSeries(values.length);
4
+ let acc = 0;
5
+ for (let i = 1; i < values.length; i++) {
6
+ const r = Math.log(values[i] / values[i - 1]);
7
+ if (cumulative) {
8
+ acc += r;
9
+ out[i] = acc;
10
+ }
11
+ else {
12
+ out[i] = r;
13
+ }
14
+ }
15
+ return out;
16
+ }
17
+ export function percentReturn(values, cumulative = false) {
18
+ const out = makeSeries(values.length);
19
+ let acc = 0;
20
+ for (let i = 1; i < values.length; i++) {
21
+ const r = values[i] / values[i - 1] - 1;
22
+ if (cumulative) {
23
+ acc += r;
24
+ out[i] = acc;
25
+ }
26
+ else {
27
+ out[i] = r;
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+ export function realizedVolatility(values, length = 30, periodsPerYear = 365) {
33
+ const rets = logReturn(values).map(v => (v === null ? 0 : v));
34
+ const out = makeSeries(values.length);
35
+ for (let i = length; i < values.length; i++) {
36
+ const s = stdev(rets, i - length + 1, i);
37
+ out[i] = s * Math.sqrt(periodsPerYear);
38
+ }
39
+ return out;
40
+ }
@@ -0,0 +1,5 @@
1
+ export declare function adx(high: number[], low: number[], close: number[], length?: number): {
2
+ adx: (number | null)[];
3
+ plusDI: (number | null)[];
4
+ minusDI: (number | null)[];
5
+ };
@@ -0,0 +1,41 @@
1
+ import { makeSeries } from "./math";
2
+ import { rma } from "./overlap";
3
+ import { trueRange } from "./volatility";
4
+ export function adx(high, low, close, length = 14) {
5
+ 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
+ const plusDM = new Array(len).fill(0);
10
+ const minusDM = new Array(len).fill(0);
11
+ for (let i = 1; i < len; i++) {
12
+ const up = high[i] - high[i - 1];
13
+ const down = low[i - 1] - low[i];
14
+ plusDM[i] = up > down && up > 0 ? up : 0;
15
+ minusDM[i] = down > up && down > 0 ? down : 0;
16
+ }
17
+ const tr = trueRange(high, low, close).map(v => (v === null ? 0 : v));
18
+ const atr = rma(tr, length);
19
+ const plus = rma(plusDM, length);
20
+ const minus = rma(minusDM, length);
21
+ const plusDI = makeSeries(len);
22
+ const minusDI = makeSeries(len);
23
+ const dx = makeSeries(len);
24
+ for (let i = 0; i < len; i++) {
25
+ if (atr[i] === null)
26
+ continue;
27
+ const atrv = atr[i];
28
+ plusDI[i] = atrv === 0 ? 0 : (plus[i] / atrv) * 100;
29
+ minusDI[i] = atrv === 0 ? 0 : (minus[i] / atrv) * 100;
30
+ const denom = plusDI[i] + minusDI[i];
31
+ dx[i] = denom === 0 ? 0 : (Math.abs(plusDI[i] - minusDI[i]) / denom) * 100;
32
+ }
33
+ const adxRaw = rma(dx.map(v => (v === null ? 0 : v)), length);
34
+ const adx = makeSeries(len);
35
+ for (let i = 0; i < len; i++) {
36
+ if (dx[i] === null)
37
+ continue;
38
+ adx[i] = adxRaw[i];
39
+ }
40
+ return { adx, plusDI, minusDI };
41
+ }
@@ -0,0 +1,3 @@
1
+ export declare function trueRange(high: number[], low: number[], close: number[]): Array<number | null>;
2
+ export declare function atr(high: number[], low: number[], close: number[], length?: number): Array<number | null>;
3
+ export declare function natr(high: number[], low: number[], close: number[], length?: number): Array<number | null>;
@@ -0,0 +1,30 @@
1
+ import { assertSameLength, makeSeries } from "./math";
2
+ import { rma } from "./overlap";
3
+ export function trueRange(high, low, close) {
4
+ assertSameLength(high, low, close);
5
+ const out = makeSeries(high.length);
6
+ for (let i = 0; i < high.length; i++) {
7
+ if (i === 0) {
8
+ out[i] = high[i] - low[i];
9
+ }
10
+ else {
11
+ const tr = Math.max(high[i] - low[i], Math.abs(high[i] - close[i - 1]), Math.abs(low[i] - close[i - 1]));
12
+ out[i] = tr;
13
+ }
14
+ }
15
+ return out;
16
+ }
17
+ export function atr(high, low, close, length = 14) {
18
+ const tr = trueRange(high, low, close).map(v => (v === null ? 0 : v));
19
+ return rma(tr, length);
20
+ }
21
+ export function natr(high, low, close, length = 14) {
22
+ const atrv = atr(high, low, close, length);
23
+ const out = makeSeries(close.length);
24
+ for (let i = 0; i < close.length; i++) {
25
+ if (atrv[i] === null)
26
+ continue;
27
+ out[i] = atrv[i] / close[i] * 100;
28
+ }
29
+ return out;
30
+ }
@@ -0,0 +1,2 @@
1
+ export declare function obv(close: number[], volume: number[]): Array<number | null>;
2
+ export declare function mfi(high: number[], low: number[], close: number[], volume: number[], length?: number): Array<number | null>;
@@ -0,0 +1,52 @@
1
+ import { makeSeries } from "./math";
2
+ export function obv(close, volume) {
3
+ if (close.length !== volume.length) {
4
+ throw new Error("All series must have the same length");
5
+ }
6
+ const out = makeSeries(close.length);
7
+ let acc = 0;
8
+ for (let i = 0; i < close.length; i++) {
9
+ if (i === 0) {
10
+ out[i] = acc;
11
+ continue;
12
+ }
13
+ if (close[i] > close[i - 1])
14
+ acc += volume[i];
15
+ else if (close[i] < close[i - 1])
16
+ acc -= volume[i];
17
+ out[i] = acc;
18
+ }
19
+ return out;
20
+ }
21
+ export function mfi(high, low, close, volume, length = 14) {
22
+ 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
+ const out = makeSeries(len);
27
+ if (length <= 0)
28
+ return out;
29
+ const tp = new Array(len);
30
+ const mf = new Array(len);
31
+ for (let i = 0; i < len; i++) {
32
+ tp[i] = (high[i] + low[i] + close[i]) / 3;
33
+ mf[i] = tp[i] * volume[i];
34
+ }
35
+ for (let i = length; i < len; i++) {
36
+ let pos = 0;
37
+ let neg = 0;
38
+ for (let j = i - length + 1; j <= i; j++) {
39
+ if (tp[j] > tp[j - 1])
40
+ pos += mf[j];
41
+ else if (tp[j] < tp[j - 1])
42
+ neg += mf[j];
43
+ }
44
+ if (pos + neg === 0) {
45
+ out[i] = 50;
46
+ continue;
47
+ }
48
+ const ratio = neg === 0 ? 0 : pos / neg;
49
+ out[i] = neg === 0 ? 100 : 100 - 100 / (1 + ratio);
50
+ }
51
+ return out;
52
+ }
@@ -0,0 +1,52 @@
1
+ export { sma, ema, rma, hl2, hlc3, ohlc4, vwap, bbands } from "./core/overlap";
2
+ export { rsi, macd, stoch } from "./core/momentum";
3
+ export { trueRange, atr, natr } from "./core/volatility";
4
+ export { logReturn, percentReturn, realizedVolatility } from "./core/performance";
5
+ export { obv, mfi } from "./core/volume";
6
+ export { adx } from "./core/trend";
7
+ export { vwapSession, fundingRateCumulative, fundingRateAPR, volatilityRegime, signedVolume, volumeDelta, orderflowImbalance } from "./core/crypto";
8
+ export * from "./types";
9
+ export declare const ta: {
10
+ vwapSession(high: number[], low: number[], close: number[], volume: number[], session: Array<string | number>): Array<number | null>;
11
+ fundingRateCumulative(values: number[]): Array<number | null>;
12
+ fundingRateAPR(values: number[], periodsPerYear?: number): Array<number | null>;
13
+ volatilityRegime(values: number[], length?: number, periodsPerYear?: number, lowZ?: number, highZ?: number): Array<number | null>;
14
+ signedVolume(open: number[], close: number[], volume: number[]): Array<number | null>;
15
+ volumeDelta(open: number[], close: number[], volume: number[], length?: number): Array<number | null>;
16
+ orderflowImbalance(open: number[], close: number[], volume: number[], length?: number): Array<number | null>;
17
+ adx(high: number[], low: number[], close: number[], length?: number): {
18
+ adx: (number | null)[];
19
+ plusDI: (number | null)[];
20
+ minusDI: (number | null)[];
21
+ };
22
+ obv(close: number[], volume: number[]): Array<number | null>;
23
+ mfi(high: number[], low: number[], close: number[], volume: number[], length?: number): Array<number | null>;
24
+ logReturn(values: number[], cumulative?: boolean): Array<number | null>;
25
+ percentReturn(values: number[], cumulative?: boolean): Array<number | null>;
26
+ realizedVolatility(values: number[], length?: number, periodsPerYear?: number): Array<number | null>;
27
+ trueRange(high: number[], low: number[], close: number[]): Array<number | null>;
28
+ atr(high: number[], low: number[], close: number[], length?: number): Array<number | null>;
29
+ natr(high: number[], low: number[], close: number[], length?: number): Array<number | null>;
30
+ macd(values: number[], fast?: number, slow?: number, signal?: number): {
31
+ macd: (number | null)[];
32
+ signal: (number | null)[];
33
+ histogram: (number | null)[];
34
+ };
35
+ rsi(values: number[], length?: number): Array<number | null>;
36
+ stoch(high: number[], low: number[], close: number[], kLength?: number, dLength?: number): {
37
+ k: (number | null)[];
38
+ d: (number | null)[];
39
+ };
40
+ sma(values: number[], length?: number): Array<number | null>;
41
+ ema(values: number[], length?: number): Array<number | null>;
42
+ rma(values: number[], length?: number): Array<number | null>;
43
+ hl2(high: number[], low: number[]): Array<number | null>;
44
+ hlc3(high: number[], low: number[], close: number[]): Array<number | null>;
45
+ ohlc4(open: number[], high: number[], low: number[], close: number[]): Array<number | null>;
46
+ vwap(high: number[], low: number[], close: number[], volume: number[], length?: number): Array<number | null>;
47
+ bbands(values: number[], length?: number, std?: number): {
48
+ basis: (number | null)[];
49
+ upper: (number | null)[];
50
+ lower: (number | null)[];
51
+ };
52
+ };
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ export { sma, ema, rma, hl2, hlc3, ohlc4, vwap, bbands } from "./core/overlap";
2
+ export { rsi, macd, stoch } from "./core/momentum";
3
+ export { trueRange, atr, natr } from "./core/volatility";
4
+ export { logReturn, percentReturn, realizedVolatility } from "./core/performance";
5
+ export { obv, mfi } from "./core/volume";
6
+ export { adx } from "./core/trend";
7
+ export { vwapSession, fundingRateCumulative, fundingRateAPR, volatilityRegime, signedVolume, volumeDelta, orderflowImbalance } from "./core/crypto";
8
+ export * from "./types";
9
+ import * as overlap from "./core/overlap";
10
+ import * as momentum from "./core/momentum";
11
+ import * as volatility from "./core/volatility";
12
+ import * as performance from "./core/performance";
13
+ import * as volume from "./core/volume";
14
+ import * as trend from "./core/trend";
15
+ import * as crypto from "./core/crypto";
16
+ export const ta = {
17
+ ...overlap,
18
+ ...momentum,
19
+ ...volatility,
20
+ ...performance,
21
+ ...volume,
22
+ ...trend,
23
+ ...crypto
24
+ };
@@ -0,0 +1,10 @@
1
+ export type NumericSeries = number[];
2
+ export type Series = Array<number | null>;
3
+ export type Candle = {
4
+ time?: number | string | Date;
5
+ open: number;
6
+ high: number;
7
+ low: number;
8
+ close: number;
9
+ volume?: number;
10
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "ta-crypto",
3
+ "version": "0.1.0",
4
+ "description": "Technical analysis for crypto markets in Node.js",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "types": "dist/index.d.ts",
15
+ "main": "dist/index.js",
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.json",
23
+ "clean": "rimraf dist",
24
+ "prepublishOnly": "npm run clean && npm run build",
25
+ "test": "node -e \"console.log('no tests yet')\""
26
+ },
27
+ "devDependencies": {
28
+ "rimraf": "^5.0.5",
29
+ "typescript": "^5.4.5"
30
+ },
31
+ "keywords": [
32
+ "crypto",
33
+ "bitcoin",
34
+ "technical-analysis",
35
+ "indicators",
36
+ "trading",
37
+ "ohlcv"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/tdamiao/pandas-btc.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/tdamiao/pandas-btc/issues"
45
+ },
46
+ "homepage": "https://github.com/tdamiao/pandas-btc#readme"
47
+ }