Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21b77d62ca | ||
|
|
6bf8ec9263 | ||
|
|
d6137d3f4e | ||
|
|
977a1ad28f | ||
|
|
095d5c0ded | ||
|
|
8222fa6ce0 | ||
|
|
fb82e3569d | ||
|
|
41b52792be | ||
|
|
e3b4473428 | ||
|
|
aea1332fef |
@@ -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"
|
||||
@@ -1,143 +1,234 @@
|
||||
# ⚡️ Latent Micro-Regime Early Detection in Limit Order Books
|
||||
<div align="center">
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/detection.gif" width="850" alt="Detection Timeline Visualization"/>
|
||||
</p>
|
||||
<br/>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════════╗
|
||||
║ LATENT MICRO-REGIME EARLY DETECTION IN LIMIT ORDER BOOKS ║
|
||||
║ Identifying structural market instability before it surfaces ║
|
||||
╚══════════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
[](https://arxiv.org/abs/2604.20949)
|
||||
[](https://doi.org/10.5281/zenodo.19697687)
|
||||

|
||||

|
||||
-D4AF37?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** |
|
||||
| **2** | **Stress** | Observable dislocation, price shocks | Reactive |
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 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 │
|
||||
│ │
|
||||
│ ◄────── 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
|
||||
* **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.
|
||||
### Signal Channels
|
||||
|
||||
### 🕹 Detection Logic
|
||||
* **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.
|
||||
* **Early-Detection Constraint:** Optimization of $\tau < \sigma$, ensuring the signal precedes the event.
|
||||
| Channel | What It Measures | Why It's Early |
|
||||
|---|---|---|
|
||||
| **HMM Posterior Entropy** | Uncertainty in regime classification | Rises as the latent state becomes ambiguous, before the transition |
|
||||
| **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% |
|
||||
| **Model HMM** | **+14.95** | **100%** | 43.2% |
|
||||
| **Multi-Trigger** | **+13.15** | **100%** | 28.1% |
|
||||
| Order Imbalance | -24.84 | 54.9% | 78.7% |
|
||||
| Volatility | -32.02 | 45.5% | 43.3% |
|
||||
```
|
||||
╔════════════════════════════════════════════════════════════════╗
|
||||
║ METHOD LEAD-TIME PRECISION COVERAGE ║
|
||||
╠════════════════════════════════════════════════════════════════╣
|
||||
║ ★ Adaptive Trigger +18.6 steps 100% 52.6% ║
|
||||
║ HMM +14.9 steps 100% 43.2% ║
|
||||
║ 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:
|
||||
* **Positive Lead-Time:** Our methods detect stress *before* it happens; baselines are strictly negative (lagging).
|
||||
* **Temporal Validity:** 100% precision indicates zero "false starts" before the latent phase begins.
|
||||
* **Trade-off:** Coverage levels reflect the conservative nature of high-precision early signals.
|
||||
**Reading the table:**
|
||||
- **Positive lead-time** means the signal fires *before* stress begins. Baselines are strictly negative — they lag.
|
||||
- **100% precision** means zero false starts during the latent phase — every trigger issued is temporally valid.
|
||||
- **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
|
||||
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.
|
||||
3. **Signal Edge:** Rising-edge detection is essential to bypass the noise inherent in absolute thresholding.
|
||||
4. **Performance:** Trigger-based detection consistently outperforms classical econometric baselines.
|
||||
## Empirical Findings
|
||||
|
||||
**1. Latent instability exists and is measurable.**
|
||||
Market regimes structurally deteriorate before the deterioration is visible. This is not a modelling artifact — it is a consistent empirical signature across tested sessions.
|
||||
|
||||
**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
|
||||
.
|
||||
├── experiments/ # Iterative development v1 → v7
|
||||
├── notebooks/ # Production-grade experiment analysis
|
||||
```
|
||||
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 summary
|
||||
├── paper/ # Technical manuscript (PDF)
|
||||
├── assets/ # Visualizations and GIFs
|
||||
│ ├── figures/ # High-resolution performance plots
|
||||
│ └── summary.txt # Quantified results
|
||||
│
|
||||
├── paper/ # Technical manuscript
|
||||
├── assets/ # Visualizations, GIFs
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Reproducibility
|
||||
## Reproducibility Guarantees
|
||||
|
||||
Validated across high-compute and local environments:
|
||||
* **Cloud:** Google Colab (NVIDIA T4)
|
||||
* **Local:** Apple Silicon (M4 Pro/Max)
|
||||
This pipeline was built to be trusted.
|
||||
|
||||
```
|
||||
✓ 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
|
||||
# Clone the repository
|
||||
git clone https://github.com/your-repo/lob-early-detection.git
|
||||
# Clone
|
||||
git clone https://github.com/prakulhiremath/LOB-Latent-Regimes.git
|
||||
cd LOB-Latent-Regimes
|
||||
|
||||
# Install dependencies
|
||||
# Install
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Execute the final pipeline
|
||||
# Run the final pipeline
|
||||
python experiments/v7_final.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Contributions
|
||||
* **Causal Formulation:** Formalizing the Latent Build-up $\rightarrow$ Stress transition.
|
||||
* **Temporal Drift:** Identifying subtle drift as a precursor to liquidity voids.
|
||||
* **MAX Fusion & Rising-Edge:** Novel trigger logic for sub-millisecond microstructure data.
|
||||
* **Empirical Proof:** Demonstrating strictly positive lead-time over reactive benchmarks.
|
||||
## Scope & Limitations
|
||||
|
||||
Be precise about what this is.
|
||||
|
||||
| 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
|
||||
@article{lob_micro_regime_detection_2026,
|
||||
title={Early Detection of Latent Micro-Regimes in Limit Order Books},
|
||||
author={Hiremath, Prakul. & Hiremath, Vruksha},
|
||||
year={2026},
|
||||
@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}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
Built with ☕️ and 🐍 for <b>Reproducible Quantitative Finance.</b>
|
||||
</p>
|
||||
<div align="center">
|
||||
|
||||
Built for **reproducible research** in quantitative finance and machine learning.
|
||||
|
||||
*If the signal fires before the storm — it worked.*
|
||||
|
||||
⸻
|
||||
|
||||
</div>
|
||||
|
||||
@@ -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
@@ -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>
|
||||
Reference in New Issue
Block a user