FAWP Alpha Index — Information-Control Exclusion Principle detector
Project description
fawp-index
Your model still predicts. But you've already lost control.
fawp-index detects the moment when a system crosses into the Information-Control Exclusion Principle regime — where predictive information persists but the ability to act on it has collapsed.
| Domain | What predicts | What collapses |
|---|---|---|
| 📈 Quant finance | Factor alpha signal | Market execution edge (crowding) |
| 🌀 Dynamical systems | State forecasts | Stabilising control authority |
| 🌊 Weather / climate | Forecast skill | Intervention window |
| 🌍 Seismic | Precursor signal | Stress release control |
| 🤖 ML systems | Model predictions | Ability to retrain / intervene |
Install
pip install fawp-index # core
pip install "fawp-index[plot]" # + matplotlib figures
pip install "fawp-index[dashboard]" # + Streamlit dashboard
pip install "fawp-index[all]" # everything
What's in the package
| Module | What it does |
|---|---|
fawp_index |
Core FAWP detector, ODW detection, DataFrame API |
fawp_index.market |
Rolling FAWP scan on price / volume data |
fawp_index.watchlist |
Multi-asset, multi-timeframe watchlist scanner |
fawp_index.alerts |
Telegram / Discord / email / webhook alert engine |
fawp_index.significance |
Bootstrap significance testing |
fawp_index.compare |
Side-by-side comparison of two detections |
fawp_index.benchmarks |
Five canonical ground-truth benchmark cases |
fawp_index.report |
PDF report generator |
dashboard/app.py |
Streamlit visual dashboard |
fawp-index CLI |
Six subcommands (detect / market / watchlist / significance / benchmarks / version) |
60-second quickstart
import numpy as np
from fawp_index import FAWPAlphaIndex
pred = np.random.randn(5000)
future = pred[20:] + np.random.randn(4980) * 0.3
action = np.random.randn(4980) * 0.001 # near-zero = FAWP
obs = np.random.randn(4980) * 0.1
result = FAWPAlphaIndex().compute(pred[:4980], future, action, obs)
print(result.summary())
result.plot() # pip install "fawp-index[plot]"
Market scanner
Scan a price CSV for FAWP regimes across a rolling window:
import pandas as pd
from fawp_index.market import scan_fawp_market
df = pd.read_csv("SPY.csv", parse_dates=["Date"], index_col="Date")
scan = scan_fawp_market(df, ticker="SPY", close_col="Close", volume_col="Volume")
print(scan.summary())
scan.plot(prices=df["Close"])
scan.to_html("spy_fawp.html") # self-contained HTML report
scan.to_csv("spy_fawp.csv")
Financial interpretation:
- pred channel
I(returnₜ ; returnₜ₊Δ)— is the market still forecastable? - steer channel
I(signed_flowₜ ; returnₜ₊τ)— do your orders still move price? - FAWP window — yes you can forecast direction, but your orders no longer move price
Works without volume (falls back to lagged-return autocorrelation).
Watchlist scanner
Scan a whole watchlist and rank every asset by FAWP signal:
from fawp_index.watchlist import scan_watchlist
# With your own DataFrames:
result = scan_watchlist({"SPY": spy_df, "QQQ": qqq_df, "GLD": gld_df, "BTC": btc_df})
# Or fetch automatically with yfinance:
result = scan_watchlist(
["SPY", "QQQ", "GLD", "BTC-USD", "ETH-USD"],
period = "2y",
timeframes = ["1d", "1wk"],
)
result.rank_by("score") # strongest current regime score
result.rank_by("gap") # widest leverage gap (bits)
result.rank_by("persistence") # longest active regime
result.rank_by("freshness") # most recent new signal
result.active_regimes() # only currently flagged assets
result.top_n(5, "score")
result.to_html("watchlist.html")
result.to_csv("watchlist.csv")
result.to_json("watchlist.json")
Alerts
Fire alerts the moment a regime starts, ends, or crosses a threshold:
from fawp_index.alerts import AlertEngine
engine = AlertEngine(gap_threshold=0.05, state_path="fawp_state.json")
engine.add_terminal()
engine.add_telegram(token="BOT_TOKEN", chat_id="CHAT_ID")
engine.add_discord(webhook_url="https://discord.com/api/webhooks/...")
engine.add_email(smtp_host="smtp.gmail.com", username="you@gmail.com",
password="app_password", to_addrs=["you@gmail.com"])
engine.add_webhook("https://hooks.slack.com/services/...")
result = scan_watchlist(dfs)
alerts = engine.check(result) # NEW_FAWP / REGIME_END / GAP_THRESHOLD
engine.daily_summary(result) # condensed digest
State-aware: NEW_FAWP fires once when a regime is first detected, not on
every subsequent scan. Set state_path to persist state across runs.
Significance testing
Test whether a detected regime is statistically significant:
from fawp_index import ODWDetector, fawp_significance
odw = ODWDetector.from_e9_2_data()
sig = fawp_significance(odw, n_bootstrap=200)
print(sig.summary())
# p_fawp=1.000 p_null=0.145 significant=YES
# ci_tau_h=[31,31] ci_peak_gap=[1.538, 1.562] bits
sig.to_html("significance.html")
sig.plot()
Benchmark suite
Five canonical ground-truth cases that verify the detector is working:
from fawp_index import run_benchmarks
suite = run_benchmarks()
print(suite.summary())
suite.verify_all() # raises if any case fails
suite.to_html("benchmarks.html")
| Case | Expected | Description |
|---|---|---|
clean_control |
✅ FAWP | Textbook: steering collapses, prediction survives |
prediction_only |
❌ None | Predictable system, no steering channel |
control_only |
❌ None | Active controller, no predictive horizon |
noisy_false_positive |
❌ None | Noisy stable — designed to trap detectors |
delayed_collapse |
✅ FAWP | Fast-collapsing unstable system, narrow ODW |
Dashboard
Visual five-tab tool — Scanner, Curves, Heatmap, Significance, Export:
pip install "fawp-index[dashboard]"
fawp-dashboard # opens on http://localhost:8501
# Or run directly from the repo:
cd dashboard && streamlit run app.py
Command line
# Detect FAWP in a CSV:
fawp-index detect data.csv --state price --action trade_size --plot
# Rolling market scan:
fawp-index market SPY.csv --close Close --volume Volume --out report.html
# Scan a whole watchlist:
fawp-index watchlist spy.csv qqq.csv gld.csv --labels SPY QQQ GLD --out wl.html
fawp-index watchlist *.csv --rank-by gap --top 5
# Significance test:
fawp-index significance data.csv --state price --action trade
# Benchmark suite:
fawp-index benchmarks --verify
# Version:
fawp-index version
DataFrame API
from fawp_index import fawp_from_dataframe, fawp_rolling
# Single detection:
result = fawp_from_dataframe(df, pred_col="factor", action_col="trade", future_col="fwd_return")
# Rolling — adds fawp_pred_mi, fawp_steer_mi, fawp_gap, fawp_in_regime columns:
df_annotated = fawp_rolling(df, pred_col="returns", action_col="volume")
df_annotated[df_annotated["fawp_in_regime"]]
Sklearn compatible
from fawp_index.sklearn_api import FAWPTransformer
from sklearn.pipeline import Pipeline
pipe = Pipeline([("fawp", FAWPTransformer(pred_col=0, action_col=1, delta=20))])
pipe.fit(X)
Reproduce published figures
The E8 experimental data is bundled with the package:
from fawp_index import ODWDetector
odw = ODWDetector.from_e9_2_data()
odw.plot()
To reproduce full figure sets, clone the repo and run from there:
git clone https://github.com/DrRalphClayton/fawp-index
cd fawp-index
python examples/reproduce_e8.py --save
python examples/reproduce_e1_e7.py --save
The mathematics
The FAWP Alpha Index v2.1:
α₂(τ) = I[τ≥1] · g(τ) · (Sₘ(τ) − Ĩ_steer(τ)) · (1 + κ · R_log(τ))
g(τ)— gate: fires when pred MI > η AND steer MI ≤ εSₘ(τ)— windowed-min corrected predictive MI (persistence)Ĩ_steer(τ)— null-corrected steering MIR_log(τ)— log-slope resonance amplifier near the horizon
The agency horizon τ_h is where steering MI first falls below ε. Near τ_h, predictive MI does not fall — it surges. This is the empirical signature of the Information-Control Exclusion Principle.
Citation
@software{clayton2026fawpindex,
author = {Ralph Clayton},
title = {fawp-index: Information-Control Exclusion Principle detector},
year = {2026},
version = {0.11.0},
url = {https://github.com/DrRalphClayton/fawp-index},
doi = {10.5281/zenodo.18673949}
}
@article{clayton2026agency,
author = {Ralph Clayton},
title = {Forecasting Without Power: Agency Horizons and the Leverage Gap},
year = {2026},
doi = {10.5281/zenodo.18663547}
}
Links
- 📦 PyPI: pypi.org/project/fawp-index
- 📂 GitHub: github.com/DrRalphClayton/fawp-index
- 📄 Paper (E1–E7): doi:10.5281/zenodo.18663547
- 📄 Paper (E8): doi:10.5281/zenodo.18673949
- 📗 Book: Forecasting Without Power — Ralph Clayton (2026)
- 📊 Docs:
docs/— market, watchlist, alerts, significance, benchmarks, dashboard
Module docs
| File | Contents |
|---|---|
docs/market.md |
Market scanner API + parameters |
docs/watchlist.md |
Watchlist scanner + ranking |
docs/alerts.md |
Alert engine + channel setup |
docs/significance.md |
Significance testing methods |
docs/benchmarks.md |
Benchmark cases + verification |
docs/dashboard.md |
Dashboard install + deploy |
MIT License · Ralph Clayton · 2026
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fawp_index-0.11.0.tar.gz.
File metadata
- Download URL: fawp_index-0.11.0.tar.gz
- Upload date:
- Size: 510.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
705cb74b210cf6806274d33524e5b7f859d96ff99c89124bb6c459cc305faa06
|
|
| MD5 |
2f2daa85270a1c04d778845e97f7de02
|
|
| BLAKE2b-256 |
bffadaf3174ab198322175fda21188f2673938cf9af27681a3f055e47988b55a
|
File details
Details for the file fawp_index-0.11.0-py3-none-any.whl.
File metadata
- Download URL: fawp_index-0.11.0-py3-none-any.whl
- Upload date:
- Size: 508.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68775185ac7c4f703181f6c52b8b310d0298fd30d558caf6486a383b39cd0a56
|
|
| MD5 |
5cae34052d1da210115117a3141370dd
|
|
| BLAKE2b-256 |
68209a1ad277913d6a8ab4d94d4e47aae1e526fa449c87022d239e323866a91c
|