garch 1.2.4 → 2.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 -21
- package/README.md +191 -26
- package/build/index.cjs +2039 -275
- package/build/index.mjs +2023 -276
- package/package.json +53 -53
- package/types.d.ts +341 -14
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Petr Tripolsky
|
|
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.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Petr Tripolsky
|
|
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.md
CHANGED
|
@@ -20,7 +20,9 @@ npm install garch
|
|
|
20
20
|
|
|
21
21
|
Forecast expected price range for the next candle (t+1). Auto-selects the best model (GARCH, EGARCH, GJR-GARCH, HAR-RV or NoVaS) by QLIKE forecast-error comparison.
|
|
22
22
|
|
|
23
|
-
Uses **log-normal price bands**: `P·exp(±z·σ)
|
|
23
|
+
Uses **log-normal price bands**: `P·exp(±z·σ)`. This correctly maps log-return volatility back to price space — the corridor is asymmetric (upside > downside in absolute terms) and `lowerPrice` can never go negative.
|
|
24
|
+
|
|
25
|
+
The multiplier `z` is **calibrated on the data itself**, not taken from a Gaussian table: the model's variance series is rescaled so standardized residuals have unit variance, then `z` blends the empirical |z| quantile of those residuals with the fitted Student-t(df) quantile (the empirical part dominates where the sample supports the tail, the t part takes over in the far tail). The actual multiplier used is returned as `zScore`.
|
|
24
26
|
|
|
25
27
|
```typescript
|
|
26
28
|
import { predict } from 'garch';
|
|
@@ -35,37 +37,33 @@ const result = predict(candles, '4h');
|
|
|
35
37
|
// sigma: 0.012, // 1.2% per-period volatility
|
|
36
38
|
// move: 1177, // upward expected move (upper - current), in dollars
|
|
37
39
|
// movePercent: 1.21, // upward expected move, in percent (0–100)
|
|
38
|
-
// upperPrice: 98677, // P·exp(
|
|
39
|
-
// lowerPrice: 96337, // P·exp(
|
|
40
|
+
// upperPrice: 98677, // P·exp(+z·σ) — ceiling
|
|
41
|
+
// lowerPrice: 96337, // P·exp(-z·σ) — floor
|
|
40
42
|
// modelType: 'egarch',
|
|
43
|
+
// df: 6.3, // fitted Student-t tail thickness
|
|
44
|
+
// zScore: 0.97, // corridor multiplier actually used
|
|
41
45
|
// reliable: true
|
|
42
46
|
// }
|
|
43
47
|
|
|
44
|
-
// 95% VaR band
|
|
48
|
+
// 95% VaR band
|
|
45
49
|
const var95 = predict(candles, '4h', undefined, 0.95);
|
|
46
50
|
|
|
47
51
|
// Custom reference price (e.g. VWAP)
|
|
48
52
|
const result = predict(candles, '4h', vwap);
|
|
49
53
|
```
|
|
50
54
|
|
|
51
|
-
**Confidence → z
|
|
52
|
-
|
|
53
|
-
| `confidence` | z-score | Meaning |
|
|
54
|
-
|-------------|---------|---------|
|
|
55
|
-
| 0.6827 (default) | 1.00 | ±1σ, ~68% of moves captured |
|
|
56
|
-
| 0.90 | 1.645 | Moderate VaR |
|
|
57
|
-
| 0.95 | 1.96 | 95% VaR (standard) |
|
|
58
|
-
| 0.99 | 2.576 | Conservative VaR |
|
|
55
|
+
**Confidence → z (Gaussian reference only — actual z is data-driven):**
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
| `confidence` | Gaussian z | Typical fat-tail z (df≈5) | Meaning |
|
|
58
|
+
|-------------|-----------|---------------------------|---------|
|
|
59
|
+
| 0.6827 (default) | 1.00 | ≈0.86 | ±1σ, ~68% of moves captured |
|
|
60
|
+
| 0.90 | 1.645 | ≈1.56 | Moderate VaR |
|
|
61
|
+
| 0.95 | 1.96 | ≈1.99 | 95% VaR (standard) |
|
|
62
|
+
| 0.99 | 2.576 | ≈3.12 | Conservative VaR |
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
Any value in (0, 1) is valid. With fat-tailed market data the Gaussian table is systematically wrong — too wide in the center, dangerously narrow in the tails — which is why the corridor uses the data-calibrated multiplier instead. Check `result.zScore` for the value actually applied.
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|-------------|---|-----------|-----------|----------------|
|
|
66
|
-
| 0.6827 | 1.00 | $98,677 | $96,337 | $2,340 |
|
|
67
|
-
| 0.95 | 1.96 | $99,808 | $95,222 | $4,586 |
|
|
68
|
-
| 0.99 | 2.58 | $100,545 | $94,520 | $6,025 |
|
|
66
|
+
Higher confidence = wider corridor. `sigma` stays the same (it's the model's volatility estimate), only the z-multiplier changes.
|
|
69
67
|
|
|
70
68
|
**When to use which:**
|
|
71
69
|
|
|
@@ -80,7 +78,7 @@ Higher confidence = wider corridor. `sigma` stays the same (it's the model's vol
|
|
|
80
78
|
| `candles` | `Candle[]` | required | OHLCV candle data |
|
|
81
79
|
| `interval` | `CandleInterval` | required | Candle timeframe |
|
|
82
80
|
| `currentPrice` | `number` | last close | Reference price to center the corridor |
|
|
83
|
-
| `confidence` | `number` | `0.6827` | Two-sided probability in (0,1). Controls band width via
|
|
81
|
+
| `confidence` | `number` | `0.6827` | Two-sided probability in (0,1). Controls band width via the data-calibrated `zScore` |
|
|
84
82
|
|
|
85
83
|
**Returns:** `PredictionResult`
|
|
86
84
|
|
|
@@ -93,6 +91,8 @@ interface PredictionResult {
|
|
|
93
91
|
upperPrice: number; // P · exp(+z·σ)
|
|
94
92
|
lowerPrice: number; // P · exp(-z·σ)
|
|
95
93
|
modelType: 'garch' | 'egarch' | 'gjr-garch' | 'har-rv' | 'novas'; // Auto-selected model
|
|
94
|
+
df: number; // Fitted Student-t degrees of freedom (tail thickness)
|
|
95
|
+
zScore: number; // Corridor multiplier actually used for the bands
|
|
96
96
|
reliable: boolean; // Quality flag (convergence + persistence + Ljung-Box)
|
|
97
97
|
}
|
|
98
98
|
```
|
|
@@ -140,7 +140,7 @@ const var95 = predictRange(candles, '4h', 5, undefined, 0.95);
|
|
|
140
140
|
|
|
141
141
|
Walk-forward validation of `predict`. Uses 75% of candles for fitting, 25% for testing. Checks if the model's price corridor captures actual price moves at the required hit rate.
|
|
142
142
|
|
|
143
|
-
`confidence` and `requiredPercent` are independent: `confidence` controls the **band width** (via `
|
|
143
|
+
`confidence` and `requiredPercent` are independent: `confidence` controls the **band width** (via the data-calibrated `zScore`), `requiredPercent` controls the **pass/fail threshold**.
|
|
144
144
|
|
|
145
145
|
```typescript
|
|
146
146
|
import { backtest } from 'garch';
|
|
@@ -164,6 +164,169 @@ backtest(candles, '4h', undefined, 50); // ±1σ band, hit rate >= 50%
|
|
|
164
164
|
|
|
165
165
|
---
|
|
166
166
|
|
|
167
|
+
### `backtestStats(candles, interval, confidence?)`
|
|
168
|
+
|
|
169
|
+
Same walk-forward loop as `backtest`, but returns the raw calibration numbers instead of a pass/fail flag. A well-calibrated setup has `hitRate ≈ confidence · 100` — run this on your own market data before trusting the corridor.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
import { backtestStats } from 'garch';
|
|
173
|
+
|
|
174
|
+
const stats = backtestStats(candles, '4h', 0.95);
|
|
175
|
+
// {
|
|
176
|
+
// hits: 61, total: 66, hitRate: 92.4,
|
|
177
|
+
// verdict: 'well-calibrated', // Kupiec test: is 92.4 vs 95 failure or noise?
|
|
178
|
+
// pValue: 0.38,
|
|
179
|
+
// message: 'Coverage 92.4% vs nominal 95.0% over 66 points is consistent with a calibrated corridor (Kupiec p=0.3800).'
|
|
180
|
+
// }
|
|
181
|
+
|
|
182
|
+
// Every test point refits all 5 models, so by default the test span is
|
|
183
|
+
// subsampled to at most ~100 refits. Control it explicitly:
|
|
184
|
+
backtestStats(candles, '4h', 0.95, { stride: 1 }); // every candle (slow)
|
|
185
|
+
backtestStats(candles, '4h', 0.95, { stride: 10 }); // every 10th candle
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Returns:** `BacktestStats`
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
interface BacktestStats {
|
|
192
|
+
hits: number; // closes that landed inside the corridor
|
|
193
|
+
total: number; // walk-forward predictions made
|
|
194
|
+
hitRate: number; // hits/total · 100 — compare with confidence · 100
|
|
195
|
+
verdict: 'well-calibrated' | 'too-narrow' | 'too-wide' | 'inconclusive';
|
|
196
|
+
pValue: number; // Kupiec POF test — probability the gap is noise
|
|
197
|
+
message: string; // plain-language interpretation with the numbers filled in
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`verdict` answers the question a raw hit rate cannot: 63% observed against 68%
|
|
202
|
+
nominal is *fine* on 100 points (noise) and *broken* on 2,000 points. `too-narrow`
|
|
203
|
+
means real risk exceeds what the corridor shows; `too-wide` means decisions based
|
|
204
|
+
on it are overly conservative.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### `checkData(candles, interval)`
|
|
209
|
+
|
|
210
|
+
Pre-flight check for a new data source — run it once before wiring a feed into
|
|
211
|
+
`predict`. Errors are conditions `predict` would throw on; warnings degrade
|
|
212
|
+
quality but do not block.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { checkData } from 'garch';
|
|
216
|
+
|
|
217
|
+
const report = checkData(candles, '1h');
|
|
218
|
+
// {
|
|
219
|
+
// ok: false,
|
|
220
|
+
// issues: [
|
|
221
|
+
// { code: 'UNSORTED', severity: 'error',
|
|
222
|
+
// message: 'Candles are not sorted by timestamp (index 812) — sort ascending before calling. Check your data feed.' },
|
|
223
|
+
// { code: 'DATA_GAPS', severity: 'warning',
|
|
224
|
+
// message: '~17 missing bars (2.1%) detected from timestamps — the seasonal profile and lag structure may be distorted. Check your feed for outages.' },
|
|
225
|
+
// ],
|
|
226
|
+
// recommendedCandles: 500,
|
|
227
|
+
// }
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Detects: too few candles, broken OHLC (with the failing index), unsorted or
|
|
231
|
+
duplicated timestamps, gaps, candle spacing that contradicts the `interval`
|
|
232
|
+
argument, and feeds where too many candles have `high === low`.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### `createPredictor(interval)`
|
|
237
|
+
|
|
238
|
+
Stateful predictor for rolling use (bots, backtest loops). Each call warm-starts
|
|
239
|
+
every optimizer from the previous window's optimum — same math, roughly 3× faster
|
|
240
|
+
than calling `predict` in a loop. State is per-instrument: one predictor per symbol.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { createPredictor } from 'garch';
|
|
244
|
+
|
|
245
|
+
const predictor = createPredictor('1h');
|
|
246
|
+
setInterval(async () => {
|
|
247
|
+
const candles = await fetchCandles('BTCUSDT', '1h', 500);
|
|
248
|
+
const res = predictor.predict(candles, { confidence: 0.9 });
|
|
249
|
+
// ...
|
|
250
|
+
}, 60 * 60 * 1000);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Warnings & errors
|
|
256
|
+
|
|
257
|
+
`predict` explains itself instead of failing silently. Every result carries
|
|
258
|
+
`warnings` (plain-language, with a `critical` flag — `reliable` is simply
|
|
259
|
+
"no critical warning fired"), `modelWeights` (which model families are driving
|
|
260
|
+
the forecast) and `seasonalityDetected`:
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const res = predict(candles, '1h', { confidence: 0.9 });
|
|
264
|
+
if (!res.reliable) {
|
|
265
|
+
for (const w of res.warnings) console.log(`[${w.code}] ${w.message}`);
|
|
266
|
+
// [RESIDUAL_AUTOCORRELATION] Squared residuals stay autocorrelated (Ljung-Box p=0.012) —
|
|
267
|
+
// the model did not fully capture volatility clustering; the corridor may understate risk.
|
|
268
|
+
}
|
|
269
|
+
console.log(res.modelWeights); // { garch: 0.58, 'har-rv': 0.42 }
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Errors are typed — catch by class, not by parsing messages:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import { NotEnoughDataError, BadDataError, InvalidArgumentError } from 'garch';
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
const res = predict(candles, '1h');
|
|
279
|
+
} catch (e) {
|
|
280
|
+
if (e instanceof NotEnoughDataError) await fetchMoreHistory();
|
|
281
|
+
else if (e instanceof BadDataError) alertDataPipeline(e.message); // broken OHLC, unsorted/duplicate timestamps
|
|
282
|
+
else if (e instanceof InvalidArgumentError) throw e; // bug at the call site
|
|
283
|
+
else throw e;
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Recipes
|
|
288
|
+
|
|
289
|
+
**Stop-loss / take-profit from the corridor.** `lowerPrice` at confidence 0.90 is
|
|
290
|
+
the level price stays above with ~95% probability (each tail carries 5%):
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
const res = predictor.predict(candles, { confidence: 0.9 });
|
|
294
|
+
if (res.reliable) {
|
|
295
|
+
placeOrder({ side: 'buy', stopLoss: res.lowerPrice, takeProfit: res.upperPrice });
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Position sizing by volatility.** Risk a fixed dollar amount per corridor width
|
|
300
|
+
instead of a fixed position size:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
const riskPerTrade = 100; // dollars you accept losing if price hits the lower band
|
|
304
|
+
const size = riskPerTrade / (res.currentPrice - res.lowerPrice);
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Volatility filter.** Skip entries when the model cannot be trusted or the
|
|
308
|
+
expected move is too small to cover fees:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
const feePct = 0.1; // round-trip, percent
|
|
312
|
+
if (!res.reliable || res.movePercent < feePct * 2) return; // stand aside
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Calibration monitoring.** Once a day, verify the corridor still tells the truth
|
|
316
|
+
on your instrument — and read the verdict, not the raw rate:
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
const stats = backtestStats(candles, '1h', 0.9);
|
|
320
|
+
if (stats.verdict === 'too-narrow') notify(stats.message); // real risk > shown risk
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Multi-candle horizon.** Corridor for "where will price be 12 hours from now"
|
|
324
|
+
on 1h candles:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const res = predictor.predictRange(candles, 12, { confidence: 0.95 });
|
|
328
|
+
```
|
|
329
|
+
|
|
167
330
|
## Supported Intervals
|
|
168
331
|
|
|
169
332
|
| Interval | Min Candles | Recommended | Periods/Year | Coverage |
|
|
@@ -179,12 +342,14 @@ backtest(candles, '4h', undefined, 50); // ±1σ band, hit rate >= 50%
|
|
|
179
342
|
| `6h` | 150 | 300 | 1,460 | ~37 days |
|
|
180
343
|
| `8h` | 150 | 300 | 1,095 | ~50 days |
|
|
181
344
|
|
|
182
|
-
For intervals below 1h, per-candle Parkinson RV is noisier — more data helps OLS and QLIKE model selection.
|
|
345
|
+
For intervals below 1h, per-candle Parkinson RV is noisier — more data helps OLS and QLIKE model selection. When the candle count is below the recommended value the result carries a non-critical `LOW_SAMPLE` warning. Always check `reliable: true` (and read `warnings`) in the output.
|
|
183
346
|
|
|
184
347
|
## Timeframes
|
|
185
348
|
|
|
186
349
|
The `periodsPerYear` value controls annualization of volatility. When using `predict`/`predictRange`/`backtest`, this is handled automatically via the `interval` parameter. When using `Garch`/`Egarch` classes directly, pass `periodsPerYear` manually.
|
|
187
350
|
|
|
351
|
+
> **Scale caveat for direct class usage:** with `Candle[]` input the model recursions are driven by range-based Parkinson RV, so `params.unconditionalVariance` / `params.annualizedVol` are on the *realized-variance* scale, which can differ from the close-to-close return variance when ranges and returns disagree (gaps, thin wicks). `predict`/`predictRange` correct this automatically (standardized residuals are rescaled to unit variance before the corridor is built); raw `calibrate*` output is not corrected.
|
|
352
|
+
|
|
188
353
|
| Timeframe | `periodsPerYear` | Notes |
|
|
189
354
|
|-----------|-----------------|-------|
|
|
190
355
|
| **1m** | `525,600` | 1440/day x 365 |
|
|
@@ -379,7 +544,7 @@ After D^2 optimization, weights are **rescaled via OLS** on the realized varianc
|
|
|
379
544
|
forecast_sigma_t^2 = beta_0 + beta_1 * sigma_t^2(D^2)
|
|
380
545
|
```
|
|
381
546
|
|
|
382
|
-
D^2 acts as a data-driven smoother over RV lags — more flexible than HAR-RV's
|
|
547
|
+
D^2 acts as a data-driven smoother over RV lags — more flexible than HAR-RV's three rolling means. OLS rescaling adjusts for bias with only 2 parameters, keeping the model robust on small samples with noisy per-candle RV. Inside `predict` the NoVaS lag order grows with the sample size (~n^(1/3)) instead of being fixed at 10.
|
|
383
548
|
|
|
384
549
|
Multi-step forecast: replace future innovations with sigma^2 (since E[RV] = E[X^2] = sigma^2), then rescale:
|
|
385
550
|
|
|
@@ -424,7 +589,7 @@ The library fits all five models on every call and picks the best by QLIKE. Each
|
|
|
424
589
|
|
|
425
590
|
- **GJR-GARCH** — Same idea as EGARCH (red candles increase vol more than green ones) but the effect is milder and simpler: when the return is negative, a bonus term `gamma * epsilon^2` is added to variance. When positive — nothing extra. A binary switch rather than a continuous asymmetry. Common in altcoins and less panic-prone markets.
|
|
426
591
|
|
|
427
|
-
- **HAR-RV** — Volatility has memory at multiple horizons. The model takes a single timeframe of candles and internally builds three scales: last candle's RV (short,
|
|
592
|
+
- **HAR-RV** — Volatility has memory at multiple horizons. The model takes a single timeframe of candles and internally builds three scales: last candle's RV (short), a medium rolling average, and a long rolling average, combined via OLS regression. The `HarRv` class defaults to the textbook lags (1, 5, 22 — day/week/month in *trading days*), but `predict` chooses the lag horizons from the candle interval itself (one bar / one day / one week in bars, capped by sample size) and picks the winner by QLIKE — the daily-equity convention is not assumed for 24/7 intraday markets. Works well when different types of participants (scalpers, swing traders, institutions) all influence the same market at different speeds.
|
|
428
593
|
|
|
429
594
|
- **NoVaS** — Volatility drifts or cycles without clear shock-and-decay patterns. Slow trend changes, regime shifts, compression/expansion phases, or "breathing" patterns that don't fit any parametric formula. Model-free: finds weights `a_0...a_p` that make the normalized series as close to Gaussian as possible (minimizes D^2 = skewness^2 + (kurtosis - 3)^2). No assumptions about the distribution shape.
|
|
430
595
|
|
|
@@ -455,7 +620,7 @@ upperPrice = P · exp(+z · sigma)
|
|
|
455
620
|
lowerPrice = P · exp(-z · sigma)
|
|
456
621
|
```
|
|
457
622
|
|
|
458
|
-
where `z
|
|
623
|
+
where `z` is the corridor multiplier for the desired two-sided confidence level (returned as `zScore`). It is calibrated on the data: the empirical quantile of the standardized residuals |r/σ| blended with the fitted Student-t(df) quantile, so it needs neither the Gaussian assumption nor a fixed z table. This produces **asymmetric** bands (upside > downside in absolute terms) and guarantees `lowerPrice > 0`.
|
|
459
624
|
|
|
460
625
|
The previous linear approximation `P · (1 ± sigma)` is a first-order Taylor expansion of `exp(±sigma)`. The difference grows with sigma:
|
|
461
626
|
|
|
@@ -467,7 +632,7 @@ The previous linear approximation `P · (1 ± sigma)` is a first-order Taylor ex
|
|
|
467
632
|
|
|
468
633
|
### Probit (Inverse Normal CDF)
|
|
469
634
|
|
|
470
|
-
`probit(confidence)` computes the inverse of the standard normal CDF (Phi^{-1}). It converts a two-sided probability to a z-score:
|
|
635
|
+
`probit(confidence)` computes the inverse of the standard normal CDF (Phi^{-1}). It is the Gaussian limit of the corridor multiplier (the fitted Student-t quantile `studentTProbit(confidence, df)` converges to it as df grows). It converts a two-sided probability to a z-score:
|
|
471
636
|
|
|
472
637
|
```
|
|
473
638
|
confidence = P(-z < Z < z), Z ~ N(0,1)
|