|
| 1 | +# Machine Learning (Pure-JS) |
| 2 | + |
| 3 | +Zero-dependency ML primitives for time-series forecasting and regime detection. |
| 4 | +No native bindings, no tfjs — runs in Node, Deno, Bun, browsers. |
| 5 | + |
| 6 | +## Modules |
| 7 | + |
| 8 | +| Module | Purpose | |
| 9 | +|--------|---------| |
| 10 | +| `LSTMCell` / `GRUCell` | Forward-pass RNN cells | |
| 11 | +| `walkForward` | Time-series cross-validation | |
| 12 | +| Feature engineering | Lags, rolling stats, returns, scaling | |
| 13 | +| `trainHMM` / `viterbi` | Gaussian HMM regime detection | |
| 14 | + |
| 15 | +## LSTM / GRU Forward Pass |
| 16 | + |
| 17 | +Pre-trained weights only (no backprop). Useful for deploying models trained |
| 18 | +elsewhere (Python, JAX) into pure-JS runtimes. |
| 19 | + |
| 20 | +```typescript |
| 21 | +import { LSTMCell, randomLSTMWeights } from 'meridianalgo'; |
| 22 | + |
| 23 | +const inputSize = 5; |
| 24 | +const hiddenSize = 16; |
| 25 | +const cell = new LSTMCell(randomLSTMWeights(inputSize, hiddenSize)); |
| 26 | + |
| 27 | +const sequence = [/* ... [number[]] ... */]; |
| 28 | +const { h, c } = cell.forward(sequence); |
| 29 | +// h: final hidden state (length hiddenSize) |
| 30 | +``` |
| 31 | + |
| 32 | +GRU API mirrors LSTM, returns only `h`. |
| 33 | + |
| 34 | +Gate ordering: |
| 35 | +- LSTM: `[i, f, g, o]` (input, forget, candidate, output) |
| 36 | +- GRU: `[r, z, n]` (reset, update, candidate) |
| 37 | + |
| 38 | +Weight shapes: |
| 39 | +- LSTM: `Wi[4H × N]`, `Wh[4H × H]`, `b[4H]` |
| 40 | +- GRU: `Wi[3H × N]`, `Wh[3H × H]`, `bi[3H]`, `bh[3H]` |
| 41 | + |
| 42 | +## Walk-Forward Validation |
| 43 | + |
| 44 | +Time-series CV that respects causality. Two modes: |
| 45 | + |
| 46 | +```typescript |
| 47 | +import { walkForward } from 'meridianalgo'; |
| 48 | + |
| 49 | +const result = walkForward(X, y, { |
| 50 | + mode: 'expanding', // or 'rolling' |
| 51 | + initialTrainSize: 100, |
| 52 | + testSize: 20, |
| 53 | + step: 20, |
| 54 | + fit: (Xtrain, ytrain) => trainModel(Xtrain, ytrain), |
| 55 | + predict: (model, Xtest) => model.predict(Xtest), |
| 56 | +}); |
| 57 | + |
| 58 | +result.folds; // per-fold {predictions, actual, mse, mae, rSquared} |
| 59 | +result.combinedPredictions; // concatenated out-of-sample predictions |
| 60 | +result.meanMse; |
| 61 | +result.meanMae; |
| 62 | +``` |
| 63 | + |
| 64 | +- **Expanding**: train window grows each fold (`[0, end)`). |
| 65 | +- **Rolling**: train window slides at fixed size. |
| 66 | + |
| 67 | +## Feature Engineering |
| 68 | + |
| 69 | +```typescript |
| 70 | +import { |
| 71 | + lagFeatures, rollingMean, rollingStd, |
| 72 | + logReturns, simpleReturns, |
| 73 | + zScore, minMaxScale, diff, |
| 74 | +} from 'meridianalgo'; |
| 75 | + |
| 76 | +const lags = lagFeatures(prices, [1, 5, 10]); // matrix [n × 3] |
| 77 | +const ma = rollingMean(prices, 20); |
| 78 | +const sd = rollingStd(prices, 20); |
| 79 | +const r = logReturns(prices); |
| 80 | +const z = zScore(values); // (x - μ)/σ |
| 81 | +const scaled = minMaxScale(values); // [0, 1] |
| 82 | +``` |
| 83 | + |
| 84 | +NaN-padded arrays preserve index alignment with original series. |
| 85 | + |
| 86 | +## HMM Regime Detection |
| 87 | + |
| 88 | +Gaussian-emission HMM with Baum-Welch training (forward-backward + scaling) |
| 89 | +and Viterbi decoding in log-domain. |
| 90 | + |
| 91 | +```typescript |
| 92 | +import { trainHMM, viterbi } from 'meridianalgo'; |
| 93 | + |
| 94 | +const observations = returns; // 1-D series |
| 95 | +const k = 2; // num regimes (e.g. bull/bear) |
| 96 | + |
| 97 | +const { params, logLik, iterations } = trainHMM(observations, k, { |
| 98 | + maxIter: 100, |
| 99 | + tol: 1e-4, |
| 100 | +}); |
| 101 | + |
| 102 | +const states = viterbi(observations, params); |
| 103 | +// states[t] ∈ {0, 1, ..., k-1} |
| 104 | +``` |
| 105 | + |
| 106 | +`HMMParams`: |
| 107 | +- `pi[k]` — initial state distribution |
| 108 | +- `A[k][k]` — transition matrix |
| 109 | +- `mu[k]`, `sigma[k]` — emission Gaussian params |
| 110 | + |
| 111 | +## Limitations |
| 112 | + |
| 113 | +- No autograd / training for RNN cells (forward-only). |
| 114 | +- HMM Gaussian emissions are univariate scalar. |
| 115 | +- For heavy training workloads use Python — load weights here for inference. |
| 116 | + |
| 117 | +See also: `INDICATORS-PATTERNS.md` for streaming indicators that pair well |
| 118 | +with online ML inference. |
0 commit comments