@ch20026103/anysis 0.0.19-alpha1 → 0.0.19-beta
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/cjs/stockSkills/dmi.d.ts +28 -0
- package/dist/cjs/stockSkills/dmi.js +206 -0
- package/dist/cjs/stockSkills/dmi.test.d.ts +1 -0
- package/dist/cjs/stockSkills/dmi.test.js +71 -0
- package/dist/cjs/stockSkills/ichimoku.d.ts +1 -1
- package/dist/cjs/stockSkills/ichimoku.js +8 -8
- package/dist/cjs/stockSkills/ichimoku.test.js +1 -1
- package/dist/esm/stockSkills/dmi.d.ts +28 -0
- package/dist/esm/stockSkills/dmi.js +204 -0
- package/dist/esm/stockSkills/dmi.test.d.ts +1 -0
- package/dist/esm/stockSkills/dmi.test.js +69 -0
- package/dist/esm/stockSkills/ichimoku.d.ts +1 -1
- package/dist/esm/stockSkills/ichimoku.js +8 -8
- package/dist/esm/stockSkills/ichimoku.test.js +4 -4
- package/dist/umd/index.js +8 -8
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StockListType, StockType } from "./types";
|
|
2
|
+
export type DmiResType = {
|
|
3
|
+
dataset: StockListType;
|
|
4
|
+
pDi: number;
|
|
5
|
+
mDi: number;
|
|
6
|
+
adx: number;
|
|
7
|
+
type: number;
|
|
8
|
+
smoothTr: number;
|
|
9
|
+
smoothPdm: number;
|
|
10
|
+
smoothMdm: number;
|
|
11
|
+
dxBuffer: number[];
|
|
12
|
+
smoothAdx: number;
|
|
13
|
+
};
|
|
14
|
+
export default class Dmi {
|
|
15
|
+
init(data: StockType, type: number): DmiResType;
|
|
16
|
+
next(data: StockType, preList: DmiResType, type: number): DmiResType;
|
|
17
|
+
calculateDmiValues(list: StockListType, period?: number): {
|
|
18
|
+
pDi: number;
|
|
19
|
+
mDi: number;
|
|
20
|
+
adx: number;
|
|
21
|
+
c: number;
|
|
22
|
+
v: number;
|
|
23
|
+
l: number;
|
|
24
|
+
h: number;
|
|
25
|
+
o: number;
|
|
26
|
+
t: number;
|
|
27
|
+
}[];
|
|
28
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
14
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
15
|
+
if (ar || !(i in from)) {
|
|
16
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
17
|
+
ar[i] = from[i];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
var Dmi = /** @class */ (function () {
|
|
24
|
+
function Dmi() {
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(Dmi.prototype, "init", {
|
|
27
|
+
enumerable: false,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: function (data, type) {
|
|
31
|
+
return {
|
|
32
|
+
dataset: [data],
|
|
33
|
+
pDi: 0,
|
|
34
|
+
mDi: 0,
|
|
35
|
+
adx: 0,
|
|
36
|
+
type: type,
|
|
37
|
+
smoothTr: 0,
|
|
38
|
+
smoothPdm: 0,
|
|
39
|
+
smoothMdm: 0,
|
|
40
|
+
dxBuffer: [],
|
|
41
|
+
smoothAdx: 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(Dmi.prototype, "next", {
|
|
46
|
+
enumerable: false,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: function (data, preList, type) {
|
|
50
|
+
preList.dataset.push(data);
|
|
51
|
+
// Need type + 1 data points to have 'type' periods of change
|
|
52
|
+
if (preList.dataset.length < type + 1) {
|
|
53
|
+
return __assign(__assign({}, preList), { pDi: 0, mDi: 0, adx: 0 });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
var currentTr = 0;
|
|
57
|
+
var currentPdm = 0;
|
|
58
|
+
var currentMdm = 0;
|
|
59
|
+
// Logic to get TR, +DM, -DM for the NEWEST data point
|
|
60
|
+
// We need the newest point and the one before it.
|
|
61
|
+
// If we are in the initialization phase (length === type + 1), we process the whole window.
|
|
62
|
+
// If we are in the incremental phase (length > type + 1), we just process the last step.
|
|
63
|
+
var newSmoothTr = preList.smoothTr;
|
|
64
|
+
var newSmoothPdm = preList.smoothPdm;
|
|
65
|
+
var newSmoothMdm = preList.smoothMdm;
|
|
66
|
+
if (preList.dataset.length === type + 1) {
|
|
67
|
+
// Initialization: Calculate sum of first N periods (using N+1 data points)
|
|
68
|
+
var sumTr = 0;
|
|
69
|
+
var sumPdm = 0;
|
|
70
|
+
var sumMdm = 0;
|
|
71
|
+
for (var i = 1; i <= type; i++) {
|
|
72
|
+
var curr = preList.dataset[i];
|
|
73
|
+
var prev = preList.dataset[i - 1];
|
|
74
|
+
// TR
|
|
75
|
+
var hl = curr.h - curr.l;
|
|
76
|
+
var hpc = Math.abs(curr.h - prev.c);
|
|
77
|
+
var lpc = Math.abs(curr.l - prev.c);
|
|
78
|
+
var tr = Math.max(hl, hpc, lpc);
|
|
79
|
+
// DM
|
|
80
|
+
var hph = curr.h - prev.h;
|
|
81
|
+
var pll = prev.l - curr.l;
|
|
82
|
+
var pdm = 0;
|
|
83
|
+
var mdm = 0;
|
|
84
|
+
if (hph > pll && hph > 0) {
|
|
85
|
+
pdm = hph;
|
|
86
|
+
}
|
|
87
|
+
if (pll > hph && pll > 0) {
|
|
88
|
+
mdm = pll;
|
|
89
|
+
}
|
|
90
|
+
sumTr += tr;
|
|
91
|
+
sumPdm += pdm;
|
|
92
|
+
sumMdm += mdm;
|
|
93
|
+
}
|
|
94
|
+
// Wilder's first value is often just the Sum.
|
|
95
|
+
// But to make it consistent with the "Average" view for the formula:
|
|
96
|
+
// NextAvg = (PrevAvg * (N-1) + Curr) / N
|
|
97
|
+
// The first "PrevAvg" acts as the seed. The seed is usually the Simple Average.
|
|
98
|
+
// So we divide by type.
|
|
99
|
+
newSmoothTr = sumTr; // Some sources say keep Sum. But standard indicators often normalize to Average range.
|
|
100
|
+
// Let's check RSI implementation. RSI divides by type: gains / type.
|
|
101
|
+
// So we will divide by type to get the Average TR/DM.
|
|
102
|
+
// Wait! DMI standard often keeps the SUM for the first value?
|
|
103
|
+
// Wilder's book: "+DM14 is the sum of the +DM for the last 14 days".
|
|
104
|
+
// Then subsequent: "+DM14_today = +DM14_yesterday - (+DM14_yesterday/14) + +DM_today".
|
|
105
|
+
// This formula maintains the "Sum" magnitude (approx 14x the average).
|
|
106
|
+
// BUT, RSI implementation uses Average magnitude (0-100 range inputs usually lead to small AvgGain).
|
|
107
|
+
// Let's stick to the RSI pattern: Average.
|
|
108
|
+
// Formula: (Avg * (N-1) + Curr) / N. This maintains "Average" magnitude.
|
|
109
|
+
// If we used Sum logic: (Sum - Sum/N + Curr) = Sum * (1 - 1/N) + Curr.
|
|
110
|
+
// These are mathematically consistent in shape, just scaled by N.
|
|
111
|
+
// DI calculation is (Pdm / Tr) * 100. The scale cancels out!
|
|
112
|
+
// So using Average is safer for preventing overflow and easier to debug (per-day values).
|
|
113
|
+
newSmoothTr = sumTr / type;
|
|
114
|
+
newSmoothPdm = sumPdm / type;
|
|
115
|
+
newSmoothMdm = sumMdm / type;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Shift if needed to keep dataset size manageable, though strictly we only need last 2 points
|
|
119
|
+
// reusing rsi pattern:
|
|
120
|
+
if (preList.dataset.length > type + 1) {
|
|
121
|
+
preList.dataset.shift();
|
|
122
|
+
}
|
|
123
|
+
var curr = preList.dataset[preList.dataset.length - 1];
|
|
124
|
+
var prev = preList.dataset[preList.dataset.length - 2];
|
|
125
|
+
// TR
|
|
126
|
+
var hl = curr.h - curr.l;
|
|
127
|
+
var hpc = Math.abs(curr.h - prev.c);
|
|
128
|
+
var lpc = Math.abs(curr.l - prev.c);
|
|
129
|
+
var tr = Math.max(hl, hpc, lpc);
|
|
130
|
+
// DM
|
|
131
|
+
var hph = curr.h - prev.h;
|
|
132
|
+
var pll = prev.l - curr.l;
|
|
133
|
+
var pdm = 0;
|
|
134
|
+
var mdm = 0;
|
|
135
|
+
if (hph > pll && hph > 0)
|
|
136
|
+
pdm = hph;
|
|
137
|
+
if (pll > hph && pll > 0)
|
|
138
|
+
mdm = pll;
|
|
139
|
+
// Wilder's Smoothing (Average form)
|
|
140
|
+
newSmoothTr = (preList.smoothTr * (type - 1) + tr) / type;
|
|
141
|
+
newSmoothPdm = (preList.smoothPdm * (type - 1) + pdm) / type;
|
|
142
|
+
newSmoothMdm = (preList.smoothMdm * (type - 1) + mdm) / type;
|
|
143
|
+
}
|
|
144
|
+
// Calculate DI
|
|
145
|
+
// Avoid division by zero
|
|
146
|
+
var pDi = newSmoothTr === 0 ? 0 : (newSmoothPdm / newSmoothTr) * 100;
|
|
147
|
+
var mDi = newSmoothTr === 0 ? 0 : (newSmoothMdm / newSmoothTr) * 100;
|
|
148
|
+
// Calculate DX
|
|
149
|
+
var diDiff = Math.abs(pDi - mDi);
|
|
150
|
+
var diSum = pDi + mDi;
|
|
151
|
+
var dx = diSum === 0 ? 0 : (diDiff / diSum) * 100;
|
|
152
|
+
// ADX Logic
|
|
153
|
+
var dxBuffer = __spreadArray([], preList.dxBuffer, true);
|
|
154
|
+
var adx = preList.adx;
|
|
155
|
+
var newSmoothAdx = preList.smoothAdx;
|
|
156
|
+
if (dxBuffer.length < type) {
|
|
157
|
+
dxBuffer.push(dx);
|
|
158
|
+
// Special case: if we Just reached 'type' count, we can calc initial ADX
|
|
159
|
+
if (dxBuffer.length === type) {
|
|
160
|
+
// First ADX is average of the DX buffer?
|
|
161
|
+
// "ADX is the 14-day smoothed average of DX".
|
|
162
|
+
// First value is simple average of previous 14 DX values.
|
|
163
|
+
var sumDx = dxBuffer.reduce(function (a, b) { return a + b; }, 0);
|
|
164
|
+
newSmoothAdx = sumDx / type;
|
|
165
|
+
adx = newSmoothAdx;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Not enough data for ADX yet
|
|
169
|
+
adx = 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// We already have ADX initialized, so we smooth it
|
|
174
|
+
// Note: we don't need to keep growing dxBuffer indefinitely.
|
|
175
|
+
// We just needed it for startup.
|
|
176
|
+
// Update ADX using Wilder's smoothing
|
|
177
|
+
newSmoothAdx = (preList.smoothAdx * (type - 1) + dx) / type;
|
|
178
|
+
adx = newSmoothAdx;
|
|
179
|
+
}
|
|
180
|
+
return __assign(__assign({}, preList), { pDi: pDi, // +DI
|
|
181
|
+
mDi: mDi, // -DI
|
|
182
|
+
adx: adx, smoothTr: newSmoothTr, smoothPdm: newSmoothPdm, smoothMdm: newSmoothMdm, dxBuffer: dxBuffer, smoothAdx: newSmoothAdx });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Helper to get formatted results similar to ma.ts
|
|
187
|
+
Object.defineProperty(Dmi.prototype, "calculateDmiValues", {
|
|
188
|
+
enumerable: false,
|
|
189
|
+
configurable: true,
|
|
190
|
+
writable: true,
|
|
191
|
+
value: function (list, period) {
|
|
192
|
+
if (period === void 0) { period = 14; }
|
|
193
|
+
var res = [];
|
|
194
|
+
var state = this.init(list[0], period);
|
|
195
|
+
// First point (index 0) has 0 DMI
|
|
196
|
+
res.push(__assign(__assign({}, list[0]), { pDi: 0, mDi: 0, adx: 0 }));
|
|
197
|
+
for (var i = 1; i < list.length; i++) {
|
|
198
|
+
state = this.next(list[i], state, period);
|
|
199
|
+
res.push(__assign(__assign({}, list[i]), { pDi: state.pDi, mDi: state.mDi, adx: state.adx }));
|
|
200
|
+
}
|
|
201
|
+
return res;
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return Dmi;
|
|
205
|
+
}());
|
|
206
|
+
exports.default = Dmi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var dmi_1 = require("./dmi");
|
|
4
|
+
var test_data_test_1 = require("./test_data.test");
|
|
5
|
+
describe("test dmi methods", function () {
|
|
6
|
+
it("test init & next", function () {
|
|
7
|
+
var dmi = new dmi_1.default();
|
|
8
|
+
var period = 14;
|
|
9
|
+
// Calculate all values using loop
|
|
10
|
+
var results = dmi.calculateDmiValues(test_data_test_1.default, period);
|
|
11
|
+
var lastResult = results[results.length - 1];
|
|
12
|
+
// Verify consistency
|
|
13
|
+
// Re-run step-by-step
|
|
14
|
+
var state = dmi.init(test_data_test_1.default[0], period);
|
|
15
|
+
for (var i = 1; i < test_data_test_1.default.length; i++) {
|
|
16
|
+
state = dmi.next(test_data_test_1.default[i], state, period);
|
|
17
|
+
}
|
|
18
|
+
expect(state.pDi).toEqual(lastResult.pDi);
|
|
19
|
+
expect(state.mDi).toEqual(lastResult.mDi);
|
|
20
|
+
expect(state.adx).toEqual(lastResult.adx);
|
|
21
|
+
});
|
|
22
|
+
it("test values consistency (basic sanity)", function () {
|
|
23
|
+
// Generate a simple synthetic dataset where Trend is obvious
|
|
24
|
+
// Upward trend -> +DI should be high, -DI low
|
|
25
|
+
var upTrendData = [];
|
|
26
|
+
for (var i = 0; i < 50; i++) {
|
|
27
|
+
upTrendData.push({
|
|
28
|
+
c: 10 + i,
|
|
29
|
+
h: 11 + i,
|
|
30
|
+
l: 9 + i,
|
|
31
|
+
o: 10 + i,
|
|
32
|
+
v: 1000,
|
|
33
|
+
t: 20210101 + i,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
var dmi = new dmi_1.default();
|
|
37
|
+
var res = dmi.calculateDmiValues(upTrendData, 14);
|
|
38
|
+
var last = res[res.length - 1];
|
|
39
|
+
// +DI should be dominant
|
|
40
|
+
expect(last.pDi).toBeGreaterThan(last.mDi);
|
|
41
|
+
// ADX should be high (strong trend)
|
|
42
|
+
expect(last.adx).toBeGreaterThan(20);
|
|
43
|
+
});
|
|
44
|
+
it("test flat trend", function () {
|
|
45
|
+
// Flat data
|
|
46
|
+
var flatData = [];
|
|
47
|
+
for (var i = 0; i < 50; i++) {
|
|
48
|
+
flatData.push({
|
|
49
|
+
c: 10,
|
|
50
|
+
h: 11,
|
|
51
|
+
l: 9,
|
|
52
|
+
o: 10,
|
|
53
|
+
v: 1000,
|
|
54
|
+
t: 20210101 + i,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
var dmi = new dmi_1.default();
|
|
58
|
+
var res = dmi.calculateDmiValues(flatData, 14);
|
|
59
|
+
var last = res[res.length - 1];
|
|
60
|
+
// No directional movement
|
|
61
|
+
// h-ph = 0, pl-l = 0.
|
|
62
|
+
// +DM = 0, -DM = 0.
|
|
63
|
+
// +DI = 0, -DI = 0.
|
|
64
|
+
// ADX = 0?
|
|
65
|
+
// DX = 0.
|
|
66
|
+
expect(last.pDi).toEqual(0);
|
|
67
|
+
expect(last.mDi).toEqual(0);
|
|
68
|
+
// ADX might be 0 or close to 0
|
|
69
|
+
expect(last.adx).toEqual(0);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -15,7 +15,7 @@ interface IchimokuType {
|
|
|
15
15
|
init: (data: NewStockType) => IchimokuResType;
|
|
16
16
|
next: (data: NewStockType, preList: IchimokuResType) => IchimokuResType;
|
|
17
17
|
}
|
|
18
|
-
export default class
|
|
18
|
+
export default class Ichimoku implements IchimokuType {
|
|
19
19
|
init(data: NewStockType): IchimokuResType;
|
|
20
20
|
/**
|
|
21
21
|
* 優化說明:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
var
|
|
4
|
-
function
|
|
3
|
+
var Ichimoku = /** @class */ (function () {
|
|
4
|
+
function Ichimoku() {
|
|
5
5
|
}
|
|
6
|
-
Object.defineProperty(
|
|
6
|
+
Object.defineProperty(Ichimoku.prototype, "init", {
|
|
7
7
|
enumerable: false,
|
|
8
8
|
configurable: true,
|
|
9
9
|
writable: true,
|
|
@@ -21,7 +21,7 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
21
21
|
* 如果你的框架 (如 React state) 強制要求 immutable,則需要改回 [...prev, data] 的寫法。
|
|
22
22
|
* 下面的寫法假設可以 Mutation (這在 Class 內部運算或 Backend 處理很常見)。
|
|
23
23
|
*/
|
|
24
|
-
Object.defineProperty(
|
|
24
|
+
Object.defineProperty(Ichimoku.prototype, "next", {
|
|
25
25
|
enumerable: false,
|
|
26
26
|
configurable: true,
|
|
27
27
|
writable: true,
|
|
@@ -38,7 +38,7 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
// 核心計算邏輯
|
|
41
|
-
Object.defineProperty(
|
|
41
|
+
Object.defineProperty(Ichimoku.prototype, "calcIchimoku", {
|
|
42
42
|
enumerable: false,
|
|
43
43
|
configurable: true,
|
|
44
44
|
writable: true,
|
|
@@ -66,7 +66,7 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
66
66
|
* 1. 移除 .slice(),避免產生 Garbage Collection。
|
|
67
67
|
* 2. 使用反向迴圈 (i--),通常在 JS 引擎中微幅快一點,且語意上是「從現在往回看」。
|
|
68
68
|
*/
|
|
69
|
-
Object.defineProperty(
|
|
69
|
+
Object.defineProperty(Ichimoku.prototype, "getMidPrice", {
|
|
70
70
|
enumerable: false,
|
|
71
71
|
configurable: true,
|
|
72
72
|
writable: true,
|
|
@@ -91,6 +91,6 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
91
91
|
return (maxH + minL) / 2;
|
|
92
92
|
}
|
|
93
93
|
});
|
|
94
|
-
return
|
|
94
|
+
return Ichimoku;
|
|
95
95
|
}());
|
|
96
|
-
exports.default =
|
|
96
|
+
exports.default = Ichimoku;
|
|
@@ -17,7 +17,7 @@ var ichimoku_1 = require("./ichimoku");
|
|
|
17
17
|
var createStock = function (idx, override) {
|
|
18
18
|
return __assign({ t: idx, o: 100, c: 100, h: 100, l: 100, v: 1000 }, override);
|
|
19
19
|
};
|
|
20
|
-
(0, vitest_1.describe)("
|
|
20
|
+
(0, vitest_1.describe)("Ichimoku Algo", function () {
|
|
21
21
|
var ichimoku;
|
|
22
22
|
(0, vitest_1.beforeEach)(function () {
|
|
23
23
|
ichimoku = new ichimoku_1.default();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StockListType, StockType } from "./types";
|
|
2
|
+
export type DmiResType = {
|
|
3
|
+
dataset: StockListType;
|
|
4
|
+
pDi: number;
|
|
5
|
+
mDi: number;
|
|
6
|
+
adx: number;
|
|
7
|
+
type: number;
|
|
8
|
+
smoothTr: number;
|
|
9
|
+
smoothPdm: number;
|
|
10
|
+
smoothMdm: number;
|
|
11
|
+
dxBuffer: number[];
|
|
12
|
+
smoothAdx: number;
|
|
13
|
+
};
|
|
14
|
+
export default class Dmi {
|
|
15
|
+
init(data: StockType, type: number): DmiResType;
|
|
16
|
+
next(data: StockType, preList: DmiResType, type: number): DmiResType;
|
|
17
|
+
calculateDmiValues(list: StockListType, period?: number): {
|
|
18
|
+
pDi: number;
|
|
19
|
+
mDi: number;
|
|
20
|
+
adx: number;
|
|
21
|
+
c: number;
|
|
22
|
+
v: number;
|
|
23
|
+
l: number;
|
|
24
|
+
h: number;
|
|
25
|
+
o: number;
|
|
26
|
+
t: number;
|
|
27
|
+
}[];
|
|
28
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
13
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
14
|
+
if (ar || !(i in from)) {
|
|
15
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
16
|
+
ar[i] = from[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
|
+
};
|
|
21
|
+
var Dmi = /** @class */ (function () {
|
|
22
|
+
function Dmi() {
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(Dmi.prototype, "init", {
|
|
25
|
+
enumerable: false,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: function (data, type) {
|
|
29
|
+
return {
|
|
30
|
+
dataset: [data],
|
|
31
|
+
pDi: 0,
|
|
32
|
+
mDi: 0,
|
|
33
|
+
adx: 0,
|
|
34
|
+
type: type,
|
|
35
|
+
smoothTr: 0,
|
|
36
|
+
smoothPdm: 0,
|
|
37
|
+
smoothMdm: 0,
|
|
38
|
+
dxBuffer: [],
|
|
39
|
+
smoothAdx: 0,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(Dmi.prototype, "next", {
|
|
44
|
+
enumerable: false,
|
|
45
|
+
configurable: true,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: function (data, preList, type) {
|
|
48
|
+
preList.dataset.push(data);
|
|
49
|
+
// Need type + 1 data points to have 'type' periods of change
|
|
50
|
+
if (preList.dataset.length < type + 1) {
|
|
51
|
+
return __assign(__assign({}, preList), { pDi: 0, mDi: 0, adx: 0 });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
var currentTr = 0;
|
|
55
|
+
var currentPdm = 0;
|
|
56
|
+
var currentMdm = 0;
|
|
57
|
+
// Logic to get TR, +DM, -DM for the NEWEST data point
|
|
58
|
+
// We need the newest point and the one before it.
|
|
59
|
+
// If we are in the initialization phase (length === type + 1), we process the whole window.
|
|
60
|
+
// If we are in the incremental phase (length > type + 1), we just process the last step.
|
|
61
|
+
var newSmoothTr = preList.smoothTr;
|
|
62
|
+
var newSmoothPdm = preList.smoothPdm;
|
|
63
|
+
var newSmoothMdm = preList.smoothMdm;
|
|
64
|
+
if (preList.dataset.length === type + 1) {
|
|
65
|
+
// Initialization: Calculate sum of first N periods (using N+1 data points)
|
|
66
|
+
var sumTr = 0;
|
|
67
|
+
var sumPdm = 0;
|
|
68
|
+
var sumMdm = 0;
|
|
69
|
+
for (var i = 1; i <= type; i++) {
|
|
70
|
+
var curr = preList.dataset[i];
|
|
71
|
+
var prev = preList.dataset[i - 1];
|
|
72
|
+
// TR
|
|
73
|
+
var hl = curr.h - curr.l;
|
|
74
|
+
var hpc = Math.abs(curr.h - prev.c);
|
|
75
|
+
var lpc = Math.abs(curr.l - prev.c);
|
|
76
|
+
var tr = Math.max(hl, hpc, lpc);
|
|
77
|
+
// DM
|
|
78
|
+
var hph = curr.h - prev.h;
|
|
79
|
+
var pll = prev.l - curr.l;
|
|
80
|
+
var pdm = 0;
|
|
81
|
+
var mdm = 0;
|
|
82
|
+
if (hph > pll && hph > 0) {
|
|
83
|
+
pdm = hph;
|
|
84
|
+
}
|
|
85
|
+
if (pll > hph && pll > 0) {
|
|
86
|
+
mdm = pll;
|
|
87
|
+
}
|
|
88
|
+
sumTr += tr;
|
|
89
|
+
sumPdm += pdm;
|
|
90
|
+
sumMdm += mdm;
|
|
91
|
+
}
|
|
92
|
+
// Wilder's first value is often just the Sum.
|
|
93
|
+
// But to make it consistent with the "Average" view for the formula:
|
|
94
|
+
// NextAvg = (PrevAvg * (N-1) + Curr) / N
|
|
95
|
+
// The first "PrevAvg" acts as the seed. The seed is usually the Simple Average.
|
|
96
|
+
// So we divide by type.
|
|
97
|
+
newSmoothTr = sumTr; // Some sources say keep Sum. But standard indicators often normalize to Average range.
|
|
98
|
+
// Let's check RSI implementation. RSI divides by type: gains / type.
|
|
99
|
+
// So we will divide by type to get the Average TR/DM.
|
|
100
|
+
// Wait! DMI standard often keeps the SUM for the first value?
|
|
101
|
+
// Wilder's book: "+DM14 is the sum of the +DM for the last 14 days".
|
|
102
|
+
// Then subsequent: "+DM14_today = +DM14_yesterday - (+DM14_yesterday/14) + +DM_today".
|
|
103
|
+
// This formula maintains the "Sum" magnitude (approx 14x the average).
|
|
104
|
+
// BUT, RSI implementation uses Average magnitude (0-100 range inputs usually lead to small AvgGain).
|
|
105
|
+
// Let's stick to the RSI pattern: Average.
|
|
106
|
+
// Formula: (Avg * (N-1) + Curr) / N. This maintains "Average" magnitude.
|
|
107
|
+
// If we used Sum logic: (Sum - Sum/N + Curr) = Sum * (1 - 1/N) + Curr.
|
|
108
|
+
// These are mathematically consistent in shape, just scaled by N.
|
|
109
|
+
// DI calculation is (Pdm / Tr) * 100. The scale cancels out!
|
|
110
|
+
// So using Average is safer for preventing overflow and easier to debug (per-day values).
|
|
111
|
+
newSmoothTr = sumTr / type;
|
|
112
|
+
newSmoothPdm = sumPdm / type;
|
|
113
|
+
newSmoothMdm = sumMdm / type;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Shift if needed to keep dataset size manageable, though strictly we only need last 2 points
|
|
117
|
+
// reusing rsi pattern:
|
|
118
|
+
if (preList.dataset.length > type + 1) {
|
|
119
|
+
preList.dataset.shift();
|
|
120
|
+
}
|
|
121
|
+
var curr = preList.dataset[preList.dataset.length - 1];
|
|
122
|
+
var prev = preList.dataset[preList.dataset.length - 2];
|
|
123
|
+
// TR
|
|
124
|
+
var hl = curr.h - curr.l;
|
|
125
|
+
var hpc = Math.abs(curr.h - prev.c);
|
|
126
|
+
var lpc = Math.abs(curr.l - prev.c);
|
|
127
|
+
var tr = Math.max(hl, hpc, lpc);
|
|
128
|
+
// DM
|
|
129
|
+
var hph = curr.h - prev.h;
|
|
130
|
+
var pll = prev.l - curr.l;
|
|
131
|
+
var pdm = 0;
|
|
132
|
+
var mdm = 0;
|
|
133
|
+
if (hph > pll && hph > 0)
|
|
134
|
+
pdm = hph;
|
|
135
|
+
if (pll > hph && pll > 0)
|
|
136
|
+
mdm = pll;
|
|
137
|
+
// Wilder's Smoothing (Average form)
|
|
138
|
+
newSmoothTr = (preList.smoothTr * (type - 1) + tr) / type;
|
|
139
|
+
newSmoothPdm = (preList.smoothPdm * (type - 1) + pdm) / type;
|
|
140
|
+
newSmoothMdm = (preList.smoothMdm * (type - 1) + mdm) / type;
|
|
141
|
+
}
|
|
142
|
+
// Calculate DI
|
|
143
|
+
// Avoid division by zero
|
|
144
|
+
var pDi = newSmoothTr === 0 ? 0 : (newSmoothPdm / newSmoothTr) * 100;
|
|
145
|
+
var mDi = newSmoothTr === 0 ? 0 : (newSmoothMdm / newSmoothTr) * 100;
|
|
146
|
+
// Calculate DX
|
|
147
|
+
var diDiff = Math.abs(pDi - mDi);
|
|
148
|
+
var diSum = pDi + mDi;
|
|
149
|
+
var dx = diSum === 0 ? 0 : (diDiff / diSum) * 100;
|
|
150
|
+
// ADX Logic
|
|
151
|
+
var dxBuffer = __spreadArray([], preList.dxBuffer, true);
|
|
152
|
+
var adx = preList.adx;
|
|
153
|
+
var newSmoothAdx = preList.smoothAdx;
|
|
154
|
+
if (dxBuffer.length < type) {
|
|
155
|
+
dxBuffer.push(dx);
|
|
156
|
+
// Special case: if we Just reached 'type' count, we can calc initial ADX
|
|
157
|
+
if (dxBuffer.length === type) {
|
|
158
|
+
// First ADX is average of the DX buffer?
|
|
159
|
+
// "ADX is the 14-day smoothed average of DX".
|
|
160
|
+
// First value is simple average of previous 14 DX values.
|
|
161
|
+
var sumDx = dxBuffer.reduce(function (a, b) { return a + b; }, 0);
|
|
162
|
+
newSmoothAdx = sumDx / type;
|
|
163
|
+
adx = newSmoothAdx;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
// Not enough data for ADX yet
|
|
167
|
+
adx = 0;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
// We already have ADX initialized, so we smooth it
|
|
172
|
+
// Note: we don't need to keep growing dxBuffer indefinitely.
|
|
173
|
+
// We just needed it for startup.
|
|
174
|
+
// Update ADX using Wilder's smoothing
|
|
175
|
+
newSmoothAdx = (preList.smoothAdx * (type - 1) + dx) / type;
|
|
176
|
+
adx = newSmoothAdx;
|
|
177
|
+
}
|
|
178
|
+
return __assign(__assign({}, preList), { pDi: pDi, // +DI
|
|
179
|
+
mDi: mDi, // -DI
|
|
180
|
+
adx: adx, smoothTr: newSmoothTr, smoothPdm: newSmoothPdm, smoothMdm: newSmoothMdm, dxBuffer: dxBuffer, smoothAdx: newSmoothAdx });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
// Helper to get formatted results similar to ma.ts
|
|
185
|
+
Object.defineProperty(Dmi.prototype, "calculateDmiValues", {
|
|
186
|
+
enumerable: false,
|
|
187
|
+
configurable: true,
|
|
188
|
+
writable: true,
|
|
189
|
+
value: function (list, period) {
|
|
190
|
+
if (period === void 0) { period = 14; }
|
|
191
|
+
var res = [];
|
|
192
|
+
var state = this.init(list[0], period);
|
|
193
|
+
// First point (index 0) has 0 DMI
|
|
194
|
+
res.push(__assign(__assign({}, list[0]), { pDi: 0, mDi: 0, adx: 0 }));
|
|
195
|
+
for (var i = 1; i < list.length; i++) {
|
|
196
|
+
state = this.next(list[i], state, period);
|
|
197
|
+
res.push(__assign(__assign({}, list[i]), { pDi: state.pDi, mDi: state.mDi, adx: state.adx }));
|
|
198
|
+
}
|
|
199
|
+
return res;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return Dmi;
|
|
203
|
+
}());
|
|
204
|
+
export default Dmi;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Dmi from "./dmi";
|
|
2
|
+
import data from "./test_data.test";
|
|
3
|
+
describe("test dmi methods", function () {
|
|
4
|
+
it("test init & next", function () {
|
|
5
|
+
var dmi = new Dmi();
|
|
6
|
+
var period = 14;
|
|
7
|
+
// Calculate all values using loop
|
|
8
|
+
var results = dmi.calculateDmiValues(data, period);
|
|
9
|
+
var lastResult = results[results.length - 1];
|
|
10
|
+
// Verify consistency
|
|
11
|
+
// Re-run step-by-step
|
|
12
|
+
var state = dmi.init(data[0], period);
|
|
13
|
+
for (var i = 1; i < data.length; i++) {
|
|
14
|
+
state = dmi.next(data[i], state, period);
|
|
15
|
+
}
|
|
16
|
+
expect(state.pDi).toEqual(lastResult.pDi);
|
|
17
|
+
expect(state.mDi).toEqual(lastResult.mDi);
|
|
18
|
+
expect(state.adx).toEqual(lastResult.adx);
|
|
19
|
+
});
|
|
20
|
+
it("test values consistency (basic sanity)", function () {
|
|
21
|
+
// Generate a simple synthetic dataset where Trend is obvious
|
|
22
|
+
// Upward trend -> +DI should be high, -DI low
|
|
23
|
+
var upTrendData = [];
|
|
24
|
+
for (var i = 0; i < 50; i++) {
|
|
25
|
+
upTrendData.push({
|
|
26
|
+
c: 10 + i,
|
|
27
|
+
h: 11 + i,
|
|
28
|
+
l: 9 + i,
|
|
29
|
+
o: 10 + i,
|
|
30
|
+
v: 1000,
|
|
31
|
+
t: 20210101 + i,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
var dmi = new Dmi();
|
|
35
|
+
var res = dmi.calculateDmiValues(upTrendData, 14);
|
|
36
|
+
var last = res[res.length - 1];
|
|
37
|
+
// +DI should be dominant
|
|
38
|
+
expect(last.pDi).toBeGreaterThan(last.mDi);
|
|
39
|
+
// ADX should be high (strong trend)
|
|
40
|
+
expect(last.adx).toBeGreaterThan(20);
|
|
41
|
+
});
|
|
42
|
+
it("test flat trend", function () {
|
|
43
|
+
// Flat data
|
|
44
|
+
var flatData = [];
|
|
45
|
+
for (var i = 0; i < 50; i++) {
|
|
46
|
+
flatData.push({
|
|
47
|
+
c: 10,
|
|
48
|
+
h: 11,
|
|
49
|
+
l: 9,
|
|
50
|
+
o: 10,
|
|
51
|
+
v: 1000,
|
|
52
|
+
t: 20210101 + i,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
var dmi = new Dmi();
|
|
56
|
+
var res = dmi.calculateDmiValues(flatData, 14);
|
|
57
|
+
var last = res[res.length - 1];
|
|
58
|
+
// No directional movement
|
|
59
|
+
// h-ph = 0, pl-l = 0.
|
|
60
|
+
// +DM = 0, -DM = 0.
|
|
61
|
+
// +DI = 0, -DI = 0.
|
|
62
|
+
// ADX = 0?
|
|
63
|
+
// DX = 0.
|
|
64
|
+
expect(last.pDi).toEqual(0);
|
|
65
|
+
expect(last.mDi).toEqual(0);
|
|
66
|
+
// ADX might be 0 or close to 0
|
|
67
|
+
expect(last.adx).toEqual(0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -15,7 +15,7 @@ interface IchimokuType {
|
|
|
15
15
|
init: (data: NewStockType) => IchimokuResType;
|
|
16
16
|
next: (data: NewStockType, preList: IchimokuResType) => IchimokuResType;
|
|
17
17
|
}
|
|
18
|
-
export default class
|
|
18
|
+
export default class Ichimoku implements IchimokuType {
|
|
19
19
|
init(data: NewStockType): IchimokuResType;
|
|
20
20
|
/**
|
|
21
21
|
* 優化說明:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
var
|
|
2
|
-
function
|
|
1
|
+
var Ichimoku = /** @class */ (function () {
|
|
2
|
+
function Ichimoku() {
|
|
3
3
|
}
|
|
4
|
-
Object.defineProperty(
|
|
4
|
+
Object.defineProperty(Ichimoku.prototype, "init", {
|
|
5
5
|
enumerable: false,
|
|
6
6
|
configurable: true,
|
|
7
7
|
writable: true,
|
|
@@ -19,7 +19,7 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
19
19
|
* 如果你的框架 (如 React state) 強制要求 immutable,則需要改回 [...prev, data] 的寫法。
|
|
20
20
|
* 下面的寫法假設可以 Mutation (這在 Class 內部運算或 Backend 處理很常見)。
|
|
21
21
|
*/
|
|
22
|
-
Object.defineProperty(
|
|
22
|
+
Object.defineProperty(Ichimoku.prototype, "next", {
|
|
23
23
|
enumerable: false,
|
|
24
24
|
configurable: true,
|
|
25
25
|
writable: true,
|
|
@@ -36,7 +36,7 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
// 核心計算邏輯
|
|
39
|
-
Object.defineProperty(
|
|
39
|
+
Object.defineProperty(Ichimoku.prototype, "calcIchimoku", {
|
|
40
40
|
enumerable: false,
|
|
41
41
|
configurable: true,
|
|
42
42
|
writable: true,
|
|
@@ -64,7 +64,7 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
64
64
|
* 1. 移除 .slice(),避免產生 Garbage Collection。
|
|
65
65
|
* 2. 使用反向迴圈 (i--),通常在 JS 引擎中微幅快一點,且語意上是「從現在往回看」。
|
|
66
66
|
*/
|
|
67
|
-
Object.defineProperty(
|
|
67
|
+
Object.defineProperty(Ichimoku.prototype, "getMidPrice", {
|
|
68
68
|
enumerable: false,
|
|
69
69
|
configurable: true,
|
|
70
70
|
writable: true,
|
|
@@ -89,6 +89,6 @@ var IchimokuCloud = /** @class */ (function () {
|
|
|
89
89
|
return (maxH + minL) / 2;
|
|
90
90
|
}
|
|
91
91
|
});
|
|
92
|
-
return
|
|
92
|
+
return Ichimoku;
|
|
93
93
|
}());
|
|
94
|
-
export default
|
|
94
|
+
export default Ichimoku;
|
|
@@ -10,15 +10,15 @@ var __assign = (this && this.__assign) || function () {
|
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
12
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
13
|
-
import
|
|
13
|
+
import Ichimoku from "./ichimoku";
|
|
14
14
|
// 輔助函式:快速產生測試用的假資料
|
|
15
15
|
var createStock = function (idx, override) {
|
|
16
16
|
return __assign({ t: idx, o: 100, c: 100, h: 100, l: 100, v: 1000 }, override);
|
|
17
17
|
};
|
|
18
|
-
describe("
|
|
18
|
+
describe("Ichimoku Algo", function () {
|
|
19
19
|
var ichimoku;
|
|
20
20
|
beforeEach(function () {
|
|
21
|
-
ichimoku = new
|
|
21
|
+
ichimoku = new Ichimoku();
|
|
22
22
|
});
|
|
23
23
|
describe("init", function () {
|
|
24
24
|
it("should initialize with correct structure", function () {
|
|
@@ -112,7 +112,7 @@ describe("IchimokuCloud Algo", function () {
|
|
|
112
112
|
// 注意:上面的 init 和 next 已經把資料寫死為 100 了,這在單元測試有點難搞
|
|
113
113
|
// 所以我們用更簡單的方法:在 next 過程中精準控制
|
|
114
114
|
// --- 重來:精準控制版 ---
|
|
115
|
-
var ichi = new
|
|
115
|
+
var ichi = new Ichimoku();
|
|
116
116
|
var r = ichi.init(createStock(0, { h: 120, l: 80 })); // Kijun Range: H=120, L=80
|
|
117
117
|
// 填滿中間 16 筆 (Index 1~16),數值平穩不影響極值,且不讓 Tenkan 抓到
|
|
118
118
|
for (var i = 1; i <= 16; i++) {
|
package/dist/umd/index.js
CHANGED
|
@@ -1869,10 +1869,10 @@
|
|
|
1869
1869
|
return Mfi;
|
|
1870
1870
|
}());
|
|
1871
1871
|
|
|
1872
|
-
var
|
|
1873
|
-
function
|
|
1872
|
+
var Ichimoku = /** @class */ (function () {
|
|
1873
|
+
function Ichimoku() {
|
|
1874
1874
|
}
|
|
1875
|
-
Object.defineProperty(
|
|
1875
|
+
Object.defineProperty(Ichimoku.prototype, "init", {
|
|
1876
1876
|
enumerable: false,
|
|
1877
1877
|
configurable: true,
|
|
1878
1878
|
writable: true,
|
|
@@ -1890,7 +1890,7 @@
|
|
|
1890
1890
|
* 如果你的框架 (如 React state) 強制要求 immutable,則需要改回 [...prev, data] 的寫法。
|
|
1891
1891
|
* 下面的寫法假設可以 Mutation (這在 Class 內部運算或 Backend 處理很常見)。
|
|
1892
1892
|
*/
|
|
1893
|
-
Object.defineProperty(
|
|
1893
|
+
Object.defineProperty(Ichimoku.prototype, "next", {
|
|
1894
1894
|
enumerable: false,
|
|
1895
1895
|
configurable: true,
|
|
1896
1896
|
writable: true,
|
|
@@ -1907,7 +1907,7 @@
|
|
|
1907
1907
|
}
|
|
1908
1908
|
});
|
|
1909
1909
|
// 核心計算邏輯
|
|
1910
|
-
Object.defineProperty(
|
|
1910
|
+
Object.defineProperty(Ichimoku.prototype, "calcIchimoku", {
|
|
1911
1911
|
enumerable: false,
|
|
1912
1912
|
configurable: true,
|
|
1913
1913
|
writable: true,
|
|
@@ -1935,7 +1935,7 @@
|
|
|
1935
1935
|
* 1. 移除 .slice(),避免產生 Garbage Collection。
|
|
1936
1936
|
* 2. 使用反向迴圈 (i--),通常在 JS 引擎中微幅快一點,且語意上是「從現在往回看」。
|
|
1937
1937
|
*/
|
|
1938
|
-
Object.defineProperty(
|
|
1938
|
+
Object.defineProperty(Ichimoku.prototype, "getMidPrice", {
|
|
1939
1939
|
enumerable: false,
|
|
1940
1940
|
configurable: true,
|
|
1941
1941
|
writable: true,
|
|
@@ -1960,7 +1960,7 @@
|
|
|
1960
1960
|
return (maxH + minL) / 2;
|
|
1961
1961
|
}
|
|
1962
1962
|
});
|
|
1963
|
-
return
|
|
1963
|
+
return Ichimoku;
|
|
1964
1964
|
}());
|
|
1965
1965
|
|
|
1966
1966
|
function add(a, b) {
|
|
@@ -2003,7 +2003,7 @@
|
|
|
2003
2003
|
exports.Boll = Boll;
|
|
2004
2004
|
exports.Ema = Ema;
|
|
2005
2005
|
exports.Gold = Gold;
|
|
2006
|
-
exports.Ichimoku =
|
|
2006
|
+
exports.Ichimoku = Ichimoku;
|
|
2007
2007
|
exports.Kd = Kd;
|
|
2008
2008
|
exports.Ma = Ma;
|
|
2009
2009
|
exports.Macd = MACD;
|