@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,73 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
name: Build & Publish
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v6
|
|
18
|
+
|
|
19
|
+
- name: Setup pnpm
|
|
20
|
+
uses: pnpm/action-setup@v6
|
|
21
|
+
|
|
22
|
+
- name: Setup Node.js (LTS)
|
|
23
|
+
uses: actions/setup-node@v6
|
|
24
|
+
with:
|
|
25
|
+
node-version: lts/*
|
|
26
|
+
registry-url: "https://registry.npmjs.org"
|
|
27
|
+
cache: "pnpm"
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: pnpm install --frozen-lockfile
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: pnpm test
|
|
34
|
+
|
|
35
|
+
- name: TypeScript type check
|
|
36
|
+
run: pnpm exec tsc --noEmit
|
|
37
|
+
|
|
38
|
+
- name: Build (Node + Browser)
|
|
39
|
+
run: pnpm run build
|
|
40
|
+
|
|
41
|
+
- name: Verify build output
|
|
42
|
+
run: |
|
|
43
|
+
test -f dist/index.mjs || (echo "Missing dist/index.mjs" && exit 1)
|
|
44
|
+
test -f dist/index.cjs || (echo "Missing dist/index.cjs" && exit 1)
|
|
45
|
+
test -f dist/index.d.cts || (echo "Missing dist/index.d.cts" && exit 1)
|
|
46
|
+
echo "All build artifacts verified."
|
|
47
|
+
|
|
48
|
+
- name: Update package version from release tag
|
|
49
|
+
uses: BellCubeDev/update-package-version-by-release-tag@v2
|
|
50
|
+
|
|
51
|
+
- name: Verify version matches release tag
|
|
52
|
+
run: |
|
|
53
|
+
PKG_VERSION=$(node -p "require('./package.json').version")
|
|
54
|
+
TAG_VERSION=${GITHUB_REF_NAME#v}
|
|
55
|
+
echo "Package version: $PKG_VERSION"
|
|
56
|
+
echo "Release tag: $TAG_VERSION"
|
|
57
|
+
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
|
|
58
|
+
echo "::error::package.json version ($PKG_VERSION) does not match release tag ($TAG_VERSION)"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
- name: Publish to npm
|
|
63
|
+
run: pnpm publish --access public --no-git-checks
|
|
64
|
+
env:
|
|
65
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
66
|
+
|
|
67
|
+
- name: Summary
|
|
68
|
+
run: |
|
|
69
|
+
echo "## Published to npm" >> $GITHUB_STEP_SUMMARY
|
|
70
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
71
|
+
echo "- Package: \`@stvy/fund-indicators@${GITHUB_REF_NAME#v}\`" >> $GITHUB_STEP_SUMMARY
|
|
72
|
+
echo "- Registry: https://www.npmjs.com/package/@stvy/fund-indicators" >> $GITHUB_STEP_SUMMARY
|
|
73
|
+
echo "- Includes browser bundles ESM" >> $GITHUB_STEP_SUMMARY
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# AGENTS.md — @stvy/fund-indicators
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**@stvy/fund-indicators** is a TypeScript library for computing quantitative fund NAV (Net Asset Value) analysis indicators. Unlike stock-oriented TA libraries, this project is purpose-built for **fund NAV series** — daily closing prices with no intraday high/low and no volume data. It covers five domains: technical indicators, risk/performance metrics, statistical features, DCA (Dollar-Cost Averaging) simulation, and chart pattern recognition.
|
|
6
|
+
|
|
7
|
+
- **Package name**: `@stvy/fund-indicators`
|
|
8
|
+
- **License**: MIT
|
|
9
|
+
- **Runtime**: Node.js >= 18
|
|
10
|
+
- **Language**: TypeScript (strict mode, target ES2020)
|
|
11
|
+
- **Main entry**: `dist/index.js` (CommonJS)
|
|
12
|
+
- **Browser entry**: `dist/browser/fund-indicators.min.js` (IIFE) / `dist/browser/fund-indicators.esm.js` (ESM)
|
|
13
|
+
- **Types**: `dist/index.d.ts`
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
The source lives in `src/` with 5 feature modules, 1 type-definition file, 1 barrel export, and 1 custom type declaration:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
index.ts — Barrel re-export of all public functions and types
|
|
24
|
+
types.ts — 30+ interfaces and type aliases
|
|
25
|
+
technical.ts — Trend, momentum, oscillator, and channel indicators
|
|
26
|
+
risk.ts — Volatility, drawdown, VaR/CVaR, Sharpe, Alpha/Beta, etc.
|
|
27
|
+
statistics.ts — Skewness, kurtosis, Hurst exponent, GARCH, autocorrelation
|
|
28
|
+
dca.ts — DCA simulation, IRR, smart DCA, take-profit/stop-loss
|
|
29
|
+
pattern.ts — Support/resistance, double bottom/top, gaps, head-and-shoulders
|
|
30
|
+
jstat.d.ts — Custom ambient type declaration for the jstat package
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Module Breakdown
|
|
34
|
+
|
|
35
|
+
#### `technical.ts` — Technical Indicators
|
|
36
|
+
Moving averages (SMA, EMA, WMA, DEMA, TEMA, KAMA), MACD, RSI, KDJ, Bollinger Bands, Donchian Channel, Keltner Channel, ADX, ATR, CCI, ROC, Momentum, Williams %R, Stochastic RSI, SAR, TRIX, DPO, Mass Index, BIAS (deviation rate), NAV percentile, MA cross-signal detection, MA alignment detection.
|
|
37
|
+
|
|
38
|
+
Uses the `technicalindicators` npm package internally (aliased as `TI_*`). Indicators that need candle input (ADX, CCI, ATR, KDJ, SAR, Williams %R) use the `navToHLC()` helper to set `high = low = close = NAV`.
|
|
39
|
+
|
|
40
|
+
#### `risk.ts` — Risk and Performance Metrics
|
|
41
|
+
NAV-to-returns conversion (`navToReturns`), annualized return (geometric), total return, annualized/downside/rolling volatility, volatility cone, max drawdown (with recovery detection), max drawdown duration, VaR (historical and parametric), CVaR, Sharpe/Sortino/Calmar/Treynor/Omega ratios, win rate, profit/loss ratio, profit factor, consecutive win/loss streaks, Beta, Alpha, tracking error, information ratio, and aggregate `riskMetrics()` / `performanceMetrics()` / `benchmarkMetrics()` summaries.
|
|
42
|
+
|
|
43
|
+
#### `statistics.ts` — Statistical Features
|
|
44
|
+
Full statistical feature set (`statisticalFeatures`, `navStatisticalFeatures`), Hurst exponent via R/S analysis, autocorrelation (ACF), Ljung-Box test, return quantiles, rolling skewness/kurtosis, GARCH(1,1) volatility forecast.
|
|
45
|
+
|
|
46
|
+
#### `dca.ts` — DCA and P&L Analysis
|
|
47
|
+
DCA simulation (`simulateDCA`), IRR via Newton's method with bisection fallback, take-profit/stop-loss signals, trailing stop, safety margin, price position, smart DCA multiplier, tiered buy/sell signals, position P&L calculation.
|
|
48
|
+
|
|
49
|
+
#### `pattern.ts` — Pattern Recognition
|
|
50
|
+
Support/resistance levels (KDE-inspired local-extrema clustering), double bottom (W) / double top (M) detection, gap detection (up/down with fill tracking), trend strength scoring (0-100, multi-factor), head-and-shoulders top/bottom identification.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Key Conventions
|
|
55
|
+
|
|
56
|
+
### Input: `number[]` (NAV Series)
|
|
57
|
+
|
|
58
|
+
All functions take a `number[]` representing a time-ordered NAV series as their primary input. The `NavSeries` type alias is defined in `types.ts`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
export type NavSeries = number[];
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### No Volume, No Intraday High/Low
|
|
65
|
+
|
|
66
|
+
Fund NAV has a single daily value. For candle-based indicators that expect high/low/close, the `navToHLC()` helper in `technical.ts` sets all three to the same value:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
function navToHLC(nav: NavSeries) {
|
|
70
|
+
return nav.map((v) => ({ high: v, low: v, close: v }));
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Annualization: 242 Trading Days
|
|
75
|
+
|
|
76
|
+
The constant `TRADING_DAYS_PER_YEAR = 242` in `risk.ts` reflects the A-share (China) market convention. It is used for annualizing volatility, returns, and IRR calculations throughout the library.
|
|
77
|
+
|
|
78
|
+
### Output Arrays Are Padded with `null`
|
|
79
|
+
|
|
80
|
+
All array-returning indicators produce output arrays whose length matches the input NAV series. Leading positions (where the indicator has not yet "warmed up") are filled with `null`. This is done by the `padLeft` helper in `technical.ts`:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
function padLeft(arr: (number | undefined)[], totalLen: number): (number | null)[] {
|
|
84
|
+
const padLen = totalLen - arr.length;
|
|
85
|
+
const result: (number | null)[] = new Array(padLen).fill(null);
|
|
86
|
+
for (const v of arr) {
|
|
87
|
+
result.push(v ?? null);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Returns Calculation
|
|
94
|
+
|
|
95
|
+
Daily returns are computed via `navToReturns()` in `risk.ts`:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
export function navToReturns(nav: NavSeries): ReturnSeries {
|
|
99
|
+
const returns: ReturnSeries = [];
|
|
100
|
+
for (let i = 1; i < nav.length; i++) {
|
|
101
|
+
returns.push((nav[i] - nav[i - 1]) / nav[i - 1]);
|
|
102
|
+
}
|
|
103
|
+
return returns;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The output length is `nav.length - 1`.
|
|
108
|
+
|
|
109
|
+
### Types in `types.ts`
|
|
110
|
+
|
|
111
|
+
All public interfaces are defined in `src/types.ts` (30+ types). Key categories:
|
|
112
|
+
- **Basic**: `NavSeries`, `ReturnSeries`, `DateSeries`
|
|
113
|
+
- **Technical**: `MAResult`, `MACDResult`, `BollingerResult`, `ChannelResult`, `RSIResult`, `KDJResult`, `ADXResult`, `SARResult`, `MACrossSignal`, `MAAlignmentResult`
|
|
114
|
+
- **Risk/Performance**: `DrawdownResult`, `RiskMetrics`, `PerformanceMetrics`, `BenchmarkMetrics`
|
|
115
|
+
- **DCA**: `DCAConfig`, `DCAResult`, `TakeProfitStopLossSignal`
|
|
116
|
+
- **Statistics**: `StatisticalFeatures`, `HurstResult`, `AutocorrelationResult`
|
|
117
|
+
- **Pattern**: `SupportResistanceResult`, `DoubleBottomTopResult`, `GapResult`
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Build System
|
|
122
|
+
|
|
123
|
+
### Commands
|
|
124
|
+
|
|
125
|
+
| Command | Description |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `npm run build:node` | `tsc` -- Compiles `src/` to `dist/*.js` + `dist/*.d.ts` (CommonJS, ES2020 target) |
|
|
128
|
+
| `npm run build:browser` | `node scripts/build-browser.mjs` -- Bundles with esbuild to `dist/browser/` (IIFE as `FundIndicators` global + ESM) |
|
|
129
|
+
| `npm run build` | Runs both `build:node` and `build:browser` sequentially |
|
|
130
|
+
| `npm test` | `tsx test/index.test.ts` -- Runs all tests via tsx (no compilation step needed) |
|
|
131
|
+
| `npx tsc --noEmit` | Type-check only, useful during development |
|
|
132
|
+
|
|
133
|
+
### TypeScript Configuration (`tsconfig.json`)
|
|
134
|
+
|
|
135
|
+
- **target**: ES2020
|
|
136
|
+
- **module**: CommonJS
|
|
137
|
+
- **rootDir**: `./src`
|
|
138
|
+
- **outDir**: `./dist`
|
|
139
|
+
- **strict**: true
|
|
140
|
+
- **declaration**: true (generates `.d.ts` files)
|
|
141
|
+
- **declarationMap**: true
|
|
142
|
+
- **sourceMap**: true
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Dependencies
|
|
147
|
+
|
|
148
|
+
### Runtime Dependencies
|
|
149
|
+
|
|
150
|
+
| Package | Purpose |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `technicalindicators` ^3.1.0 | Classic TA library: SMA, EMA, WMA, MACD, RSI, Stochastic, BollingerBands, ADX, CCI, ROC, WilliamsR, StochasticRSI, ATR, PSAR, VWAP |
|
|
153
|
+
| `simple-statistics` ^7.8.0 | Statistical functions: mean, median, standardDeviation, sampleSkewness, sampleKurtosis, linearRegression, quantileSorted, sampleCovariance, variance, sampleCorrelation |
|
|
154
|
+
| `jstat` ^1.9.6 | Probability distributions: `jStat.normal.inv()` used for parametric VaR. **Has no `@types` package** -- a custom declaration file `src/jstat.d.ts` provides ambient types |
|
|
155
|
+
|
|
156
|
+
### Dev Dependencies
|
|
157
|
+
|
|
158
|
+
| Package | Purpose |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `typescript` ^5.3.0 | Compiler |
|
|
161
|
+
| `esbuild` ^0.28.0 | Browser bundling (IIFE + ESM) |
|
|
162
|
+
| `tsx` ^4.7.0 | TypeScript execution for running tests directly |
|
|
163
|
+
| `@types/node` ^20.0.0 | Node.js type definitions |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Testing Approach
|
|
168
|
+
|
|
169
|
+
### Single Test File
|
|
170
|
+
|
|
171
|
+
All tests live in `test/index.test.ts`. There is **no test framework** -- it uses a custom `assert()` function and manual pass/fail counting:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
let passed = 0;
|
|
175
|
+
let failed = 0;
|
|
176
|
+
|
|
177
|
+
function assert(condition: boolean, message: string) {
|
|
178
|
+
if (condition) {
|
|
179
|
+
passed++;
|
|
180
|
+
console.log(` [PASS] ${message}`);
|
|
181
|
+
} else {
|
|
182
|
+
failed++;
|
|
183
|
+
console.log(` [FAIL] ${message}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The process exits with code 1 if any test fails: `process.exit(failed > 0 ? 1 : 0)`.
|
|
189
|
+
|
|
190
|
+
### Mock NAV Data
|
|
191
|
+
|
|
192
|
+
Tests use synthetic NAV data generated with a **seeded pseudo-random number generator** (linear congruential generator, seed=42) and **Box-Muller transform** for normal distribution:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
function generateMockNav(days: number = 500, startNav: number = 1.0, seed: number = 42): number[]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Parameters: ~0.03% daily drift (annualized ~7%), ~1.5% daily volatility. A separate benchmark series uses seed=123.
|
|
199
|
+
|
|
200
|
+
### Test Coverage
|
|
201
|
+
|
|
202
|
+
The test file covers all 5 modules with ~35 assertions organized into sections:
|
|
203
|
+
- Moving averages (SMA, EMA, KAMA, DEMA)
|
|
204
|
+
- MACD, RSI, KDJ, Bollinger Bands
|
|
205
|
+
- Channels (Donchian, Keltner)
|
|
206
|
+
- Other technicals (ADX, CCI, ROC, Momentum, BIAS, TRIX, percentile)
|
|
207
|
+
- MA cross signals and alignment
|
|
208
|
+
- Risk metrics (volatility, drawdown, VaR, CVaR, risk summary)
|
|
209
|
+
- Performance metrics (Sharpe, Sortino, Calmar, Omega, win rate, profit factor)
|
|
210
|
+
- Benchmark comparison (Alpha, Beta, tracking error, information ratio)
|
|
211
|
+
- Statistical features, Hurst exponent, autocorrelation, Ljung-Box, GARCH
|
|
212
|
+
- DCA simulation, take-profit/stop-loss, trailing stop
|
|
213
|
+
- Smart DCA (safety margin, price position, multiplier, tiered signals)
|
|
214
|
+
- Pattern recognition (support/resistance, double bottom/top, gaps, trend strength, head-and-shoulders)
|
|
215
|
+
|
|
216
|
+
### Running Tests
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm test # Run all tests
|
|
220
|
+
npx tsx test/index.test.ts # Equivalent direct invocation
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Adding a New Indicator
|
|
226
|
+
|
|
227
|
+
Follow these steps when adding a new indicator to the library:
|
|
228
|
+
|
|
229
|
+
### 1. Add the Function to the Appropriate Module
|
|
230
|
+
|
|
231
|
+
Place it in the module that matches its domain:
|
|
232
|
+
- `src/technical.ts` -- Trend, momentum, oscillator, channel indicators
|
|
233
|
+
- `src/risk.ts` -- Risk, performance, and benchmark metrics
|
|
234
|
+
- `src/statistics.ts` -- Statistical features and tests
|
|
235
|
+
- `src/dca.ts` -- DCA simulation and P&L analysis
|
|
236
|
+
- `src/pattern.ts` -- Pattern recognition and detection
|
|
237
|
+
|
|
238
|
+
### 2. Add an Interface to `types.ts` (If Needed)
|
|
239
|
+
|
|
240
|
+
If the function returns a new result shape, define an interface in `src/types.ts`. Follow the existing naming convention: result interfaces use PascalCase with a descriptive suffix (e.g., `MAResult`, `HurstResult`, `GapResult`).
|
|
241
|
+
|
|
242
|
+
### 3. Export from `src/index.ts`
|
|
243
|
+
|
|
244
|
+
Add the function to the appropriate `export { ... } from './module'` block in `src/index.ts`. All public API surface goes through this barrel file.
|
|
245
|
+
|
|
246
|
+
### 4. Add a Test Case in `test/index.test.ts`
|
|
247
|
+
|
|
248
|
+
Add a test section (using `section('...')` for console output grouping) and assertions using the `assert()` helper. Call the new function with the mock NAV data and verify:
|
|
249
|
+
- Output array length matches input (if applicable)
|
|
250
|
+
- Values are in expected ranges
|
|
251
|
+
- Non-null results where expected
|
|
252
|
+
|
|
253
|
+
### 5. Verify
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npm test # All tests must pass
|
|
257
|
+
npx tsc --noEmit # Type-check without emitting
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Things to Watch Out For
|
|
263
|
+
|
|
264
|
+
### `padLeft` Alignment
|
|
265
|
+
|
|
266
|
+
The `padLeft` helper in `technical.ts` left-pads output arrays with `null` to match input length. When chaining indicators or manually combining results, always verify array alignment. Off-by-one errors here silently corrupt downstream calculations.
|
|
267
|
+
|
|
268
|
+
### KDJ Computation
|
|
269
|
+
|
|
270
|
+
KDJ is computed from `Stochastic` output for K and D, then J is calculated manually as `J = 3K - 2D`. The J array is derived element-wise, and any position where K or D is `null` must also produce `null` for J.
|
|
271
|
+
|
|
272
|
+
### DEMA and TEMA Nested EMA Offset Tracking
|
|
273
|
+
|
|
274
|
+
DEMA (`2*EMA - EMA(EMA)`) and TEMA (`3*EMA1 - 3*EMA2 + EMA3`) perform nested EMA calculations. Each successive EMA starts later than the previous one, so the code must carefully track array offsets when combining them into a full-length output array. Getting the offset wrong produces misaligned or `NaN` results.
|
|
275
|
+
|
|
276
|
+
### Hurst Exponent: R/S Analysis
|
|
277
|
+
|
|
278
|
+
The Hurst exponent implementation uses Rescaled Range (R/S) analysis:
|
|
279
|
+
1. Generate logarithmically-spaced window sizes
|
|
280
|
+
2. For each window, divide the return series into sub-periods
|
|
281
|
+
3. Compute the rescaled range `R/S` for each sub-period
|
|
282
|
+
4. Perform log-log linear regression; the slope is the Hurst exponent
|
|
283
|
+
|
|
284
|
+
Interpretation thresholds: `H > 0.55` = trending, `H < 0.45` = mean-reverting, else random walk. The result is clamped to [0, 1].
|
|
285
|
+
|
|
286
|
+
### GARCH(1,1): Simplified Moment Estimation
|
|
287
|
+
|
|
288
|
+
The GARCH(1,1) implementation uses **simplified moment estimation** (not maximum likelihood estimation). Parameters are estimated from the autocorrelation of squared returns. This is fast and adequate for approximate volatility forecasting, but not suitable for academic-grade inference.
|
|
289
|
+
|
|
290
|
+
For short series (< 30 observations), it falls back to unconditional variance with default parameters (`alpha=0.1`, `beta=0.8`).
|
|
291
|
+
|
|
292
|
+
### jstat Has No `@types` Package
|
|
293
|
+
|
|
294
|
+
The `jstat` package does not ship TypeScript types and has no `@types/jstat` on DefinitelyTyped. A custom ambient declaration file at `src/jstat.d.ts` declares the module with the specific functions used:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
declare module 'jstat' {
|
|
298
|
+
export const jStat: {
|
|
299
|
+
normal: { pdf, cdf, inv };
|
|
300
|
+
chisquare: { cdf, inv };
|
|
301
|
+
studentt: { cdf, inv };
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
If you need additional jstat functions, extend this declaration file rather than adding inline `any` casts.
|
|
307
|
+
|
|
308
|
+
### `TRADING_DAYS_PER_YEAR` Is Hardcoded
|
|
309
|
+
|
|
310
|
+
The constant `TRADING_DAYS_PER_YEAR = 242` is defined in `risk.ts` and used for all annualization. It is not configurable via function parameters. If you need a different market convention (e.g., 252 for US markets), you must modify this constant.
|
|
311
|
+
|
|
312
|
+
### Returns vs. NAV Input
|
|
313
|
+
|
|
314
|
+
Some functions take a NAV series (`number[]`) and internally convert to returns (e.g., `sharpeRatio`, `performanceMetrics`, `statisticalFeatures`). Others take a pre-computed returns series (e.g., `annualizedVolatility`, `calculateVaR`, `autocorrelation`). Check the function signature carefully -- passing the wrong type will produce silently incorrect results rather than a type error, since both are `number[]`.
|
|
315
|
+
|
|
316
|
+
### IRR Convergence
|
|
317
|
+
|
|
318
|
+
The IRR calculation in `dca.ts` uses Newton's method with a bisection fallback. It clamps the rate to `[-0.99, 10]` to prevent divergence. For unusual cash flow patterns (e.g., very few investments or extreme NAV changes), IRR may return `null` if neither method converges.
|
|
319
|
+
|
|
320
|
+
### No Test Framework
|
|
321
|
+
|
|
322
|
+
The test suite uses a custom runner, not Jest/Mocha/Vitest. There is no `describe`/`it` nesting, no snapshot testing, and no mocking infrastructure. Tests are simple imperative assertions against deterministic mock data. Adding parallel test execution or coverage tooling requires introducing a proper test framework.
|