<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TickDistill</title>
    <description>The latest articles on DEV Community by TickDistill (tickdistill).</description>
    <link>https://dev.to/tickdistill</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F13796%2F90b85469-c8b4-4e6f-8381-2977f3a1dff0.png</url>
      <title>DEV Community: TickDistill</title>
      <link>https://dev.to/tickdistill</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tickdistill"/>
    <language>en</language>
    <item>
      <title>Sigma-Normalization: Why Order-Flow Signals Should Be Measured in Standard Deviations, Not Raw Numbers</title>
      <dc:creator>TickDistill</dc:creator>
      <pubDate>Thu, 25 Jun 2026 15:01:13 +0000</pubDate>
      <link>https://dev.to/tickdistill/sigma-normalization-why-order-flow-signals-should-be-measured-in-standard-deviations-not-raw-3id5</link>
      <guid>https://dev.to/tickdistill/sigma-normalization-why-order-flow-signals-should-be-measured-in-standard-deviations-not-raw-3id5</guid>
      <description>&lt;h1&gt;
  
  
  Sigma-Normalization: Why Order-Flow Signals Should Be Measured in Standard Deviations, Not Raw Numbers
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;By TickDistill — order-flow microstructure signals. Educational content, not financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;A raw number like "a 200-contract order" or "$3M of buying" means nothing on its own. The useful question is &lt;strong&gt;"how unusual is this, here, right now?"&lt;/strong&gt; Sigma-normalization answers exactly that: it re-expresses a measurement as &lt;strong&gt;how many standard deviations&lt;/strong&gt; it sits from its own recent distribution. The output is a &lt;strong&gt;z-score&lt;/strong&gt; — a measure of &lt;em&gt;rarity&lt;/em&gt;, not of &lt;em&gt;magnitude&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why raw thresholds break
&lt;/h2&gt;

&lt;p&gt;Fixed, absolute thresholds fail in two directions at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Across instruments.&lt;/strong&gt; 200 contracts may be enormous on one market and trivial on another. A dollar figure that is "big" for one asset is noise for a more liquid one. A single hard-coded number cannot be right everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Across regimes.&lt;/strong&gt; The same market is calm in one week and violent the next. A print that is extreme in a quiet regime is ordinary during high volatility. A threshold fixed last month is wrong this month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sigma-normalization solves both: by dividing by the &lt;strong&gt;local standard deviation&lt;/strong&gt;, "big" always means "big &lt;em&gt;relative to what this market has been doing lately&lt;/em&gt;." The signal becomes &lt;strong&gt;comparable across assets&lt;/strong&gt; and &lt;strong&gt;adaptive across regimes&lt;/strong&gt; — without you re-tuning anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "normalized" actually buys you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Portability.&lt;/strong&gt; The same signal logic runs on BTC, ETH, SOL — and later on regulated futures like the E-mini S&amp;amp;P 500 — by swapping a per-market profile, not by rewriting the signal. One definition, many markets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rarity as the unit.&lt;/strong&gt; When a signal says "2.5σ," it is telling you this event is genuinely uncommon for this market. An 80/20 directional split is rare (high sigma); a 60/40 split is normal (low sigma) and carries little information — even though both look "directional" on the surface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable interpretation.&lt;/strong&gt; A "2σ" event means the same &lt;em&gt;kind&lt;/em&gt; of thing whether the market is calm or wild, because the yardstick moves with the market.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The non-negotiable part: point-in-time correctness
&lt;/h2&gt;

&lt;p&gt;A normalization is only honest if its yardstick uses &lt;strong&gt;only the past&lt;/strong&gt;. If the standard deviation is computed using data from &lt;em&gt;after&lt;/em&gt; the signal moment — even by accident — the backtest looks brilliant and the live signal disappoints. This is &lt;strong&gt;look-ahead bias&lt;/strong&gt;, and it is the most common way order-flow research fools itself.&lt;/p&gt;

&lt;p&gt;TickDistill computes every baseline &lt;strong&gt;causally&lt;/strong&gt; (rolling, past-only) and &lt;strong&gt;excludes recurring mechanical windows&lt;/strong&gt; that would otherwise distort the statistics — for example an index's market-on-close auction, or a perpetual's funding settlements, where huge volume appears for structural reasons rather than informational ones. We treat point-in-time correctness as a hard rule, because a backtest you cannot trust is worse than no backtest at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this shows up in the product
&lt;/h2&gt;

&lt;p&gt;Every TickDistill signal expresses its thresholds in &lt;strong&gt;sigma units&lt;/strong&gt;, not raw numbers. That is why the same package behaves sensibly across our supported assets, why "rare" means rare, and why our backtests are built to be reproducible point-in-time. When you later tune a signal's sensitivity (see &lt;em&gt;signal knobs&lt;/em&gt;), you are moving a threshold &lt;strong&gt;in sigma&lt;/strong&gt; — a number that keeps its meaning as markets change.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TickDistill sells clean, computed order-flow inputs — not trading advice or guaranteed alpha. Backtests are illustrative and not a promise of future results.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>algotrading</category>
      <category>crypto</category>
      <category>python</category>
    </item>
    <item>
      <title>What Is Point-in-Time Correctness? Why No-Look-Ahead Makes or Breaks a Backtest</title>
      <dc:creator>TickDistill</dc:creator>
      <pubDate>Thu, 25 Jun 2026 14:55:28 +0000</pubDate>
      <link>https://dev.to/tickdistill/what-is-point-in-time-correctness-why-no-look-ahead-makes-or-breaks-a-backtest-2fd6</link>
      <guid>https://dev.to/tickdistill/what-is-point-in-time-correctness-why-no-look-ahead-makes-or-breaks-a-backtest-2fd6</guid>
      <description>&lt;h1&gt;
  
  
  What Is Point-in-Time Correctness? Why No-Look-Ahead Makes or Breaks a Backtest
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;By TickDistill — order-flow microstructure signals. Educational content, not financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;Point-in-time correctness is the guarantee that every computation at time &lt;code&gt;t&lt;/code&gt; uses &lt;strong&gt;only data from strictly before &lt;code&gt;t&lt;/code&gt;&lt;/strong&gt;. Violating this constraint — accidentally or structurally — is called &lt;strong&gt;look-ahead bias&lt;/strong&gt;, and it is the most common way order-flow research produces backtest results that cannot be reproduced in live trading. TickDistill treats point-in-time correctness as a hard engineering invariant: every baseline, every normalization, every mask is causal by construction, and every backtest result can be independently reproduced from the same inputs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What does "point-in-time correct" mean, exactly?
&lt;/h2&gt;

&lt;p&gt;Point-in-time correctness means that the value of any signal emitted at timestamp &lt;code&gt;t&lt;/code&gt; is a deterministic function of data with timestamps &lt;code&gt;t' &amp;lt; t&lt;/code&gt; only. No observation from &lt;code&gt;t' ≥ t&lt;/code&gt; enters the computation — not the current bucket, not a future bucket, not in the normalization denominator, not in the exclusion mask calibration.&lt;/p&gt;

&lt;p&gt;The strict inequality matters. Including the current observation (&lt;code&gt;t' ≤ t&lt;/code&gt; instead of &lt;code&gt;t' &amp;lt; t&lt;/code&gt;) is still a form of contamination: the baseline that normalizes a measurement must not include that same measurement, or the z-score becomes self-referential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why look-ahead bias is so easy to introduce by accident
&lt;/h2&gt;

&lt;p&gt;Look-ahead bias does not require deliberate cheating. It emerges from common implementation shortcuts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;How it happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;In-sample normalization&lt;/td&gt;
&lt;td&gt;Computing the mean/std over the entire history, then using it to normalize each historical point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rolling window off-by-one&lt;/td&gt;
&lt;td&gt;A &lt;code&gt;pandas.rolling().mean()&lt;/code&gt; default that includes the current row in the window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global volatility estimate&lt;/td&gt;
&lt;td&gt;Using the full-period σ as the denominator for a z-score computed at each past point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classifier training&lt;/td&gt;
&lt;td&gt;Training a trade-side classifier on the same period you backtest the signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mask calibration&lt;/td&gt;
&lt;td&gt;Identifying "noisy" windows after the fact and masking them retroactively&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each of these makes a past computation depend on future information. The backtest looks cleaner than it is; live performance does not benefit from knowledge of the future.&lt;/p&gt;




&lt;h2&gt;
  
  
  The causal baseline: &lt;code&gt;t' &amp;lt; t&lt;/code&gt; strictly
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;causal baseline&lt;/strong&gt; is a rolling statistic — mean, standard deviation, or exponentially weighted equivalent — computed at each point using only the observations that were available at that point in history.&lt;/p&gt;

&lt;p&gt;The public z-score formula is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z_t = ( x_t − μ_t ) / σ_t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;μ_t&lt;/code&gt; and &lt;code&gt;σ_t&lt;/code&gt; are estimated from &lt;code&gt;{ x_{t'} : t' &amp;lt; t }&lt;/code&gt; exclusively. This is standard practice for normalizing order-flow quantities against a causal baseline (Easley, López de Prado, O'Hara 2012).&lt;/p&gt;

&lt;p&gt;Two choices of baseline are common in practice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Baseline type&lt;/th&gt;
&lt;th&gt;Formula&lt;/th&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rolling window of N observations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;μ_t = (1/N) Σ_{i=t-N}^{t-1} x_i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Equal weight, sharp cutoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exponentially weighted (EWM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;μ_t = (1−λ) Σ_{k=0}^{∞} λ^k x_{t-1-k}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smooth decay, infinite memory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The decay parameter &lt;code&gt;λ&lt;/code&gt; corresponds to a half-life &lt;code&gt;h&lt;/code&gt; via &lt;code&gt;λ = exp(−ln2/h)&lt;/code&gt;. A longer half-life makes the baseline more stable across regime changes; a shorter half-life makes it more adaptive. The calibration of this parameter is proprietary — what matters for correctness is that whichever estimator is used, it uses &lt;code&gt;t' &amp;lt; t&lt;/code&gt; only. TickDistill uses a causal EWM baseline, and the current observation never enters the estimate that normalizes it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mechanical windows: why some events must be excluded from the baseline
&lt;/h2&gt;

&lt;p&gt;Even a perfectly causal baseline can be distorted by recurring mechanical events — moments when volume or imbalance is large for structural reasons rather than informational ones.&lt;/p&gt;

&lt;p&gt;A clear example is the perpetual futures funding settlement at 00:00, 08:00, and 16:00 UTC (public exchange schedule, Binance and most major venues). At these moments, a funding payment causes predictable positioning activity that is unrelated to informed order flow. Including funding spikes in the baseline causes the baseline σ to inflate, which then suppresses the z-score of genuine order-flow events in surrounding windows.&lt;/p&gt;

&lt;p&gt;The solution is an &lt;strong&gt;exclusion mask&lt;/strong&gt;: data within a mechanical window is excluded from updating the baseline. The mask is applied causally — it defines which observations are allowed to enter the rolling statistic. Observations inside the mask are not deleted; the signal may still be computed over them, but the baseline parameters are not updated from them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;μ_t = EWM over { x_{t'} : t' &amp;lt; t  AND  t' ∉ mask }
σ_t = EWM-std over the same filtered set
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which windows to mask, and at what granularity, is a calibration decision that depends on the instrument, the signal, and the empirical effect of the mechanical event on the signal's distribution. The general principle — exclude mechanical events from the normalization baseline — is textbook practice; the specific calendar is proprietary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Warm-up periods: when a causal baseline is not yet reliable
&lt;/h2&gt;

&lt;p&gt;A rolling or exponentially weighted estimator requires a minimum number of observations before its estimates are stable. Emitting z-scores before the warm-up completes produces values with high estimation error, which corrupt any downstream comparison.&lt;/p&gt;

&lt;p&gt;TickDistill enforces two distinct warm-up criteria before emitting any signal value:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Signal window warm-up.&lt;/strong&gt; A signal that is itself a rolling statistic (e.g., VPIN, a moving imbalance) requires its own window to be filled before it produces a meaningful value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baseline warm-up.&lt;/strong&gt; The causal baseline &lt;code&gt;(μ_t, σ_t)&lt;/code&gt; requires a sufficient number of non-masked observations before its estimates stabilize. For an EWM baseline with half-life &lt;code&gt;h&lt;/code&gt;, stability is reached after approximately &lt;code&gt;5h&lt;/code&gt; observations — the point at which the weight of the initialization drops below roughly 3%.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No signal point is emitted until both criteria are satisfied. A missing warm-up is equivalent to a form of look-ahead: the estimator behaves as if it has more historical information than it does.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anti-look-ahead: the test that verifies the guarantee (Test 5)
&lt;/h2&gt;

&lt;p&gt;The claim of point-in-time correctness is verifiable. The test is direct: compute signal values over a stream, then modify trades at timestamps &lt;code&gt;t' &amp;gt; t&lt;/code&gt;, and confirm that the signal value at &lt;code&gt;t&lt;/code&gt; is identical.&lt;/p&gt;

&lt;p&gt;Formally, for any &lt;code&gt;t&lt;/code&gt; and any perturbation of &lt;code&gt;{ x_{t'} : t' &amp;gt; t }&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;signal(t | history up to t)  =  signal(t | history up to t, perturbed future)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this equality fails, the computation has a look-ahead dependency. This test is mandatory in TickDistill's test suite and covers every path: the signal window, the baseline estimator, the mask exclusion, and the BVC price-change estimator &lt;code&gt;σ_dP&lt;/code&gt; (which uses its own causal window over past price differences between sub-bars, never the full sample).&lt;/p&gt;




&lt;h2&gt;
  
  
  Reproducibility: why point-in-time correctness enables version-pinned backtests
&lt;/h2&gt;

&lt;p&gt;Point-in-time correctness is a prerequisite for reproducibility. A backtest result from a point-in-time-correct pipeline is a deterministic function of four inputs: &lt;code&gt;(signal, params, range, version)&lt;/code&gt; — because each signal is itself a pure &lt;a href="//parametric-signals-as-functions.md"&gt;parametric function &lt;code&gt;f(primitive, params)&lt;/code&gt;&lt;/a&gt;. Given the same four inputs, the same output must emerge, regardless of when the query runs.&lt;/p&gt;

&lt;p&gt;This enables two capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permalink/content-hash.&lt;/strong&gt; Every backtest result can be identified by a hash of its inputs. The result is shareable and reproducible indefinitely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version pinning.&lt;/strong&gt; When a signal formula is updated (v1 → v2), backtest queries pinned to v1 continue to reproduce the v1 result exactly. Code and data definitions are frozen together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither capability is possible if the computation is contaminated by look-ahead, because future data would make the output depend on when the query runs, not only on the declared inputs. See also What makes a backtest reproducible? Permalinks and version pinning.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this connects to sigma-normalization and signal quality
&lt;/h2&gt;

&lt;p&gt;Sigma-normalization — expressing a signal in units of standard deviations from its own rolling baseline — is only honest if the baseline is causal. An in-sample standard deviation is not a yardstick; it is a measurement taken with a ruler that was calibrated using the answer.&lt;/p&gt;

&lt;p&gt;The practical consequence is that live signals and historically backfilled signals use the same code path: the causal baseline estimator, the same mask, the same warm-up logic. There is no separate "backtest mode" that uses full-sample statistics. The backtest is the same computation run over historical data. See &lt;a href="//sigma-normalization.md"&gt;Why Order-Flow Signals Should Be Measured in Standard Deviations&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the pipeline enforces these guarantees
&lt;/h2&gt;

&lt;p&gt;Three architectural properties enforce point-in-time correctness end-to-end:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Single-pass streaming.&lt;/strong&gt; Each day of data is processed in order, one observation at a time. The state at time &lt;code&gt;t&lt;/code&gt; is built from the stream up to &lt;code&gt;t&lt;/code&gt;; no random access to future records is possible. See &lt;a href="//single-pass-streaming-etl-and-discard.md"&gt;Single-pass streaming ETL and discard&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable daily partitions.&lt;/strong&gt; Processed outputs are stored as immutable Parquet partitions. Reprocessing a day overwrites its slice cleanly and produces the identical result (idempotence). This is verified by the QA gate. See &lt;a href="//data-quality-gates.md"&gt;How We Validate Market Data Before It Becomes a Signal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Causal baseline module.&lt;/strong&gt; The baseline estimator is a shared module used by every signal processor. It carries its own state forward per stream and enforces the &lt;code&gt;t' &amp;lt; t&lt;/code&gt; constraint at the interface level, so individual signal implementations cannot accidentally access current or future baseline values.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is look-ahead bias, in one sentence?&lt;/strong&gt;&lt;br&gt;
Look-ahead bias is the use of information from time &lt;code&gt;t' ≥ t&lt;/code&gt; when computing a signal value for time &lt;code&gt;t&lt;/code&gt;, causing backtest results to reflect knowledge the strategy could not have had.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does an in-sample standard deviation cause look-ahead bias?&lt;/strong&gt;&lt;br&gt;
An in-sample standard deviation is computed over the entire historical period. Using it to normalize a point in the middle of that period means the denominator includes observations that occurred after that point — information the model would not have had in real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is an exclusion mask and why does it not create look-ahead bias?&lt;/strong&gt;&lt;br&gt;
An exclusion mask is a set of timestamp intervals whose observations are not allowed to update the rolling baseline. The mask must itself be defined causally — based on a public, fixed event schedule (like exchange funding times), not identified from the data after the fact. A mask derived by examining data is a form of look-ahead; a mask derived from a published schedule is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does warm-up affect live trading or only backtests?&lt;/strong&gt;&lt;br&gt;
Both. In a live deployment, a signal cannot emit values until its baseline has accumulated the required number of non-masked observations. In a historical backtest, the same warm-up logic applies: signal points are absent from the first segment of history until both the signal window and the baseline window are filled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How can I verify that a signal is point-in-time correct?&lt;/strong&gt;&lt;br&gt;
The direct test: compute signal values on a stream, modify observations after a target time &lt;code&gt;t&lt;/code&gt;, and confirm the signal value at &lt;code&gt;t&lt;/code&gt; is unchanged. Any dependency on future data will cause the value to change. This test must cover the signal formula, the baseline estimator, the mask, and any classifier sub-component that has its own rolling estimate.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TickDistill sells clean, computed order-flow inputs — not trading advice or guaranteed alpha. Backtests are illustrative and not a promise of future results.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>algotrading</category>
      <category>crypto</category>
      <category>python</category>
    </item>
    <item>
      <title>What Is Cumulative Volume Delta (CVD) and How Do You Read It?</title>
      <dc:creator>TickDistill</dc:creator>
      <pubDate>Thu, 25 Jun 2026 14:55:23 +0000</pubDate>
      <link>https://dev.to/tickdistill/what-is-cumulative-volume-delta-cvd-and-how-do-you-read-it-2jia</link>
      <guid>https://dev.to/tickdistill/what-is-cumulative-volume-delta-cvd-and-how-do-you-read-it-2jia</guid>
      <description>&lt;h1&gt;
  
  
  What Is Cumulative Volume Delta (CVD) and How Do You Read It?
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;By TickDistill — order-flow microstructure signals. Educational content, not financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cumulative Volume Delta (CVD)&lt;/strong&gt; is the running sum of signed taker volume — buy-aggressor volume minus sell-aggressor volume — accumulated over time. CVD tells you whether buyers or sellers have been the aggressive side of the tape across a session, and by how much. The key diagnostic use is comparing CVD's direction with price's direction: when they diverge, the market is revealing a mismatch between aggression and outcome that experienced traders call an absorption tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is signed taker volume and why does it matter?
&lt;/h2&gt;

&lt;p&gt;Signed taker volume is the volume of each trade tagged with the direction of the aggressor — the participant who crossed the spread and consumed resting liquidity. A buy-aggressor trade receives a positive sign; a sell-aggressor trade receives a negative sign.&lt;/p&gt;

&lt;p&gt;The sign comes from the exchange's own tape. On Binance, for example, the &lt;code&gt;aggTrades&lt;/code&gt; feed records the &lt;code&gt;isBuyerMaker&lt;/code&gt; flag per trade: if &lt;code&gt;isBuyerMaker&lt;/code&gt; is false, the buyer was the taker (aggressive side) and the trade signs positive; if &lt;code&gt;isBuyerMaker&lt;/code&gt; is true, the seller was the taker and the trade signs negative.&lt;/p&gt;

&lt;p&gt;Signed volume is the input that separates order-flow analysis from pure price analysis. Price tells you &lt;em&gt;where&lt;/em&gt; the market ended up. Signed volume tells you &lt;em&gt;who&lt;/em&gt; drove it there — and at what cost in aggression.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the CVD formula?
&lt;/h2&gt;

&lt;p&gt;CVD is the running cumulative sum of signed taker volume. For a sequence of trades &lt;code&gt;i = 1, 2, …, t&lt;/code&gt;, each with size &lt;code&gt;v_i&lt;/code&gt; and aggressor sign &lt;code&gt;s_i ∈ {+1, −1}&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CVD(t) = Σ_{i=1}^{t}  s_i · v_i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;s_i = +1&lt;/code&gt; for buy-aggressor trades and &lt;code&gt;s_i = −1&lt;/code&gt; for sell-aggressor trades.&lt;/p&gt;

&lt;p&gt;This is public, textbook math. The formula is standard across microstructure practitioners and appears in order-flow literature under various names — "delta," "cumulative delta," or "signed flow" — all referring to the same accumulation of directional taker pressure.&lt;/p&gt;

&lt;p&gt;Over a fixed session window &lt;code&gt;[0, T]&lt;/code&gt;, CVD equals the total buy-aggressor volume minus the total sell-aggressor volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CVD(T) = V_buy_aggressor − V_sell_aggressor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A positive CVD means buyers have been more aggressive than sellers over the window. A negative CVD means sellers have been more aggressive. CVD of zero means aggression has been balanced, regardless of where price moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you reset or window CVD?
&lt;/h2&gt;

&lt;p&gt;CVD is defined relative to a starting point. Practitioners reset it at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session open&lt;/strong&gt; — accumulates intraday aggression from the first print.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed time windows&lt;/strong&gt; — e.g., rolling 1-hour or 4-hour CVD to capture regime changes within a session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Candle boundaries&lt;/strong&gt; — per-bar delta (the signed volume of a single candle) is the incremental building block; summing across candles reconstructs session CVD.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The choice of reset window is a calibration decision. A short window is more reactive but noisier; a long window integrates more signal but can obscure intraday shifts. TickDistill's computation applies a causal baseline — using only past data — so that the z-score of CVD at any point reflects its rarity relative to its own recent history, not a hard-coded threshold. See &lt;a href="//sigma-normalization.md"&gt;Sigma-Normalization: Why Order-Flow Signals Should Be Measured in Standard Deviations, Not Raw Numbers&lt;/a&gt; for the method.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does CVD-vs-price divergence mean?
&lt;/h2&gt;

&lt;p&gt;CVD-vs-price divergence is the condition where price and CVD move in opposite directions over the same window. It is the primary diagnostic use of CVD in order-flow analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bearish divergence&lt;/strong&gt; — price makes a higher high but CVD makes a lower high (or fails to confirm): buy-side aggression is weakening even as price moves up. Someone is selling passively into the aggressive buying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bullish divergence&lt;/strong&gt; — price makes a lower low but CVD makes a higher low (or flattens): sell-side aggression is weakening even as price moves down. Someone is buying passively into the aggressive selling.&lt;/p&gt;

&lt;p&gt;Divergence is a necessary observation, not a trading signal by itself. The textbook interpretation is that a large passive order — a "wall" of resting liquidity — is absorbing the aggressive flow without yielding price movement proportional to the pressure applied.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVD direction&lt;/th&gt;
&lt;th&gt;Price direction&lt;/th&gt;
&lt;th&gt;Reading&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rising&lt;/td&gt;
&lt;td&gt;Rising&lt;/td&gt;
&lt;td&gt;Confirmed upside aggression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Falling&lt;/td&gt;
&lt;td&gt;Falling&lt;/td&gt;
&lt;td&gt;Confirmed downside aggression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flat or falling&lt;/td&gt;
&lt;td&gt;Rising&lt;/td&gt;
&lt;td&gt;Passive selling absorbing buys (bearish divergence)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flat or rising&lt;/td&gt;
&lt;td&gt;Falling&lt;/td&gt;
&lt;td&gt;Passive buying absorbing sells (bullish divergence)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rising sharply&lt;/td&gt;
&lt;td&gt;Price contained&lt;/td&gt;
&lt;td&gt;Potential absorption: aggression without price progress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What is the aggressor side and why is it the right input?
&lt;/h2&gt;

&lt;p&gt;The aggressor side is the party who initiated the trade by submitting a market order or marketable limit order that crossed the spread. The aggressor consumes resting liquidity; the passive side provides it.&lt;/p&gt;

&lt;p&gt;Aggressor identification is the correct input for CVD for two reasons. First, the aggressor's decision is &lt;em&gt;unconditional&lt;/em&gt; — they chose to trade at the prevailing price, revealing genuine directional intent. Second, the passive side's resting order has no direction information encoded in the act of being filled; a limit order sitting at the bid reveals nothing about the limit-order poster's view of the next move.&lt;/p&gt;

&lt;p&gt;Without aggressor-side tagging, signed volume is approximation. Methods like the Lee-Ready rule (Lee and Ready, 1991, &lt;em&gt;Journal of Finance&lt;/em&gt;) classify trades by comparing the transaction price to the prevailing bid-ask midpoint — buys above midpoint, sells below, with a tick test for trades at the midpoint — and are used when exchange tape does not include the flag. Centralized exchanges publishing the raw &lt;code&gt;aggTrades&lt;/code&gt; feed (as Binance does) provide the clean input directly, making tick-classification algorithms unnecessary for those venues.&lt;/p&gt;

&lt;p&gt;For decentralized markets and OTC venues where no central tape exists, CVD cannot be computed in its clean form. CVD is a centralized-exchange signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does TickDistill compute CVD?
&lt;/h2&gt;

&lt;p&gt;TickDistill sources aggressor-tagged trades from the Binance Vision &lt;code&gt;aggTrades&lt;/code&gt; historical dumps (the &lt;code&gt;isBuyerMaker&lt;/code&gt; field per trade), which record the aggressor side directly from the exchange matching engine. This is the L1 data layer — the same layer that powers trade imbalance and big-order detection (see &lt;a href="//what-is-trade-imbalance-order-flow.md"&gt;What Is Trade Imbalance in Order Flow?&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The raw CVD accumulation is public math. TickDistill's value-add is in expressing CVD in causal sigma units — recomputing a rolling z-score of the running delta so that "high CVD" means high &lt;em&gt;relative to what this market has been doing lately&lt;/em&gt;, not high in absolute terms. A $100M positive CVD on BTC is very different information in a low-volume regime than in a high-volume regime. The z-score makes both comparable and the rarity reading stable across market conditions.&lt;/p&gt;

&lt;p&gt;The calibration of the baseline window is proprietary. What matters conceptually is that every baseline uses only past data (no look-ahead) and excludes mechanical windows — such as perpetual funding settlements at 00:00, 08:00, and 16:00 UTC — where volume spikes for structural reasons rather than informational ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  How is CVD related to trade imbalance?
&lt;/h2&gt;

&lt;p&gt;Trade imbalance is the per-window ratio of buy-aggressor volume to total volume, expressed as a fraction or percentage. CVD is the running sum of the signed delta. They measure the same underlying phenomenon — directional taker pressure — at different time horizons and in different units.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CVD&lt;/th&gt;
&lt;th&gt;Trade imbalance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unit&lt;/td&gt;
&lt;td&gt;Signed volume (contracts / USD notional)&lt;/td&gt;
&lt;td&gt;Fraction ∈ [−1, +1] or percentage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time scope&lt;/td&gt;
&lt;td&gt;Accumulates across a session or window&lt;/td&gt;
&lt;td&gt;Per fixed window (e.g., 30 s, 5 min)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary use&lt;/td&gt;
&lt;td&gt;Trend of aggressor pressure over session&lt;/td&gt;
&lt;td&gt;Snapshot of directional pressure in a window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Divergence read&lt;/td&gt;
&lt;td&gt;vs price over the same session&lt;/td&gt;
&lt;td&gt;vs recent baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;See &lt;a href="//what-is-trade-imbalance-order-flow.md"&gt;What Is Trade Imbalance in Order Flow?&lt;/a&gt; for trade imbalance in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the limits of CVD?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CVD cannot identify individual actors.&lt;/strong&gt; The Binance &lt;code&gt;aggTrades&lt;/code&gt; feed aggregates trades that execute within a short time window (100ms) at the same price and on the same aggressor side into a single record — which is not necessarily a single order. It cannot prove that multiple aggressive prints on the same side are from one participant. High positive CVD means many buy-aggressor fills occurred; it does not prove a single large buyer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVD does not capture passive pressure.&lt;/strong&gt; A market absorbed by large resting orders shows up as price containment in the face of CVD pressure — that is the divergence read — but CVD itself only accumulates the aggressive side. The passive actor's size is inferred, not measured, without a full order book (L2 data).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVD is directional, not predictive.&lt;/strong&gt; CVD measures the &lt;em&gt;current state&lt;/em&gt; of signed aggressor accumulation. It does not predict future price direction. A strongly positive CVD means buyers have been aggressive; it says nothing about whether that aggression will continue or whether it has already been fully absorbed. See What Is Order Flow Absorption? for how absorption detection builds on this observation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVD resets matter.&lt;/strong&gt; Two traders viewing "CVD" may disagree because they use different reset windows. Always specify the window when communicating CVD readings.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is CVD the same as OBV (On-Balance Volume)?&lt;/strong&gt;&lt;br&gt;
No. OBV (Granville, 1963) adds the full candle volume if price closes up and subtracts it if price closes down — it uses price direction to sign the volume. CVD uses the exchange tape's aggressor flag to sign each individual trade. CVD is more granular and more accurate about who initiated each trade; OBV is an approximation available wherever OHLCV data exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can CVD be computed for spot forex or equities?&lt;/strong&gt;&lt;br&gt;
CVD in its precise form requires a centralized tape that records the aggressor side. Spot forex has no single central tape, so clean CVD is not available there. Equity markets publish signed trade data via their own tape (e.g., NYSE TAQ) and Lee-Ready classification is widely applied where the flag is absent. TickDistill currently covers centralized crypto venues where the aggressor flag is available natively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What does it mean when CVD is flat but price moves sharply?&lt;/strong&gt;&lt;br&gt;
Flat CVD with sharp price movement means aggressor pressure was balanced, but price moved anyway. This often reflects a large passive order being pulled (removed from the book) rather than absorbed — the market found no resting liquidity at a level and price gapped through it. It is a different condition from absorption and can signal fragility rather than a strong passive actor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Does a large positive CVD mean the market will go higher?&lt;/strong&gt;&lt;br&gt;
No. CVD is a measure of past aggressor accumulation, not a forecast. Large positive CVD means buyers have been aggressive over the measurement window. Whether that aggression has already been absorbed, whether it is exhausted, or whether it will continue is a separate question requiring additional context — including price behavior, absorption signals, and regime state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Where does CVD fit in a broader order-flow stack?&lt;/strong&gt;&lt;br&gt;
CVD is a foundational L1 metric — it requires only the trade tape with aggressor tagging, no order book. It sits above pure OHLCV (which has no aggressor information) and below L2 signals that use full book depth. It feeds divergence reads, absorption detection, and trade imbalance analysis. See &lt;a href="//what-is-order-flow-microstructure.md"&gt;What Is Order Flow Microstructure?&lt;/a&gt; for the full hierarchy.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TickDistill sells clean, computed order-flow inputs — not trading advice or guaranteed alpha. Backtests are illustrative and not a promise of future results.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>algotrading</category>
      <category>crypto</category>
      <category>python</category>
    </item>
    <item>
      <title>What Is Order-Flow Microstructure? A Plain-English Guide to Reading the Tape</title>
      <dc:creator>TickDistill</dc:creator>
      <pubDate>Thu, 25 Jun 2026 14:18:37 +0000</pubDate>
      <link>https://dev.to/tickdistill/what-is-order-flow-microstructure-a-plain-english-guide-to-reading-the-tape-58dj</link>
      <guid>https://dev.to/tickdistill/what-is-order-flow-microstructure-a-plain-english-guide-to-reading-the-tape-58dj</guid>
      <description>&lt;h1&gt;
  
  
  What Is Order-Flow Microstructure? A Plain-English Guide to Reading the Tape
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;By TickDistill — order-flow microstructure signals. Educational content, not financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Order-flow microstructure&lt;/strong&gt; is the study of who initiates each trade — the buyer or the seller — and what the accumulated pattern of those initiations reveals about supply, demand, and near-term price pressure. Every trade has an aggressor: the side that crossed the spread and lifted or hit the resting quote. Microstructure measures that aggressor-side imbalance, its size, its clustering, and its context, rather than price or volume alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the tape, and why does it matter?
&lt;/h2&gt;

&lt;p&gt;The tape is the real-time stream of every executed trade: timestamp, price, size, and — critically — which side was the aggressor. On centralized venues such as CME Globex or Binance, each matched trade carries a flag identifying whether the buyer or the seller was the market-order initiator. That flag is the foundation of all order-flow analysis.&lt;/p&gt;

&lt;p&gt;Without the aggressor field, a 1,000-contract trade is ambiguous: it could be a buyer lifting offers (demand), a seller hitting bids (supply), or a mix. With the aggressor field, the tape becomes directional: you can separate buying pressure from selling pressure in every window you choose to measure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the aggressor side, and how is it identified?
&lt;/h2&gt;

&lt;p&gt;The aggressor side is the counterparty that submitted a market order (or a marketable limit order) and consumed resting liquidity. The passive side is the limit-order resting in the book. Every trade has exactly one aggressor and one passive participant.&lt;/p&gt;

&lt;p&gt;On Binance aggTrades, the aggressor field is the &lt;code&gt;isBuyerMaker&lt;/code&gt; boolean: when &lt;code&gt;isBuyerMaker = false&lt;/code&gt;, the buyer is the aggressor (lifted offers); when &lt;code&gt;isBuyerMaker = true&lt;/code&gt;, the seller is the aggressor (hit bids). This is the L1 layer: trade data with aggressor identification, available free from Binance Vision historical dumps.&lt;/p&gt;

&lt;p&gt;Note: aggTrades on a single venue cannot prove a single actor placed a given volume. A large buy-side print may be one institutional order or many retail orders arriving simultaneously. L1 measures the &lt;em&gt;directional pressure&lt;/em&gt;, not the identity of the participant.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is signed order flow, and what does it measure?
&lt;/h2&gt;

&lt;p&gt;Signed order flow (also called trade imbalance) is the net difference between buy-aggressed and sell-aggressed volume (or trade count) over a fixed time window. It is positive when buyers are more aggressive than sellers, negative when the reverse holds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;signed_flow_t = sum(buy_volume_i) - sum(sell_volume_i)   for trades i in [t-w, t]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;w&lt;/code&gt; is the aggregation window. This is a &lt;em&gt;trade-level&lt;/em&gt; quantity in the Kyle (1985) tradition — it is computed entirely from the executed tape (the L1 layer), and it is what most practitioners loosely call "order-flow imbalance." It is a real, useful signal, but it is &lt;strong&gt;not&lt;/strong&gt; the same object as the order-book OFI of Cont, Kukanov, and Stoikov.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A distinction that matters.&lt;/strong&gt; True &lt;strong&gt;order-flow imbalance (OFI)&lt;/strong&gt; in the sense of Cont, Kukanov, and Stoikov (2014) — extended to generalized and cross-asset OFI by Cont, Cucuringu, and Zhang (2023) — is defined on &lt;strong&gt;order-book events at the best bid and ask&lt;/strong&gt;: queue additions, size changes, and cancellations that move the top of book, not just the executions that cross the spread. CKS report a high contemporaneous R² for that book-event OFI regressed on mid-price changes (their cross-sectional average across 50 NYSE stocks is around 65%), and find the intraday price impact approximately linear in OFI. That headline result belongs to the &lt;strong&gt;book-event&lt;/strong&gt; definition, not to the trade-signed-volume formula above. Because it reads the resting book, true OFI requires &lt;strong&gt;Level-2 (L2) order-book data&lt;/strong&gt;, which is why TickDistill places it at the L4 book layer (paid), not the free trade tier. For the precise three-case construction and the depth-normalization, see What Is Order-Flow Imbalance (OFI), and Why Does It Need the Order Book?.&lt;/p&gt;

&lt;p&gt;TickDistill computes a causal z-score of signed order flow — expressed in standard deviations from its own rolling distribution — and emits a rarity reading. The exact window and normalization parameters are calibrated per market and are proprietary; why that calibration matters is explained in &lt;a href="//sigma-normalization.md"&gt;Sigma-Normalization&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cumulative Volume Delta (CVD), and how does it relate to OFI?
&lt;/h2&gt;

&lt;p&gt;Cumulative Volume Delta (CVD) is the running sum of signed volume — buy-aggressed volume minus sell-aggressed volume — accumulated without resetting over a session or period. Where OFI is a windowed snapshot, CVD is a cumulative ledger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CVD_t = CVD_{t-1} + (buy_volume_t - sell_volume_t)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CVD is widely cited in technical and market-microstructure communities as a divergence indicator: when price makes a new high but CVD does not, the buying pressure underpinning that rally is weakening relative to price. This divergence is a standard observation in practitioner microstructure literature.&lt;/p&gt;

&lt;p&gt;TickDistill provides CVD as part of its free signal tier. For a detailed treatment, see &lt;a href="//what-is-cumulative-volume-delta-cvd.md"&gt;What Is Cumulative Volume Delta (CVD)?&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is VPIN, and what does it measure?
&lt;/h2&gt;

&lt;p&gt;VPIN (Volume-synchronized Probability of Informed Trading) is a measure of order-flow toxicity, introduced by Easley, López de Prado, and O'Hara (2012). It estimates the probability that the counterparty to a trade is an informed trader, using volume time rather than clock time.&lt;/p&gt;

&lt;p&gt;The public formula slices traded volume into equal-size buckets, then computes the proportion of each bucket that is buy-initiated versus sell-initiated, averaged over a rolling support window of &lt;code&gt;N&lt;/code&gt; buckets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPIN = (1/N) * sum_{i=1..N} ( |buy_volume_i - sell_volume_i| / V )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where each bucket holds a fixed volume &lt;code&gt;V&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is the number of buckets in the support window. In their original construction, Easley, López de Prado, and O'Hara (2012) set the bucket size to one-fiftieth of average daily volume — &lt;code&gt;V = (daily volume) / 50&lt;/code&gt; — and compute VPIN over a rolling support window of &lt;code&gt;N = 50&lt;/code&gt; buckets. They document that VPIN was elevated and rising in the hours leading up to the 2010 Flash Crash.&lt;/p&gt;

&lt;p&gt;VPIN is not directional: a high VPIN reading signals that informed order flow is elevated — that the market is experiencing toxicity — not which direction price will move. It is a jump-risk input, not a price forecast.&lt;/p&gt;

&lt;p&gt;TickDistill computes a causal z-score of VPIN rather than applying the folklore 0.7 cutoff from practitioner literature. The threshold that turns a VPIN level into a regime state is proprietary; the calibration exists because different markets and regimes have different baseline toxicity distributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is price impact, and what formulas describe it?
&lt;/h2&gt;

&lt;p&gt;Price impact is the change in mid-price caused by a trade of a given size. Kyle (1985) introduced the linear price impact model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delta_p = lambda * (buy_volume - sell_volume)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;lambda&lt;/code&gt; (Kyle's lambda) is the price impact coefficient, estimated from the regression of mid-price changes on signed order flow. A higher lambda means the market is less liquid: each unit of aggressive order flow moves the price more.&lt;/p&gt;

&lt;p&gt;The square-root market impact law (Bouchaud, Gefen, Potters, and Wyart 2004; and the empirical surveys in Bouchaud et al. 2018) describes average permanent impact as growing sub-linearly with order size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;impact ~ sigma * sqrt(Q / V_daily)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;Q&lt;/code&gt; is order size, &lt;code&gt;V_daily&lt;/code&gt; is average daily volume, and &lt;code&gt;sigma&lt;/code&gt; is volatility. The prefactor is instrument-dependent and empirically estimated — TickDistill does not publish the prefactor for any specific market. The key insight is that impact is concave in size, which is why large orders are typically split into child orders.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the data layers, and what can each layer measure?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Event type&lt;/th&gt;
&lt;th&gt;Data source&lt;/th&gt;
&lt;th&gt;What it enables&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;L0&lt;/td&gt;
&lt;td&gt;OHLCV candles (&lt;code&gt;CandleEvent&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Exchange klines (free, universal)&lt;/td&gt;
&lt;td&gt;Volatility, momentum, VWAP, range breakouts — the universal baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1&lt;/td&gt;
&lt;td&gt;Trades with aggressor side (&lt;code&gt;TradeEvent&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Binance aggTrades, CME tick (free/paid)&lt;/td&gt;
&lt;td&gt;CVD, signed order flow, VPIN, big-order detection, trade imbalance, cross-venue flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L2 derivatives&lt;/td&gt;
&lt;td&gt;Funding, open interest, liquidations (&lt;code&gt;MetricEvent&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Coinalyze, exchange REST (free for crypto)&lt;/td&gt;
&lt;td&gt;Funding regime, OI shift/divergence, liquidation pressure, squeeze composites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L4 order book&lt;/td&gt;
&lt;td&gt;Limit-order book snapshots and updates (&lt;code&gt;BookEvent&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Tardis.dev incremental L2 (paid)&lt;/td&gt;
&lt;td&gt;True OFI at the queue level, absorption, liquidity maps, iceberg detection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;L0 signals are universal across all markets. L1 signals require a centralized tape with aggressor identification, which is available on crypto centralized exchanges and CME futures, but not on fragmented forex spot markets where there is no single tape. L4 signals require paid historical book data or self-captured streaming book data.&lt;/p&gt;

&lt;p&gt;TickDistill's architecture converts every raw exchange format into a normalized internal event schema (&lt;code&gt;TradeEvent&lt;/code&gt;, &lt;code&gt;BookEvent&lt;/code&gt;, etc.) so that signal processors run the same logic across BTC, ETH, SOL, and later ES/NQ futures — only the per-market calibration profile changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is big-order detection, and why does it matter?
&lt;/h2&gt;

&lt;p&gt;Big-order detection identifies individual trade prints or clusters of prints that exceed a statistically rare size threshold for their market and regime. A single large aggressive print may indicate institutional urgency; a cluster of large prints on one side without interleaving from the opposite side suggests a sweep — a coordinated clearing of resting liquidity.&lt;/p&gt;

&lt;p&gt;The size threshold for "big" is meaningless as an absolute dollar figure. A $10M notional print is routine for BTC and enormous for a small-cap futures contract. The threshold must be expressed in sigma units relative to the local distribution — exactly the sigma-normalization principle described earlier.&lt;/p&gt;

&lt;p&gt;TickDistill generates big-order primitive records as a shared foundation that downstream paid signals (density, sweep geometry, conviction zones) consume. The primitive itself is free; the derived geometry is paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is trade imbalance, and how does it differ from CVD?
&lt;/h2&gt;

&lt;p&gt;Trade imbalance is the ratio or difference between buy-aggressed and sell-aggressed volume within a fixed clock-time window, reported as a snapshot rather than accumulated. Where CVD grows indefinitely and requires a reset decision, trade imbalance is self-contained within its window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;imbalance_t = (buy_volume - sell_volume) / (buy_volume + sell_volume)   in [-1, +1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An imbalance near +1 means nearly all aggressive volume in the window was buy-initiated; near -1, sell-initiated; near 0, balanced. For a full treatment, see &lt;a href="//what-is-trade-imbalance-order-flow.md"&gt;What Is Trade Imbalance in Order Flow?&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the derivatives layer (L2), and what does it add?
&lt;/h2&gt;

&lt;p&gt;The L2 derivatives layer covers open interest (OI), funding rates, and liquidations — data that reflects the positioning and financing state of futures and perpetual markets rather than the immediate tape flow.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Public formula / reference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;funding_regime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;State of the perpetual funding rate (positive/negative/extreme)&lt;/td&gt;
&lt;td&gt;Perp mechanics: funding = premium index, settled on Binance's public 8-hour schedule (00:00 / 08:00 / 16:00 UTC) — published exchange mechanics, unrelated to any TickDistill normalization or exclusion window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oi_shift&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OI × price direction → 4-regime positioning classifier&lt;/td&gt;
&lt;td&gt;Hong and Yogo (2012) document OI as a positioning signal; 4-quadrant framework is standard practitioner microstructure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;liq_pressure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Liquidation volume and cascade risk&lt;/td&gt;
&lt;td&gt;Liquidations are public (exchange API); cascade risk from Osler (2005) stop-order clustering logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;squeeze&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Composite of OI, funding, long/short ratio, liquidations&lt;/td&gt;
&lt;td&gt;Composite; calibration proprietary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The derivatives layer is free to access on crypto exchanges (Coinalyze, Binance REST), making L2 derivative signals available at no data cost. The computation and normalization are what TickDistill adds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is cross-venue flow, and what does it measure?
&lt;/h2&gt;

&lt;p&gt;Cross-venue flow divergence measures whether aggressive order flow on one exchange is running ahead of flow on another. If buy-side pressure is building faster on Exchange A than Exchange B for the same underlying asset, one of two things is happening: either arbitrage bots have not yet equalized the imbalance, or the flow is genuinely instrument-specific.&lt;/p&gt;

&lt;p&gt;Lead-lag analysis at the tick level uses tools from the Hawkes process literature (Bacry, Mastromatteo, and Muzy 2015 for Hawkes-based flow clustering) and the Hayashi-Yoshida estimator (Hayashi and Yoshida 2005) for covariation of non-synchronous tick series. At the tick level, lead-lag relationships are noisy and venue-dependent; TickDistill treats the &lt;code&gt;lead_s&lt;/code&gt; and log-likelihood ratio statistics as diagnostics, not definitive directional claims.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Does order-flow microstructure work on forex spot markets?&lt;/strong&gt;&lt;br&gt;
No, not in the same way. Forex spot has no single centralized tape — trades are bilateral and OTC, so there is no universal aggressor field. Trade-aggressor signals (CVD, VPIN, signed order flow, trade imbalance) are not applicable to forex spot without a primary dealer tape, and true order-book OFI is unavailable without a unified L2 book. They apply to centralized crypto exchanges and CME futures, which have unified matching engines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Is order-flow microstructure the same as technical analysis?&lt;/strong&gt;&lt;br&gt;
No. Technical analysis derives signals from OHLCV price and volume aggregates (RSI, MACD, Bollinger bands). Order-flow microstructure operates on the underlying trade-by-trade record — the signed, directional raw material that OHLCV data discards. A 1-minute candle collapses thousands of individual trades into six numbers; microstructure reads each trade before that collapse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: What does "aggressor side" mean on an exchange that aggregates trades?&lt;/strong&gt;&lt;br&gt;
On Binance, the &lt;code&gt;aggTrades&lt;/code&gt; feed combines consecutive trades at the same price and direction into a single record, tagged with &lt;code&gt;isBuyerMaker&lt;/code&gt;. The aggregation is mechanical and does not imply a single actor placed all that volume. The aggressor field tells you which side was market-order-initiated; it does not identify the participant or prove institutional intent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why does the normalization window matter more than the raw signal?&lt;/strong&gt;&lt;br&gt;
Because a raw imbalance of 60% buy is meaningless without context. In a calm BTC session 60% may be a 2σ rarity; during a volatile session the same 60% might be ordinary noise. Sigma-normalization makes the rarity reading consistent across regimes and assets. The choice of normalization window determines how "recent" the baseline is — too short and it is noisy; too long and it is stale. Calibration is proprietary for each market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I combine microstructure signals to build a directional strategy?&lt;/strong&gt;&lt;br&gt;
TickDistill sells individual, independently computed signal states — not a combined directional system. Combining signals, deciding weights, and managing the resulting strategy is the client's domain. We sell the measurement, not the alpha — see &lt;a href="//why-sell-the-measurement-not-the-alpha.md"&gt;Why Sell the Measurement, Not the Alpha?&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TickDistill sells clean, computed order-flow inputs — not trading advice or guaranteed alpha. Backtests are illustrative and not a promise of future results.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>quant</category>
      <category>algotrading</category>
      <category>crypto</category>
      <category>python</category>
    </item>
  </channel>
</rss>
