@stvy/fund-indicators 1.0.0 → 1.0.5
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/.github/workflows/publish.yml +73 -0
- package/AGENTS.md +322 -0
- package/dist/index.cjs +7014 -0
- package/dist/index.d.cts +779 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.mjs +6917 -0
- package/package.json +15 -32
- package/pnpm-workspace.yaml +2 -0
- package/src/dca.ts +420 -0
- package/src/index.ts +133 -0
- package/src/jstat.d.ts +17 -0
- package/src/pattern.ts +447 -0
- package/src/risk.ts +516 -0
- package/src/statistics.ts +428 -0
- package/src/technical.ts +738 -0
- package/src/types.ts +369 -0
- package/test/index.test.ts +355 -0
- package/tsconfig.json +20 -0
- package/dist/browser/fund-indicators.esm.js +0 -7505
- package/dist/browser/fund-indicators.esm.min.js +0 -8
- package/dist/browser/fund-indicators.esm.min.js.map +0 -7
- package/dist/browser/fund-indicators.js +0 -7517
- package/dist/browser/fund-indicators.min.js +0 -8
- package/dist/browser/fund-indicators.min.js.map +0 -7
- package/dist/dca.d.ts +0 -91
- package/dist/dca.d.ts.map +0 -1
- package/dist/dca.js +0 -354
- package/dist/dca.js.map +0 -1
- package/dist/index.d.ts +0 -18
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -141
- package/dist/index.js.map +0 -1
- package/dist/pattern.d.ts +0 -60
- package/dist/pattern.d.ts.map +0 -1
- package/dist/pattern.js +0 -386
- package/dist/pattern.js.map +0 -1
- package/dist/risk.d.ts +0 -115
- package/dist/risk.d.ts.map +0 -1
- package/dist/risk.js +0 -502
- package/dist/risk.js.map +0 -1
- package/dist/statistics.d.ts +0 -78
- package/dist/statistics.d.ts.map +0 -1
- package/dist/statistics.js +0 -402
- package/dist/statistics.js.map +0 -1
- package/dist/technical.d.ts +0 -105
- package/dist/technical.d.ts.map +0 -1
- package/dist/technical.js +0 -633
- package/dist/technical.js.map +0 -1
- package/dist/types.d.ts +0 -327
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -7
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 基金指标工具库 - 验证测试
|
|
3
|
+
* 使用模拟净值数据验证所有指标的计算
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as indicators from '../src/index';
|
|
7
|
+
|
|
8
|
+
// ============================================================
|
|
9
|
+
// 生成模拟净值数据
|
|
10
|
+
// ============================================================
|
|
11
|
+
|
|
12
|
+
function generateMockNav(days: number = 500, startNav: number = 1.0, seed: number = 42): number[] {
|
|
13
|
+
const nav: number[] = [startNav];
|
|
14
|
+
let s = seed;
|
|
15
|
+
const random = () => {
|
|
16
|
+
s = (s * 1103515245 + 12345) & 0x7fffffff;
|
|
17
|
+
return s / 0x7fffffff;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
for (let i = 1; i < days; i++) {
|
|
21
|
+
// 模拟一个有趋势和波动的净值序列
|
|
22
|
+
const trend = 0.0003; // 日均涨幅 ~0.03%(年化约7%)
|
|
23
|
+
const volatility = 0.015; // 日波动率 ~1.5%
|
|
24
|
+
const r1 = random();
|
|
25
|
+
const r2 = random();
|
|
26
|
+
// Box-Muller 正态分布
|
|
27
|
+
const z = Math.sqrt(-2 * Math.log(r1 || 0.001)) * Math.cos(2 * Math.PI * r2);
|
|
28
|
+
const dailyReturn = trend + volatility * z;
|
|
29
|
+
nav.push(nav[i - 1] * (1 + dailyReturn));
|
|
30
|
+
}
|
|
31
|
+
return nav;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function generateBenchmarkNav(days: number = 500): number[] {
|
|
35
|
+
return generateMockNav(days, 1.0, 123);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ============================================================
|
|
39
|
+
// 测试工具
|
|
40
|
+
// ============================================================
|
|
41
|
+
|
|
42
|
+
let passed = 0;
|
|
43
|
+
let failed = 0;
|
|
44
|
+
|
|
45
|
+
function assert(condition: boolean, message: string) {
|
|
46
|
+
if (condition) {
|
|
47
|
+
passed++;
|
|
48
|
+
console.log(` [PASS] ${message}`);
|
|
49
|
+
} else {
|
|
50
|
+
failed++;
|
|
51
|
+
console.log(` [FAIL] ${message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function section(name: string) {
|
|
56
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
57
|
+
console.log(` ${name}`);
|
|
58
|
+
console.log('='.repeat(60));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ============================================================
|
|
62
|
+
// 运行测试
|
|
63
|
+
// ============================================================
|
|
64
|
+
|
|
65
|
+
const nav = generateMockNav(500);
|
|
66
|
+
const benchmarkNav = generateBenchmarkNav(500);
|
|
67
|
+
|
|
68
|
+
console.log(`\n模拟数据: ${nav.length} 天净值`);
|
|
69
|
+
console.log(`起始净值: ${nav[0].toFixed(4)}, 最新净值: ${nav[nav.length - 1].toFixed(4)}`);
|
|
70
|
+
console.log(`累计收益: ${(indicators.totalReturn(nav) * 100).toFixed(2)}%\n`);
|
|
71
|
+
|
|
72
|
+
// ---- 技术指标 ----
|
|
73
|
+
section('技术指标 - 均线');
|
|
74
|
+
|
|
75
|
+
const sma20 = indicators.sma(nav, 20);
|
|
76
|
+
assert(sma20.values.length === nav.length, 'SMA 输出长度与输入一致');
|
|
77
|
+
assert(sma20.current != null, `SMA(20) 当前值: ${sma20.current?.toFixed(4)}`);
|
|
78
|
+
assert(sma20.values[0] === null, 'SMA 前 N-1 个为 null');
|
|
79
|
+
|
|
80
|
+
const ema20 = indicators.ema(nav, 20);
|
|
81
|
+
assert(ema20.current != null, `EMA(20) 当前值: ${ema20.current?.toFixed(4)}`);
|
|
82
|
+
|
|
83
|
+
const kama10 = indicators.kama(nav, 10);
|
|
84
|
+
assert(kama10.current != null, `KAMA(10) 当前值: ${kama10.current?.toFixed(4)}`);
|
|
85
|
+
|
|
86
|
+
const demaResult = indicators.dema(nav, 20);
|
|
87
|
+
assert(demaResult.current != null, `DEMA(20) 当前值: ${demaResult.current?.toFixed(4)}`);
|
|
88
|
+
|
|
89
|
+
// ---- MACD ----
|
|
90
|
+
section('技术指标 - MACD');
|
|
91
|
+
|
|
92
|
+
const macdResult = indicators.macd(nav);
|
|
93
|
+
assert(macdResult.currentDIF != null, `MACD DIF: ${macdResult.currentDIF?.toFixed(6)}`);
|
|
94
|
+
assert(macdResult.currentDEA != null, `MACD DEA: ${macdResult.currentDEA?.toFixed(6)}`);
|
|
95
|
+
assert(macdResult.currentHistogram != null, `MACD 柱状线: ${macdResult.currentHistogram?.toFixed(6)}`);
|
|
96
|
+
|
|
97
|
+
// ---- RSI ----
|
|
98
|
+
section('技术指标 - RSI');
|
|
99
|
+
|
|
100
|
+
const rsi14 = indicators.rsi(nav, 14);
|
|
101
|
+
assert(rsi14.current != null && rsi14.current >= 0 && rsi14.current <= 100,
|
|
102
|
+
`RSI(14): ${rsi14.current?.toFixed(2)}(范围 0-100)`);
|
|
103
|
+
|
|
104
|
+
// ---- KDJ ----
|
|
105
|
+
section('技术指标 - KDJ');
|
|
106
|
+
|
|
107
|
+
const kdjResult = indicators.kdj(nav);
|
|
108
|
+
assert(kdjResult.currentK != null, `KDJ K: ${kdjResult.currentK?.toFixed(2)}`);
|
|
109
|
+
assert(kdjResult.currentD != null, `KDJ D: ${kdjResult.currentD?.toFixed(2)}`);
|
|
110
|
+
assert(kdjResult.currentJ != null, `KDJ J: ${kdjResult.currentJ?.toFixed(2)}`);
|
|
111
|
+
|
|
112
|
+
// ---- 布林带 ----
|
|
113
|
+
section('技术指标 - 布林带');
|
|
114
|
+
|
|
115
|
+
const bb = indicators.bollingerBands(nav);
|
|
116
|
+
assert(bb.middle[nav.length - 1] != null, '布林带中轨有值');
|
|
117
|
+
assert(bb.upper[nav.length - 1] != null && bb.lower[nav.length - 1] != null,
|
|
118
|
+
`布林带: 上轨=${bb.upper[nav.length - 1]?.toFixed(4)}, 下轨=${bb.lower[nav.length - 1]?.toFixed(4)}`);
|
|
119
|
+
|
|
120
|
+
const lastBB_U = bb.upper[nav.length - 1]!;
|
|
121
|
+
const lastBB_L = bb.lower[nav.length - 1]!;
|
|
122
|
+
const lastBB_M = bb.middle[nav.length - 1]!;
|
|
123
|
+
assert(lastBB_U > lastBB_M && lastBB_M > lastBB_L, '上轨 > 中轨 > 下轨');
|
|
124
|
+
|
|
125
|
+
// ---- 通道 ----
|
|
126
|
+
section('技术指标 - 通道');
|
|
127
|
+
|
|
128
|
+
const dc = indicators.donchianChannel(nav, 20);
|
|
129
|
+
assert(dc.upper[nav.length - 1] != null, `唐奇安通道上轨: ${dc.upper[nav.length - 1]?.toFixed(4)}`);
|
|
130
|
+
|
|
131
|
+
const kc = indicators.keltnerChannel(nav);
|
|
132
|
+
assert(kc.upper[nav.length - 1] != null, `肯特纳通道上轨: ${kc.upper[nav.length - 1]?.toFixed(4)}`);
|
|
133
|
+
|
|
134
|
+
// ---- 其他技术指标 ----
|
|
135
|
+
section('技术指标 - 其他');
|
|
136
|
+
|
|
137
|
+
const adxResult = indicators.adx(nav);
|
|
138
|
+
assert(adxResult.currentADX != null, `ADX: ${adxResult.currentADX?.toFixed(2)}`);
|
|
139
|
+
|
|
140
|
+
const cciResult = indicators.cci(nav);
|
|
141
|
+
assert(cciResult.current != null, `CCI: ${cciResult.current?.toFixed(2)}`);
|
|
142
|
+
|
|
143
|
+
const rocResult = indicators.roc(nav, 12);
|
|
144
|
+
assert(rocResult.current != null, `ROC(12): ${rocResult.current?.toFixed(4)}`);
|
|
145
|
+
|
|
146
|
+
const momResult = indicators.momentum(nav, 10);
|
|
147
|
+
assert(momResult.current != null, `Momentum(10): ${momResult.current?.toFixed(4)}`);
|
|
148
|
+
|
|
149
|
+
const biasResult = indicators.bias(nav, 20);
|
|
150
|
+
assert(biasResult.current != null, `BIAS(20): ${biasResult.current?.toFixed(4)}%`);
|
|
151
|
+
|
|
152
|
+
const trixResult = indicators.trix(nav, 12);
|
|
153
|
+
assert(trixResult.current != null, `TRIX(12): ${trixResult.current?.toFixed(4)}`);
|
|
154
|
+
|
|
155
|
+
const pctile = indicators.navPercentile(nav);
|
|
156
|
+
assert(pctile >= 0 && pctile <= 100, `净值百分位: ${pctile.toFixed(2)}%`);
|
|
157
|
+
|
|
158
|
+
// ---- 均线交叉 ----
|
|
159
|
+
section('均线交叉信号');
|
|
160
|
+
|
|
161
|
+
const crossSignal = indicators.detectCrossSignal(sma20, ema20);
|
|
162
|
+
console.log(` 最近交叉信号: ${crossSignal.type} (index: ${crossSignal.index})`);
|
|
163
|
+
|
|
164
|
+
const alignment = indicators.detectMAAlignment([
|
|
165
|
+
indicators.sma(nav, 5),
|
|
166
|
+
indicators.sma(nav, 10),
|
|
167
|
+
indicators.sma(nav, 20),
|
|
168
|
+
indicators.sma(nav, 60),
|
|
169
|
+
]);
|
|
170
|
+
console.log(` 均线排列: ${alignment.alignment}, 发散度: ${alignment.divergence.toFixed(6)}`);
|
|
171
|
+
|
|
172
|
+
// ---- 风险指标 ----
|
|
173
|
+
section('风险与波动指标');
|
|
174
|
+
|
|
175
|
+
const returns = indicators.navToReturns(nav);
|
|
176
|
+
assert(returns.length === nav.length - 1, `收益率序列长度: ${returns.length}`);
|
|
177
|
+
|
|
178
|
+
const annVol = indicators.annualizedVolatility(returns);
|
|
179
|
+
assert(annVol > 0, `年化波动率: ${(annVol * 100).toFixed(2)}%`);
|
|
180
|
+
|
|
181
|
+
const ddVol = indicators.downsideVolatility(returns);
|
|
182
|
+
assert(ddVol > 0, `下行波动率: ${(ddVol * 100).toFixed(2)}%`);
|
|
183
|
+
|
|
184
|
+
const dd = indicators.maxDrawdown(nav);
|
|
185
|
+
assert(dd.maxDrawdown < 0, `最大回撤: ${(dd.maxDrawdown * 100).toFixed(2)}%`);
|
|
186
|
+
assert(dd.durationDays >= 0, `回撤持续天数: ${dd.durationDays}`);
|
|
187
|
+
|
|
188
|
+
const var95 = indicators.calculateVaR(returns, 0.95);
|
|
189
|
+
assert(var95 < 0, `VaR(95%): ${(var95 * 100).toFixed(4)}%`);
|
|
190
|
+
|
|
191
|
+
const cvar95 = indicators.calculateCVaR(returns, 0.95);
|
|
192
|
+
assert(cvar95 <= var95, `CVaR(95%): ${(cvar95 * 100).toFixed(4)}%`);
|
|
193
|
+
|
|
194
|
+
const rm = indicators.riskMetrics(nav);
|
|
195
|
+
assert(rm.var99 < rm.var95, `VaR(99%) < VaR(95%): ${(rm.var99 * 100).toFixed(4)}% < ${(rm.var95 * 100).toFixed(4)}%`);
|
|
196
|
+
|
|
197
|
+
// ---- 绩效指标 ----
|
|
198
|
+
section('绩效评价指标');
|
|
199
|
+
|
|
200
|
+
const sharpe = indicators.sharpeRatio(nav);
|
|
201
|
+
console.log(` 夏普比率: ${sharpe.toFixed(4)}`);
|
|
202
|
+
|
|
203
|
+
const sortino = indicators.sortinoRatio(nav);
|
|
204
|
+
console.log(` 索提诺比率: ${sortino.toFixed(4)}`);
|
|
205
|
+
|
|
206
|
+
const calmar = indicators.calmarRatio(nav);
|
|
207
|
+
console.log(` 卡尔玛比率: ${calmar.toFixed(4)}`);
|
|
208
|
+
|
|
209
|
+
const omega = indicators.omegaRatio(nav);
|
|
210
|
+
console.log(` Omega 比率: ${omega.toFixed(4)}`);
|
|
211
|
+
|
|
212
|
+
const wr = indicators.winRate(returns);
|
|
213
|
+
console.log(` 胜率: ${(wr * 100).toFixed(2)}%`);
|
|
214
|
+
|
|
215
|
+
const plr = indicators.profitLossRatio(returns);
|
|
216
|
+
console.log(` 盈亏比: ${plr.toFixed(4)}`);
|
|
217
|
+
|
|
218
|
+
const pf = indicators.profitFactor(returns);
|
|
219
|
+
console.log(` 利润因子: ${pf.toFixed(4)}`);
|
|
220
|
+
|
|
221
|
+
const consec = indicators.consecutiveWinLoss(returns);
|
|
222
|
+
console.log(` 最大连续盈利: ${consec.maxWins} 天, 最大连续亏损: ${consec.maxLosses} 天`);
|
|
223
|
+
|
|
224
|
+
const pm = indicators.performanceMetrics(nav);
|
|
225
|
+
assert(pm.sharpeRatio !== 0 || pm.annualizedReturn === 0, '绩效指标汇总计算成功');
|
|
226
|
+
|
|
227
|
+
// ---- 基准对比 ----
|
|
228
|
+
section('基准对比指标(Alpha/Beta/跟踪误差)');
|
|
229
|
+
|
|
230
|
+
const bm = indicators.benchmarkMetrics(nav, benchmarkNav);
|
|
231
|
+
console.log(` Alpha: ${(bm.alpha * 100).toFixed(2)}%`);
|
|
232
|
+
console.log(` Beta: ${bm.beta.toFixed(4)}`);
|
|
233
|
+
console.log(` 跟踪误差: ${(bm.trackingError * 100).toFixed(2)}%`);
|
|
234
|
+
console.log(` 信息比率: ${bm.informationRatio.toFixed(4)}`);
|
|
235
|
+
console.log(` 相关系数: ${bm.correlation.toFixed(4)}`);
|
|
236
|
+
console.log(` R²: ${bm.rSquared.toFixed(4)}`);
|
|
237
|
+
|
|
238
|
+
// ---- 统计特征 ----
|
|
239
|
+
section('统计特征');
|
|
240
|
+
|
|
241
|
+
const stats = indicators.statisticalFeatures(nav);
|
|
242
|
+
console.log(` 均值: ${(stats.mean * 100).toFixed(4)}%`);
|
|
243
|
+
console.log(` 标准差: ${(stats.stdDev * 100).toFixed(4)}%`);
|
|
244
|
+
console.log(` 偏度: ${stats.skewness.toFixed(4)}`);
|
|
245
|
+
console.log(` 峰度: ${stats.kurtosis.toFixed(4)}`);
|
|
246
|
+
console.log(` Jarque-Bera: ${stats.jarqueBera.toFixed(2)}`);
|
|
247
|
+
|
|
248
|
+
// ---- Hurst 指数 ----
|
|
249
|
+
section('赫斯特指数');
|
|
250
|
+
|
|
251
|
+
const hurst = indicators.hurstExponent(nav);
|
|
252
|
+
console.log(` Hurst 指数: ${hurst.hurstExponent.toFixed(4)}`);
|
|
253
|
+
console.log(` 解读: ${hurst.interpretation}`);
|
|
254
|
+
console.log(` R²: ${hurst.rSquared.toFixed(4)}`);
|
|
255
|
+
assert(hurst.hurstExponent >= 0 && hurst.hurstExponent <= 1, 'Hurst 指数在 [0,1] 范围内');
|
|
256
|
+
|
|
257
|
+
// ---- 自相关 ----
|
|
258
|
+
section('自相关分析');
|
|
259
|
+
|
|
260
|
+
const acf = indicators.autocorrelation(nav, 10);
|
|
261
|
+
console.log(` Lag-0: ${acf.coefficients[0].toFixed(4)}`);
|
|
262
|
+
console.log(` Lag-1: ${acf.coefficients[1]?.toFixed(4) ?? 'N/A'}`);
|
|
263
|
+
console.log(` 解读: ${acf.interpretation}`);
|
|
264
|
+
|
|
265
|
+
const lb = indicators.ljungBoxTest(returns, 10);
|
|
266
|
+
console.log(` Ljung-Box Q: ${lb.qStatistic.toFixed(4)}, p-value: ${lb.approximatePValue.toFixed(4)}`);
|
|
267
|
+
|
|
268
|
+
// ---- GARCH ----
|
|
269
|
+
section('GARCH(1,1) 波动率预测');
|
|
270
|
+
|
|
271
|
+
const garch = indicators.garch11(returns);
|
|
272
|
+
console.log(` omega: ${garch.omega.toFixed(8)}`);
|
|
273
|
+
console.log(` alpha: ${garch.alpha.toFixed(4)}`);
|
|
274
|
+
console.log(` beta: ${garch.beta.toFixed(4)}`);
|
|
275
|
+
console.log(` 下期波动率预测: ${(Math.sqrt(garch.nextPeriodForecast) * Math.sqrt(242) * 100).toFixed(2)}% (年化)`);
|
|
276
|
+
|
|
277
|
+
// ---- 定投分析 ----
|
|
278
|
+
section('定投模拟');
|
|
279
|
+
|
|
280
|
+
const dcaResult = indicators.simulateDCA(nav, { amount: 1000, interval: 22 });
|
|
281
|
+
console.log(` 定投次数: ${dcaResult.totalInvestments}`);
|
|
282
|
+
console.log(` 总投入: ¥${dcaResult.totalInvested.toFixed(2)}`);
|
|
283
|
+
console.log(` 当前市值: ¥${dcaResult.currentValue.toFixed(2)}`);
|
|
284
|
+
console.log(` 平均成本: ${dcaResult.averageCost.toFixed(4)}`);
|
|
285
|
+
console.log(` 收益率: ${(dcaResult.returnRate * 100).toFixed(2)}%`);
|
|
286
|
+
console.log(` IRR: ${dcaResult.irr != null ? (dcaResult.irr * 100).toFixed(2) + '%' : 'N/A'}`);
|
|
287
|
+
|
|
288
|
+
// ---- 止盈止损 ----
|
|
289
|
+
section('止盈止损信号');
|
|
290
|
+
|
|
291
|
+
const costPrice = nav[Math.floor(nav.length / 2)]; // 假设中间位置买入
|
|
292
|
+
const tpSl = indicators.takeProfitStopLoss(nav, costPrice, 0.3, -0.15);
|
|
293
|
+
console.log(` 成本价: ${costPrice.toFixed(4)}`);
|
|
294
|
+
console.log(` 当前盈亏: ${(tpSl.currentPnL * 100).toFixed(2)}%`);
|
|
295
|
+
console.log(` 触发止盈: ${tpSl.takeProfitTriggered}`);
|
|
296
|
+
console.log(` 触发止损: ${tpSl.stopLossTriggered}`);
|
|
297
|
+
|
|
298
|
+
const trail = indicators.trailingStop(nav, 0.1, costPrice);
|
|
299
|
+
console.log(` 跟踪止盈触发: ${trail.triggered}`);
|
|
300
|
+
console.log(` 最高点回撤: ${(trail.drawdownFromPeak * 100).toFixed(2)}%`);
|
|
301
|
+
|
|
302
|
+
// ---- 智能定投 ----
|
|
303
|
+
section('智能定投');
|
|
304
|
+
|
|
305
|
+
const sm = indicators.safetyMargin(nav);
|
|
306
|
+
console.log(` 安全边际(距历史高点回撤): ${(sm * 100).toFixed(2)}%`);
|
|
307
|
+
|
|
308
|
+
const pp = indicators.pricePosition(nav);
|
|
309
|
+
console.log(` 历史价格位置: ${(pp * 100).toFixed(2)}%`);
|
|
310
|
+
|
|
311
|
+
const smartMult = indicators.smartDCAMultiplier(nav);
|
|
312
|
+
console.log(` 智能定投倍数: ${smartMult.toFixed(2)}x`);
|
|
313
|
+
|
|
314
|
+
const buyLevel = indicators.tieredBuySignal(nav);
|
|
315
|
+
console.log(` 分批买入层级: ${buyLevel}`);
|
|
316
|
+
|
|
317
|
+
const sellLevel = indicators.tieredSellSignal(nav);
|
|
318
|
+
console.log(` 分批卖出层级: ${sellLevel}`);
|
|
319
|
+
|
|
320
|
+
// ---- 形态识别 ----
|
|
321
|
+
section('形态识别');
|
|
322
|
+
|
|
323
|
+
const sr = indicators.supportResistance(nav);
|
|
324
|
+
console.log(` 支撑位数量: ${sr.supports.length}`);
|
|
325
|
+
if (sr.supports.length > 0) {
|
|
326
|
+
console.log(` 最近支撑: ${sr.supports[0].level.toFixed(4)} (强度: ${sr.supports[0].strength.toFixed(4)})`);
|
|
327
|
+
}
|
|
328
|
+
console.log(` 阻力位数量: ${sr.resistances.length}`);
|
|
329
|
+
if (sr.resistances.length > 0) {
|
|
330
|
+
console.log(` 最近阻力: ${sr.resistances[0].level.toFixed(4)} (强度: ${sr.resistances[0].strength.toFixed(4)})`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const dbt = indicators.doubleBottomTop(nav, 60);
|
|
334
|
+
console.log(` 双底/双顶: ${dbt.type} (confidence: ${dbt.confidence.toFixed(2)})`);
|
|
335
|
+
|
|
336
|
+
const gaps = indicators.detectGaps(nav, 0.02);
|
|
337
|
+
console.log(` 缺口数量: ${gaps.length}`);
|
|
338
|
+
for (const g of gaps.slice(0, 3)) {
|
|
339
|
+
console.log(` ${g.type} @ index ${g.startIndex}, 大小: ${(g.gapSize * 100).toFixed(2)}%, 已回补: ${g.filled}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const ts = indicators.trendStrength(nav, 20);
|
|
343
|
+
console.log(` 趋势强度评分: ${ts.toFixed(2)}/100`);
|
|
344
|
+
|
|
345
|
+
const hs = indicators.headAndShoulders(nav, 90);
|
|
346
|
+
console.log(` 头肩形态: ${hs.type} (confidence: ${hs.confidence.toFixed(2)})`);
|
|
347
|
+
|
|
348
|
+
// ---- 汇总 ----
|
|
349
|
+
section('测试结果汇总');
|
|
350
|
+
console.log(`\n 通过: ${passed}`);
|
|
351
|
+
console.log(` 失败: ${failed}`);
|
|
352
|
+
console.log(` 总计: ${passed + failed}`);
|
|
353
|
+
console.log(` 结果: ${failed === 0 ? 'ALL PASSED' : 'SOME FAILED'}\n`);
|
|
354
|
+
|
|
355
|
+
process.exit(failed > 0 ? 1 : 0);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"moduleResolution": "Node16",
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist", "test"],
|
|
20
|
+
}
|