fintech-algorithms
Using a coding agent? Give it the skill: npx skills add IslamBaraka90/Fintech-Algorithms-Library What it does →

Tick-Run Bars

Install and import#

bash
npm install fintech-algorithms
ts
import { constructBars } from "fintech-algorithms/market-data-engineering/bar-construction/tick-run-bars";

Signature#

constructBars(trades, config)

Closes a bar when a run of same-signed trades exceeds what the recent buy probability makes plausible. Where imbalance bars react to net one-sidedness, run bars react to *persistence*.

Parameters#

NameTypeNotes
tradesTrade[]The raw tape in chronological order. Each trade carries tradeId, timestamp, session, symbol, price, volume and currency.
config{ initialTickSign: number; initialExpectedTicks: number; initialBuyProbability: number; alphaTicks: number; alphaBuyProbability: number; thresholdFloorTicks: number; thresholdMultiplier: number; closePartial?: boolean }initialBuyProbability seeds the estimate of how often trades arrive buyer-initiated, updated by alphaBuyProbability. The threshold is bounded below by thresholdFloorTicks.

Returns#

Bar[] · length fewer

One bar per run event.

Errors#

  • When any alpha or probability falls outside 0…1 — throws

Complexity: time O(n), space O(bars).

Worked example#

verified This is the worked example published in the article, replayed by the test suite on every run. The output cannot drift.

Input#

trades
[
  {
    "tradeId": "W01",
    "timestamp": "2026-01-05T14:30:00.000Z",
    "session": "S1",
    "symbol": "SYNTH",
    "price": 100,
    "volume": 10,
    "currency": "USD"
  },
  {
    "tradeId": "W02",
    "timestamp": "2026-01-05T14:30:01.000Z",
    "session": "S1",
    "symbol": "SYNTH",
    "price": 100,
    "volume": 20,
    "currency": "USD"
  },
  {
    "tradeId": "W03",
    "timestamp": "2026-01-05T14:30:02.000Z",
    "session": "S1",
    "symbol": "SYNTH",
    "price": 99.9,
    "volume": 15,
    "currency": "USD"
  }
]

Showing 3 of 8 elements.

config
{
  "closePartial": true,
  "initialTickSign": 1,
  "initialExpectedTicks": 4,
  "initialBuyProbability": 0.625,
  "alphaTicks": 0.5,
  "alphaBuyProbability": 0.5,
  "thresholdFloorTicks": 2,
  "thresholdMultiplier": 1
}

Call#

constructBars(trades, config)

Returns#

array of 2 objects

[
  {
    "firstTradeId": "W01",
    "lastTradeId": "W04",
    "tickCount": 4,
    "buyTicks": 3,
    "sellTicks": 1,
    "dominantSide": "buy",
    "thresholdTicks": 2.5,
    "overshootTicks": 0.5,
    "closeReason": "threshold"
  },
  {
    "firstTradeId": "W05",
    "lastTradeId": "W08",
    "tickCount": 4,
    "buyTicks": 1,
    "sellTicks": 3,
    "dominantSide": "sell",
    "thresholdTicks": 2.75,
    "overshootTicks": 0.25,
    "closeReason": "threshold"
  }
]

Diagrams#

Tick-Run Bars — article hero
Tick-Run Bars — boundary and state
Tick-Run Bars — construction anatomy

Calculation flow#

Causal construction flow — Tick-Run Bars
flowchart TD
    A["Receive cleaned chronological trade"] --> B{"New session?"}
    B -->|Yes| C["Emit or drop partial; reset price, sign, expectations, and bar state"]
    B -->|No| D["Keep session state"]
    C --> E["Assign tick sign; flat carries prior sign"]
    D --> E
    E --> F["Update OHLCV, N+, and N−"]
    F --> G{"max(N+, N−) >= frozen h?"}
    G -->|No| A
    G -->|Yes| H["Emit complete bar with lineage and diagnostics"]
    H --> I["Update E[T] and p+ from the completed bar"]
    I --> J["Freeze the next threshold"]
    J --> A

How it works#

This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.

Read the article →

References#

The rest of the Bar Construction family#