Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

divergence-detector

RSI/momentum divergence detection that provably does not repaint. Most public divergence indicators draw a divergence at the pivot bar — bars before the pivot was even knowable — and silently redraw it as new data arrives. This library emits a divergence only at the bar where it first became knowable, stamps that bar on the signal (confirmedAtIndex), and ships a test suite that proves reported divergences never mutate or disappear as bars append. Built on market-structure-engine for pivot detection. Part of the Visor chart intelligence stack.

Method

Oscillators

  • Wilder RSI (default, period 14), implemented from scratch: close-to-close changes, first averages = simple mean of the first period changes, then Wilder smoothing avg = (avg·(p−1) + x)/p. Verified against the classic published worked example to 4 decimal places. Conventions: avgLoss = 0 → 100, dead-flat → 50. First value at bar index period.
  • ROC / momentum: 100·(close − close[p ago])/close[p ago].
  • Pluggable: anything implementing { name, range, update(candle): number|null, reset() } works — warm-up must be a contiguous null prefix; declare range (e.g. [0,100]) or null for unbounded oscillators.

Pivots and pairing

Price pivots are k-bar fractal swings from market-structure-engine (default fractalK = 2): a swing high needs k strictly-lower highs on each side, so it confirms exactly k bars after the pivot. Oscillator pivots use the same k-bar rule applied to the oscillator series. A price pivot and an oscillator pivot of the same side pair when their bar indices differ by at most maxBarOffset (default 3); matching is greedy nearest-first and each pivot pairs at most once.

Divergence classification

Each new pair is compared with the previous pair on its side:

type price oscillator side
regular bullish LL HL lows
regular bearish HH LH highs
hidden bullish HL LL lows
hidden bearish LH HH highs

Exact ties in either series → no divergence.

Strength score (0–1)

ps  = clamp(±1, ((p2 − p1)/p1) / 0.02)      # price leg, saturates at 2%
os  = clamp(±1, (o2 − o1) / (0.15·span))    # osc leg, saturates at 15% of span
slopeDifferential = |ps − os| / 2           # legs oppose by definition → (0,1]
pivotExtremity    = min(1, |o2 − mid| / (0.3·span))
strength = clamp(0, 1, 0.5·slopeDifferential + 0.5·pivotExtremity)

span/mid come from the oscillator's declared range (RSI: 100/50), or from the running observed min/max at confirmation time for unbounded oscillators — still computed from past data only. The constants are pragmatic saturation points, not fitted parameters; the formula is deliberately simple, monotonic in divergence magnitude, and rewards divergences at oscillator extremes.

Confirmation lag — the honest part

A divergence needs its later price pivot and later oscillator pivot confirmed:

  • every pivot confirms fractalK bars after its bar;
  • the paired oscillator pivot may sit up to maxBarOffset bars past the price pivot;
  • worst-case lag: maxBarOffset + fractalK bars after the later price pivot (default: 5 bars). Typical case (aligned pivots): fractalK bars.

confirmedAtIndex on every Divergence records the exact bar the signal became knowable; the adapter deliberately places markers there, not at the pivot. The test suite asserts the prefix property on real BTC data and seeded random walks: process(candles[0..n]) reports exactly the divergences of the full run with confirmedAtIndex < n.

Install

npm install divergence-detector

Node 20+. Runtime dependency: market-structure-engine only.

Quickstart

import { DivergenceDetector, rsi } from 'divergence-detector';

const detector = new DivergenceDetector({ oscillator: rsi(14), fractalK: 2 });

// batch over history…
const { divergences } = detector.process(candles);

// …then stream live closed bars
const step = detector.update(nextClosedCandle);
for (const d of step.divergences) {
  console.log(d.type, 'confirmed at bar', d.confirmedAtIndex, 'strength', d.strength);
}

Batch and incremental modes produce identical output for the same candles (tested).

API

new DivergenceDetector(options?)

option default meaning
oscillator rsi(14) any Oscillator implementation
fractalK 2 pivot confirmation half-width (price and oscillator)
maxBarOffset 3 max index distance between paired pivots
  • process(candles): { divergences } — reset + run over the whole array.
  • update(candle): { divergences, oscValue } — feed one closed candle; returns the divergences that became knowable on this bar plus the oscillator value.
  • getters: divergences, oscillatorName, candlesProcessed; reset().

Divergence

{
  type: 'regular-bullish' | 'regular-bearish' | 'hidden-bullish' | 'hidden-bearish',
  side: 'low' | 'high',
  priceSegment: { from, to },   // { index, time, price, confirmedAtIndex }
  oscSegment:   { from, to },   // { index, time, value, confirmedAtIndex }
  confirmedAtIndex, confirmedAtTime,
  strength,                     // 0–1
  oscillator,                   // e.g. 'rsi(14)'
}

Oscillators

rsi(period = 14), roc(period = 10), or bring your own Oscillator.

Overlay adapter

  • toDivergenceSegments(divergences) — two PaneSegments per divergence (price pane + oscillator pane) with styleKey and strength for theming.
  • toConfirmationMarkers(divergences) — one marker per divergence at the confirmation bar (not the pivot bar), arrow up/down by direction.

Demo

npm install && npm --prefix ../market-structure-engine run build
npm run demo   # RSI(14) divergences on 1000 real BTCUSDT 1h candles

Tests

npm test

Wilder RSI exactness against the published reference sequence, exact-segment synthetic divergences of all four types (via a scripted oscillator), pairing and maxBarOffset behaviour, the non-repaint prefix property on real and synthetic data with both bounded and unbounded oscillators, batch ≡ incremental equivalence, and a real-fixture snapshot.

Screenshots

Chart overlays coming soon.


Educational and analytical tooling. Not investment advice, not a trading signal service, and not a financial promotion.

About

Provably non-repainting RSI/momentum divergence detection against confirmed price pivots

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages