pyvsmc
High-performance NumPy/Numba market-structure engine for Smart Money Concepts (SMC).
pyvsmc provides NumPy/Numba + Polars implementations of SMC/ICT concepts — Fair Value Gaps, fractal swings, BOS/CHOCH, Order Blocks, Liquidity sweeps, Premium/Discount & OTE — with appropriate data structures and low asymptotic complexity.
Architecture: NumPy for bulk transforms, Numba njit(cache=True) for stateful/first-passage scans, Polars for DataFrame integration. Not “zero loops” — correctness and complexity first.
Features
| Module | Concept | Key Function |
|---|---|---|
fvg |
Fair Value Gap / Imbalance + CE 50% + IFVG | detect_fvg() |
swings |
Fractal Swing Highs & Lows | detect_swings() |
structure |
BOS & CHOCH (first-cross, break_mode) | detect_structure() |
order_blocks |
Order Block + zone_mode + breaker | detect_order_blocks() |
liquidity |
Equal highs/lows (swing-based) + sweeps | detect_liquidity() |
zones |
Premium/Discount + OTE 0.618/0.705/0.786 | detect_zones() |
polars_ext |
Polars .smc namespace |
df.smc.add_all() |
- Correctness: Strict
mypy, NaN-aware, first-cross BOS, CE50/full/inverted tracking. - Polars:
pl.DataFrame.smc.*+add_smc_columns();fvg/swingshave nativeLazyFrameexpr, other modules fallback honestcollect()(documented). - Tested: 92 tests covering empty, flat, NaN, dense gaps, mitigation, EQH/EQL, OTE, LazyFrame.
Installation
pip install pyvsmc
With Polars (recommended):
pip install "pyvsmc[dev]" # includes polars, pytest, ruff, mypy, numba
# or
pip install pyvsmc polars numba
From source:
git clone https://github.com/Khaymat/pyvsmc
cd pyvsmc
pip install -e ".[dev]"
Requirements: Python >=3.10, numpy>=1.24.0, polars>=0.20.0 (optional), numba>=0.56 (optional, for JIT).
Quickstart
NumPy API
import numpy as np
import pyvsmc as smc
high = np.array([10.0, 11.2, 10.8, 12.5, 11.0, 13.0])
low = np.array([ 9.5, 9.8, 10.0, 11.8, 10.5, 12.2])
close = np.array([10.0, 10.5, 10.2, 12.2, 11.1, 12.8])
open_ = np.array([ 9.8, 10.0, 10.4, 11.0, 11.5, 12.0])
# 1. FVG + CE/IFVG
fvg = smc.detect_fvg(high, low, close=close, compute_mitigation=True)
print(fvg.bullish, fvg.ce_level, fvg.mitigated_50, fvg.inverted)
# 2. Swings
swings = smc.detect_swings(high, low, window_size=2)
# 3. Structure — break_mode close/wick/both, first-cross
structure = smc.detect_structure(high, low, close, window_size=2, break_mode="close")
print(structure.bos_bullish, structure.trend)
# 4. Order Blocks — zone_mode full/body/mean_threshold + breaker
obs = smc.detect_order_blocks(open_, high, low, close, lookback=5, zone_mode="body", compute_mitigation=True)
print(obs.bullish_ob, obs.is_breaker)
# 5. Liquidity + Zones
liq = smc.detect_liquidity(high, low, close, equal_threshold=0.001)
zones = smc.detect_zones(high, low, close)
print(liq.equal_swing_high, liq.sweep_high, zones.in_ote)
Polars API
import polars as pl
import pyvsmc # registers .smc namespace
df = pl.DataFrame({"open": open_, "high": high, "low": low, "close": close})
# Eager
from pyvsmc.polars_ext import add_smc_columns
df = add_smc_columns(df, window_size=2, fvg_mitigation=True)
# Lazy (fvg/swings native, others collect fallback)
ldf = df.lazy()
ldf = ldf.pipe(lambda d: pyvsmc.fvg_polars(d)) # native expr
# Namespace — fvg/swings have native LazyFrame support; other modules use eager fallback
df = pl.DataFrame({"open": open_, "high": high, "low": low, "close": close})
df = df.smc.add_all(window_size=2, include_liquidity=True)
df = df.smc.fvg(min_gap_size=0.5)
df = df.smc.swings(window_size=2)
df = df.smc.structure(window_size=2)
df = df.smc.order_blocks(lookback=5)
df = df.smc.liquidity(equal_threshold=0.001)
df = df.smc.zones(eq_threshold=0.02)
API Reference
detect_fvg(high, low, min_gap_size=None, min_gap_size_pct=None, *, compute_mitigation=False, close=None)
Bullish Low[i] > High[i-2] [High[i-2], Low[i]], Bearish High[i] < Low[i-2].
Returns FVGResult with bullish/bearish, bullish_upper/lower, ce_level, gap_size, mitigated/mitigated_50/mitigated_full/inverted + indices. close enables IFVG.
detect_swings(high, low, window_size=2, tie="all")
High[i]==max(window), Low[i]==min(window). tie="all" (current default, legacy — every equal extremum), "first" (one deterministic per plateau, flat →0), "strict" (unique). Returns SwingResult.
detect_structure(high, low, close, window_size=2, *, break_mode="close", tie="all", swing_high=None, swing_low=None)
break_mode="close"|"wick"|"both", first-cross, tie forwarded when swings computed internally. If swing_high/swing_low supplied (both or neither, else ValueError), they are used verbatim and window_size/tie are ignored for swing generation.
detect_order_blocks(open_, high, low, close, *, lookback=10, zone_mode="full", compute_mitigation=False, tie="all", break_mode="close")
zone_mode="full"|"body"|"mean_threshold", tie forwarded to structure/swings, break_mode forwarded. Returns bullish_ob/bearish_ob, ob_high/low, mitigated/is_breaker.
detect_liquidity(high, low, close, *, equal_threshold=0.001, sweep_lookback=20, window_size=2, tie="all", swing_high=None, swing_low=None)
tie and optional swing_high/swing_low injection (both or neither). Returns equal_high/low + equal_swing_high/low (EQH/EQL), sweep_high/low.
detect_zones(high, low, close, window_size=2, eq_threshold=0.02, tie="all", swing_high=None, swing_low=None)
tie and optional swing injection as above. Returns premium/discount/equilibrium, range_high/low, ote_high/low/705, in_ote.
All have *_polars variants (swings_polars, structure_polars with break_mode/tie, liquidity_polars/zones_polars with tie, order_blocks_polars with zone_mode/tie/break_mode). add_smc_columns exposes tie, structure_break_mode, ob_zone_mode/ob_tie (defaults preserve 0.3.x).
Testing
pip install -e ".[dev]"
pytest -v
pytest --cov=pyvsmc --cov-report=term-missing
Run type checks and lint:
mypy src/pyvsmc
ruff check src/pyvsmc tests
Project Structure
pyvsmc/
├── pyproject.toml
├── README.md
├── src/pyvsmc/
│ ├── __init__.py
│ ├── py.typed
│ ├── fvg.py
│ ├── swings.py
│ ├── structure.py
│ ├── order_blocks.py
│ ├── liquidity.py
│ ├── zones.py
│ └── polars_ext.py
├── src/pysmc/ # shim deprecated → pyvsmc
├── benchmarks/
│ ├── bench.py
│ └── results.json
└── tests/
├── test_fvg.py + test_fvg_regression.py
├── test_swings.py
├── test_structure.py
├── test_order_blocks.py
├── test_liquidity.py
├── test_zones.py
└── test_polars_ext.py
Performance & Complexity
Benchmark 0.3.2 (synthetic OHLC random, window_size=2, 3 runs median, tracemalloc peak):
| n | swings | fvg | fvg+mit | struct | OB | liquidity | zones | polars eager |
|---|---|---|---|---|---|---|---|---|
| 1k | 0.79ms | 1.16ms | 1.96ms | 17.5ms* | 19.6ms | 11ms | 1.79ms | 58ms |
| 10k | 2.57ms | 1.90ms | 2.40ms | 19.7ms* | 28.8ms | 154ms | 4.5ms | 206ms |
| 100k | 23ms | 8.4ms | 19ms | 47ms | 74ms | 1306ms | 39ms | 1452ms |
* struct cold JIT ~15ms, warm 47ms/100k
Typical random-data speedup (preserved): 0.3.1 fvg_mit 963ms → 0.3.2 16ms at 100k ≈ 57× faster for d small. Pathological g=Θ(n), d=Θ(n) (all gaps mitigate at last bar, see benchmarks/bench_adversarial.py) remains worst O(g·n)=Θ(n²) — 10k 0.41s → 40k 6.5s → 80k 31s, same as 0.3.1 family.
Complexity (corrected):
swingsO(n),fvgw/o mitO(n)(bulk),fvg existence flagsO(n)via NaN-safe reverse-cum,fvg first-indexO(n + g·d)avg/practical, worstO(g·n)Θ(n²)wheng=Θ(n)andd=Θ(n).structureO(n)Numbacache=True(top-level),OBO(m·L)boundedL=lookback,liquidity EQHO(s)diff.
Limitations: fvg mit worst Θ(n²) — avoid compute_mitigation=True on 500k+ dense adversarial gaps; for typical random gaps it's ~16ms/100k. polars Lazy for fvg/swings native (shift/rolling_max), others fallback honest collect()->eager->lazy; order_blocks many impulses → lookback bound.
Financial & Legal Disclaimer
IMPORTANT — PLEASE READ CAREFULLY
pyvsmc is an open-source analytics and research library. It is provided solely for educational, informational, and research purposes.
- Not Financial Advice. Nothing in this library, its documentation, examples, or outputs constitutes financial, investment, trading, or other professional advice.
- No Warranty of Accuracy or Fitness. SMC are interpretive frameworks.
- Use at Your Own Risk. Trading involves substantial risk of loss.
- No Liability. Authors disclaim all liability.
- Do Your Own Research (DYOR).
License
MIT License — see LICENSE for details.
Copyright (c) 2026 pyvsmc contributors.
Contributing
Issues and pull requests are welcome. Please run ruff, mypy, and pytest before submitting.
Acknowledgements
Built with NumPy, Numba and Polars. SMC concepts as described by the broader ICT / Smart Money community.
Metadata
Release files for pyvsmc 0.3.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pyvsmc-0.3.7.tar.gz | 53.3 kB | Without provenance | Details for pyvsmc-0.3.7.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pyvsmc-0.3.7-py3-none-any.whl | Python 3 | none | any | Details for pyvsmc-0.3.7-py3-none-any.whl |
Total release size: 94.1 kB
Release files / pyvsmc-0.3.7.tar.gz
| Download URL | pyvsmc-0.3.7.tar.gz |
|---|---|
| Size | 53.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
caf0ecc5a2dc7a70015c39c4993d1a01a989ec359e8085830ecbd1673c4461d9
|
|
BLAKE2b-256 checksum How to use checksums |
1ba169bce7167e0590dd56a43b631bab0758007f71673a75c03e7d90feddad53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Release files / pyvsmc-0.3.7-py3-none-any.whl
| Download URL | pyvsmc-0.3.7-py3-none-any.whl |
|---|---|
| Size | 40.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
99b9e195a4adb55acfaab5fb22af1cd5c4c3a97debfe55ae230c8d52df673bee
|
|
BLAKE2b-256 checksum How to use checksums |
54601ea29ad8f5500e9c4164142c27b9b96788bc81b39c766eb55fbe6ba18cac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Security for pyvsmc 0.3.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.