10 Commits
Author SHA1 Message Date
jackyu66git 21b77d62ca feat: 接入 Google Analytics (G-LVVXH3TL04) 2026-07-02 15:57:50 +08:00
PRAKUL HIREMATHandGitHub 6bf8ec9263 Create index.html 2026-06-07 22:17:50 +05:30
PRAKUL HIREMATHandGitHub d6137d3f4e Update README.md 2026-06-05 14:37:57 +05:30
PRAKUL HIREMATHandGitHub 977a1ad28f Create CITATION.cff 2026-05-31 00:47:09 +05:30
PRAKUL HIREMATHandGitHub 095d5c0ded Add badges for arXiv, DOI, and method details 2026-04-24 11:24:48 +05:30
PRAKUL HIREMATHandGitHub 8222fa6ce0 Update header formatting in README.md 2026-04-22 21:18:57 +05:30
PRAKUL HIREMATHandGitHub fb82e3569d Update README by removing author details
Removed author names and unnecessary line breaks from README.
2026-04-22 21:18:40 +05:30
PRAKUL HIREMATHandGitHub 41b52792be Update DOI badge and precision/lead-time labels 2026-04-22 21:17:02 +05:30
PRAKUL HIREMATHandGitHub e3b4473428 Remove unnecessary HTML entity from README 2026-04-22 21:15:17 +05:30
PRAKUL HIREMATHandGitHub aea1332fef Revise README for improved structure and content
Updated README to enhance formatting, improve clarity, and add new sections.
2026-04-22 21:10:48 +05:30
4 changed files with 1537 additions and 82 deletions
+18
View File
@@ -0,0 +1,18 @@
cff-version: 1.2.0
message: "If you use this software or refer to the research in your work, please cite it as below."
authors:
- family-names: "Hiremath"
given-names: "Prakul Sunil"
- family-names: "Hiremath"
given-names: "Vruksha Arun"
title: "Early Detection of Latent Micro-Regimes in Limit Order Books"
version: "1.0.0"
doi: "10.5281/zenodo.19697687"
date-released: "2026-04-30"
url: "https://github.com/prakulhiremath/LOB-Latent-Regimes"
keywords:
- "limit-order-book"
- "market-microstructure"
- "hidden-markov-models"
- "regime-detection"
- "quantitative-finance"
+172 -81
View File
@@ -1,143 +1,234 @@
# ⚡️ Latent Micro-Regime Early Detection in Limit Order Books <div align="center">
<p align="center"> <br/>
<img src="assets/detection.gif" width="850" alt="Detection Timeline Visualization"/>
</p>
<p align="center"> ```
<a href="#"><img src="[https://img.shields.io/badge/Method-Trigger%20Based-0078D4?style=for-the-badge&logo=lightning](https://img.shields.io/badge/Method-Trigger%20Based-0078D4?style=for-the-badge&logo=lightning)"/></a> ╔══════════════════════════════════════════════════════════════════╗
<a href="#"><img src="[https://img.shields.io/badge/Model-HMM-8A2BE2?style=for-the-badge&logo=scikitlearn](https://img.shields.io/badge/Model-HMM-8A2BE2?style=for-the-badge&logo=scikitlearn)"/></a> ║ LATENT MICRO-REGIME EARLY DETECTION IN LIMIT ORDER BOOKS ║
<a href="#"><img src="[https://img.shields.io/badge/Precision-100%25-D4AF37?style=for-the-badge&logo=target](https://img.shields.io/badge/Precision-100%25-D4AF37?style=for-the-badge&logo=target)"/></a> ║ Identifying structural market instability before it surfaces ║
<a href="#"><img src="[https://img.shields.io/badge/Lead--Time-Positive-28A745?style=for-the-badge&logo=clock](https://img.shields.io/badge/Lead--Time-Positive-28A745?style=for-the-badge&logo=clock)"/></a> ╚══════════════════════════════════════════════════════════════════╝
<a href="[https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)"><img src="[https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)"/></a> ```
</p> [![arXiv](https://img.shields.io/badge/arXiv-2604.20949-b31b1b.svg?style=flat-square)](https://arxiv.org/abs/2604.20949)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19697687.svg?sanitize=true)](https://doi.org/10.5281/zenodo.19697687)
![Method](https://img.shields.io/badge/Method-Trigger%20Based-0078D4?style=flat-square)
![Model](https://img.shields.io/badge/Model-HMM-8A2BE2?style=flat-square)
![Precision](https://img.shields.io/badge/Precision-100%25%20(eval)-D4AF37?style=flat-square)
![Lead-Time](https://img.shields.io/badge/Lead--Time-Positive-28A745?style=flat-square)
![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
<img src="assets/detection.gif" width="860" alt="Detection Timeline — Latent regime transitions identified before observable stress"/>
<br/>
</div>
--- ---
## 🔬 Overview ## The Core Idea
This research investigates whether **latent microstructure dynamics** in Limit Order Books (LOB) can be mathematically identified *before* observable liquidity stress manifests. > **Market stress does not arrive without warning. It *accumulates*.**
Traditional signals like volatility and order imbalance are **reactive**—they trigger only after a dislocation has occurred. This project introduces a predictive framework focusing on the **Latent Build-up Phase**, identifying structural instability before it translates into price or spread shocks. Classical indicators — volatility, order imbalance, spread widening — are *reactive*. By the time they fire, the dislocation has already begun.
This research asks a harder question:
> *Can we detect the structural deterioration that **precedes** observable stress — before it becomes visible in price or spread?*
The answer is yes. We call it the **Latent Build-up Phase**.
--- ---
## 🧠 Core Methodology: The Latent Build-up ## What "Latent" Means Here
Market stress is rarely instantaneous; it is preceded by **structural deterioration**. We model this as a three-state latent process: The market moves through three regimes. The critical one is invisible to standard monitors:
| State | Regime | Market Description | Signal Characteristic | ```
| :--- | :--- | :--- | :--- | ┌─────────────────────────────────────────────────────────────────┐
| **0** | **Stable** | Balanced liquidity, high resilience | Equilibrium | │ │
| **1** | **Latent Build-up** | Depth erosion, subtle spread drift | **Hidden Instability** | │ STATE 0 ──────────────► STATE 1 ──────────────► STATE 2 │
| **2** | **Stress** | Observable dislocation, price shocks | Reactive | │ │
│ Stable Latent Build-up Stress │
│ ───────── ─────────────── ────── │
│ Balanced liquidity Depth eroding Price shock │
│ High resilience Spread drifting Visible │
│ Equilibrium ⚠ Hidden instability Reactive │
│ │
│ ◄────── detection window ──────► │
│ ↑ ↑ │
│ our signal fires stress begins │
│ │
└─────────────────────────────────────────────────────────────────┘
```
> **Key Discovery:** A delayed transition from **State 1 → State 2** creates a deterministic prediction window, allowing for early detection with strictly positive lead-time. **The key insight:** The transition from State 1 → State 2 is not instantaneous. There is a measurable delay — and within that delay lives our detection window. We exploit it.
--- ---
## 🛠 Detection Framework ## Detection Framework
The detector employs a high-fidelity fusion of probabilistic and temporal signals to identify the "inflection point" of market health. Three independent signal channels. One fused trigger.
### 📡 Signal Integration ### Signal Channels
* **Probabilistic Instability:** HMM posterior entropy monitoring.
* **Temporal Drift:** Recursive analysis of spread and depth dynamics.
* **Structural Decay:** Real-time tracking of depth erosion and order flow toxicity.
### 🕹 Detection Logic | Channel | What It Measures | Why It's Early |
* **MAX-Trigger Fusion:** Cross-channel integration to capture the first sign of decay. |---|---|---|
* **Rising-Edge Detection:** Focusing on the *onset* of change rather than absolute thresholds. | **HMM Posterior Entropy** | Uncertainty in regime classification | Rises as the latent state becomes ambiguous, before the transition |
* **Early-Detection Constraint:** Optimization of $\tau < \sigma$, ensuring the signal precedes the event. | **Temporal Depth Drift** | Recursive tracking of LOB depth erosion | Captures slow structural decay invisible to snapshot metrics |
| **Order Flow Toxicity** | Imbalance between informed and uninformed flow | Signals adverse selection building in the book |
### Trigger Logic
```
MAX-Fusion Trigger
├── Rising-edge detection (onset of change, not absolute level)
├── Cross-channel aggregation (fire when any channel breaches threshold)
└── Early-detection constraint τ < σ (signal must precede stress)
```
**Rising-edge detection** is the key design choice. We don't ask "is the spread wide?" We ask "is it *getting* wider *right now*?" This bypasses the noise floor that kills absolute-threshold methods.
--- ---
## 📊 Results & Performance ## Results
| Method | Mean $\Delta$ (Lead-Time) | Precision | Coverage | ```
| :--- | :--- | :--- | :--- | ╔════════════════════════════════════════════════════════════════╗
| **Adaptive Trigger** | **+18.62** | **100%** | 52.6% | ║ METHOD LEAD-TIME PRECISION COVERAGE ║
| **Model HMM** | **+14.95** | **100%** | 43.2% | ╠════════════════════════════════════════════════════════════════╣
| **Multi-Trigger** | **+13.15** | **100%** | 28.1% | ║ ★ Adaptive Trigger +18.6 steps 100% 52.6% ║
| Order Imbalance | -24.84 | 54.9% | 78.7% | ║ HMM +14.9 steps 100% 43.2% ║
| Volatility | -32.02 | 45.5% | 43.3% | ║ Multi-Trigger +13.1 steps 100% 28.1% ║
╠════════════════════════════════════════════════════════════════╣
║ ✗ Order Imbalance 24.8 steps 54.9% 78.7% ║
║ ✗ Volatility 32.0 steps 45.5% 43.3% ║
╚════════════════════════════════════════════════════════════════╝
```
### Critical Interpretations: **Reading the table:**
* **Positive Lead-Time:** Our methods detect stress *before* it happens; baselines are strictly negative (lagging). - **Positive lead-time** means the signal fires *before* stress begins. Baselines are strictly negative — they lag.
* **Temporal Validity:** 100% precision indicates zero "false starts" before the latent phase begins. - **100% precision** means zero false starts during the latent phase — every trigger issued is temporally valid.
* **Trade-off:** Coverage levels reflect the conservative nature of high-precision early signals. - **Coverage** reflects selectivity: we fire only when we're certain. The conservative nature of high-precision detection is a design property, not a flaw.
> These results are reported under evaluated pipeline settings with full reproducibility guarantees (see below).
--- ---
## 📈 Key Findings ## Empirical Findings
1. **Latent Instability exists:** Market regimes degrade structurally before they degrade visually.
2. **Primary Indicators:** Depth erosion and HMM entropy are the most robust early-warning metrics. **1. Latent instability exists and is measurable.**
3. **Signal Edge:** Rising-edge detection is essential to bypass the noise inherent in absolute thresholding. Market regimes structurally deteriorate before the deterioration is visible. This is not a modelling artifact — it is a consistent empirical signature across tested sessions.
4. **Performance:** Trigger-based detection consistently outperforms classical econometric baselines.
**2. Depth erosion is the most reliable early signal.**
Depth decay in the LOB precedes spread widening and price impact. If the book is thinning quietly, something is coming.
**3. HMM posterior entropy is a structural stress barometer.**
As the market approaches a regime transition, the HMM becomes uncertain — and that uncertainty is itself informative.
**4. Rising-edge detection outperforms threshold detection.**
The onset of deterioration carries more information than its magnitude. Threshold-based methods are too noisy; they fire on noise and miss the trend.
**5. Trigger-based fusion consistently beats classical econometric baselines** — not marginally, but categorically. The comparison is not between better and worse versions of the same approach. It is between a predictive framework and a reactive one.
--- ---
## 📂 Repository Structure ## Repository Structure
```bash ```
. LOB-Latent-Regimes/
├── experiments/ # Iterative development v1 → v7
├── notebooks/ # Production-grade experiment analysis ├── experiments/
│ ├── v1_baseline.py # Initial HMM formulation
│ ├── v2_entropy.py # Posterior entropy tracking
│ ├── v3_depth_drift.py # Temporal depth signal
│ ├── v4_triggers.py # Trigger logic development
│ ├── v5_fusion.py # MAX-fusion framework
│ ├── v6_rising_edge.py # Rising-edge detection
│ └── v7_final.py # ★ Production pipeline
├── notebooks/
│ └── analysis.ipynb # Experiment analysis + figures
├── results/ ├── results/
│ ├── figures/ # High-resolution performance plots │ ├── figures/ # High-resolution performance plots
│ └── summary.txt # Quantified results summary │ └── summary.txt # Quantified results
├── paper/ # Technical manuscript (PDF)
├── assets/ # Visualizations and GIFs ├── paper/ # Technical manuscript
├── assets/ # Visualizations, GIFs
└── README.md └── README.md
``` ```
--- ---
## 🚀 Reproducibility ## Reproducibility Guarantees
Validated across high-compute and local environments: This pipeline was built to be trusted.
* **Cloud:** Google Colab (NVIDIA T4)
* **Local:** Apple Silicon (M4 Pro/Max) ```
✓ Fully causal — no lookahead bias at any stage
✓ Rolling normalization only — no global statistics that leak future data
✓ HMM re-fit periodically — no leakage across the evaluation window
✓ Deterministic seeds — results are exact across runs
✓ Validated on Google Colab (NVIDIA T4) and Apple Silicon (M4 Pro/Max)
```
---
## Quick Start
### Quick Start
```bash ```bash
# Clone the repository # Clone
git clone https://github.com/your-repo/lob-early-detection.git git clone https://github.com/prakulhiremath/LOB-Latent-Regimes.git
cd LOB-Latent-Regimes
# Install dependencies # Install
pip install -r requirements.txt pip install -r requirements.txt
# Execute the final pipeline # Run the final pipeline
python experiments/v7_final.py python experiments/v7_final.py
``` ```
--- ---
## 📝 Contributions ## Scope & Limitations
* **Causal Formulation:** Formalizing the Latent Build-up $\rightarrow$ Stress transition.
* **Temporal Drift:** Identifying subtle drift as a precursor to liquidity voids. Be precise about what this is.
* **MAX Fusion & Rising-Edge:** Novel trigger logic for sub-millisecond microstructure data.
* **Empirical Proof:** Demonstrating strictly positive lead-time over reactive benchmarks. | This repo **is** | This repo **is not** |
|---|---|
| A detection framework for latent regime transitions | A trading strategy |
| An empirical study of LOB microstructure | Optimised for execution latency |
| A reproducible research pipeline | A production system |
| A contribution to predictive market microstructure | Financial advice |
--- ---
## 📑 Citation ## Contributions
- **Causal formulation** of the latent build-up → stress transition as a three-state latent process
- **Temporal drift identification** — subtle depth and spread drift as a leading precursor to liquidity voids
- **MAX-fusion + rising-edge trigger** — novel detection logic for sub-millisecond microstructure data
- **Empirical demonstration** of strictly positive lead-time over reactive benchmarks across all evaluated regimes
---
## Citation
```bibtex ```bibtex
@article{lob_micro_regime_detection_2026, @article{hiremath2026lob,
title={Early Detection of Latent Micro-Regimes in Limit Order Books}, title = {Early Detection of Latent Micro-Regimes in Limit Order Books},
author={Hiremath, Prakul. & Hiremath, Vruksha}, author = {Hiremath, Prakul Sunil and Hiremath, Vruksha Arun},
year={2026}, year = {2026},
doi = {10.5281/zenodo.19697687}
} }
``` ```
--- ---
<p align="center"> <div align="center">
Built with ☕️ and 🐍 for <b>Reproducible Quantitative Finance.</b>
</p>
Built for **reproducible research** in quantitative finance and machine learning.
*If the signal fires before the storm — it worked.*
</div>
+359
View File
@@ -0,0 +1,359 @@
#!/usr/bin/env python3
"""
Generate HMM regime chart as single-file HTML with pure Canvas rendering.
No external dependencies — works everywhere.
"""
import json, sys, os, glob
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from hmmlearn.hmm import GaussianHMM
import warnings
warnings.filterwarnings("ignore")
SEED = 42
N_REGIMES = 3
STEP = 5
def load_snapshots(filepath):
snaps = []
with open(filepath) as f:
for line in f:
line = line.strip()
if line:
snaps.append(json.loads(line))
return snaps
def extract_features(snapshots):
records = []
for s in snapshots:
ts = s["_collect_ts"]
bids = sorted(s["bids"], key=lambda x: x[0], reverse=True)
asks = sorted(s["asks"], key=lambda x: x[0])
bb = bids[0][0] if bids else 0
ba = asks[0][0] if asks else 0
mid = (bb + ba) / 2 if bb and ba else 0
spread = ba - bb if bb and ba else 0
bd = sum(b[1] for b in bids[:10])
ad = sum(a[1] for a in asks[:10])
depth = bd + ad
bv = sum(b[0] * b[1] for b in bids[:10])
av = sum(a[0] * a[1] for a in asks[:10])
imbalance = (bv - av) / (bv + av + 1e-12)
records.append({"ts": ts, "mid": mid, "spread": spread, "depth": depth, "imbalance": imbalance})
return pd.DataFrame(records)
def engineer_features(df, win=20):
s, d, imb = df["spread"].values, df["depth"].values, df["imbalance"].values
rv = pd.Series(df["mid"]).pct_change().rolling(win, min_periods=1).std().fillna(0).values
ofi = pd.Series(df["mid"]).diff().rolling(win, min_periods=1).mean().fillna(0).values
return np.column_stack([s, d, imb, rv, ofi])
def fit_hmm(X):
best_score, best_model = -np.inf, None
for k in range(12):
m = GaussianHMM(n_components=N_REGIMES, covariance_type="full",
n_iter=400, tol=1e-7, random_state=SEED + k,
init_params="stmc", params="stmc")
try:
m.fit(X)
sc = m.score(X)
if sc > best_score:
best_score, best_model = sc, m
except Exception:
continue
if best_model is None:
raise RuntimeError("HMM fitting failed.")
return best_model
HTML = """<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LVVXH3TL04"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LVVXH3TL04');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>LINK HMM Regime — Canvas Chart</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0d1117;color:#c9d1d9;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;overflow:hidden}
#topbar{background:#161b22;padding:10px 20px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #30363d;flex-wrap:wrap;gap:8px}
#topbar h1{font-size:15px;font-weight:600;color:#f0f6fc}
.legend{display:flex;gap:14px;font-size:12px;flex-wrap:wrap}
.legend span{display:flex;align-items:center;gap:5px}
.legend i{display:inline-block;width:10px;height:10px;border-radius:2px}
#chart{width:100%;height:calc(100vh - 46px);display:block}
#loading{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);color:#8b949e;font-size:14px;z-index:10}
#tooltip{position:fixed;background:#21262d;color:#c9d1d9;padding:6px 10px;border-radius:6px;font-size:12px;border:1px solid #30363d;pointer-events:none;display:none;white-space:nowrap;z-index:20}
</style>
</head>
<body>
<div id="topbar">
<h1>LINK/USD — HMM Regime Detection (12h)</h1>
<div class="legend">
<span><i style="background:rgba(46,204,113,0.4);border:1px solid #2ecc71"></i> Low Spread</span>
<span><i style="background:rgba(241,196,15,0.3);border:1px solid #f1c40f"></i> Mid Spread</span>
<span><i style="background:rgba(231,76,60,0.3);border:1px solid #e74c3c"></i> High Spread</span>
<span style="color:#8b949e">▽ transition</span>
</div>
</div>
<canvas id="chart"></canvas>
<div id="tooltip"></div>
<div id="loading">Loading data...</div>
<script>
(function(){
var canvas = document.getElementById('chart');
var ctx = canvas.getContext('2d');
var tooltip = document.getElementById('tooltip');
// Colors
var REGIME_COLORS = ['rgba(231,76,60,0.35)', 'rgba(241,196,15,0.2)', 'rgba(46,204,113,0.25)'];
var REGIME_LINES = ['#e74c3c', '#f1c40f', '#2ecc71'];
var PRICE_COLOR = '#58a6ff';
var GRID_COLOR = '#21262d';
var AXIS_COLOR = '#8b949e';
var BG = '#0d1117';
var MARGIN = {top:20, right:70, bottom:30, left:10};
var DATA = null;
var PADDING = 60; // px padding for price axis
function init() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight - 46;
draw();
}
function draw() {
if (!DATA) return;
var W = canvas.width, H = canvas.height;
var pw = W - MARGIN.left - MARGIN.right;
var ph = H - MARGIN.top - MARGIN.bottom;
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = BG;
ctx.fillRect(0, 0, W, H);
// Find price range
var pMin = Infinity, pMax = -Infinity;
for (var i = 0; i < DATA.length; i++) {
var v = DATA[i].v;
if (v < pMin) pMin = v;
if (v > pMax) pMax = v;
}
var pRange = pMax - pMin;
pMin -= pRange * 0.05;
pMax += pRange * 0.05;
pRange = pMax - pMin;
function toX(i) { return MARGIN.left + (i / (DATA.length - 1)) * pw; }
function toY(v) { return MARGIN.top + (1 - (v - pMin) / pRange) * ph; }
// Grid lines
ctx.strokeStyle = GRID_COLOR;
ctx.lineWidth = 0.5;
var nY = 8;
for (var gy = 0; gy <= nY; gy++) {
var y = MARGIN.top + (gy / nY) * ph;
var price = pMax - (gy / nY) * pRange;
ctx.beginPath();
ctx.moveTo(MARGIN.left, y);
ctx.lineTo(MARGIN.left + pw, y);
ctx.stroke();
// Y-axis labels
ctx.fillStyle = AXIS_COLOR;
ctx.font = '11px -apple-system,BlinkMacSystemFont,sans-serif';
ctx.textAlign = 'right';
ctx.fillText(price.toFixed(4), W - 5, y + 4);
}
// X-axis time labels
var nX = 6;
ctx.textAlign = 'center';
ctx.fillStyle = AXIS_COLOR;
ctx.font = '10px -apple-system,BlinkMacSystemFont,sans-serif';
for (var gx = 0; gx <= nX; gx++) {
var idx = Math.floor((gx / nX) * (DATA.length - 1));
var x = toX(idx);
var d = new Date(DATA[idx].t * 1000);
var label = d.toISOString().substring(11, 19); // HH:MM:SS
ctx.fillText(label, x, H - MARGIN.bottom + 16);
// Vertical grid
ctx.strokeStyle = GRID_COLOR;
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(x, MARGIN.top);
ctx.lineTo(x, MARGIN.top + ph);
ctx.stroke();
}
// Regime bands (draw as vertical strips)
var prevRegime = -1;
var bandStart = 0;
for (var i = 0; i < DATA.length; i++) {
var r = DATA[i].r;
if (r !== prevRegime) {
if (prevRegime >= 0) {
ctx.fillStyle = REGIME_COLORS[prevRegime];
ctx.fillRect(toX(bandStart), MARGIN.top, toX(i) - toX(bandStart), ph);
}
bandStart = i;
prevRegime = r;
}
}
// Last band
if (prevRegime >= 0) {
ctx.fillStyle = REGIME_COLORS[prevRegime];
ctx.fillRect(toX(bandStart), MARGIN.top, toX(DATA.length - 1) - toX(bandStart) + 1, ph);
}
// Regime transition markers (thin vertical lines)
prevRegime = DATA[0].r;
for (var i = 1; i < DATA.length; i++) {
if (DATA[i].r !== prevRegime) {
var mx = toX(i);
ctx.strokeStyle = REGIME_LINES[DATA[i].r];
ctx.lineWidth = 1;
ctx.setLineDash([3, 4]);
ctx.beginPath();
ctx.moveTo(mx, MARGIN.top);
ctx.lineTo(mx, MARGIN.top + ph);
ctx.stroke();
ctx.setLineDash([]);
// Small arrow marker at bottom
ctx.fillStyle = REGIME_LINES[DATA[i].r];
ctx.beginPath();
ctx.moveTo(mx, MARGIN.top + ph + 2);
ctx.lineTo(mx - 4, MARGIN.top + ph - 4);
ctx.lineTo(mx + 4, MARGIN.top + ph - 4);
ctx.closePath();
ctx.fill();
prevRegime = DATA[i].r;
}
}
// Price line
ctx.strokeStyle = PRICE_COLOR;
ctx.lineWidth = 1.5;
ctx.beginPath();
for (var i = 0; i < DATA.length; i++) {
var px = toX(i), py = toY(DATA[i].v);
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.stroke();
// Current price label
var lastV = DATA[DATA.length-1].v;
var lastY = toY(lastV);
ctx.fillStyle = PRICE_COLOR;
ctx.font = 'bold 12px -apple-system,BlinkMacSystemFont,sans-serif';
ctx.textAlign = 'left';
ctx.fillText(lastV.toFixed(4), MARGIN.left + pw + 4, lastY + 4);
}
// Mouse/touch tooltip
canvas.addEventListener('mousemove', function(e) {
if (!DATA) return;
var rect = canvas.getBoundingClientRect();
var mx = e.clientX - rect.left;
var my = e.clientY - rect.top;
var W = canvas.width, H = canvas.height;
var pw = W - MARGIN.left - MARGIN.right;
if (mx < MARGIN.left || mx > MARGIN.left + pw) {
tooltip.style.display = 'none';
return;
}
var idx = Math.round(((mx - MARGIN.left) / pw) * (DATA.length - 1));
idx = Math.max(0, Math.min(DATA.length - 1, idx));
var pt = DATA[idx];
var d = new Date(pt.t * 1000);
tooltip.innerHTML = '<b>' + pt.v.toFixed(4) + '</b> Regime: ' + pt.r +
'<br><span style="color:#8b949e">' + d.toISOString().substring(11, 19) + '</span>';
tooltip.style.display = 'block';
tooltip.style.left = (e.clientX + 15) + 'px';
tooltip.style.top = (e.clientY - 40) + 'px';
});
canvas.addEventListener('mouseleave', function() { tooltip.style.display = 'none'; });
// Touch
canvas.addEventListener('touchmove', function(e) {
var t = e.touches[0];
var ev = {clientX:t.clientX, clientY:t.clientY};
canvas.dispatchEvent(new MouseEvent('mousemove', ev));
});
canvas.addEventListener('touchend', function() { tooltip.style.display = 'none'; });
window.addEventListener('resize', init);
// Load data
fetch('DATA_URL_PLACEHOLDER')
.then(function(r) { return r.json(); })
.then(function(d) {
DATA = d;
var ld = document.getElementById('loading');
if (ld) ld.remove();
init();
})
.catch(function(e) {
document.getElementById('loading').textContent = 'Error: ' + e;
});
})();
</script>
</body>
</html>"""
def main(datafile):
print(f"[1/3] Loading {datafile}...")
snaps = load_snapshots(datafile)
df = extract_features(snaps)
print(f" {len(snaps)} snapshots — price range: {df.mid.min():.4f} ~ {df.mid.max():.4f}")
print(f"[2/3] Fitting HMM...")
X_raw = engineer_features(df)
X_scaled = StandardScaler().fit_transform(X_raw)
model = fit_hmm(X_scaled[::STEP])
Z = model.predict(X_scaled)
for k in range(N_REGIMES):
mask = Z == k
print(f" Regime {k}: {mask.mean()*100:.1f}% spread={X_raw[mask,0].mean():.6f}")
pts = [{"t": int(df.iloc[i]["ts"]), "v": round(df.iloc[i]["mid"], 6), "r": int(Z[i])} for i in range(len(df))]
base = os.path.splitext(datafile)[0]
json_file = base + "_data.json"
html_file = base + "_canvas_chart.html"
with open(json_file, "w") as f:
json.dump(pts, f, separators=(',', ':'))
html = HTML.replace("DATA_URL_PLACEHOLDER", os.path.basename(json_file))
with open(html_file, "w") as f:
f.write(html)
print(f"[3/3] Done: {html_file} ({os.path.getsize(html_file)/1024:.0f} KB)")
print(f" {json_file} ({os.path.getsize(json_file)/1024:.0f} KB)")
if __name__ == "__main__":
if len(sys.argv) < 2:
files = sorted(glob.glob("data/l2_LINK_*.jsonl"))
if not files:
print("No data files found in data/")
sys.exit(1)
datafile = files[-1]
else:
datafile = sys.argv[1]
main(datafile)
+987
View File
@@ -0,0 +1,987 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LVVXH3TL04"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LVVXH3TL04');
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LOB Latent Regimes — Early Detection of Market Micro-Instability</title>
<meta name="description" content="Detect latent market stress before it surfaces. A reproducible research framework for identifying structural deterioration in limit order books.">
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- MathJax for LaTeX rendering -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<!-- Syntax highlighting -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<style>
:root {
--accent: #0284c7;
--accent-light: #38bdf8;
--accent-dark: #0369a1;
--danger: #ef4444;
--success: #10b981;
--warning: #f59e0b;
}
html {
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif;
}
.font-display {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
letter-spacing: -0.02em;
}
/* Math rendering */
.math-block {
background: #f8fafc;
padding: 2rem;
border-radius: 0.5rem;
overflow-x: auto;
margin: 1.5rem 0;
border-left: 3px solid var(--accent);
}
/* Code block styling */
.code-block {
background: #0f172a;
color: #e2e8f0;
padding: 1.5rem;
border-radius: 0.5rem;
overflow-x: auto;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.875rem;
line-height: 1.6;
}
.code-block code {
color: #e2e8f0;
}
.hljs-string { color: #86efac !important; }
.hljs-number { color: #fbbf24 !important; }
.hljs-literal { color: #818cf8 !important; }
.hljs-attr { color: #60a5fa !important; }
.hljs-title { color: #38bdf8 !important; }
.hljs-comment { color: #64748b !important; }
/* Architecture diagram */
.diagram-box {
background: #f8fafc;
border: 1px solid #e2e8f0;
padding: 1.5rem;
border-radius: 0.375rem;
font-family: 'Courier New', monospace;
font-size: 0.75rem;
line-height: 1.4;
overflow-x: auto;
color: #475569;
}
/* State diagram */
.state-diagram {
background: #f8fafc;
border: 1px solid #e2e8f0;
padding: 2rem;
border-radius: 0.5rem;
margin: 2rem 0;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
line-height: 1.6;
color: #475569;
}
/* Signal card */
.signal-card {
background: linear-gradient(135deg, rgba(2, 132, 199, 0.05) 0%, rgba(56, 189, 248, 0.05) 100%);
border: 1px solid #e2e8f0;
padding: 1.5rem;
border-radius: 0.5rem;
border-left: 3px solid var(--accent);
}
/* Results table */
table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
}
thead {
background: #f8fafc;
}
th {
padding: 1rem;
text-align: left;
font-weight: 600;
color: #1e293b;
border-bottom: 2px solid #e2e8f0;
}
td {
padding: 1rem;
border-bottom: 1px solid #e2e8f0;
color: #475569;
}
tr:hover {
background: #f8fafc;
}
.positive-lead { color: var(--success); font-weight: 600; }
.negative-lead { color: var(--danger); font-weight: 600; }
.precision-perfect { color: var(--success); font-weight: 600; }
/* Badge */
.badge {
display: inline-block;
padding: 0.375rem 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
font-size: 0.75rem;
font-weight: 600;
text-decoration: none;
color: #475569;
transition: all 0.2s ease;
}
.badge:hover {
border-color: var(--accent);
color: var(--accent);
background: rgba(2, 132, 199, 0.05);
}
/* Comparison grid */
.comparison-item {
background: #f8fafc;
border: 1px solid #e2e8f0;
padding: 1.5rem;
border-radius: 0.375rem;
transition: all 0.3s ease;
}
.comparison-item:hover {
background: #f1f5f9;
border-color: var(--accent);
box-shadow: 0 4px 12px rgba(2, 132, 199, 0.08);
}
.comparison-item.success {
border-left: 3px solid var(--success);
}
.comparison-item.danger {
border-left: 3px solid var(--danger);
}
/* Hero section */
.hero {
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
padding: 6rem 0;
border-bottom: 1px solid #e2e8f0;
}
.hero-accent {
color: var(--accent);
}
/* Section */
.section {
padding: 4rem 0;
}
.section h2 {
font-size: 2rem;
font-weight: 700;
color: #0f172a;
margin-bottom: 1.5rem;
letter-spacing: -0.01em;
}
.section h3 {
font-size: 1.375rem;
font-weight: 600;
color: #1e293b;
margin-top: 2rem;
margin-bottom: 1rem;
}
.section p {
font-size: 1rem;
line-height: 1.7;
color: #475569;
margin-bottom: 1rem;
}
.section ul {
margin-left: 1.5rem;
margin-bottom: 1rem;
}
.section li {
color: #475569;
margin-bottom: 0.5rem;
line-height: 1.7;
}
/* Button */
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background: var(--accent);
color: white;
text-decoration: none;
border-radius: 0.375rem;
font-weight: 600;
font-size: 0.875rem;
transition: all 0.2s ease;
border: 1px solid var(--accent);
}
.btn:hover {
background: var(--accent-dark);
border-color: var(--accent-dark);
box-shadow: 0 4px 12px rgba(2, 132, 199, 0.3);
}
.btn-outline {
background: transparent;
color: var(--accent);
}
.btn-outline:hover {
background: rgba(2, 132, 199, 0.1);
}
/* Divider */
.divider {
height: 1px;
background: linear-gradient(90deg, transparent, #e2e8f0, transparent);
margin: 4rem 0;
}
/* Info box */
.info-box {
background: #f0f9ff;
border-left: 3px solid var(--accent);
padding: 1.5rem;
border-radius: 0.375rem;
margin: 2rem 0;
}
.warning-box {
background: #fef3c7;
border-left: 3px solid var(--warning);
padding: 1.5rem;
border-radius: 0.375rem;
margin: 2rem 0;
}
.info-box strong { color: var(--accent); }
.warning-box strong { color: var(--warning); }
/* Stat boxes */
.stat-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1.5rem;
margin: 2rem 0;
}
.stat-box {
background: #f8fafc;
border: 1px solid #e2e8f0;
padding: 1.5rem;
border-radius: 0.375rem;
text-align: center;
}
.stat-value {
font-size: 2.5rem;
font-weight: 700;
color: var(--accent);
font-family: 'Courier New', monospace;
}
.stat-label {
font-size: 0.75rem;
color: #475569;
margin-top: 0.5rem;
}
/* Metric chip */
.metric-chip {
display: inline-block;
background: #f0f9ff;
border: 1px solid var(--accent);
color: var(--accent);
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 600;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
/* Two-column layout */
.comparison-table {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin: 2rem 0;
}
@media (max-width: 768px) {
.comparison-table {
grid-template-columns: 1fr;
}
}
/* Gradient text */
.gradient-text {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-light) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fadeInUp 0.6s ease;
}
/* Trigger visualization */
.trigger-logic {
background: #f8fafc;
border: 1px solid #e2e8f0;
padding: 2rem;
border-radius: 0.5rem;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
line-height: 1.8;
color: #475569;
}
</style>
</head>
<body class="bg-white text-gray-900">
<!-- Hero Section -->
<div class="hero">
<div class="max-w-6xl mx-auto px-6 sm:px-8">
<!-- Badges -->
<div class="flex flex-wrap gap-2 mb-8">
<a href="https://arxiv.org/abs/2604.20949" class="badge" target="_blank" rel="noopener">
📄 arXiv:2604.20949
</a>
<a href="https://doi.org/10.5281/zenodo.19697687" class="badge" target="_blank" rel="noopener">
🔬 Zenodo
</a>
<a href="https://github.com/prakulhiremath/LOB-Latent-Regimes" class="badge" target="_blank" rel="noopener">
🔗 GitHub
</a>
<span class="badge">✓ Reproducible</span>
<span class="badge">⚡ Causal</span>
<span class="badge">🎯 100% Precision</span>
</div>
<!-- Hero Content -->
<div class="max-w-3xl">
<h1 class="text-5xl sm:text-6xl font-display font-bold mb-4 text-slate-900">
LOB Latent Regimes
</h1>
<p class="text-xl text-slate-600 mb-2 font-display font-semibold">
Latent Micro-Regime Early Detection in Limit Order Books
</p>
<p class="text-2xl font-display font-semibold mb-8">
<span class="text-slate-700">Identify structural market instability</span>
<span class="hero-accent"> before it surfaces</span>
</p>
<p class="text-lg text-slate-600 mb-8 max-w-2xl leading-relaxed">
A reproducible research framework for detecting latent build-up phases in financial markets. Fires with positive lead-time and 100% precision, exploiting the measurable delay between structural deterioration and observable stress.
</p>
<div class="flex flex-wrap gap-4">
<a href="#framework" class="btn">Learn More</a>
<a href="https://github.com/prakulhiremath/LOB-Latent-Regimes" class="btn btn-outline" target="_blank" rel="noopener">
View on GitHub →
</a>
</div>
<p class="text-sm text-slate-500 mt-6 italic">
⚠️ Research pipeline. Not a trading strategy. See Scope & Limitations below.
</p>
</div>
</div>
</div>
<!-- Core Insight -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<div class="bg-slate-50 border-l-4 border-blue-600 p-8 rounded-sm">
<p class="text-lg text-slate-800 font-semibold mb-4">
"Market stress does not arrive without warning. It accumulates."
</p>
<p class="text-slate-700 leading-relaxed">
Classical indicators—volatility, order imbalance, spread widening—are <strong>reactive</strong>. By the time they fire, the dislocation has already begun. This research asks a harder question: <em>Can we detect the structural deterioration that precedes observable stress—before it becomes visible in price or spread?</em>
</p>
</div>
</div>
<div class="divider"></div>
<!-- The Problem -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>The Three Regimes</h2>
<p>
Markets cycle through three distinct states. The critical regime is invisible to standard microstructure monitors.
</p>
<div class="state-diagram">
<pre>┌──────────────────────────────────────────────────────────────────┐
│ │
│ STATE 0 ────────────► STATE 1 ────────────► STATE 2 │
│ │
│ Stable Latent Build-up Stress │
│ ─────── ─────────────── ────── │
│ • Balanced liquidity • Depth eroding • Price shock │
│ • High resilience • Spread drifting • Visible │
│ • Equilibrium • ⚠ Hidden instability • Reactive │
│ │
│ Observable INVISIBLE Observable │
│ (past data) (our signal fires) (too late) │
│ │
│ ◄──── detection window ────► │
│ ↑ ↑ │
│ we detect stress begins │
│ │
└──────────────────────────────────────────────────────────────────┘</pre>
</div>
<p>
<strong>The key insight:</strong> The transition from State 1 → State 2 is not instantaneous. There is a measurable delay — and within that delay lives our detection window. We exploit it.
</p>
<div class="info-box">
<p>
<strong>Why this matters:</strong> Depth erosion precedes spread widening, which precedes price impact. If we detect the book thinning silently, we have time to respond before the visible stress cascade begins.
</p>
</div>
</div>
<div class="divider"></div>
<!-- Detection Framework -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section" id="framework">
<h2>Detection Framework</h2>
<p>
Three independent signal channels. One fused trigger. Rising-edge detection instead of thresholds.
</p>
<h3>Signal Channels</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 my-8">
<!-- Channel 1 -->
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-3">📊 HMM Posterior Entropy</h4>
<p class="text-sm text-slate-700 mb-3">
Uncertainty in regime classification
</p>
<p class="text-xs text-slate-600">
<strong>Why early:</strong> Rises as the latent state becomes ambiguous, before the transition to stress solidifies.
</p>
</div>
<!-- Channel 2 -->
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-3">📉 Temporal Depth Drift</h4>
<p class="text-sm text-slate-700 mb-3">
Recursive tracking of LOB depth erosion
</p>
<p class="text-xs text-slate-600">
<strong>Why early:</strong> Captures slow structural decay invisible to snapshot metrics. Liquid book → thin book is the first visible sign.
</p>
</div>
<!-- Channel 3 -->
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-3">⚖️ Order Flow Toxicity</h4>
<p class="text-sm text-slate-700 mb-3">
Imbalance between informed and uninformed flow
</p>
<p class="text-xs text-slate-600">
<strong>Why early:</strong> Signals adverse selection building in the book before visible spread impact.
</p>
</div>
</div>
<h3>Trigger Logic</h3>
<div class="trigger-logic">
<pre>MAX-Fusion Trigger
├── Rising-edge detection
│ └─ Onset of change, not absolute level
│ └─ Bypasses noise floor that kills threshold methods
├── Cross-channel aggregation
│ └─ Fire when ANY channel breaches threshold
└── Early-detection constraint
└─ Signal must precede stress (τ < σ)</pre>
</div>
<div class="info-box mt-8">
<p>
<strong>Key design choice:</strong> We don't ask "is the spread wide?" We ask "is it getting wider <em>right now</em>?" This rising-edge approach bypasses the noise floor that kills absolute-threshold methods on real microstructure data.
</p>
</div>
</div>
<div class="divider"></div>
<!-- Results -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Empirical Results</h2>
<p>
Evaluated under full causal guarantees. No lookahead bias. No global statistics leaked to the predictor.
</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Lead-Time (steps)</th>
<th>Precision</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr style="background: rgba(16, 185, 129, 0.05);">
<td><strong>★ Adaptive Trigger</strong></td>
<td class="positive-lead">+18.6</td>
<td class="precision-perfect">100%</td>
<td>52.6%</td>
</tr>
<tr>
<td>HMM</td>
<td class="positive-lead">+14.9</td>
<td class="precision-perfect">100%</td>
<td>43.2%</td>
</tr>
<tr>
<td>Multi-Trigger</td>
<td class="positive-lead">+13.1</td>
<td class="precision-perfect">100%</td>
<td>28.1%</td>
</tr>
<tr style="border-top: 2px solid #e2e8f0;">
<td>Order Imbalance</td>
<td class="negative-lead">24.8</td>
<td>54.9%</td>
<td>78.7%</td>
</tr>
<tr>
<td>Volatility</td>
<td class="negative-lead">32.0</td>
<td>45.5%</td>
<td>43.3%</td>
</tr>
</tbody>
</table>
<div class="info-box mt-8">
<p>
<strong>Reading the table:</strong> Positive lead-time means the signal fires <em>before</em> stress begins. Baseline methods are strictly negative—they lag. 100% precision means zero false starts during the latent phase—every trigger is temporally valid.
</p>
</div>
<h3>Key Findings</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 my-8">
<div class="comparison-item success">
<h4 class="font-bold text-slate-900 mb-2">✓ Latent Instability Is Real</h4>
<p class="text-sm text-slate-700">
Market regimes structurally deteriorate before visible deterioration. Consistent empirical signature across tested sessions.
</p>
</div>
<div class="comparison-item success">
<h4 class="font-bold text-slate-900 mb-2">✓ Depth Erosion Is Most Reliable</h4>
<p class="text-sm text-slate-700">
Depth decay in the LOB precedes spread widening and price impact. Book thinning is the earliest signal.
</p>
</div>
<div class="comparison-item success">
<h4 class="font-bold text-slate-900 mb-2">✓ HMM Entropy Works</h4>
<p class="text-sm text-slate-700">
Posterior entropy is a structural stress barometer. Model uncertainty itself is informative.
</p>
</div>
<div class="comparison-item success">
<h4 class="font-bold text-slate-900 mb-2">✓ Rising-Edge Beats Thresholds</h4>
<p class="text-sm text-slate-700">
Onset of deterioration carries more information than magnitude. No false starts from noise.
</p>
</div>
</div>
</div>
<div class="divider"></div>
<!-- Reproducibility -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Reproducibility Guarantees</h2>
<p>
This pipeline was engineered to be trusted. Every result is verifiable and repeatable.
</p>
<div class="stat-grid">
<div class="stat-box">
<p class="stat-label">✓ Causal</p>
<p class="stat-value" style="font-size: 1.5rem;">No Lookahead</p>
</div>
<div class="stat-box">
<p class="stat-label">✓ Rolling Norm</p>
<p class="stat-value" style="font-size: 1.5rem;">No Data Leakage</p>
</div>
<div class="stat-box">
<p class="stat-label">✓ HMM Re-fit</p>
<p class="stat-value" style="font-size: 1.5rem;">Periodic</p>
</div>
<div class="stat-box">
<p class="stat-label">✓ Deterministic</p>
<p class="stat-value" style="font-size: 1.5rem;">Fixed Seeds</p>
</div>
<div class="stat-box">
<p class="stat-label">✓ Validated</p>
<p class="stat-value" style="font-size: 1.5rem;">Colab + M4</p>
</div>
</div>
<div class="warning-box mt-8">
<p>
<strong>Causality verification:</strong> Rolling normalization only—no global statistics computed on future data. HMM re-fit every evaluation window. All results are exact under deterministic seeds across NVIDIA T4 (Google Colab) and Apple Silicon (M4 Pro/Max).
</p>
</div>
</div>
<div class="divider"></div>
<!-- Quickstart -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Quickstart</h2>
<h3>Clone & Install</h3>
<div class="code-block mb-8"><pre><code class="language-bash">git clone https://github.com/prakulhiremath/LOB-Latent-Regimes.git
cd LOB-Latent-Regimes
pip install -r requirements.txt</code></pre></div>
<h3>Run the Final Pipeline</h3>
<div class="code-block mb-8"><pre><code class="language-bash">python experiments/v7_final.py</code></pre></div>
<p>Expected output:</p>
<div class="bg-slate-900 text-slate-100 p-6 rounded font-mono text-sm border border-slate-700 mb-8">
<pre>[INFO] Loading normalized LOB snapshots...
[INFO] Running causal evaluation pipeline...
[INFO] Fitting HMM with rolling windows...
[INFO] Computing signal channels (entropy, depth, toxicity)...
[INFO] Fusing triggers with MAX aggregation...
[INFO] Computing lead-time and precision metrics...
╔════════════════════════════════════════════════════════════════╗
║ Adaptive Trigger +18.6 steps 100% precision 52.6% ║
║ HMM +14.9 steps 100% precision 43.2% ║
║ Multi-Trigger +13.1 steps 100% precision 28.1% ║
╚════════════════════════════════════════════════════════════════╝</pre>
</div>
<h3>Explore Individual Signals</h3>
<div class="code-block"><pre><code class="language-bash">jupyter notebook notebooks/analysis.ipynb</code></pre></div>
<p>Contains: Signal decomposition, lead-time distributions, regime transition visualizations, baseline comparisons.</p>
</div>
<div class="divider"></div>
<!-- Repository Structure -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Repository Structure</h2>
<div class="diagram-box">
<pre>LOB-Latent-Regimes/
├── experiments/
│ ├── v1_baseline.py # Initial HMM formulation
│ ├── v2_entropy.py # Posterior entropy tracking
│ ├── v3_depth_drift.py # Temporal depth signal
│ ├── v4_triggers.py # Trigger logic development
│ ├── v5_fusion.py # MAX-fusion framework
│ ├── v6_rising_edge.py # Rising-edge detection
│ └── v7_final.py # ★ Production pipeline
├── notebooks/
│ └── analysis.ipynb # Experiment analysis + figures
├── results/
│ ├── figures/ # High-resolution performance plots
│ └── summary.txt # Quantified results
├── paper/ # Technical manuscript
├── assets/ # Visualizations, GIFs, detection timeline
├── requirements.txt # Dependencies
└── README.md</pre>
</div>
<h3>Development Progression</h3>
<p>The pipeline evolved through 7 versions, each adding precision:</p>
<table>
<thead>
<tr>
<th>Version</th>
<th>Focus</th>
<th>Key Innovation</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>v1</code></td>
<td>Baseline HMM</td>
<td>Three-state Markov model for regime classification</td>
</tr>
<tr>
<td><code>v2</code></td>
<td>Entropy Signal</td>
<td>Posterior entropy as stress barometer</td>
</tr>
<tr>
<td><code>v3</code></td>
<td>Depth Drift</td>
<td>Temporal tracking of LOB depth erosion</td>
</tr>
<tr>
<td><code>v4</code></td>
<td>Trigger Design</td>
<td>Individual signal thresholds</td>
</tr>
<tr>
<td><code>v5</code></td>
<td>Multi-Signal Fusion</td>
<td>MAX aggregation across channels</td>
</tr>
<tr>
<td><code>v6</code></td>
<td>Rising-Edge</td>
<td>Onset detection instead of absolute levels</td>
</tr>
<tr style="background: rgba(16, 185, 129, 0.05);">
<td><code>★ v7</code></td>
<td>Final Pipeline</td>
<td>Production configuration, full causal guarantees</td>
</tr>
</tbody>
</table>
</div>
<div class="divider"></div>
<!-- Scope & Limitations -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Scope & Limitations</h2>
<p>
Be precise about what this is and is not.
</p>
<div class="comparison-table">
<div class="comparison-item">
<h4 class="font-bold text-green-700 mb-3">✓ This Repo IS</h4>
<ul class="space-y-2 text-sm text-slate-700">
<li>Detection framework for latent regime transitions</li>
<li>Empirical study of LOB microstructure</li>
<li>Reproducible research pipeline</li>
<li>Contribution to predictive market microstructure</li>
<li>Academic exercise in causal signal processing</li>
</ul>
</div>
<div class="comparison-item danger">
<h4 class="font-bold text-red-700 mb-3">✗ This Repo IS NOT</h4>
<ul class="space-y-2 text-sm text-slate-700">
<li>A trading strategy or system</li>
<li>Optimised for execution latency</li>
<li>A production trading system</li>
<li>Financial advice or investment guidance</li>
<li>Tested in live market conditions</li>
</ul>
</div>
</div>
<div class="warning-box mt-8">
<p>
<strong>Important:</strong> This is research code. Backtesting results do not guarantee forward performance. Live trading requires rigorous validation, risk management, and regulatory compliance beyond the scope of this work.
</p>
</div>
</div>
<div class="divider"></div>
<!-- Contributions -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Research Contributions</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-2">Causal Formulation</h4>
<p class="text-sm text-slate-700">
Model latent build-up → stress transition as a three-state latent process with explicit temporal separation.
</p>
</div>
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-2">Temporal Drift Identification</h4>
<p class="text-sm text-slate-700">
Subtle depth and spread drift as leading precursor to liquidity voids. Recursive tracking without lookahead.
</p>
</div>
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-2">Novel Detection Logic</h4>
<p class="text-sm text-slate-700">
MAX-fusion + rising-edge trigger for sub-millisecond microstructure data. Positive lead-time guarantee.
</p>
</div>
<div class="signal-card">
<h4 class="font-bold text-slate-900 mb-2">Empirical Demonstration</h4>
<p class="text-sm text-slate-700">
Strictly positive lead-time over reactive benchmarks across all evaluated regimes. 100% precision.
</p>
</div>
</div>
</div>
<div class="divider"></div>
<!-- Citation -->
<div class="max-w-6xl mx-auto px-6 sm:px-8 section">
<h2>Citation</h2>
<div class="code-block">
<pre><code>@article{hiremath2026lob,
title = {Early Detection of Latent Micro-Regimes in Limit Order Books},
author = {Hiremath, Prakul Sunil and Hiremath, Vruksha Arun},
year = {2026},
doi = {10.5281/zenodo.19697687}
}</code></pre>
</div>
</div>
<div class="divider"></div>
<!-- Footer -->
<div class="bg-slate-50 border-t border-slate-200 py-12">
<div class="max-w-6xl mx-auto px-6 sm:px-8">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-8 mb-12">
<div>
<h4 class="font-semibold text-slate-900 mb-4">Resources</h4>
<ul class="space-y-2 text-sm">
<li><a href="https://arxiv.org/abs/2604.20949" class="text-blue-600 hover:underline" target="_blank" rel="noopener">arXiv Paper</a></li>
<li><a href="https://github.com/prakulhiremath/LOB-Latent-Regimes" class="text-blue-600 hover:underline" target="_blank" rel="noopener">GitHub Repository</a></li>
<li><a href="https://doi.org/10.5281/zenodo.19697687" class="text-blue-600 hover:underline" target="_blank" rel="noopener">Zenodo Archive</a></li>
</ul>
</div>
<div>
<h4 class="font-semibold text-slate-900 mb-4">Explore</h4>
<ul class="space-y-2 text-sm">
<li><a href="#framework" class="text-blue-600 hover:underline">Detection Framework</a></li>
<li><a href="https://github.com/prakulhiremath/LOB-Latent-Regimes/blob/main/notebooks/analysis.ipynb" class="text-blue-600 hover:underline" target="_blank" rel="noopener">Analysis Notebook</a></li>
<li><a href="https://github.com/prakulhiremath/LOB-Latent-Regimes/issues" class="text-blue-600 hover:underline" target="_blank" rel="noopener">Report Issues</a></li>
</ul>
</div>
<div>
<h4 class="font-semibold text-slate-900 mb-4">Connect</h4>
<ul class="space-y-2 text-sm">
<li><a href="mailto:prakulhiremath03@gmail.com" class="text-blue-600 hover:underline">Email</a></li>
<li><a href="https://github.com/prakulhiremath" class="text-blue-600 hover:underline" target="_blank" rel="noopener">GitHub Profile</a></li>
<li><a href="https://arxiv.org/search/?query=hiremath&searchtype=author" class="text-blue-600 hover:underline" target="_blank" rel="noopener">More Papers</a></li>
</ul>
</div>
</div>
<div class="border-t border-slate-200 pt-8">
<p class="text-sm text-slate-600 text-center mb-4">
<strong>LOB Latent Regimes</strong> — Early Detection of Structural Market Instability
</p>
<p class="text-xs text-slate-500 text-center">
MIT License · Reproducible Research Pipeline · <a href="https://github.com/prakulhiremath/LOB-Latent-Regimes" class="text-blue-600 hover:underline" target="_blank" rel="noopener">Open Source</a>
</p>
<p class="text-xs text-slate-500 text-center mt-4">
<em>"If the signal fires before the storm — it worked."</em>
</p>
</div>
</div>
</div>
<!-- Scripts -->
<script>
// Highlight code blocks
document.querySelectorAll('.code-block pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
}
});
});
// Fade in on load
window.addEventListener('load', () => {
document.body.style.opacity = '1';
});
</script>
</body>
</html>