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 +21 -0
- package/README.md +114 -0
- package/dist/core/crypto.d.ts +7 -0
- package/dist/core/crypto.js +98 -0
- package/dist/core/math.d.ts +7 -0
- package/dist/core/math.js +37 -0
- package/dist/core/momentum.d.ts +10 -0
- package/dist/core/momentum.js +81 -0
- package/dist/core/overlap.d.ts +12 -0
- package/dist/core/overlap.js +102 -0
- package/dist/core/performance.d.ts +3 -0
- package/dist/core/performance.js +40 -0
- package/dist/core/trend.d.ts +5 -0
- package/dist/core/trend.js +41 -0
- package/dist/core/volatility.d.ts +3 -0
- package/dist/core/volatility.js +30 -0
- package/dist/core/volume.d.ts +2 -0
- package/dist/core/volume.js +52 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +24 -0
- package/dist/types.d.ts +10 -0
- package/dist/types.js +1 -0
- package/package.json +47 -0
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
|
+
[](https://www.npmjs.com/package/ta-crypto)
|
|
4
|
+
[](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,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,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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
};
|
package/dist/types.d.ts
ADDED
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
|
+
}
|