MLToolkit

307
MLToolkit

Overview

MLToolkit is a Pine Script v6 library of machine learning building blocks: k-nearest-neighbour (KNN) search and voting, Lorentzian classification, Nadaraya-Watson kernel regression and kernel density estimation (KDE). It is written for Pine's execution limits. A training set is a ring buffer, so adding a sample never shifts arrays. Neighbour search stops measuring a candidate as soon as it can no longer make the top k. Kernel weights are computed once instead of on every bar. Density estimates are built on a binned grid instead of summing every sample at every point.

What's inside

  • Features: minMax() (rolling 0..1 scaling), zScore() and squash() (tanh scaling of unbounded values to -1..+1), so features on different scales can share one distance.
  • Distances: distance() with the Lorentzian (sum of log(1 + |difference|)), Euclidean, Manhattan and Chebyshev metrics and optional per-feature weights.
  • Dataset: newDataset(features, capacity), add() (skips samples containing na) and fisherWeights(), which weighs each feature by how well it separates positive from negative labels (Fisher ratio), with a floor so no feature is switched off.
  • knn(): the k nearest samples with uniform, inverse-distance or kernel voting. It returns a Neighbors object with the vote, the weighted probability of a positive label, the weighted mean label and the mean neighbour distance (a closeness gauge). Spacing visits every n-th sample, which makes the search faster and keeps neighbours from being consecutive bars of one move. skipNewest leaves the most recent samples out.
  • Lorentzian classification: directionLabel() (+1 / -1 / 0 for price higher / lower after N bars), lorentzianPredict() (uniform vote of the k nearest by Lorentzian distance with chronological spacing) and the optional filters filterVolatility(), filterRegime() and filterAdx().
  • Kernel regression: kernelWeights() precomputes the weights once, and nadarayaWatson() applies them as one dot product per bar. Gaussian, rational quadratic, Epanechnikov, triangular and uniform kernels are available. Only current and past bars are used.
  • Kernel density estimation: kde() with linear binning, a kernel truncated where it is negligible, and Silverman (bandwidthSilverman) or Scott (bandwidthScott) bandwidths. The Density result answers densityAt(), cdfAt(), quantile() (inverse CDF) and mode().


Quick start

Pine Script®


KDE example: learn where RSI usually tops out from its own history.

Pine Script®


Notes

  • Pair features with labels that are known at the time: add the features from N bars ago together with the label of the last N bars, as in the example. Adding the current features with a future label would leak future data into the backtest.
  • Search cost grows with capacity / spacing x features per call. Run knn() on confirmed bars or limit the capacity on long histories to stay within TradingView's time limits.
  • Call nadarayaWatson() once per bar, unconditionally, so its history is consistent.
  • The classifier and filters describe how past data was arranged. They are tools for research, not a forecast of future prices.


Credits

The concepts come from published work on TradingView, re-implemented here in my own code:
  • Lorentzian distance classification with chronological spacing and volatility / regime / ADX filters: "Machine Learning: Lorentzian Classification" by jdehorty.
  • Kernel functions, KDE and the Silverman bandwidth: the MathStatisticsKernelFunctions and MathStatisticsKernelDensityEstimation libraries by RicardoSantos.
  • KNN voting on market features: "Machine Learning: kNN-based Strategy" by capissimo, the kNN library by lastguru, and the KNN trend and momentum indicators by tkarolak and SatohK.
  • The Nadaraya-Watson estimator, the rational quadratic kernel, linear binning, and the Silverman and Scott bandwidth rules are standard statistics.


Demo

Add the library to a chart on its own to see a demo that uses every exported function. Values without a price scale (the KNN probability, distances) are in the Data Window, and the KDE results are written to the Pine Logs pane.


Library "MLToolkit"
Machine learning building blocks for Pine Script, written for speed: k-nearest-neighbour search over a
ring-buffer dataset with partial-distance pruning and chronological spacing, Lorentzian / Euclidean /
Manhattan / Chebyshev distances, distance-weighted voting, Fisher-ratio feature weights, a Lorentzian
direction classifier with volatility, regime and ADX filters, Nadaraya-Watson kernel regression with
precomputed weights, and binned kernel density estimation with Silverman / Scott bandwidths, CDF,
quantiles and mode.

kernelValue(k, u, alpha)
  Kernel value at a scaled distance.
  Parameters:
    k (series Kernel): (Kernel) The kernel.
    u (float): (series float) Distance divided by the bandwidth.
    alpha (float): (series float) Rational quadratic shape (larger = closer to Gaussian). Optional. Default is 1.
  Returns: (float) The kernel value (not normalised).

minMax(x, len)
  Rolling min-max scaling to 0..1 (0.5 when the window is flat).
  Parameters:
    x (float): (series float) Value.
    len (int): (series int) Window, in bars.
  Returns: (float) The scaled value.

zScore(x, len)
  Rolling z-score: distance from the mean in standard deviations.
  Parameters:
    x (float): (series float) Value.
    len (int): (series int) Window, in bars.
  Returns: (float) The z-score (0 when the window is flat).

squash(x, scale)
  Squashes an unbounded value into -1..+1 with tanh(x / scale).
  Parameters:
    x (float): (series float) Value.
    scale (float): (series float) Value that maps to about 0.76.
  Returns: (float) The squashed value.

distance(a, b, metric, weights)
  Distance between two feature vectors of the same size.
  Parameters:
    a (array<float>): (array<float>) First vector.
    b (array<float>): (array<float>) Second vector.
    metric (series Metric): (Metric) Distance metric. Optional. Default is `Metric.lorentzian`.
    weights (array<float>): (array<float>) Per-feature weights; an empty array weighs every feature 1. Optional.
  Returns: (float) The distance.

newDataset(nFeatures, capacity)
  Creates an empty dataset.
  Parameters:
    nFeatures (int): (series int) Features per sample.
    capacity (int): (series int) Maximum samples kept (the oldest are overwritten).
  Returns: (Dataset) The dataset.

method add(d, features, label)
  Adds a sample (skipped when any feature or the label is `na`).
  Namespace types: Dataset
  Parameters:
    d (Dataset): (Dataset) The dataset.
    features (array<float>): (array<float>) Feature vector.
    label (float): (series float) Label, e.g. +1 / -1 / 0.
  Returns: (bool) The sample was added.

method fisherWeights(d, minWeight, minSamples)
  Feature weights from the Fisher ratio of each feature: (mean of positive-label samples - mean of
negative-label samples)^2 / (variance of each group summed), scaled so the best feature is 1 and floored.
Features that separate the classes well get more say in the distance. Stores them in `d.weights`.
  Namespace types: Dataset
  Parameters:
    d (Dataset): (Dataset) The dataset (labels > 0 vs < 0; zero labels are ignored).
    minWeight (float): (series float) Smallest weight a feature can get (0-1). Optional. Default is 0.25.
    minSamples (int): (series int) Samples needed per class before weights are computed. Optional. Default is 20.
  Returns: (array<float>) The weights (also stored in the dataset; empty until there are enough samples).

method knn(d, q, k, metric, weighting, spacing, skipNewest)
  Finds the k nearest stored samples to a query and lets them vote. Keeps the k best in a small buffer and
tracks the current worst, so a candidate's distance stops accumulating as soon as it cannot qualify.
`spacing` visits every n-th sample (chronological spacing), which both speeds the search up and avoids
neighbours that are just consecutive bars of the same move.
  Namespace types: Dataset
  Parameters:
    d (Dataset): (Dataset) The training set.
    q (array<float>): (array<float>) Query feature vector.
    k (int): (series int) Neighbours.
    metric (series Metric): (Metric) Distance metric. Optional. Default is `Metric.lorentzian`.
    weighting (series Weighting): (Weighting) Voting scheme. Optional. Default is `Weighting.uniform`.
    spacing (int): (series int) Visit every n-th sample, newest first. Optional. Default is 1.
    skipNewest (int): (series int) Samples at the newest end to skip. Optional. Default is 0.
  Returns: (Neighbors) The vote and neighbour statistics.

directionLabel(src, horizon)
  Direction label for training: +1 if `src` is higher than `horizon` bars ago, -1 if lower, 0 if equal.
Pair it with the features as they were `horizon` bars ago, so no sample uses future data.
  Parameters:
    src (float): (series float) Price source.
    horizon (int): (series int) Bars ahead the label looks.
  Returns: (int) +1, -1 or 0 (`na` while history is short).

method lorentzianPredict(d, q, k, spacing)
  Lorentzian classification: the uniform vote of the k nearest samples by Lorentzian distance, visiting
every `spacing`-th sample. With +1 / -1 / 0 labels the result ranges from -k to +k.
  Namespace types: Dataset
  Parameters:
    d (Dataset): (Dataset) The training set.
    q (array<float>): (array<float>) Current features.
    k (int): (series int) Neighbours. Optional. Default is 8.
    spacing (int): (series int) Chronological spacing. Optional. Default is 4.
  Returns: (float) The prediction (positive = bullish).

filterVolatility(fast, slow)
  Volatility filter: recent volatility above the longer-term level (markets that are moving).
  Parameters:
    fast (simple int): (simple int) Fast ATR length. Optional. Default is 1.
    slow (simple int): (simple int) Slow ATR length. Optional. Default is 10.
  Returns: (bool) Recent ATR is above the slow ATR.

filterRegime(src, len, threshold)
  Regime filter: the slope of a smoothed source, in ATRs per bar, is at least `threshold` in absolute
terms (trending rather than ranging). Use a negative threshold to accept mild slopes.
  Parameters:
    src (float): (series float) Price source.
    len (simple int): (simple int) EMA and ATR length. Optional. Default is 20.
    threshold (float): (series float) Minimum absolute slope in ATRs per bar. Optional. Default is 0.05.
  Returns: (bool) The market is trending by this measure.

filterAdx(len, threshold)
  ADX filter: trend strength at or above a threshold.
  Parameters:
    len (simple int): (simple int) DMI / ADX length. Optional. Default is 14.
    threshold (float): (series float) Minimum ADX. Optional. Default is 20.
  Returns: (bool) ADX is at least the threshold.

kernelWeights(k, lookback, bandwidth, alpha)
  Precomputes Nadaraya-Watson weights for lags 0..lookback-1, so the regression costs one dot product per
bar instead of recomputing every kernel value. Declare the result with `var`.
  Parameters:
    k (series Kernel): (Kernel) Kernel.
    lookback (int): (series int) Bars used.
    bandwidth (float): (series float) Bandwidth, in bars (larger = smoother).
    alpha (float): (series float) Rational quadratic shape. Optional. Default is 1.
  Returns: (array<float>) The weights, newest bar first.

nadarayaWatson(src, weights)
  Nadaraya-Watson kernel regression estimate of `src` with precomputed weights (see `kernelWeights()`).
Uses only current and past bars, so it does not repaint. Call it once per bar, unconditionally.
  Parameters:
    src (float): (series float) Series to smooth.
    weights (array<float>): (array<float>) Weights from `kernelWeights()`, newest bar first.
  Returns: (float) The estimate, `na` until any weighted bar has data.

bandwidthSilverman(samples)
  Silverman's rule-of-thumb bandwidth: 0.9 x min(standard deviation, IQR / 1.34) x n^(-1/5). Robust to
skewed and heavy-tailed samples.
  Parameters:
    samples (array<float>): (array<float>) Samples.
  Returns: (float) The bandwidth (`na` for fewer than 2 samples).

bandwidthScott(samples)
  Scott's rule bandwidth: 1.06 x standard deviation x n^(-1/5). Best for near-normal samples.
  Parameters:
    samples (array<float>): (array<float>) Samples.
  Returns: (float) The bandwidth (`na` for fewer than 2 samples).

kde(samples, bins, bandwidth, kernel, lo, hi)
  Binned kernel density estimate. Samples are spread onto the grid by linear binning (each sample splits its
weight between its two nearest grid points), then the grid is convolved with the kernel truncated where
it is negligible. Cost is O(samples + bins x kernel width) instead of O(samples x bins).
  Parameters:
    samples (array<float>): (array<float>) Samples.
    bins (int): (series int) Grid points. Optional. Default is 100.
    bandwidth (float): (series float) Bandwidth; `na` uses Silverman's rule. Optional.
    kernel (series Kernel): (Kernel) Kernel. Optional. Default is `Kernel.gaussian`.
    lo (float): (series float) Grid start; `na` = lowest sample - 3 bandwidths. Optional.
    hi (float): (series float) Grid end; `na` = highest sample + 3 bandwidths. Optional.
  Returns: (Density) The estimate, or `na` for an empty sample.

method densityAt(d, x)
  Density at a value (linear interpolation; 0 outside the grid).
  Namespace types: Density
  Parameters:
    d (Density): (Density) The estimate.
    x (float): (series float) Value.
  Returns: (float) The density.

method cdfAt(d, x)
  Share of the distribution at or below a value (0 below the grid, 1 above it).
  Namespace types: Density
  Parameters:
    d (Density): (Density) The estimate.
    x (float): (series float) Value.
  Returns: (float) The cumulative share (0-1).

method quantile(d, p)
  Value below which a share `p` of the distribution lies (inverse CDF, interpolated).
  Namespace types: Density
  Parameters:
    d (Density): (Density) The estimate.
    p (float): (series float) Share, 0-1.
  Returns: (float) The quantile.

method mode(d)
  The value with the highest density (the distribution's peak).
  Namespace types: Density
  Parameters:
    d (Density): (Density) The estimate.
  Returns: (float) The mode.

Dataset
  A fixed-capacity training set stored as a ring buffer: adding a sample overwrites the oldest one in O(1)
  Fields:
    x (matrix<float>): Feature matrix, one row per sample.
    y (array<float>): Labels.
    bars (array<int>): Bar index at which each sample was added.
    weights (array<float>): Per-feature weights used by `knn()` (empty = equal weights). See `fisherWeights()`.
    capacity (series int): Maximum number of samples.
    count (series int): Samples stored.
    head (series int): Slot the next sample is written to.

Neighbors
  Result of a neighbour search.
  Fields:
    vote (series float): Sum of neighbour labels, weighted by the voting scheme (with uniform voting and labels of +1 / -1 this
    probUp (series float): Weighted share of neighbours with a positive label (0-1), `na` when none were found.
    meanLabel (series float): Weighted mean label (useful for regression targets).
    meanDist (series float): Mean distance to the neighbours (a confidence gauge: smaller = closer analogues).
    found (series int): Neighbours found (at most k).

Density
  A kernel density estimate on an evenly spaced grid.
  Fields:
    lo (series float): Grid start.
    step (series float): Grid spacing.
    pdf (array<float>): Density at each grid point (integrates to 1).
    cdf (array<float>): Cumulative share at each grid point (0-1).
    bandwidth (series float): Bandwidth used.

כתב ויתור

המידע והפרסומים אינם מיועדים להיות, ואינם מהווים, ייעוץ או המלצה פיננסית, השקעתית, מסחרית או מכל סוג אחר המסופקת או מאושרת על ידי TradingView. קרא עוד ב־תנאי השימוש.