@ch20026103/anysis 0.0.19-alpha1 → 0.0.19
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/index.d.ts +1 -0
- package/dist/cjs/index.js +3 -1
- 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/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- 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 +267 -65
- package/package.json +1 -1
package/dist/cjs/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { default as Williams } from "./stockSkills/williams.js";
|
|
|
21
21
|
export { default as ObvEma } from "./stockSkills/obv_ema.js";
|
|
22
22
|
export { default as Mfi } from "./stockSkills/mfi.js";
|
|
23
23
|
export { default as Ichimoku } from "./stockSkills/ichimoku.js";
|
|
24
|
+
export { default as Dmi } from "./stockSkills/dmi.js";
|
|
24
25
|
export { add } from "./test/add.js";
|
|
25
26
|
export { minus } from "./test/minus.js";
|
|
26
27
|
export { default as calculateDivisionFactor } from "./utils/calculateDivisionFactor.js";
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseLsusbOutput = exports.isJSON = exports.calculateDivisionFactor = exports.minus = exports.add = exports.Ichimoku = exports.Mfi = exports.ObvEma = exports.Williams = exports.Vma = exports.Week = exports.dateFormat = exports.Rsi = exports.Obv = exports.Macd = exports.Ma = exports.Kd = exports.Gold = exports.Ema = exports.Boll = exports.calcSeasonalIndicesNoTrend = exports.weightMovingAverages = exports.movingAverages = exports.exponentialSmoothing = exports.findTroughByGradient = exports.findPeaksByGradient = exports.SwingExtremesType = exports.SwingExtremes = exports.slope = exports.simpleRegressionModel = exports.Angle = void 0;
|
|
3
|
+
exports.parseLsusbOutput = exports.isJSON = exports.calculateDivisionFactor = exports.minus = exports.add = exports.Dmi = exports.Ichimoku = exports.Mfi = exports.ObvEma = exports.Williams = exports.Vma = exports.Week = exports.dateFormat = exports.Rsi = exports.Obv = exports.Macd = exports.Ma = exports.Kd = exports.Gold = exports.Ema = exports.Boll = exports.calcSeasonalIndicesNoTrend = exports.weightMovingAverages = exports.movingAverages = exports.exponentialSmoothing = exports.findTroughByGradient = exports.findPeaksByGradient = exports.SwingExtremesType = exports.SwingExtremes = exports.slope = exports.simpleRegressionModel = exports.Angle = void 0;
|
|
4
4
|
/*
|
|
5
5
|
請注意,在 src/index.ts 中,我的導入包含文件擴展名(.js)。
|
|
6
6
|
如果需要支持 Node.js 和構建工具(ex: webpack),則不需要這樣做。 **因為commonJs默認js副檔名**
|
|
@@ -53,6 +53,8 @@ var mfi_js_1 = require("./stockSkills/mfi.js");
|
|
|
53
53
|
Object.defineProperty(exports, "Mfi", { enumerable: true, get: function () { return mfi_js_1.default; } });
|
|
54
54
|
var ichimoku_js_1 = require("./stockSkills/ichimoku.js");
|
|
55
55
|
Object.defineProperty(exports, "Ichimoku", { enumerable: true, get: function () { return ichimoku_js_1.default; } });
|
|
56
|
+
var dmi_js_1 = require("./stockSkills/dmi.js");
|
|
57
|
+
Object.defineProperty(exports, "Dmi", { enumerable: true, get: function () { return dmi_js_1.default; } });
|
|
56
58
|
var add_js_1 = require("./test/add.js");
|
|
57
59
|
Object.defineProperty(exports, "add", { enumerable: true, get: function () { return add_js_1.add; } });
|
|
58
60
|
var minus_js_1 = require("./test/minus.js");
|
|
@@ -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();
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export { default as Williams } from "./stockSkills/williams.js";
|
|
|
21
21
|
export { default as ObvEma } from "./stockSkills/obv_ema.js";
|
|
22
22
|
export { default as Mfi } from "./stockSkills/mfi.js";
|
|
23
23
|
export { default as Ichimoku } from "./stockSkills/ichimoku.js";
|
|
24
|
+
export { default as Dmi } from "./stockSkills/dmi.js";
|
|
24
25
|
export { add } from "./test/add.js";
|
|
25
26
|
export { minus } from "./test/minus.js";
|
|
26
27
|
export { default as calculateDivisionFactor } from "./utils/calculateDivisionFactor.js";
|
package/dist/esm/index.js
CHANGED
|
@@ -24,6 +24,7 @@ export { default as Williams } from "./stockSkills/williams.js";
|
|
|
24
24
|
export { default as ObvEma } from "./stockSkills/obv_ema.js";
|
|
25
25
|
export { default as Mfi } from "./stockSkills/mfi.js";
|
|
26
26
|
export { default as Ichimoku } from "./stockSkills/ichimoku.js";
|
|
27
|
+
export { default as Dmi } from "./stockSkills/dmi.js";
|
|
27
28
|
export { add } from "./test/add.js";
|
|
28
29
|
export { minus } from "./test/minus.js";
|
|
29
30
|
export { default as calculateDivisionFactor } from "./utils/calculateDivisionFactor.js";
|
|
@@ -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 {};
|