@stvy/fund-indicators 1.0.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/LICENSE +21 -0
- package/README.en.md +617 -0
- package/README.md +617 -0
- package/dist/browser/fund-indicators.esm.js +7505 -0
- package/dist/browser/fund-indicators.esm.min.js +8 -0
- package/dist/browser/fund-indicators.esm.min.js.map +7 -0
- package/dist/browser/fund-indicators.js +7517 -0
- package/dist/browser/fund-indicators.min.js +8 -0
- package/dist/browser/fund-indicators.min.js.map +7 -0
- package/dist/dca.d.ts +91 -0
- package/dist/dca.d.ts.map +1 -0
- package/dist/dca.js +354 -0
- package/dist/dca.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -0
- package/dist/pattern.d.ts +60 -0
- package/dist/pattern.d.ts.map +1 -0
- package/dist/pattern.js +386 -0
- package/dist/pattern.js.map +1 -0
- package/dist/risk.d.ts +115 -0
- package/dist/risk.d.ts.map +1 -0
- package/dist/risk.js +502 -0
- package/dist/risk.js.map +1 -0
- package/dist/statistics.d.ts +78 -0
- package/dist/statistics.d.ts.map +1 -0
- package/dist/statistics.js +402 -0
- package/dist/statistics.js.map +1 -0
- package/dist/technical.d.ts +105 -0
- package/dist/technical.d.ts.map +1 -0
- package/dist/technical.js +633 -0
- package/dist/technical.js.map +1 -0
- package/dist/types.d.ts +327 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 fund-indicators contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
# @stvy/fund-indicators
|
|
2
|
+
|
|
3
|
+
**[中文](README.md)** | English
|
|
4
|
+
|
|
5
|
+
**Automated Trading Indicator Library for Funds** — Compute all technical indicators from historical NAV (Net Asset Value) data.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@stvy/fund-indicators)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://www.typescriptlang.org/)
|
|
10
|
+
[](https://github.com/stevieyu/fund-indicators/actions/workflows/ci.yml)
|
|
11
|
+
|
|
12
|
+
A quantitative analysis library designed specifically for OTC funds, index funds, and ETF feeder funds. Simply pass in a daily NAV array to compute 70+ technical indicators, risk metrics, performance metrics, statistical features, DCA (Dollar-Cost Averaging) analysis, and pattern recognition signals.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Zero-friction input**: Every function requires only a single `number[]` (daily NAV array)
|
|
19
|
+
- **TypeScript native**: Full type definitions and JSDoc annotations for excellent IDE support
|
|
20
|
+
- **Comprehensive coverage**: Technical indicators + risk/performance + statistical features + DCA analysis + pattern recognition — 5 modules, 70+ functions
|
|
21
|
+
- **Fund-optimized**: Annualization parameters based on 242 A-share trading days; supports OTC fund NAV analysis
|
|
22
|
+
- **Lightweight dependencies**: Only depends on `technicalindicators` + `simple-statistics` + `jstat`
|
|
23
|
+
- **Battle-tested**: 35 automated tests covering all core functionality
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @stvy/fund-indicators
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Browser Usage
|
|
34
|
+
|
|
35
|
+
This library also ships browser-ready builds that can be used directly in HTML pages.
|
|
36
|
+
|
|
37
|
+
**Option 1: `<script>` tag (IIFE)**
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<script src="https://unpkg.com/@stvy/fund-indicators/dist/browser/fund-indicators.min.js"></script>
|
|
41
|
+
<script>
|
|
42
|
+
const nav = [1.0, 1.02, 1.01, 1.05, 1.03, 1.08];
|
|
43
|
+
|
|
44
|
+
const ma = FundIndicators.sma(nav, 3);
|
|
45
|
+
console.log('SMA(3):', ma.current);
|
|
46
|
+
|
|
47
|
+
const rsi = FundIndicators.rsi(nav, 3);
|
|
48
|
+
console.log('RSI(3):', rsi.current);
|
|
49
|
+
|
|
50
|
+
const sharpe = FundIndicators.sharpeRatio(nav);
|
|
51
|
+
console.log('Sharpe Ratio:', sharpe);
|
|
52
|
+
</script>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Option 2: `<script type="module">` (ESM)**
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<script type="module">
|
|
59
|
+
import { sma, rsi, sharpeRatio } from 'https://unpkg.com/@stvy/fund-indicators/dist/browser/fund-indicators.esm.js';
|
|
60
|
+
|
|
61
|
+
const nav = [1.0, 1.02, 1.01, 1.05, 1.03, 1.08];
|
|
62
|
+
|
|
63
|
+
console.log('SMA(3):', sma(nav, 3).current);
|
|
64
|
+
console.log('RSI(3):', rsi(nav, 3).current);
|
|
65
|
+
console.log('Sharpe Ratio:', sharpeRatio(nav));
|
|
66
|
+
</script>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Build artifacts:**
|
|
70
|
+
|
|
71
|
+
| File | Format | Size | Use Case |
|
|
72
|
+
|------|--------|------|----------|
|
|
73
|
+
| `fund-indicators.js` | IIFE | ~257 KB | `<script>` tag; exposes global `FundIndicators` |
|
|
74
|
+
| `fund-indicators.min.js` | IIFE minified | ~99 KB | Recommended for production |
|
|
75
|
+
| `fund-indicators.esm.js` | ESM | ~241 KB | `<script type="module">` or frontend bundlers |
|
|
76
|
+
| `fund-indicators.esm.min.js` | ESM minified | ~99 KB | Recommended for production ESM |
|
|
77
|
+
|
|
78
|
+
> Browser builds bundle all dependencies inline — no additional installations required.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import {
|
|
86
|
+
sma, macd, rsi, kdj, bollingerBands,
|
|
87
|
+
sharpeRatio, maxDrawdown, performanceMetrics,
|
|
88
|
+
hurstExponent, simulateDCA,
|
|
89
|
+
} from '@stvy/fund-indicators';
|
|
90
|
+
|
|
91
|
+
// Your fund's historical NAV series (sorted by date ascending)
|
|
92
|
+
const nav = [1.0, 1.02, 1.01, 1.05, 1.03, 1.08, /* ... */];
|
|
93
|
+
|
|
94
|
+
// ── Technical Indicators ──
|
|
95
|
+
const ma20 = sma(nav, 20);
|
|
96
|
+
console.log('20-day MA:', ma20.current);
|
|
97
|
+
|
|
98
|
+
const m = macd(nav);
|
|
99
|
+
console.log('MACD DIF/DEA:', m.currentDIF, m.currentDEA);
|
|
100
|
+
|
|
101
|
+
const r = rsi(nav, 14);
|
|
102
|
+
console.log('RSI(14):', r.current);
|
|
103
|
+
|
|
104
|
+
const k = kdj(nav);
|
|
105
|
+
console.log('KDJ:', k.currentK, k.currentD, k.currentJ);
|
|
106
|
+
|
|
107
|
+
// ── Risk & Performance ──
|
|
108
|
+
console.log('Sharpe Ratio:', sharpeRatio(nav, 0.025));
|
|
109
|
+
console.log('Max Drawdown:', maxDrawdown(nav).maxDrawdown);
|
|
110
|
+
|
|
111
|
+
const perf = performanceMetrics(nav);
|
|
112
|
+
console.log('Annualized Return:', perf.annualizedReturn);
|
|
113
|
+
console.log('Sortino Ratio:', perf.sortinoRatio);
|
|
114
|
+
|
|
115
|
+
// ── Statistical Features ──
|
|
116
|
+
const hurst = hurstExponent(nav);
|
|
117
|
+
console.log('Hurst Exponent:', hurst.hurstExponent, '→', hurst.interpretation);
|
|
118
|
+
|
|
119
|
+
// ── DCA Simulation ──
|
|
120
|
+
const dca = simulateDCA(nav, { amount: 1000, interval: 22 });
|
|
121
|
+
console.log('DCA Return Rate:', dca.returnRate, 'IRR:', dca.irr);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Module Documentation
|
|
127
|
+
|
|
128
|
+
### 1. Technical Indicators Module (`technical`)
|
|
129
|
+
|
|
130
|
+
Compute trend, momentum, oscillator, and channel indicators from historical NAV data.
|
|
131
|
+
|
|
132
|
+
#### Moving Averages
|
|
133
|
+
|
|
134
|
+
| Function | Description | Parameters |
|
|
135
|
+
|----------|-------------|------------|
|
|
136
|
+
| `sma(nav, period)` | Simple Moving Average | `period` default 20 |
|
|
137
|
+
| `ema(nav, period)` | Exponential Moving Average | `period` default 20 |
|
|
138
|
+
| `wma(nav, period)` | Weighted Moving Average | `period` default 20 |
|
|
139
|
+
| `dema(nav, period)` | Double Exponential Moving Average | `period` default 20 |
|
|
140
|
+
| `tema(nav, period)` | Triple Exponential Moving Average | `period` default 20 |
|
|
141
|
+
| `kama(nav, period, fast?, slow?)` | Kaufman Adaptive Moving Average | `period` default 10 |
|
|
142
|
+
|
|
143
|
+
All moving average functions return `MAResult`:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface MAResult {
|
|
147
|
+
values: (number | null)[]; // Array same length as input; first N-1 entries are null
|
|
148
|
+
current: number | null; // Last valid value
|
|
149
|
+
period: number;
|
|
150
|
+
type: string;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Example — Moving average crossover detection:**
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const fast = ema(nav, 10);
|
|
158
|
+
const slow = ema(nav, 30);
|
|
159
|
+
const signal = detectCrossSignal(fast, slow);
|
|
160
|
+
|
|
161
|
+
if (signal.type === 'golden_cross') console.log('Golden Cross! Buy signal');
|
|
162
|
+
if (signal.type === 'death_cross') console.log('Death Cross! Sell signal');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Example — Moving average alignment:**
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const alignment = detectMAAlignment([
|
|
169
|
+
sma(nav, 5), sma(nav, 10), sma(nav, 20), sma(nav, 60),
|
|
170
|
+
]);
|
|
171
|
+
console.log(alignment.alignment); // 'bullish' | 'bearish' | 'neutral'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### MACD
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const result = macd(nav, 12, 26, 9);
|
|
178
|
+
// result.currentDIF — DIF line (fast line)
|
|
179
|
+
// result.currentDEA — DEA line (slow line)
|
|
180
|
+
// result.currentHistogram — Histogram (bar chart values)
|
|
181
|
+
// result.dif / .dea / .histogram — Full series arrays
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Momentum & Oscillator Indicators
|
|
185
|
+
|
|
186
|
+
| Function | Description | Typical Usage |
|
|
187
|
+
|----------|-------------|---------------|
|
|
188
|
+
| `rsi(nav, period?)` | RSI (Relative Strength Index) | >70 overbought, <30 oversold |
|
|
189
|
+
| `kdj(nav, kPeriod?, kSmooth?, dPeriod?)` | KDJ Stochastic (includes J value) | K/D golden/death cross |
|
|
190
|
+
| `roc(nav, period?)` | Rate of Change | Momentum strength |
|
|
191
|
+
| `momentum(nav, period?)` | Momentum Indicator | Trend acceleration/deceleration |
|
|
192
|
+
| `williamsR(nav, period?)` | Williams %R | Overbought/oversold |
|
|
193
|
+
| `cci(nav, period?)` | Commodity Channel Index | >100 / <-100 |
|
|
194
|
+
| `trix(nav, period?)` | Triple-Smoothed Exponential | Noise-filtered trend |
|
|
195
|
+
| `dpo(nav, period?)` | Detrended Price Oscillator | Cycle identification |
|
|
196
|
+
| `stochasticRSI(nav, ...)` | Stochastic RSI | More sensitive overbought/oversold |
|
|
197
|
+
| `massIndex(nav, emaPeriod?, sumPeriod?)` | Mass Index | Reversal point detection |
|
|
198
|
+
|
|
199
|
+
#### Channels & Volatility
|
|
200
|
+
|
|
201
|
+
| Function | Description |
|
|
202
|
+
|----------|-------------|
|
|
203
|
+
| `bollingerBands(nav, period?, stdDev?)` | Bollinger Bands (includes bandwidth and %B) |
|
|
204
|
+
| `donchianChannel(nav, period?)` | Donchian Channel |
|
|
205
|
+
| `keltnerChannel(nav, emaPeriod?, atrPeriod?, multiplier?)` | Keltner Channel |
|
|
206
|
+
| `adx(nav, period?)` | ADX (Average Directional Index) + DI+/DI- |
|
|
207
|
+
| `atr(nav, period?)` | ATR (Average True Range, approximated) |
|
|
208
|
+
| `sar(nav, step?, max?)` | Parabolic SAR |
|
|
209
|
+
|
|
210
|
+
#### Derived Indicators
|
|
211
|
+
|
|
212
|
+
| Function | Description |
|
|
213
|
+
|----------|-------------|
|
|
214
|
+
| `bias(nav, period?)` | BIAS = (NAV - MA) / MA × 100 |
|
|
215
|
+
| `navPercentile(nav, lookback?)` | NAV percentile within historical range (0–100) |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
### 2. Risk & Performance Module (`risk`)
|
|
220
|
+
|
|
221
|
+
#### Basic Utilities
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import { navToReturns, totalReturn, annualizeReturn } from '@stvy/fund-indicators';
|
|
225
|
+
|
|
226
|
+
const returns = navToReturns(nav); // NAV → daily returns
|
|
227
|
+
const tr = totalReturn(nav); // Cumulative return
|
|
228
|
+
const ar = annualizeReturn(returns); // Annualized return (geometric mean)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Volatility
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { annualizedVolatility, downsideVolatility, rollingVolatility, volatilityCone } from '@stvy/fund-indicators';
|
|
235
|
+
|
|
236
|
+
annualizedVolatility(returns); // Annualized volatility
|
|
237
|
+
downsideVolatility(returns, 0.025); // Downside volatility
|
|
238
|
+
rollingVolatility(returns, 20); // Rolling 20-day volatility
|
|
239
|
+
volatilityCone(returns, [5,10,20,60,120]); // Volatility cone
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
#### Drawdown Analysis
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const dd = maxDrawdown(nav, dates?);
|
|
246
|
+
|
|
247
|
+
// dd.maxDrawdown — Maximum drawdown (negative value, e.g. -0.25)
|
|
248
|
+
// dd.peakIndex — Index of the drawdown peak (start)
|
|
249
|
+
// dd.troughIndex — Index of the drawdown trough (bottom)
|
|
250
|
+
// dd.durationDays — Duration of the drawdown in days
|
|
251
|
+
// dd.recoveryDays — Recovery days (null if not yet recovered)
|
|
252
|
+
// dd.drawdownSeries — Full drawdown series
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### VaR / CVaR
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
calculateVaR(returns, 0.95, 'historical'); // 95% Historical Simulation VaR
|
|
259
|
+
calculateVaR(returns, 0.99, 'parametric'); // 99% Parametric VaR
|
|
260
|
+
calculateCVaR(returns, 0.95); // 95% Conditional Value at Risk
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### Risk Metrics Summary
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
const risk = riskMetrics(nav, 0.025);
|
|
267
|
+
// risk.annualizedVolatility — Annualized volatility
|
|
268
|
+
// risk.downsideVolatility — Downside volatility
|
|
269
|
+
// risk.maxDrawdown — Maximum drawdown
|
|
270
|
+
// risk.maxDrawdownDuration — Max drawdown duration in days
|
|
271
|
+
// risk.var95 / risk.var99 — VaR
|
|
272
|
+
// risk.cvar95 / risk.cvar99 — CVaR
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### Performance Metrics
|
|
276
|
+
|
|
277
|
+
| Function | Formula |
|
|
278
|
+
|----------|---------|
|
|
279
|
+
| `sharpeRatio(nav, rf?)` | (Annualized Return - Risk-Free Rate) / Annualized Volatility |
|
|
280
|
+
| `sortinoRatio(nav, rf?)` | (Annualized Return - Risk-Free Rate) / Downside Volatility |
|
|
281
|
+
| `calmarRatio(nav)` | Annualized Return / \|Max Drawdown\| |
|
|
282
|
+
| `treynorRatio(nav, benchmark, rf?)` | (Annualized Return - Risk-Free Rate) / Beta |
|
|
283
|
+
| `omegaRatio(nav, threshold?)` | Weighted Upside Return / Weighted Downside Loss |
|
|
284
|
+
| `winRate(returns)` | Positive-return days / Total days |
|
|
285
|
+
| `profitLossRatio(returns)` | Average Gain / Average Loss |
|
|
286
|
+
| `profitFactor(returns)` | Total Gains / Total Losses |
|
|
287
|
+
| `consecutiveWinLoss(returns)` | Max consecutive winning/losing days |
|
|
288
|
+
|
|
289
|
+
**Get all performance metrics at once:**
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
const perf = performanceMetrics(nav, 0.025);
|
|
293
|
+
// perf.totalReturn / .annualizedReturn
|
|
294
|
+
// perf.sharpeRatio / .sortinoRatio / .calmarRatio
|
|
295
|
+
// perf.omegaRatio / .winRate / .profitLossRatio / .profitFactor
|
|
296
|
+
// perf.maxConsecutiveWins / .maxConsecutiveLosses
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Benchmark Comparison Metrics
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const bm = benchmarkMetrics(fundNav, benchmarkNav, 0.025);
|
|
303
|
+
// bm.alpha — Alpha (excess return)
|
|
304
|
+
// bm.beta — Beta coefficient
|
|
305
|
+
// bm.trackingError — Tracking error (annualized)
|
|
306
|
+
// bm.informationRatio — Information ratio
|
|
307
|
+
// bm.correlation — Correlation coefficient
|
|
308
|
+
// bm.rSquared — R²
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
### 3. Statistical Features Module (`statistics`)
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import {
|
|
317
|
+
statisticalFeatures, hurstExponent, autocorrelation,
|
|
318
|
+
ljungBoxTest, garch11, returnQuantiles,
|
|
319
|
+
rollingSkewness, rollingKurtosis,
|
|
320
|
+
} from '@stvy/fund-indicators';
|
|
321
|
+
|
|
322
|
+
// Full statistical feature set
|
|
323
|
+
const stats = statisticalFeatures(nav);
|
|
324
|
+
// stats.mean / .median / .stdDev
|
|
325
|
+
// stats.skewness / .kurtosis
|
|
326
|
+
// stats.jarqueBera — Normality test
|
|
327
|
+
|
|
328
|
+
// Hurst Exponent
|
|
329
|
+
const h = hurstExponent(nav);
|
|
330
|
+
// h.hurstExponent — >0.5 trending / <0.5 mean-reverting / ≈0.5 random
|
|
331
|
+
// h.interpretation — 'trending' | 'mean_reverting' | 'random_walk'
|
|
332
|
+
|
|
333
|
+
// Autocorrelation analysis
|
|
334
|
+
const acf = autocorrelation(nav, 20);
|
|
335
|
+
// acf.coefficients — Autocorrelation coefficients at each lag
|
|
336
|
+
// acf.interpretation — 'persistent' | 'anti_persistent' | 'no_correlation'
|
|
337
|
+
|
|
338
|
+
// Ljung-Box test
|
|
339
|
+
const lb = ljungBoxTest(returns, 10);
|
|
340
|
+
// lb.qStatistic / lb.approximatePValue
|
|
341
|
+
|
|
342
|
+
// GARCH(1,1) volatility forecast
|
|
343
|
+
const g = garch11(returns);
|
|
344
|
+
// g.nextPeriodForecast — Next-period conditional variance forecast
|
|
345
|
+
// g.omega / g.alpha / g.beta — GARCH parameters
|
|
346
|
+
|
|
347
|
+
// Return quantiles
|
|
348
|
+
const quantiles = returnQuantiles(nav, [0.01, 0.05, 0.5, 0.95, 0.99]);
|
|
349
|
+
|
|
350
|
+
// Rolling skewness / kurtosis
|
|
351
|
+
const skew = rollingSkewness(returns, 60);
|
|
352
|
+
const kurt = rollingKurtosis(returns, 60);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
### 4. DCA & PnL Analysis Module (`dca`)
|
|
358
|
+
|
|
359
|
+
#### DCA Simulation
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
const dca = simulateDCA(nav, {
|
|
363
|
+
amount: 1000, // Investment amount per period
|
|
364
|
+
interval: 22, // DCA interval (trading days), 22 ≈ monthly
|
|
365
|
+
startIndex: 0, // Starting index
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// dca.totalInvestments — Number of investments made
|
|
369
|
+
// dca.totalInvested — Total amount invested
|
|
370
|
+
// dca.currentValue — Current market value
|
|
371
|
+
// dca.averageCost — Average holding cost
|
|
372
|
+
// dca.returnRate — Return rate
|
|
373
|
+
// dca.irr — Internal Rate of Return (annualized)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
#### Take-Profit & Stop-Loss
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
// Fixed take-profit / stop-loss
|
|
380
|
+
const signal = takeProfitStopLoss(nav, costPrice, 0.30, -0.15);
|
|
381
|
+
// signal.takeProfitTriggered — Whether the 30% take-profit was triggered
|
|
382
|
+
// signal.stopLossTriggered — Whether the 15% stop-loss was triggered
|
|
383
|
+
|
|
384
|
+
// Trailing stop (triggered when price drops N% from peak)
|
|
385
|
+
const trail = trailingStop(nav, 0.10, costPrice);
|
|
386
|
+
// trail.triggered — Whether triggered
|
|
387
|
+
// trail.drawdownFromPeak — Drawdown from peak
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
#### Smart DCA
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
// Dynamically adjust investment amount based on NAV relative to moving average
|
|
394
|
+
const multiplier = smartDCAMultiplier(nav, 250, 2.0, 0.5);
|
|
395
|
+
// > 1 when below MA (invest more), < 1 when above MA (invest less)
|
|
396
|
+
|
|
397
|
+
// Tiered buy / sell signals
|
|
398
|
+
const buyLevel = tieredBuySignal(nav, [0.3, 0.2, 0.1]);
|
|
399
|
+
const sellLevel = tieredSellSignal(nav, [0.7, 0.8, 0.9]);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Utility Functions
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
safetyMargin(nav); // Drawdown from all-time high
|
|
406
|
+
pricePosition(nav); // Current price position between historical high and low (0–1)
|
|
407
|
+
positionPnL(nav, buyIdx, sellIdx?, amount?); // Single-position PnL details
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
### 5. Pattern Recognition Module (`pattern`)
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import {
|
|
416
|
+
supportResistance, doubleBottomTop,
|
|
417
|
+
detectGaps, trendStrength, headAndShoulders,
|
|
418
|
+
} from '@stvy/fund-indicators';
|
|
419
|
+
|
|
420
|
+
// Support / Resistance levels
|
|
421
|
+
const sr = supportResistance(nav, 0.02, 3);
|
|
422
|
+
// sr.supports[0].level / .strength / .touches
|
|
423
|
+
// sr.resistances[0].level / .strength / .touches
|
|
424
|
+
|
|
425
|
+
// Double Bottom (W-bottom) / Double Top (M-top)
|
|
426
|
+
const dbt = doubleBottomTop(nav, 60, 0.03, 10);
|
|
427
|
+
// dbt.type — 'double_bottom' | 'double_top' | 'none'
|
|
428
|
+
// dbt.breakout — Whether neckline breakout occurred
|
|
429
|
+
// dbt.confidence — Signal strength 0–1
|
|
430
|
+
|
|
431
|
+
// Gap detection
|
|
432
|
+
const gaps = detectGaps(nav, 0.02);
|
|
433
|
+
// gaps[i].type — 'gap_up' | 'gap_down'
|
|
434
|
+
// gaps[i].gapSize — Gap size
|
|
435
|
+
// gaps[i].filled — Whether the gap has been filled
|
|
436
|
+
|
|
437
|
+
// Trend strength score
|
|
438
|
+
const score = trendStrength(nav, 20); // 0–100
|
|
439
|
+
|
|
440
|
+
// Head and Shoulders pattern
|
|
441
|
+
const hs = headAndShoulders(nav, 90);
|
|
442
|
+
// hs.type — 'head_and_shoulders_top' | 'head_and_shoulders_bottom' | 'none'
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## Full API Reference
|
|
448
|
+
|
|
449
|
+
| Module | Functions | Input | Output |
|
|
450
|
+
|--------|-----------|-------|--------|
|
|
451
|
+
| **Moving Averages** | `sma` `ema` `wma` `dema` `tema` `kama` | `nav, period` | `MAResult` |
|
|
452
|
+
| **Trend** | `macd` | `nav, fast?, slow?, signal?` | `MACDResult` |
|
|
453
|
+
| **Momentum** | `rsi` `kdj` `roc` `momentum` `williamsR` `cci` `trix` `dpo` `stochasticRSI` `massIndex` | `nav, period?` | `MAResult` / `KDJResult` |
|
|
454
|
+
| **Channels** | `bollingerBands` `donchianChannel` `keltnerChannel` | `nav, ...params` | `BollingerResult` / `ChannelResult` |
|
|
455
|
+
| **Trend Strength** | `adx` `atr` `sar` | `nav, period?` | `ADXResult` / `MAResult` / `SARResult` |
|
|
456
|
+
| **Derived** | `bias` `navPercentile` `detectCrossSignal` `detectMAAlignment` | `nav, ...` | Varies |
|
|
457
|
+
| **Volatility** | `annualizedVolatility` `downsideVolatility` `rollingVolatility` `volatilityCone` | `returns` | `number` / `array` |
|
|
458
|
+
| **Drawdown** | `maxDrawdown` `maxDrawdownDuration` | `nav, dates?` | `DrawdownResult` / `number` |
|
|
459
|
+
| **VaR** | `calculateVaR` `calculateCVaR` | `returns, confidence` | `number` |
|
|
460
|
+
| **Risk Summary** | `riskMetrics` | `nav, rf?` | `RiskMetrics` |
|
|
461
|
+
| **Performance** | `sharpeRatio` `sortinoRatio` `calmarRatio` `treynorRatio` `omegaRatio` `winRate` `profitLossRatio` `profitFactor` `consecutiveWinLoss` `performanceMetrics` | `nav` / `returns` | `number` / `PerformanceMetrics` |
|
|
462
|
+
| **Benchmark** | `calculateBeta` `calculateAlpha` `trackingError` `informationRatio` `benchmarkMetrics` | `nav, benchmark` | `number` / `BenchmarkMetrics` |
|
|
463
|
+
| **Statistics** | `statisticalFeatures` `navStatisticalFeatures` | `nav` | `StatisticalFeatures` |
|
|
464
|
+
| **Hurst** | `hurstExponent` | `nav, ...` | `HurstResult` |
|
|
465
|
+
| **Autocorrelation** | `autocorrelation` `ljungBoxTest` | `nav, maxLag` | `AutocorrelationResult` |
|
|
466
|
+
| **GARCH** | `garch11` | `returns` | `{ conditionalVariance, nextPeriodForecast, ... }` |
|
|
467
|
+
| **Distribution** | `returnQuantiles` `rollingSkewness` `rollingKurtosis` | `nav` / `returns` | `Map` / `array` |
|
|
468
|
+
| **DCA** | `simulateDCA` | `nav, config` | `DCAResult` |
|
|
469
|
+
| **Take-Profit / Stop-Loss** | `takeProfitStopLoss` `trailingStop` | `nav, cost, ...` | `TakeProfitStopLossSignal` |
|
|
470
|
+
| **Smart DCA** | `smartDCAMultiplier` `tieredBuySignal` `tieredSellSignal` | `nav, ...` | `number` |
|
|
471
|
+
| **Utilities** | `safetyMargin` `pricePosition` `positionPnL` | `nav, ...` | `number` / `object` |
|
|
472
|
+
| **Patterns** | `supportResistance` `doubleBottomTop` `detectGaps` `trendStrength` `headAndShoulders` | `nav, ...` | Varies |
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Data Requirements
|
|
477
|
+
|
|
478
|
+
This library only requires a **historical daily NAV series** — no volume, holdings, or fund flow data is needed.
|
|
479
|
+
|
|
480
|
+
Input format:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
// NAV array sorted by date ascending
|
|
484
|
+
const nav: number[] = [1.0000, 1.0032, 0.9985, 1.0120, ...];
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Important notes:
|
|
488
|
+
|
|
489
|
+
- The NAV series should be sorted in chronological order (earliest first)
|
|
490
|
+
- A minimum of 60 data points is recommended; some indicators (e.g. Hurst Exponent, GARCH) work best with 250+
|
|
491
|
+
- Dividend distributions cause abrupt NAV drops — use **adjusted NAV** (cumulative NAV or dividend-reinvested NAV) for accurate results
|
|
492
|
+
- Annualization parameters are based on **242 A-share trading days**; modify the `TRADING_DAYS_PER_YEAR` constant to adapt to other markets
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## Development Guide
|
|
497
|
+
|
|
498
|
+
### Prerequisites
|
|
499
|
+
|
|
500
|
+
- Node.js >= 18
|
|
501
|
+
- npm >= 9
|
|
502
|
+
|
|
503
|
+
### Local Development
|
|
504
|
+
|
|
505
|
+
```bash
|
|
506
|
+
# Clone the repository
|
|
507
|
+
git clone https://github.com/stevieyu/fund-indicators.git
|
|
508
|
+
cd fund-indicators
|
|
509
|
+
|
|
510
|
+
# Install dependencies
|
|
511
|
+
npm install
|
|
512
|
+
|
|
513
|
+
# Run tests
|
|
514
|
+
npm test
|
|
515
|
+
|
|
516
|
+
# Compile TypeScript
|
|
517
|
+
npm run build
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Project Structure
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
fund-indicators/
|
|
524
|
+
├── src/
|
|
525
|
+
│ ├── index.ts # Unified export entry point
|
|
526
|
+
│ ├── types.ts # Full type definitions (30+ interfaces)
|
|
527
|
+
│ ├── technical.ts # Technical indicators (MA/MACD/RSI/KDJ/Bollinger/Channels)
|
|
528
|
+
│ ├── risk.ts # Risk & performance (Volatility/Drawdown/VaR/Sharpe/Alpha/Beta)
|
|
529
|
+
│ ├── statistics.ts # Statistical features (Skewness/Kurtosis/Hurst/GARCH/Autocorrelation)
|
|
530
|
+
│ ├── dca.ts # DCA analysis (Simulation/IRR/Smart DCA/Take-Profit & Stop-Loss)
|
|
531
|
+
│ ├── pattern.ts # Pattern recognition (Support/Resistance/Double Bottom-Top/H&S/Gaps)
|
|
532
|
+
│ └── jstat.d.ts # jstat type declarations
|
|
533
|
+
├── scripts/
|
|
534
|
+
│ └── build-browser.mjs # Browser build script (esbuild)
|
|
535
|
+
├── test/
|
|
536
|
+
│ └── index.test.ts # 35 validation tests
|
|
537
|
+
├── dist/
|
|
538
|
+
│ ├── *.js / *.d.ts # Node.js build artifacts (CommonJS + type declarations)
|
|
539
|
+
│ └── browser/ # Browser build artifacts (IIFE + ESM)
|
|
540
|
+
├── .github/
|
|
541
|
+
│ └── workflows/
|
|
542
|
+
│ ├── ci.yml # CI pipeline (Node LTS)
|
|
543
|
+
│ └── publish.yml # Auto-publish to npm on release
|
|
544
|
+
├── package.json
|
|
545
|
+
├── tsconfig.json
|
|
546
|
+
├── .gitignore
|
|
547
|
+
├── LICENSE
|
|
548
|
+
└── README.md
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## Dependencies
|
|
554
|
+
|
|
555
|
+
| Dependency | Purpose | Size |
|
|
556
|
+
|------------|---------|------|
|
|
557
|
+
| [technicalindicators](https://github.com/anandanand84/technicalindicators) | Classic technical indicator calculations (MA/MACD/RSI/Bollinger Bands, etc.) | ~150KB |
|
|
558
|
+
| [simple-statistics](https://github.com/simple-statistics/simple-statistics) | Statistical functions (std dev, skewness, kurtosis, regression, quantiles, etc.) | ~50KB |
|
|
559
|
+
| [jstat](https://github.com/jstat/jstat) | Probability distribution functions (normal, chi-squared, t-distribution; used for VaR) | ~30KB |
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## Contributing
|
|
564
|
+
|
|
565
|
+
Contributions are welcome! Please follow these steps:
|
|
566
|
+
|
|
567
|
+
1. Fork the repository
|
|
568
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
569
|
+
3. Write code and corresponding tests
|
|
570
|
+
4. Make sure tests pass (`npm test`) and TypeScript compiles without errors (`npm run build`)
|
|
571
|
+
5. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
572
|
+
6. Push to the branch (`git push origin feature/amazing-feature`)
|
|
573
|
+
7. Open a Pull Request
|
|
574
|
+
|
|
575
|
+
### Commit Convention
|
|
576
|
+
|
|
577
|
+
This project follows the [Conventional Commits](https://www.conventionalcommits.org/) specification:
|
|
578
|
+
|
|
579
|
+
- `feat:` New features
|
|
580
|
+
- `fix:` Bug fixes
|
|
581
|
+
- `docs:` Documentation changes
|
|
582
|
+
- `test:` Test-related changes
|
|
583
|
+
- `refactor:` Code refactoring
|
|
584
|
+
- `perf:` Performance improvements
|
|
585
|
+
- `chore:` Build/tooling changes
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
589
|
+
## FAQ
|
|
590
|
+
|
|
591
|
+
**Q: Fund NAV data has no volume — can I still use indicators like ATR and OBV?**
|
|
592
|
+
|
|
593
|
+
A: Yes, partially. Since fund NAV has no intraday high/low, this library sets high, low, and close all to the daily NAV. ATR degenerates to EMA(|ΔNAV|), which still serves as a useful measure of daily volatility magnitude. OBV, however, is meaningless without volume data and is not recommended.
|
|
594
|
+
|
|
595
|
+
**Q: Why is the annualization parameter set to 242?**
|
|
596
|
+
|
|
597
|
+
A: The A-share market has approximately 242 trading days per year. If you are analyzing Hong Kong stocks (~250 days) or US stocks (~252 days), you can fork the repository and modify the `TRADING_DAYS_PER_YEAR` constant.
|
|
598
|
+
|
|
599
|
+
**Q: How should I handle NAV drops caused by dividend distributions?**
|
|
600
|
+
|
|
601
|
+
A: Use **adjusted NAV** (cumulative NAV or dividend-reinvested NAV) as input. If you use unit NAV, dividend distributions will cause sudden drops that distort indicator calculations. The library's `detectGaps` function can help identify such anomalous jumps.
|
|
602
|
+
|
|
603
|
+
**Q: Why are my Hurst Exponent results unstable?**
|
|
604
|
+
|
|
605
|
+
A: The Hurst Exponent's R/S analysis is sensitive to data length. At least 500 data points are recommended. With short datasets, R² tends to be low and results are unreliable. You can assess result reliability via `hurst.rSquared` (R² > 0.9 is considered good).
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## License
|
|
610
|
+
|
|
611
|
+
[MIT](LICENSE)
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
## Acknowledgments
|
|
616
|
+
|
|
617
|
+
Technical indicator calculations in this project are powered by [technicalindicators](https://github.com/anandanand84/technicalindicators), statistical analysis by [simple-statistics](https://github.com/simple-statistics/simple-statistics), and probability distribution computations by [jstat](https://github.com/jstat/jstat). Our thanks to these outstanding open-source projects.
|