@tranding-custom-chart/financial 0.0.0-stage → 2.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/README.md +351 -2
- package/dist/adapter.d.ts +24 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +47 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/indicators/atr.d.ts +13 -0
- package/dist/indicators/atr.d.ts.map +1 -0
- package/dist/indicators/atr.js +40 -0
- package/dist/indicators/bollinger.d.ts +12 -0
- package/dist/indicators/bollinger.d.ts.map +1 -0
- package/dist/indicators/bollinger.js +34 -0
- package/dist/indicators/ema.d.ts +7 -0
- package/dist/indicators/ema.d.ts.map +1 -0
- package/dist/indicators/ema.js +26 -0
- package/dist/indicators/heikin-ashi.d.ts +14 -0
- package/dist/indicators/heikin-ashi.d.ts.map +1 -0
- package/dist/indicators/heikin-ashi.js +36 -0
- package/dist/indicators/kagi.d.ts +20 -0
- package/dist/indicators/kagi.d.ts.map +1 -0
- package/dist/indicators/kagi.js +122 -0
- package/dist/indicators/line-break.d.ts +18 -0
- package/dist/indicators/line-break.d.ts.map +1 -0
- package/dist/indicators/line-break.js +73 -0
- package/dist/indicators/macd.d.ts +13 -0
- package/dist/indicators/macd.d.ts.map +1 -0
- package/dist/indicators/macd.js +60 -0
- package/dist/indicators/renko.d.ts +19 -0
- package/dist/indicators/renko.d.ts.map +1 -0
- package/dist/indicators/renko.js +84 -0
- package/dist/indicators/rsi.d.ts +7 -0
- package/dist/indicators/rsi.d.ts.map +1 -0
- package/dist/indicators/rsi.js +47 -0
- package/dist/indicators/sma.d.ts +15 -0
- package/dist/indicators/sma.d.ts.map +1 -0
- package/dist/indicators/sma.js +37 -0
- package/dist/indicators/supertrend.d.ts +8 -0
- package/dist/indicators/supertrend.d.ts.map +1 -0
- package/dist/indicators/supertrend.js +88 -0
- package/dist/indicators/volume-profile.d.ts +10 -0
- package/dist/indicators/volume-profile.d.ts.map +1 -0
- package/dist/indicators/volume-profile.js +134 -0
- package/dist/indicators/vwap.d.ts +7 -0
- package/dist/indicators/vwap.d.ts.map +1 -0
- package/dist/indicators/vwap.js +33 -0
- package/dist/indicators/wma.d.ts +7 -0
- package/dist/indicators/wma.d.ts.map +1 -0
- package/dist/indicators/wma.js +21 -0
- package/dist/layout/pane-layout.d.ts +19 -0
- package/dist/layout/pane-layout.d.ts.map +1 -0
- package/dist/layout/pane-layout.js +39 -0
- package/package.json +74 -4
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/heikin-ashi.ts
|
|
3
|
+
* Transforms standard OHLCV candlesticks into smoothed Heikin-Ashi candles.
|
|
4
|
+
*
|
|
5
|
+
* Formulas:
|
|
6
|
+
* - Close_HA = (Open + High + Low + Close) / 4
|
|
7
|
+
* - Open_HA[0] = (Open[0] + Close[0]) / 2
|
|
8
|
+
* - Open_HA[i] = (Open_HA[i-1] + Close_HA[i-1]) / 2
|
|
9
|
+
* - High_HA = max(High, Open_HA, Close_HA)
|
|
10
|
+
* - Low_HA = min(Low, Open_HA, Close_HA)
|
|
11
|
+
*/
|
|
12
|
+
export function computeHeikinAshi(bars) {
|
|
13
|
+
if (bars.length === 0)
|
|
14
|
+
return [];
|
|
15
|
+
const result = [];
|
|
16
|
+
for (let i = 0; i < bars.length; i++) {
|
|
17
|
+
const bar = bars[i];
|
|
18
|
+
const haClose = (bar.open + bar.high + bar.low + bar.close) / 4;
|
|
19
|
+
const haOpen = i === 0
|
|
20
|
+
? (bar.open + bar.close) / 2
|
|
21
|
+
: (result[i - 1].open + result[i - 1].close) / 2;
|
|
22
|
+
const haHigh = Math.max(bar.high, haOpen, haClose);
|
|
23
|
+
const haLow = Math.min(bar.low, haOpen, haClose);
|
|
24
|
+
result.push({
|
|
25
|
+
time: bar.time,
|
|
26
|
+
open: haOpen,
|
|
27
|
+
high: haHigh,
|
|
28
|
+
low: haLow,
|
|
29
|
+
close: haClose,
|
|
30
|
+
volume: bar.volume,
|
|
31
|
+
dateStr: bar.dateStr,
|
|
32
|
+
qualityFlags: bar.qualityFlags,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/kagi.ts
|
|
3
|
+
* Algorithmic Kagi line generation from sequential OHLCV bars.
|
|
4
|
+
* Kagi charts filter time and noise, indicating trend reversals and Yang (bullish) / Yin (bearish) inflections.
|
|
5
|
+
*/
|
|
6
|
+
import type { OHLCVBar } from '@tranding-custom-chart/core';
|
|
7
|
+
export interface KagiSegment extends OHLCVBar {
|
|
8
|
+
readonly isYang: boolean;
|
|
9
|
+
readonly isUp: boolean;
|
|
10
|
+
readonly originalIndex: number;
|
|
11
|
+
}
|
|
12
|
+
export interface KagiOptions {
|
|
13
|
+
readonly reversalAmount?: number | undefined;
|
|
14
|
+
readonly reversalPercent?: number | undefined;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Computes Kagi trend segments from standard OHLCV bars.
|
|
18
|
+
*/
|
|
19
|
+
export declare function computeKagi(bars: readonly OHLCVBar[], options?: KagiOptions): readonly KagiSegment[];
|
|
20
|
+
//# sourceMappingURL=kagi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kagi.d.ts","sourceRoot":"","sources":["../../src/indicators/kagi.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,WAAY,SAAQ,QAAQ;IAC3C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,OAAO,GAAE,WAAgB,GACxB,SAAS,WAAW,EAAE,CAsHxB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/kagi.ts
|
|
3
|
+
* Algorithmic Kagi line generation from sequential OHLCV bars.
|
|
4
|
+
* Kagi charts filter time and noise, indicating trend reversals and Yang (bullish) / Yin (bearish) inflections.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Computes Kagi trend segments from standard OHLCV bars.
|
|
8
|
+
*/
|
|
9
|
+
export function computeKagi(bars, options = {}) {
|
|
10
|
+
if (bars.length === 0)
|
|
11
|
+
return [];
|
|
12
|
+
const firstClose = bars[0].close;
|
|
13
|
+
const reversalPct = options.reversalPercent ?? 0.01;
|
|
14
|
+
const reversalThreshold = options.reversalAmount && options.reversalAmount > 0
|
|
15
|
+
? options.reversalAmount
|
|
16
|
+
: Math.max(0.1, Number((firstClose * reversalPct).toFixed(2)));
|
|
17
|
+
const segments = [];
|
|
18
|
+
let currentStartPrice = firstClose;
|
|
19
|
+
let currentEndPrice = firstClose;
|
|
20
|
+
let currentDirection = null;
|
|
21
|
+
let isYang = true;
|
|
22
|
+
let lastShoulder = firstClose; // Previous peak
|
|
23
|
+
let lastWaist = firstClose; // Previous trough
|
|
24
|
+
for (let i = 1; i < bars.length; i++) {
|
|
25
|
+
const bar = bars[i];
|
|
26
|
+
const price = bar.close;
|
|
27
|
+
if (currentDirection === null) {
|
|
28
|
+
if (price >= currentStartPrice + reversalThreshold) {
|
|
29
|
+
currentDirection = 'up';
|
|
30
|
+
currentEndPrice = price;
|
|
31
|
+
lastShoulder = price;
|
|
32
|
+
}
|
|
33
|
+
else if (price <= currentStartPrice - reversalThreshold) {
|
|
34
|
+
currentDirection = 'down';
|
|
35
|
+
currentEndPrice = price;
|
|
36
|
+
lastWaist = price;
|
|
37
|
+
isYang = false;
|
|
38
|
+
}
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (currentDirection === 'up') {
|
|
42
|
+
if (price > currentEndPrice) {
|
|
43
|
+
// Continue upward trend
|
|
44
|
+
currentEndPrice = price;
|
|
45
|
+
if (!isYang && price > lastShoulder) {
|
|
46
|
+
isYang = true; // Breakout above shoulder turns Yang
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (price <= currentEndPrice - reversalThreshold) {
|
|
50
|
+
// Reversal downwards
|
|
51
|
+
segments.push({
|
|
52
|
+
time: bar.time,
|
|
53
|
+
open: currentStartPrice,
|
|
54
|
+
high: currentEndPrice,
|
|
55
|
+
low: currentStartPrice,
|
|
56
|
+
close: currentEndPrice,
|
|
57
|
+
volume: bar.volume ?? 0,
|
|
58
|
+
isYang,
|
|
59
|
+
isUp: true,
|
|
60
|
+
originalIndex: i - 1,
|
|
61
|
+
dateStr: bar.dateStr,
|
|
62
|
+
});
|
|
63
|
+
lastShoulder = currentEndPrice;
|
|
64
|
+
currentStartPrice = currentEndPrice;
|
|
65
|
+
currentEndPrice = price;
|
|
66
|
+
currentDirection = 'down';
|
|
67
|
+
if (isYang && price < lastWaist) {
|
|
68
|
+
isYang = false; // Breakdown below waist turns Yin
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// currentDirection === 'down'
|
|
74
|
+
if (price < currentEndPrice) {
|
|
75
|
+
// Continue downward trend
|
|
76
|
+
currentEndPrice = price;
|
|
77
|
+
if (isYang && price < lastWaist) {
|
|
78
|
+
isYang = false; // Breakdown below waist turns Yin
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (price >= currentEndPrice + reversalThreshold) {
|
|
82
|
+
// Reversal upwards
|
|
83
|
+
segments.push({
|
|
84
|
+
time: bar.time,
|
|
85
|
+
open: currentStartPrice,
|
|
86
|
+
high: currentStartPrice,
|
|
87
|
+
low: currentEndPrice,
|
|
88
|
+
close: currentEndPrice,
|
|
89
|
+
volume: bar.volume ?? 0,
|
|
90
|
+
isYang,
|
|
91
|
+
isUp: false,
|
|
92
|
+
originalIndex: i - 1,
|
|
93
|
+
dateStr: bar.dateStr,
|
|
94
|
+
});
|
|
95
|
+
lastWaist = currentEndPrice;
|
|
96
|
+
currentStartPrice = currentEndPrice;
|
|
97
|
+
currentEndPrice = price;
|
|
98
|
+
currentDirection = 'up';
|
|
99
|
+
if (!isYang && price > lastShoulder) {
|
|
100
|
+
isYang = true; // Breakout above shoulder turns Yang
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Push terminal active segment
|
|
106
|
+
if (currentDirection !== null) {
|
|
107
|
+
const lastBar = bars[bars.length - 1];
|
|
108
|
+
segments.push({
|
|
109
|
+
time: lastBar.time,
|
|
110
|
+
open: currentStartPrice,
|
|
111
|
+
high: Math.max(currentStartPrice, currentEndPrice),
|
|
112
|
+
low: Math.min(currentStartPrice, currentEndPrice),
|
|
113
|
+
close: currentEndPrice,
|
|
114
|
+
volume: lastBar.volume ?? 0,
|
|
115
|
+
isYang,
|
|
116
|
+
isUp: currentEndPrice >= currentStartPrice,
|
|
117
|
+
originalIndex: bars.length - 1,
|
|
118
|
+
dateStr: lastBar.dateStr,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return segments;
|
|
122
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/line-break.ts
|
|
3
|
+
* Algorithmic Three-Line Break (TLB) chart generation.
|
|
4
|
+
* Filters minor oscillations, adding new blocks only when price exceeds the extreme of the prior N lines.
|
|
5
|
+
*/
|
|
6
|
+
import type { OHLCVBar } from '@tranding-custom-chart/core';
|
|
7
|
+
export interface LineBreakBar extends OHLCVBar {
|
|
8
|
+
readonly isUp: boolean;
|
|
9
|
+
readonly originalIndex: number;
|
|
10
|
+
}
|
|
11
|
+
export interface LineBreakOptions {
|
|
12
|
+
readonly lineCount?: number | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Computes Three-Line Break (TLB) bars from sequential OHLCV bars.
|
|
16
|
+
*/
|
|
17
|
+
export declare function computeLineBreak(bars: readonly OHLCVBar[], options?: LineBreakOptions): readonly LineBreakBar[];
|
|
18
|
+
//# sourceMappingURL=line-break.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-break.d.ts","sourceRoot":"","sources":["../../src/indicators/line-break.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,OAAO,GAAE,gBAAqB,GAC7B,SAAS,YAAY,EAAE,CAoEzB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/line-break.ts
|
|
3
|
+
* Algorithmic Three-Line Break (TLB) chart generation.
|
|
4
|
+
* Filters minor oscillations, adding new blocks only when price exceeds the extreme of the prior N lines.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Computes Three-Line Break (TLB) bars from sequential OHLCV bars.
|
|
8
|
+
*/
|
|
9
|
+
export function computeLineBreak(bars, options = {}) {
|
|
10
|
+
if (bars.length === 0)
|
|
11
|
+
return [];
|
|
12
|
+
const lineCount = Math.max(1, options.lineCount ?? 3);
|
|
13
|
+
const result = [];
|
|
14
|
+
// Seed with initial bar
|
|
15
|
+
const first = bars[0];
|
|
16
|
+
result.push({
|
|
17
|
+
time: first.time,
|
|
18
|
+
open: first.open,
|
|
19
|
+
high: Math.max(first.open, first.close),
|
|
20
|
+
low: Math.min(first.open, first.close),
|
|
21
|
+
close: first.close,
|
|
22
|
+
volume: first.volume ?? 0,
|
|
23
|
+
isUp: first.close >= first.open,
|
|
24
|
+
originalIndex: 0,
|
|
25
|
+
dateStr: first.dateStr,
|
|
26
|
+
});
|
|
27
|
+
for (let i = 1; i < bars.length; i++) {
|
|
28
|
+
const bar = bars[i];
|
|
29
|
+
const price = bar.close;
|
|
30
|
+
// Check extremes of recent N lines
|
|
31
|
+
const recent = result.slice(-lineCount);
|
|
32
|
+
let highestHigh = -Infinity;
|
|
33
|
+
let lowestLow = Infinity;
|
|
34
|
+
for (const line of recent) {
|
|
35
|
+
const top = Math.max(line.open, line.close);
|
|
36
|
+
const bottom = Math.min(line.open, line.close);
|
|
37
|
+
if (top > highestHigh)
|
|
38
|
+
highestHigh = top;
|
|
39
|
+
if (bottom < lowestLow)
|
|
40
|
+
lowestLow = bottom;
|
|
41
|
+
}
|
|
42
|
+
const lastLine = result[result.length - 1];
|
|
43
|
+
if (price > highestHigh) {
|
|
44
|
+
// New up line
|
|
45
|
+
result.push({
|
|
46
|
+
time: bar.time,
|
|
47
|
+
open: lastLine.isUp ? lastLine.close : lastLine.open,
|
|
48
|
+
high: price,
|
|
49
|
+
low: lastLine.isUp ? lastLine.close : lastLine.open,
|
|
50
|
+
close: price,
|
|
51
|
+
volume: bar.volume ?? 0,
|
|
52
|
+
isUp: true,
|
|
53
|
+
originalIndex: i,
|
|
54
|
+
dateStr: bar.dateStr,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (price < lowestLow) {
|
|
58
|
+
// New down line
|
|
59
|
+
result.push({
|
|
60
|
+
time: bar.time,
|
|
61
|
+
open: !lastLine.isUp ? lastLine.close : lastLine.open,
|
|
62
|
+
high: !lastLine.isUp ? lastLine.close : lastLine.open,
|
|
63
|
+
low: price,
|
|
64
|
+
close: price,
|
|
65
|
+
volume: bar.volume ?? 0,
|
|
66
|
+
isUp: false,
|
|
67
|
+
originalIndex: i,
|
|
68
|
+
dateStr: bar.dateStr,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/macd.ts
|
|
3
|
+
* Moving Average Convergence Divergence (MACD) calculation.
|
|
4
|
+
* Outputs MACD Line, Signal Line, and Histogram oscillator.
|
|
5
|
+
*/
|
|
6
|
+
import type { OHLCVBar } from '@tranding-custom-chart/core';
|
|
7
|
+
export interface MACDResult {
|
|
8
|
+
readonly macd: readonly (number | null)[];
|
|
9
|
+
readonly signal: readonly (number | null)[];
|
|
10
|
+
readonly histogram: readonly (number | null)[];
|
|
11
|
+
}
|
|
12
|
+
export declare function computeMACD(bars: readonly OHLCVBar[], fastPeriod?: number, slowPeriod?: number, signalPeriod?: number): MACDResult;
|
|
13
|
+
//# sourceMappingURL=macd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macd.d.ts","sourceRoot":"","sources":["../../src/indicators/macd.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAG5D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAChD;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,UAAU,SAAK,EACf,UAAU,SAAK,EACf,YAAY,SAAI,GACf,UAAU,CA4DZ"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/macd.ts
|
|
3
|
+
* Moving Average Convergence Divergence (MACD) calculation.
|
|
4
|
+
* Outputs MACD Line, Signal Line, and Histogram oscillator.
|
|
5
|
+
*/
|
|
6
|
+
import { computeEMA } from './ema.js';
|
|
7
|
+
export function computeMACD(bars, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
|
|
8
|
+
const n = bars.length;
|
|
9
|
+
if (n === 0) {
|
|
10
|
+
return { macd: [], signal: [], histogram: [] };
|
|
11
|
+
}
|
|
12
|
+
const fastEMA = computeEMA(bars, fastPeriod);
|
|
13
|
+
const slowEMA = computeEMA(bars, slowPeriod);
|
|
14
|
+
const macdLine = new Array(n).fill(null);
|
|
15
|
+
for (let i = 0; i < n; i++) {
|
|
16
|
+
const f = fastEMA[i];
|
|
17
|
+
const s = slowEMA[i];
|
|
18
|
+
if (f !== null && f !== undefined && s !== null && s !== undefined) {
|
|
19
|
+
macdLine[i] = f - s;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Compute Signal Line as EMA over valid MACD values
|
|
23
|
+
const signalLine = new Array(n).fill(null);
|
|
24
|
+
const validMacdIndices = [];
|
|
25
|
+
for (let i = 0; i < n; i++) {
|
|
26
|
+
if (macdLine[i] !== null) {
|
|
27
|
+
validMacdIndices.push(i);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (validMacdIndices.length >= signalPeriod) {
|
|
31
|
+
// Synthetic bar series to reuse standard EMA logic
|
|
32
|
+
const dummyBars = validMacdIndices.map((idx) => ({
|
|
33
|
+
time: bars[idx].time,
|
|
34
|
+
open: macdLine[idx],
|
|
35
|
+
high: macdLine[idx],
|
|
36
|
+
low: macdLine[idx],
|
|
37
|
+
close: macdLine[idx],
|
|
38
|
+
volume: null,
|
|
39
|
+
}));
|
|
40
|
+
const signalValues = computeEMA(dummyBars, signalPeriod);
|
|
41
|
+
for (let k = 0; k < validMacdIndices.length; k++) {
|
|
42
|
+
const origIdx = validMacdIndices[k];
|
|
43
|
+
signalLine[origIdx] = signalValues[k];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Compute Histogram: MACD Line - Signal Line
|
|
47
|
+
const histogram = new Array(n).fill(null);
|
|
48
|
+
for (let i = 0; i < n; i++) {
|
|
49
|
+
const m = macdLine[i];
|
|
50
|
+
const s = signalLine[i];
|
|
51
|
+
if (m !== null && m !== undefined && s !== null && s !== undefined) {
|
|
52
|
+
histogram[i] = m - s;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
macd: macdLine,
|
|
57
|
+
signal: signalLine,
|
|
58
|
+
histogram,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/renko.ts
|
|
3
|
+
* Algorithmic Renko brick generation from OHLCV bar observations.
|
|
4
|
+
* Renko filters time and minor fluctuations, creating bricks of a fixed price size.
|
|
5
|
+
*/
|
|
6
|
+
import type { OHLCVBar } from '@tranding-custom-chart/core';
|
|
7
|
+
export interface RenkoBrick extends OHLCVBar {
|
|
8
|
+
readonly isUp: boolean;
|
|
9
|
+
readonly originalIndex: number;
|
|
10
|
+
}
|
|
11
|
+
export interface RenkoOptions {
|
|
12
|
+
readonly brickSize?: number | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Computes Renko bricks from standard OHLCV bars.
|
|
16
|
+
* Each brick represents a fixed price movement (brickSize).
|
|
17
|
+
*/
|
|
18
|
+
export declare function computeRenko(bars: readonly OHLCVBar[], options?: RenkoOptions): readonly RenkoBrick[];
|
|
19
|
+
//# sourceMappingURL=renko.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renko.d.ts","sourceRoot":"","sources":["../../src/indicators/renko.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,UAAW,SAAQ,QAAQ;IAC1C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,OAAO,GAAE,YAAiB,GACzB,SAAS,UAAU,EAAE,CA6EvB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/renko.ts
|
|
3
|
+
* Algorithmic Renko brick generation from OHLCV bar observations.
|
|
4
|
+
* Renko filters time and minor fluctuations, creating bricks of a fixed price size.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Computes Renko bricks from standard OHLCV bars.
|
|
8
|
+
* Each brick represents a fixed price movement (brickSize).
|
|
9
|
+
*/
|
|
10
|
+
export function computeRenko(bars, options = {}) {
|
|
11
|
+
if (bars.length === 0)
|
|
12
|
+
return [];
|
|
13
|
+
// Determine brick size
|
|
14
|
+
let brickSize = options.brickSize;
|
|
15
|
+
if (!brickSize || brickSize <= 0) {
|
|
16
|
+
// Dynamic default: 1% of the starting close or median range
|
|
17
|
+
const firstClose = bars[0]?.close ?? 100;
|
|
18
|
+
brickSize = Math.max(0.1, Number((firstClose * 0.005).toFixed(2)));
|
|
19
|
+
}
|
|
20
|
+
const bricks = [];
|
|
21
|
+
let lastBrickPrice = Math.floor(bars[0].close / brickSize) * brickSize;
|
|
22
|
+
let lastDirection = null;
|
|
23
|
+
for (let i = 0; i < bars.length; i++) {
|
|
24
|
+
const bar = bars[i];
|
|
25
|
+
const price = bar.close;
|
|
26
|
+
while (true) {
|
|
27
|
+
if (price >= lastBrickPrice + brickSize) {
|
|
28
|
+
// Up brick
|
|
29
|
+
const open = lastBrickPrice;
|
|
30
|
+
const close = lastBrickPrice + brickSize;
|
|
31
|
+
bricks.push({
|
|
32
|
+
time: bar.time,
|
|
33
|
+
open,
|
|
34
|
+
high: close,
|
|
35
|
+
low: open,
|
|
36
|
+
close,
|
|
37
|
+
volume: bar.volume ? Math.round(bar.volume / 2) : 0,
|
|
38
|
+
isUp: true,
|
|
39
|
+
originalIndex: i,
|
|
40
|
+
dateStr: bar.dateStr,
|
|
41
|
+
});
|
|
42
|
+
lastBrickPrice = close;
|
|
43
|
+
lastDirection = 'up';
|
|
44
|
+
}
|
|
45
|
+
else if (price <= lastBrickPrice - brickSize) {
|
|
46
|
+
// Down brick
|
|
47
|
+
const open = lastBrickPrice;
|
|
48
|
+
const close = lastBrickPrice - brickSize;
|
|
49
|
+
bricks.push({
|
|
50
|
+
time: bar.time,
|
|
51
|
+
open,
|
|
52
|
+
high: open,
|
|
53
|
+
low: close,
|
|
54
|
+
close,
|
|
55
|
+
volume: bar.volume ? Math.round(bar.volume / 2) : 0,
|
|
56
|
+
isUp: false,
|
|
57
|
+
originalIndex: i,
|
|
58
|
+
dateStr: bar.dateStr,
|
|
59
|
+
});
|
|
60
|
+
lastBrickPrice = close;
|
|
61
|
+
lastDirection = 'down';
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// If no brick triggered (e.g. range was too narrow), create at least one baseline brick
|
|
69
|
+
if (bricks.length === 0 && bars.length > 0) {
|
|
70
|
+
const b = bars[0];
|
|
71
|
+
bricks.push({
|
|
72
|
+
time: b.time,
|
|
73
|
+
open: b.open,
|
|
74
|
+
high: Math.max(b.open, b.close),
|
|
75
|
+
low: Math.min(b.open, b.close),
|
|
76
|
+
close: b.close,
|
|
77
|
+
volume: b.volume ?? 0,
|
|
78
|
+
isUp: b.close >= b.open,
|
|
79
|
+
originalIndex: 0,
|
|
80
|
+
dateStr: b.dateStr,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return bricks;
|
|
84
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/rsi.ts
|
|
3
|
+
* Relative Strength Index (RSI) using Wilder's smoothed moving average.
|
|
4
|
+
*/
|
|
5
|
+
import type { OHLCVBar } from '@tranding-custom-chart/core';
|
|
6
|
+
export declare function computeRSI(bars: readonly OHLCVBar[], period?: number): (number | null)[];
|
|
7
|
+
//# sourceMappingURL=rsi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rsi.d.ts","sourceRoot":"","sources":["../../src/indicators/rsi.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,wBAAgB,UAAU,CACxB,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,MAAM,SAAK,GACV,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAgDnB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/rsi.ts
|
|
3
|
+
* Relative Strength Index (RSI) using Wilder's smoothed moving average.
|
|
4
|
+
*/
|
|
5
|
+
export function computeRSI(bars, period = 14) {
|
|
6
|
+
if (period <= 0 || bars.length <= period) {
|
|
7
|
+
return bars.map(() => null);
|
|
8
|
+
}
|
|
9
|
+
const result = new Array(bars.length).fill(null);
|
|
10
|
+
// Compute initial average gain and loss over first 'period' price changes
|
|
11
|
+
let totalGain = 0;
|
|
12
|
+
let totalLoss = 0;
|
|
13
|
+
for (let i = 1; i <= period; i++) {
|
|
14
|
+
const change = bars[i].close - bars[i - 1].close;
|
|
15
|
+
if (change > 0) {
|
|
16
|
+
totalGain += change;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
totalLoss += Math.abs(change);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
let avgGain = totalGain / period;
|
|
23
|
+
let avgLoss = totalLoss / period;
|
|
24
|
+
if (avgLoss === 0) {
|
|
25
|
+
result[period] = 100;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const rs = avgGain / avgLoss;
|
|
29
|
+
result[period] = 100 - 100 / (1 + rs);
|
|
30
|
+
}
|
|
31
|
+
// Smooth subsequent values using Wilder's technique
|
|
32
|
+
for (let i = period + 1; i < bars.length; i++) {
|
|
33
|
+
const change = bars[i].close - bars[i - 1].close;
|
|
34
|
+
const gain = change > 0 ? change : 0;
|
|
35
|
+
const loss = change < 0 ? Math.abs(change) : 0;
|
|
36
|
+
avgGain = (avgGain * (period - 1) + gain) / period;
|
|
37
|
+
avgLoss = (avgLoss * (period - 1) + loss) / period;
|
|
38
|
+
if (avgLoss === 0) {
|
|
39
|
+
result[i] = 100;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
const rs = avgGain / avgLoss;
|
|
43
|
+
result[i] = 100 - 100 / (1 + rs);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/sma.ts
|
|
3
|
+
* Simple Moving Average (SMA) computation with transparent warm-up and incremental updates.
|
|
4
|
+
*/
|
|
5
|
+
import type { OHLCVBar } from '@tranding-custom-chart/core';
|
|
6
|
+
/**
|
|
7
|
+
* Computes SMA over close prices.
|
|
8
|
+
* Returns an array of equal length to bars, with `null` for the warm-up periods.
|
|
9
|
+
*/
|
|
10
|
+
export declare function computeSMA(bars: readonly OHLCVBar[], period: number): (number | null)[];
|
|
11
|
+
/**
|
|
12
|
+
* Updates the latest trailing SMA value when the current live candle updates.
|
|
13
|
+
*/
|
|
14
|
+
export declare function updateTrailingSMA(bars: readonly OHLCVBar[], period: number, updatedClose: number): number | null;
|
|
15
|
+
//# sourceMappingURL=sma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sma.d.ts","sourceRoot":"","sources":["../../src/indicators/sma.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,MAAM,EAAE,MAAM,GACb,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAqBnB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,MAAM,GAAG,IAAI,CAQf"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/sma.ts
|
|
3
|
+
* Simple Moving Average (SMA) computation with transparent warm-up and incremental updates.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Computes SMA over close prices.
|
|
7
|
+
* Returns an array of equal length to bars, with `null` for the warm-up periods.
|
|
8
|
+
*/
|
|
9
|
+
export function computeSMA(bars, period) {
|
|
10
|
+
if (period <= 0 || bars.length < period) {
|
|
11
|
+
return bars.map(() => null);
|
|
12
|
+
}
|
|
13
|
+
const result = new Array(bars.length).fill(null);
|
|
14
|
+
let windowSum = 0;
|
|
15
|
+
for (let i = 0; i < bars.length; i++) {
|
|
16
|
+
windowSum += bars[i].close;
|
|
17
|
+
if (i >= period) {
|
|
18
|
+
windowSum -= bars[i - period].close;
|
|
19
|
+
}
|
|
20
|
+
if (i >= period - 1) {
|
|
21
|
+
result[i] = windowSum / period;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Updates the latest trailing SMA value when the current live candle updates.
|
|
28
|
+
*/
|
|
29
|
+
export function updateTrailingSMA(bars, period, updatedClose) {
|
|
30
|
+
if (period <= 0 || bars.length < period)
|
|
31
|
+
return null;
|
|
32
|
+
let sum = updatedClose;
|
|
33
|
+
for (let i = 1; i < period; i++) {
|
|
34
|
+
sum += bars[bars.length - 1 - i].close;
|
|
35
|
+
}
|
|
36
|
+
return sum / period;
|
|
37
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/chart-financial/src/indicators/supertrend.ts
|
|
3
|
+
* Algorithmic Supertrend calculation engine.
|
|
4
|
+
* Computes ATR-based dynamic trailing stop bands and trend direction (1 = Bullish, -1 = Bearish).
|
|
5
|
+
*/
|
|
6
|
+
import type { OHLCVBar, SupertrendResult, SupertrendOptions } from '@tranding-custom-chart/core';
|
|
7
|
+
export declare function computeSupertrend(bars: readonly OHLCVBar[], options?: SupertrendOptions): SupertrendResult;
|
|
8
|
+
//# sourceMappingURL=supertrend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supertrend.d.ts","sourceRoot":"","sources":["../../src/indicators/supertrend.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,QAAQ,EAER,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAGrC,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAuFlB"}
|