@mmflow/indicators 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/dist/index.cjs +116 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +64 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/package.json +29 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
function sma(values, period) {
|
|
5
|
+
const out = new Array(values.length);
|
|
6
|
+
let sum = 0;
|
|
7
|
+
for (let i = 0; i < values.length; i++) {
|
|
8
|
+
sum += values[i];
|
|
9
|
+
if (i >= period) sum -= values[i - period];
|
|
10
|
+
out[i] = i >= period - 1 ? sum / period : NaN;
|
|
11
|
+
}
|
|
12
|
+
return out;
|
|
13
|
+
}
|
|
14
|
+
function ema(values, period) {
|
|
15
|
+
const out = new Array(values.length);
|
|
16
|
+
if (values.length < period) {
|
|
17
|
+
return out.fill(NaN);
|
|
18
|
+
}
|
|
19
|
+
const k = 2 / (period + 1);
|
|
20
|
+
let sum = 0;
|
|
21
|
+
for (let i = 0; i < period; i++) {
|
|
22
|
+
sum += values[i];
|
|
23
|
+
out[i] = NaN;
|
|
24
|
+
}
|
|
25
|
+
let prev = sum / period;
|
|
26
|
+
out[period - 1] = prev;
|
|
27
|
+
for (let i = period; i < values.length; i++) {
|
|
28
|
+
prev = values[i] * k + prev * (1 - k);
|
|
29
|
+
out[i] = prev;
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function vwap(candles) {
|
|
34
|
+
let cumPV = 0;
|
|
35
|
+
let cumV = 0;
|
|
36
|
+
return candles.map((c) => {
|
|
37
|
+
const tp = (c.h + c.l + c.c) / 3;
|
|
38
|
+
cumPV += tp * c.v;
|
|
39
|
+
cumV += c.v;
|
|
40
|
+
return cumV > 0 ? cumPV / cumV : NaN;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function rsi(values, period = 14) {
|
|
44
|
+
const out = new Array(values.length).fill(NaN);
|
|
45
|
+
if (values.length <= period) return out;
|
|
46
|
+
let gainSum = 0;
|
|
47
|
+
let lossSum = 0;
|
|
48
|
+
for (let i = 1; i <= period; i++) {
|
|
49
|
+
const d = values[i] - values[i - 1];
|
|
50
|
+
if (d > 0) gainSum += d;
|
|
51
|
+
else lossSum += -d;
|
|
52
|
+
}
|
|
53
|
+
let avgGain = gainSum / period;
|
|
54
|
+
let avgLoss = lossSum / period;
|
|
55
|
+
out[period] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
56
|
+
for (let i = period + 1; i < values.length; i++) {
|
|
57
|
+
const d = values[i] - values[i - 1];
|
|
58
|
+
const gain = d > 0 ? d : 0;
|
|
59
|
+
const loss = d < 0 ? -d : 0;
|
|
60
|
+
avgGain = (avgGain * (period - 1) + gain) / period;
|
|
61
|
+
avgLoss = (avgLoss * (period - 1) + loss) / period;
|
|
62
|
+
out[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function macd(values, fast = 12, slow = 26, signalPeriod = 9) {
|
|
67
|
+
const emaFast = ema(values, fast);
|
|
68
|
+
const emaSlow = ema(values, slow);
|
|
69
|
+
const macdLine = values.map((_, i) => {
|
|
70
|
+
const a = emaFast[i];
|
|
71
|
+
const b = emaSlow[i];
|
|
72
|
+
return isFinite(a) && isFinite(b) ? a - b : NaN;
|
|
73
|
+
});
|
|
74
|
+
const firstValid = macdLine.findIndex((v) => isFinite(v));
|
|
75
|
+
const signalLine = new Array(values.length).fill(NaN);
|
|
76
|
+
if (firstValid >= 0) {
|
|
77
|
+
const tail = macdLine.slice(firstValid).filter((v) => isFinite(v));
|
|
78
|
+
const sig = ema(tail, signalPeriod);
|
|
79
|
+
for (let i = 0; i < sig.length; i++) {
|
|
80
|
+
signalLine[firstValid + i] = sig[i];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const histogram = macdLine.map(
|
|
84
|
+
(v, i) => isFinite(v) && isFinite(signalLine[i]) ? v - signalLine[i] : NaN
|
|
85
|
+
);
|
|
86
|
+
return { macd: macdLine, signal: signalLine, histogram };
|
|
87
|
+
}
|
|
88
|
+
function tpsSmoothed(series, period = 9) {
|
|
89
|
+
if (!Array.isArray(series) || series.length === 0) return [];
|
|
90
|
+
const k = 2 / (Math.max(2, period) + 1);
|
|
91
|
+
const out = new Array(series.length).fill(null);
|
|
92
|
+
let prev = null;
|
|
93
|
+
for (let i = 0; i < series.length; i++) {
|
|
94
|
+
const v = series[i];
|
|
95
|
+
if (v == null || !Number.isFinite(v)) {
|
|
96
|
+
out[i] = null;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (prev == null) {
|
|
100
|
+
prev = v;
|
|
101
|
+
} else {
|
|
102
|
+
prev = v * k + prev * (1 - k);
|
|
103
|
+
}
|
|
104
|
+
out[i] = prev;
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
exports.ema = ema;
|
|
110
|
+
exports.macd = macd;
|
|
111
|
+
exports.rsi = rsi;
|
|
112
|
+
exports.sma = sma;
|
|
113
|
+
exports.tpsSmoothed = tpsSmoothed;
|
|
114
|
+
exports.vwap = vwap;
|
|
115
|
+
//# sourceMappingURL=index.cjs.map
|
|
116
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Lightweight technical-indicator math for the chart.\n//\n// All inputs are arrays of numbers (typically closes); outputs match length\n// with NaN for the warm-up period. Lightweight-charts series will skip NaN\n// data points so the indicator only renders once it has enough history.\n//\n// We deliberately do not import a heavy TA library — these implementations\n// are exact, tested against TradingView's published formulas, and tree-shake\n// to ~1 KB.\n\n/**\n * Simple Moving Average over the last `period` samples.\n */\nexport function sma(values: number[], period: number): number[] {\n const out = new Array<number>(values.length);\n let sum = 0;\n for (let i = 0; i < values.length; i++) {\n sum += values[i];\n if (i >= period) sum -= values[i - period];\n out[i] = i >= period - 1 ? sum / period : NaN;\n }\n return out;\n}\n\n/**\n * Exponential Moving Average. Seeds with SMA over the first `period` samples.\n * k = 2 / (period + 1)\n * ema_t = close_t * k + ema_{t-1} * (1 - k)\n */\nexport function ema(values: number[], period: number): number[] {\n const out = new Array<number>(values.length);\n if (values.length < period) {\n return out.fill(NaN);\n }\n const k = 2 / (period + 1);\n // Seed with SMA over the first window.\n let sum = 0;\n for (let i = 0; i < period; i++) {\n sum += values[i];\n out[i] = NaN;\n }\n let prev = sum / period;\n out[period - 1] = prev;\n for (let i = period; i < values.length; i++) {\n prev = values[i] * k + prev * (1 - k);\n out[i] = prev;\n }\n return out;\n}\n\n/**\n * VWAP, anchored to the start of the input. Caller can slice by session.\n * vwap_t = Σ(typicalPrice_i * volume_i) / Σ(volume_i)\n * typicalPrice_i = (high_i + low_i + close_i) / 3\n */\nexport function vwap(\n candles: Array<{ h: number; l: number; c: number; v: number }>\n): number[] {\n let cumPV = 0;\n let cumV = 0;\n return candles.map((c) => {\n const tp = (c.h + c.l + c.c) / 3;\n cumPV += tp * c.v;\n cumV += c.v;\n return cumV > 0 ? cumPV / cumV : NaN;\n });\n}\n\n/**\n * Wilder's RSI(14). Uses the smoothed-average variant TradingView uses by\n * default (not the simple-average flavor — that one diverges quickly).\n *\n * gain_t = max(close_t - close_{t-1}, 0)\n * loss_t = max(close_{t-1} - close_t, 0)\n * avgGain_t = (avgGain_{t-1} * (period - 1) + gain_t) / period\n * avgLoss_t = (avgLoss_{t-1} * (period - 1) + loss_t) / period\n * rs = avgGain / avgLoss\n * rsi = 100 - 100 / (1 + rs)\n */\nexport function rsi(values: number[], period = 14): number[] {\n const out = new Array<number>(values.length).fill(NaN);\n if (values.length <= period) return out;\n\n let gainSum = 0;\n let lossSum = 0;\n for (let i = 1; i <= period; i++) {\n const d = values[i] - values[i - 1];\n if (d > 0) gainSum += d;\n else lossSum += -d;\n }\n let avgGain = gainSum / period;\n let avgLoss = lossSum / period;\n out[period] =\n avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);\n\n for (let i = period + 1; i < values.length; i++) {\n const d = values[i] - values[i - 1];\n const gain = d > 0 ? d : 0;\n const loss = d < 0 ? -d : 0;\n avgGain = (avgGain * (period - 1) + gain) / period;\n avgLoss = (avgLoss * (period - 1) + loss) / period;\n out[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);\n }\n return out;\n}\n\n/**\n * MACD(12, 26, 9). Returns the MACD line, signal line, and histogram.\n * macd = ema(close, fast) - ema(close, slow)\n * signal = ema(macd, signalPeriod)\n * hist = macd - signal\n */\nexport function macd(\n values: number[],\n fast = 12,\n slow = 26,\n signalPeriod = 9\n): { macd: number[]; signal: number[]; histogram: number[] } {\n const emaFast = ema(values, fast);\n const emaSlow = ema(values, slow);\n const macdLine = values.map((_, i) => {\n const a = emaFast[i];\n const b = emaSlow[i];\n return isFinite(a) && isFinite(b) ? a - b : NaN;\n });\n // Signal EMA needs to skip the leading NaNs to seed correctly.\n const firstValid = macdLine.findIndex((v) => isFinite(v));\n const signalLine = new Array<number>(values.length).fill(NaN);\n if (firstValid >= 0) {\n const tail = macdLine.slice(firstValid).filter((v) => isFinite(v));\n const sig = ema(tail, signalPeriod);\n for (let i = 0; i < sig.length; i++) {\n signalLine[firstValid + i] = sig[i];\n }\n }\n const histogram = macdLine.map((v, i) =>\n isFinite(v) && isFinite(signalLine[i]) ? v - signalLine[i] : NaN\n );\n return { macd: macdLine, signal: signalLine, histogram };\n}\n\n/**\n * Phase 24.1 — EMA-smoothed Trades-Per-Second.\n *\n * The raw TPS series from a footprint aggregator is spiky (each bar's\n * value is `count / barDuration`). Callers that want a smoother\n * baseline read (e.g. for spotting unusual sustained activity instead\n * of single-bar bursts) can pass through this helper.\n *\n * `null` entries (degenerate bars — single-trade bar, < 250 ms\n * elapsed) bypass the EMA and stay null in the output. The smoother\n * resumes from the next non-null sample without leaking the gap into\n * the running average.\n *\n * @param series per-bar TPS values, in chart order. Use `null` for\n * bars where TPS is meaningless.\n * @param period EMA window (default 9 bars).\n */\nexport function tpsSmoothed(\n series: Array<number | null>,\n period = 9,\n): Array<number | null> {\n if (!Array.isArray(series) || series.length === 0) return [];\n const k = 2 / (Math.max(2, period) + 1);\n const out: Array<number | null> = new Array(series.length).fill(null);\n let prev: number | null = null;\n for (let i = 0; i < series.length; i++) {\n const v = series[i];\n if (v == null || !Number.isFinite(v)) {\n out[i] = null;\n continue;\n }\n if (prev == null) {\n // Seed the EMA on the first usable sample.\n prev = v;\n } else {\n prev = v * k + prev * (1 - k);\n }\n out[i] = prev;\n }\n return out;\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Moving Average over the last `period` samples.
|
|
3
|
+
*/
|
|
4
|
+
declare function sma(values: number[], period: number): number[];
|
|
5
|
+
/**
|
|
6
|
+
* Exponential Moving Average. Seeds with SMA over the first `period` samples.
|
|
7
|
+
* k = 2 / (period + 1)
|
|
8
|
+
* ema_t = close_t * k + ema_{t-1} * (1 - k)
|
|
9
|
+
*/
|
|
10
|
+
declare function ema(values: number[], period: number): number[];
|
|
11
|
+
/**
|
|
12
|
+
* VWAP, anchored to the start of the input. Caller can slice by session.
|
|
13
|
+
* vwap_t = Σ(typicalPrice_i * volume_i) / Σ(volume_i)
|
|
14
|
+
* typicalPrice_i = (high_i + low_i + close_i) / 3
|
|
15
|
+
*/
|
|
16
|
+
declare function vwap(candles: Array<{
|
|
17
|
+
h: number;
|
|
18
|
+
l: number;
|
|
19
|
+
c: number;
|
|
20
|
+
v: number;
|
|
21
|
+
}>): number[];
|
|
22
|
+
/**
|
|
23
|
+
* Wilder's RSI(14). Uses the smoothed-average variant TradingView uses by
|
|
24
|
+
* default (not the simple-average flavor — that one diverges quickly).
|
|
25
|
+
*
|
|
26
|
+
* gain_t = max(close_t - close_{t-1}, 0)
|
|
27
|
+
* loss_t = max(close_{t-1} - close_t, 0)
|
|
28
|
+
* avgGain_t = (avgGain_{t-1} * (period - 1) + gain_t) / period
|
|
29
|
+
* avgLoss_t = (avgLoss_{t-1} * (period - 1) + loss_t) / period
|
|
30
|
+
* rs = avgGain / avgLoss
|
|
31
|
+
* rsi = 100 - 100 / (1 + rs)
|
|
32
|
+
*/
|
|
33
|
+
declare function rsi(values: number[], period?: number): number[];
|
|
34
|
+
/**
|
|
35
|
+
* MACD(12, 26, 9). Returns the MACD line, signal line, and histogram.
|
|
36
|
+
* macd = ema(close, fast) - ema(close, slow)
|
|
37
|
+
* signal = ema(macd, signalPeriod)
|
|
38
|
+
* hist = macd - signal
|
|
39
|
+
*/
|
|
40
|
+
declare function macd(values: number[], fast?: number, slow?: number, signalPeriod?: number): {
|
|
41
|
+
macd: number[];
|
|
42
|
+
signal: number[];
|
|
43
|
+
histogram: number[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Phase 24.1 — EMA-smoothed Trades-Per-Second.
|
|
47
|
+
*
|
|
48
|
+
* The raw TPS series from a footprint aggregator is spiky (each bar's
|
|
49
|
+
* value is `count / barDuration`). Callers that want a smoother
|
|
50
|
+
* baseline read (e.g. for spotting unusual sustained activity instead
|
|
51
|
+
* of single-bar bursts) can pass through this helper.
|
|
52
|
+
*
|
|
53
|
+
* `null` entries (degenerate bars — single-trade bar, < 250 ms
|
|
54
|
+
* elapsed) bypass the EMA and stay null in the output. The smoother
|
|
55
|
+
* resumes from the next non-null sample without leaking the gap into
|
|
56
|
+
* the running average.
|
|
57
|
+
*
|
|
58
|
+
* @param series per-bar TPS values, in chart order. Use `null` for
|
|
59
|
+
* bars where TPS is meaningless.
|
|
60
|
+
* @param period EMA window (default 9 bars).
|
|
61
|
+
*/
|
|
62
|
+
declare function tpsSmoothed(series: Array<number | null>, period?: number): Array<number | null>;
|
|
63
|
+
|
|
64
|
+
export { ema, macd, rsi, sma, tpsSmoothed, vwap };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Moving Average over the last `period` samples.
|
|
3
|
+
*/
|
|
4
|
+
declare function sma(values: number[], period: number): number[];
|
|
5
|
+
/**
|
|
6
|
+
* Exponential Moving Average. Seeds with SMA over the first `period` samples.
|
|
7
|
+
* k = 2 / (period + 1)
|
|
8
|
+
* ema_t = close_t * k + ema_{t-1} * (1 - k)
|
|
9
|
+
*/
|
|
10
|
+
declare function ema(values: number[], period: number): number[];
|
|
11
|
+
/**
|
|
12
|
+
* VWAP, anchored to the start of the input. Caller can slice by session.
|
|
13
|
+
* vwap_t = Σ(typicalPrice_i * volume_i) / Σ(volume_i)
|
|
14
|
+
* typicalPrice_i = (high_i + low_i + close_i) / 3
|
|
15
|
+
*/
|
|
16
|
+
declare function vwap(candles: Array<{
|
|
17
|
+
h: number;
|
|
18
|
+
l: number;
|
|
19
|
+
c: number;
|
|
20
|
+
v: number;
|
|
21
|
+
}>): number[];
|
|
22
|
+
/**
|
|
23
|
+
* Wilder's RSI(14). Uses the smoothed-average variant TradingView uses by
|
|
24
|
+
* default (not the simple-average flavor — that one diverges quickly).
|
|
25
|
+
*
|
|
26
|
+
* gain_t = max(close_t - close_{t-1}, 0)
|
|
27
|
+
* loss_t = max(close_{t-1} - close_t, 0)
|
|
28
|
+
* avgGain_t = (avgGain_{t-1} * (period - 1) + gain_t) / period
|
|
29
|
+
* avgLoss_t = (avgLoss_{t-1} * (period - 1) + loss_t) / period
|
|
30
|
+
* rs = avgGain / avgLoss
|
|
31
|
+
* rsi = 100 - 100 / (1 + rs)
|
|
32
|
+
*/
|
|
33
|
+
declare function rsi(values: number[], period?: number): number[];
|
|
34
|
+
/**
|
|
35
|
+
* MACD(12, 26, 9). Returns the MACD line, signal line, and histogram.
|
|
36
|
+
* macd = ema(close, fast) - ema(close, slow)
|
|
37
|
+
* signal = ema(macd, signalPeriod)
|
|
38
|
+
* hist = macd - signal
|
|
39
|
+
*/
|
|
40
|
+
declare function macd(values: number[], fast?: number, slow?: number, signalPeriod?: number): {
|
|
41
|
+
macd: number[];
|
|
42
|
+
signal: number[];
|
|
43
|
+
histogram: number[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Phase 24.1 — EMA-smoothed Trades-Per-Second.
|
|
47
|
+
*
|
|
48
|
+
* The raw TPS series from a footprint aggregator is spiky (each bar's
|
|
49
|
+
* value is `count / barDuration`). Callers that want a smoother
|
|
50
|
+
* baseline read (e.g. for spotting unusual sustained activity instead
|
|
51
|
+
* of single-bar bursts) can pass through this helper.
|
|
52
|
+
*
|
|
53
|
+
* `null` entries (degenerate bars — single-trade bar, < 250 ms
|
|
54
|
+
* elapsed) bypass the EMA and stay null in the output. The smoother
|
|
55
|
+
* resumes from the next non-null sample without leaking the gap into
|
|
56
|
+
* the running average.
|
|
57
|
+
*
|
|
58
|
+
* @param series per-bar TPS values, in chart order. Use `null` for
|
|
59
|
+
* bars where TPS is meaningless.
|
|
60
|
+
* @param period EMA window (default 9 bars).
|
|
61
|
+
*/
|
|
62
|
+
declare function tpsSmoothed(series: Array<number | null>, period?: number): Array<number | null>;
|
|
63
|
+
|
|
64
|
+
export { ema, macd, rsi, sma, tpsSmoothed, vwap };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function sma(values, period) {
|
|
3
|
+
const out = new Array(values.length);
|
|
4
|
+
let sum = 0;
|
|
5
|
+
for (let i = 0; i < values.length; i++) {
|
|
6
|
+
sum += values[i];
|
|
7
|
+
if (i >= period) sum -= values[i - period];
|
|
8
|
+
out[i] = i >= period - 1 ? sum / period : NaN;
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
12
|
+
function ema(values, period) {
|
|
13
|
+
const out = new Array(values.length);
|
|
14
|
+
if (values.length < period) {
|
|
15
|
+
return out.fill(NaN);
|
|
16
|
+
}
|
|
17
|
+
const k = 2 / (period + 1);
|
|
18
|
+
let sum = 0;
|
|
19
|
+
for (let i = 0; i < period; i++) {
|
|
20
|
+
sum += values[i];
|
|
21
|
+
out[i] = NaN;
|
|
22
|
+
}
|
|
23
|
+
let prev = sum / period;
|
|
24
|
+
out[period - 1] = prev;
|
|
25
|
+
for (let i = period; i < values.length; i++) {
|
|
26
|
+
prev = values[i] * k + prev * (1 - k);
|
|
27
|
+
out[i] = prev;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
function vwap(candles) {
|
|
32
|
+
let cumPV = 0;
|
|
33
|
+
let cumV = 0;
|
|
34
|
+
return candles.map((c) => {
|
|
35
|
+
const tp = (c.h + c.l + c.c) / 3;
|
|
36
|
+
cumPV += tp * c.v;
|
|
37
|
+
cumV += c.v;
|
|
38
|
+
return cumV > 0 ? cumPV / cumV : NaN;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function rsi(values, period = 14) {
|
|
42
|
+
const out = new Array(values.length).fill(NaN);
|
|
43
|
+
if (values.length <= period) return out;
|
|
44
|
+
let gainSum = 0;
|
|
45
|
+
let lossSum = 0;
|
|
46
|
+
for (let i = 1; i <= period; i++) {
|
|
47
|
+
const d = values[i] - values[i - 1];
|
|
48
|
+
if (d > 0) gainSum += d;
|
|
49
|
+
else lossSum += -d;
|
|
50
|
+
}
|
|
51
|
+
let avgGain = gainSum / period;
|
|
52
|
+
let avgLoss = lossSum / period;
|
|
53
|
+
out[period] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
54
|
+
for (let i = period + 1; i < values.length; i++) {
|
|
55
|
+
const d = values[i] - values[i - 1];
|
|
56
|
+
const gain = d > 0 ? d : 0;
|
|
57
|
+
const loss = d < 0 ? -d : 0;
|
|
58
|
+
avgGain = (avgGain * (period - 1) + gain) / period;
|
|
59
|
+
avgLoss = (avgLoss * (period - 1) + loss) / period;
|
|
60
|
+
out[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function macd(values, fast = 12, slow = 26, signalPeriod = 9) {
|
|
65
|
+
const emaFast = ema(values, fast);
|
|
66
|
+
const emaSlow = ema(values, slow);
|
|
67
|
+
const macdLine = values.map((_, i) => {
|
|
68
|
+
const a = emaFast[i];
|
|
69
|
+
const b = emaSlow[i];
|
|
70
|
+
return isFinite(a) && isFinite(b) ? a - b : NaN;
|
|
71
|
+
});
|
|
72
|
+
const firstValid = macdLine.findIndex((v) => isFinite(v));
|
|
73
|
+
const signalLine = new Array(values.length).fill(NaN);
|
|
74
|
+
if (firstValid >= 0) {
|
|
75
|
+
const tail = macdLine.slice(firstValid).filter((v) => isFinite(v));
|
|
76
|
+
const sig = ema(tail, signalPeriod);
|
|
77
|
+
for (let i = 0; i < sig.length; i++) {
|
|
78
|
+
signalLine[firstValid + i] = sig[i];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const histogram = macdLine.map(
|
|
82
|
+
(v, i) => isFinite(v) && isFinite(signalLine[i]) ? v - signalLine[i] : NaN
|
|
83
|
+
);
|
|
84
|
+
return { macd: macdLine, signal: signalLine, histogram };
|
|
85
|
+
}
|
|
86
|
+
function tpsSmoothed(series, period = 9) {
|
|
87
|
+
if (!Array.isArray(series) || series.length === 0) return [];
|
|
88
|
+
const k = 2 / (Math.max(2, period) + 1);
|
|
89
|
+
const out = new Array(series.length).fill(null);
|
|
90
|
+
let prev = null;
|
|
91
|
+
for (let i = 0; i < series.length; i++) {
|
|
92
|
+
const v = series[i];
|
|
93
|
+
if (v == null || !Number.isFinite(v)) {
|
|
94
|
+
out[i] = null;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (prev == null) {
|
|
98
|
+
prev = v;
|
|
99
|
+
} else {
|
|
100
|
+
prev = v * k + prev * (1 - k);
|
|
101
|
+
}
|
|
102
|
+
out[i] = prev;
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { ema, macd, rsi, sma, tpsSmoothed, vwap };
|
|
108
|
+
//# sourceMappingURL=index.js.map
|
|
109
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Lightweight technical-indicator math for the chart.\n//\n// All inputs are arrays of numbers (typically closes); outputs match length\n// with NaN for the warm-up period. Lightweight-charts series will skip NaN\n// data points so the indicator only renders once it has enough history.\n//\n// We deliberately do not import a heavy TA library — these implementations\n// are exact, tested against TradingView's published formulas, and tree-shake\n// to ~1 KB.\n\n/**\n * Simple Moving Average over the last `period` samples.\n */\nexport function sma(values: number[], period: number): number[] {\n const out = new Array<number>(values.length);\n let sum = 0;\n for (let i = 0; i < values.length; i++) {\n sum += values[i];\n if (i >= period) sum -= values[i - period];\n out[i] = i >= period - 1 ? sum / period : NaN;\n }\n return out;\n}\n\n/**\n * Exponential Moving Average. Seeds with SMA over the first `period` samples.\n * k = 2 / (period + 1)\n * ema_t = close_t * k + ema_{t-1} * (1 - k)\n */\nexport function ema(values: number[], period: number): number[] {\n const out = new Array<number>(values.length);\n if (values.length < period) {\n return out.fill(NaN);\n }\n const k = 2 / (period + 1);\n // Seed with SMA over the first window.\n let sum = 0;\n for (let i = 0; i < period; i++) {\n sum += values[i];\n out[i] = NaN;\n }\n let prev = sum / period;\n out[period - 1] = prev;\n for (let i = period; i < values.length; i++) {\n prev = values[i] * k + prev * (1 - k);\n out[i] = prev;\n }\n return out;\n}\n\n/**\n * VWAP, anchored to the start of the input. Caller can slice by session.\n * vwap_t = Σ(typicalPrice_i * volume_i) / Σ(volume_i)\n * typicalPrice_i = (high_i + low_i + close_i) / 3\n */\nexport function vwap(\n candles: Array<{ h: number; l: number; c: number; v: number }>\n): number[] {\n let cumPV = 0;\n let cumV = 0;\n return candles.map((c) => {\n const tp = (c.h + c.l + c.c) / 3;\n cumPV += tp * c.v;\n cumV += c.v;\n return cumV > 0 ? cumPV / cumV : NaN;\n });\n}\n\n/**\n * Wilder's RSI(14). Uses the smoothed-average variant TradingView uses by\n * default (not the simple-average flavor — that one diverges quickly).\n *\n * gain_t = max(close_t - close_{t-1}, 0)\n * loss_t = max(close_{t-1} - close_t, 0)\n * avgGain_t = (avgGain_{t-1} * (period - 1) + gain_t) / period\n * avgLoss_t = (avgLoss_{t-1} * (period - 1) + loss_t) / period\n * rs = avgGain / avgLoss\n * rsi = 100 - 100 / (1 + rs)\n */\nexport function rsi(values: number[], period = 14): number[] {\n const out = new Array<number>(values.length).fill(NaN);\n if (values.length <= period) return out;\n\n let gainSum = 0;\n let lossSum = 0;\n for (let i = 1; i <= period; i++) {\n const d = values[i] - values[i - 1];\n if (d > 0) gainSum += d;\n else lossSum += -d;\n }\n let avgGain = gainSum / period;\n let avgLoss = lossSum / period;\n out[period] =\n avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);\n\n for (let i = period + 1; i < values.length; i++) {\n const d = values[i] - values[i - 1];\n const gain = d > 0 ? d : 0;\n const loss = d < 0 ? -d : 0;\n avgGain = (avgGain * (period - 1) + gain) / period;\n avgLoss = (avgLoss * (period - 1) + loss) / period;\n out[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);\n }\n return out;\n}\n\n/**\n * MACD(12, 26, 9). Returns the MACD line, signal line, and histogram.\n * macd = ema(close, fast) - ema(close, slow)\n * signal = ema(macd, signalPeriod)\n * hist = macd - signal\n */\nexport function macd(\n values: number[],\n fast = 12,\n slow = 26,\n signalPeriod = 9\n): { macd: number[]; signal: number[]; histogram: number[] } {\n const emaFast = ema(values, fast);\n const emaSlow = ema(values, slow);\n const macdLine = values.map((_, i) => {\n const a = emaFast[i];\n const b = emaSlow[i];\n return isFinite(a) && isFinite(b) ? a - b : NaN;\n });\n // Signal EMA needs to skip the leading NaNs to seed correctly.\n const firstValid = macdLine.findIndex((v) => isFinite(v));\n const signalLine = new Array<number>(values.length).fill(NaN);\n if (firstValid >= 0) {\n const tail = macdLine.slice(firstValid).filter((v) => isFinite(v));\n const sig = ema(tail, signalPeriod);\n for (let i = 0; i < sig.length; i++) {\n signalLine[firstValid + i] = sig[i];\n }\n }\n const histogram = macdLine.map((v, i) =>\n isFinite(v) && isFinite(signalLine[i]) ? v - signalLine[i] : NaN\n );\n return { macd: macdLine, signal: signalLine, histogram };\n}\n\n/**\n * Phase 24.1 — EMA-smoothed Trades-Per-Second.\n *\n * The raw TPS series from a footprint aggregator is spiky (each bar's\n * value is `count / barDuration`). Callers that want a smoother\n * baseline read (e.g. for spotting unusual sustained activity instead\n * of single-bar bursts) can pass through this helper.\n *\n * `null` entries (degenerate bars — single-trade bar, < 250 ms\n * elapsed) bypass the EMA and stay null in the output. The smoother\n * resumes from the next non-null sample without leaking the gap into\n * the running average.\n *\n * @param series per-bar TPS values, in chart order. Use `null` for\n * bars where TPS is meaningless.\n * @param period EMA window (default 9 bars).\n */\nexport function tpsSmoothed(\n series: Array<number | null>,\n period = 9,\n): Array<number | null> {\n if (!Array.isArray(series) || series.length === 0) return [];\n const k = 2 / (Math.max(2, period) + 1);\n const out: Array<number | null> = new Array(series.length).fill(null);\n let prev: number | null = null;\n for (let i = 0; i < series.length; i++) {\n const v = series[i];\n if (v == null || !Number.isFinite(v)) {\n out[i] = null;\n continue;\n }\n if (prev == null) {\n // Seed the EMA on the first usable sample.\n prev = v;\n } else {\n prev = v * k + prev * (1 - k);\n }\n out[i] = prev;\n }\n return out;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mmflow/indicators",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --treeshake",
|
|
27
|
+
"typecheck": "tsc --noEmit"
|
|
28
|
+
}
|
|
29
|
+
}
|