PyCheck — ML Safety Analyzer
32 rules. Zero dependencies. Catches the bugs that cost millions.
PyCheck is a static AST analyzer that detects temporal lookahead bias, causal confusion, and unguarded uncertainty in Python ML and trading scripts — before you run a single line of code.
pip install pycheck-neuron
pycheck my_strategy.py
The Problem It Solves
These bugs are invisible to Python, pytest, and your IDE. They only show up in production — often after you've already lost money or shipped a flawed model.
Lookahead bias — the single most expensive silent bug in quantitative ML:
# This looks fine. Python won't complain. Your backtest will show 94% accuracy.
# In production, you lose everything — because this uses data from the future.
df['target'] = df['close'].shift(-1) # ERROR[T001]: future data access
X_scaled = scaler.fit_transform(X) # ERROR[T010]: test set leaks into training
cv = KFold(n_splits=5, shuffle=True) # ERROR[T012]: shuffles time — leaks future
PyCheck catches all three before execution:
=================================================================
PyCheck — NEURON ML Safety Analyzer
Scanning: strategy.py
Rules: 32 active + Data Flow & Provenance Engine
=================================================================
ERROR[T001]: .shift(-1) accesses data 1 rows INTO THE FUTURE
--> strategy.py:4:14
4 | df['target'] = df['close'].shift(-1)
^^^^^^^^^^^
help: Use .shift(1) to access past data instead
ERROR[T010]: .fit_transform() on full dataset leaks test statistics into training
--> strategy.py:8:11
8 | X_scaled = scaler.fit_transform(X)
^^^^^^^^^^^
help: Use .fit(X_train).transform(X_train), then .transform(X_test) separately
ERROR[T012]: KFold() shuffles time-series data across folds, leaking future into training
--> strategy.py:11:5
11 | cv = KFold(n_splits=5, shuffle=True)
^^^^^^^^^^^
help: Use TimeSeriesSplit() for temporal cross-validation
-----------------------------------------------------------------
Summary: 3 error(s), 0 warning(s)
These 3 error(s) would be COMPILE-TIME ERRORS in NEURON
Python detected: 0 of these issues at runtime
-----------------------------------------------------------------
What It Catches
Temporal Leak Rules (17 rules — T001–T017)
| Rule | What It Catches | Severity |
|---|---|---|
T001 |
.shift(-1) — accessing future data |
Error |
T002 |
train_test_split() on time-series data |
Error |
T003 |
.rolling() computed before the train/test split |
Warning |
T004 |
.expanding() computed before split |
Warning |
T005 |
.pct_change(-1) — future percent returns |
Error |
T006 |
iloc[i + 1] inside a loop — future index access |
Error |
T007 |
.bfill() — backward fill uses future to fill past |
Error |
T008 |
.interpolate(method='cubic') — non-causal interpolation |
Warning |
T009 |
.rolling(center=True) — centered window leaks future |
Error |
T010 |
.fit_transform() on full dataset — test leaks into train |
Error |
T011 |
StandardScaler / MinMaxScaler fitted before split |
Error |
T012 |
KFold instead of TimeSeriesSplit |
Error |
T013 |
.resample() on full dataset before split |
Warning |
T014 |
.diff(-5) — future difference computation |
Error |
T015 |
Negative .shift() inside groupby().transform() |
Error |
T016 |
pd.merge_asof() with direction='nearest' or 'forward' |
Error |
T017 |
Target laundering & un-split target encoding (regime_success_rate) |
Error |
Causal Confusion Rules (7 rules — C001–C007)
| Rule | What It Catches | Severity |
|---|---|---|
C001 |
.corr() used as a basis for trading or treatment decisions |
Warning |
C002 |
Target column included as a feature | Error |
C003 |
Post-treatment variable included in features | Warning |
C004 |
Filtering by outcome variable — survivorship bias | Error |
C005 |
.dropna() removing non-random rows — selection bias |
Warning |
C006 |
p-value threshold without multiple comparison correction | Warning |
C007 |
.corrwith() used for causal feature selection |
Warning |
Uncertainty Rules (6 rules — U001–U006)
| Rule | What It Catches | Severity |
|---|---|---|
U001 |
.predict() without confidence or probability scores |
Warning |
U002 |
Hardcoded decision threshold (e.g. if pred > 0.5) |
Warning |
U003 |
.predict_proba() without calibration check |
Info |
U004 |
Single model without ensemble or uncertainty estimate | Info |
U005 |
.score() evaluated on training data only |
Error |
U006 |
.predict() inside a production loop without guard |
Error |
Data Quality Rules (2 rules)
| Rule | What It Catches | Severity |
|---|---|---|
D002 |
except: pass — silently swallowed pipeline errors |
Warning |
D003 |
Magic numbers in data operations | Info |
Data Flow & Provenance Tracking Engine
Beyond syntactic pattern matching, PyCheck includes a two-pass AST data flow engine that traces data lineage across reassignments, aliases, joins, and helper functions:
1. Future Taint Propagation (F001)
Traces future-leaked signals across multi-level variable indirection:
future_ret = df['close'].shift(-1) # Future taint source
signal = future_ret - df['close'] # Taint propagates through arithmetic
model.fit(X, signal) # ERROR[F001]: tainted data reaches .fit() sink
2. Partition Provenance & Contamination (F002)
Identifies training partitions, test partitions, and cross-fold leakage:
# Naming conventions alone are insufficient when variables get reassigned:
X_train = pd.concat([X_train, X_test]) # Tainted with test fold data
scaler.fit_transform(X_train) # ERROR[F002]: test partition data in training sink
PyCheck follows:
- Concatenation & Merges:
pd.concat,np.concatenate,.append(),.merge(),.join(). - Aliases:
X_eval = X_testfollowed by combination or fitting. - Inter-Procedural Helper Functions: Functions returning expressions derived from test partition arguments.
CLI Usage
pycheck script.py # Errors + warnings (default)
pycheck script.py --info # Include info-level diagnostics
pycheck script.py --quiet # Errors only
pycheck script.py --json # JSON output — pipe into CI/CD
pycheck --list # List all 32 rules
pycheck --help
JSON Output (CI/CD Integration)
pycheck strategy.py --json | jq '.[] | select(.severity == "error")'
Returns structured output for automated pipelines:
[
{
"line": 22,
"col": 14,
"severity": "error",
"code": "T001",
"message": ".shift(-5) accesses data 5 rows INTO THE FUTURE",
"help": "Use .shift(5) to access past data instead"
}
]
Exit Codes
0— No errors found1— One or more errors found
Clean integration with make, pre-commit, GitHub Actions, and GitLab CI.
Enterprise CI/CD & GitLab Integration
PyCheck natively exports into the GitLab Code Quality report format (gl-codequality.json), enabling quantitative teams, hedge funds, and enterprise banks to guard against lookahead bias directly in their Merge Requests.
# Output GitLab Code Quality JSON directly to stdout
pycheck . --gitlab
# Or write report artifact while printing directory summary in CI job trace
pycheck . --gitlab --output gl-codequality.json
Turnkey .gitlab-ci.yml
Add this job to your repository to automatically block any Merge Request containing lookahead leaks:
pycheck:
stage: test
image: python:3.11-slim
before_script:
- pip install pycheck-neuron
script:
- pycheck . --gitlab --output gl-codequality.json
artifacts:
reports:
codequality: gl-codequality.json
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
allow_failure: false
GitLab will automatically annotate the Merge Request diff with red warnings on every line where temporal data leaks or unguarded point predictions are introduced.
Real-World Examples
The examples/ directory contains recreations of actual ML disasters:
| File | What It Demonstrates |
|---|---|
zillow_ibuying.py |
11 bugs replicating the structural failures behind the $881M Zillow Offers write-down |
yfinance_strategy.py |
The classic quant blog tutorial: shift(-5) + fit_transform before split |
sentdex_tutorial.py |
A widely-shared ML tutorial with silent time-series cross-validation errors |
leaky_pipeline.py |
A complete end-to-end pipeline showing taint propagation across 4 variables |
clean_pipeline.py |
The correct version — 0 errors, 0 warnings |
VS Code Extension
The pycheck-vscode extension gives you real-time red/yellow squiggles as you write:
- Inline error markers on every flagged line
- Hover tooltips with rule description and fix suggestion
- Status bar showing live error/warning count
PyCheck Cloud & Enterprise SaaS
For institutional hedge funds, algorithmic trading desks, and enterprise ML engineering teams, PyCheck Cloud provides continuous compliance and automated gatekeeping:
- Automated PR / MR Gatekeeping: GitHub App & GitLab integration that automatically blocks pull requests containing temporal leaks, lookahead joins, or cross-fold contamination before they reach production.
- Proof-of-Causality Audit Certificates: Cryptographically verifiable backtest audit reports (PDF & JSON) certifying that quantitative strategies and feature pipelines are provably free of temporal lookahead bias for LP allocators and risk committees.
- Firm-Wide Policy Engine: Author proprietary firm-specific invariants, restrict internal data feed schemas, and monitor leak incident trends across 50+ quant researchers.
- Air-Gapped & On-Prem Deployment: Private VPC and on-premise Kubernetes support for proprietary trading strategies that cannot leave your internal network.
See the Enterprise CI/CD Integration section or visit https://github.com/neuronlabs-ai/pycheck to learn more or request institutional access.
PyCheck & NEURON: Working Hand-in-Hand
PyCheck and the NEURON Programming Language represent a dual-defense ecosystem against algorithmic failures:
| System | Role | Mechanism | Guarantee |
|---|---|---|---|
| PyCheck | The Diagnostic Layer for existing Python code | 2-pass AST data flow & provenance static analysis | Flags known leakage patterns & contamination in seconds |
| NEURON | The Permanent Cure for future algorithmic systems | Type-theoretic stochastic filtration proofs ($\mathcal{F}_t$) | 100% mathematical compile-time proof; zero runtime overhead |
The Migration Pathway
- Audit Today with PyCheck: Run
pycheckacross existing Python/Pandas/PyTorch repositories to identify lookahead leaks and target laundering. - Transpile with
py2nr: Use the automated transpiler to convert validated feature pipelines into typed NEURON modules. - Deploy with 100% Compile-Time Safety:
# In NEURON, lookahead bias is a compile-time type error — not a runtime surprise:
fn strategy(prices: Temporal[Tensor, 0]) -> Tensor:
let future = prices.shift(1) # NEURON uses causal algebraic offsets (+1 is future)
# error[TemporalLeak]: operation requires filtration F_{t+1}, but context is F_t (offset 0)
# Lookahead bias rejected at compile time before execution.
Explore the language: https://github.com/neuronlabs-ai/neuron-lang
License
MIT — free for any use.
PyCheck is built on the NEURON type theory for temporal, causal, and uncertainty safety in ML programs.
Metadata
Release files for pycheck-neuron 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pycheck_neuron-1.0.2.tar.gz | 42.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pycheck_neuron-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 75.8 kB
Release files / pycheck_neuron-1.0.2.tar.gz
| Download URL | pycheck_neuron-1.0.2.tar.gz |
|---|---|
| Size | 42.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8f5ceb1675db1c78db0e9400694d3af3e2384a7376b8bf2fc66fca2c23b81eae
|
|
BLAKE2b-256 checksum How to use checksums |
7c1d77b4552711d94903ffda6c35966cd6bb66641b7665b8e98879e45c48d251
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|
Release files / pycheck_neuron-1.0.2-py3-none-any.whl
| Download URL | pycheck_neuron-1.0.2-py3-none-any.whl |
|---|---|
| Size | 33.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9e79e685900ef9f98848c65c7ef54c2995a21f0ac4f008c3fe4e4a85c88425a
|
|
BLAKE2b-256 checksum How to use checksums |
8ddaf399e78187c3708a1efaa0657e3edfafbbe2431e88c1e94e1962545abe02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.9
|