Cash Window Projection · Methodology
Version 1.3 · thresholds are provisional judgment calls, to be revisited after 3 months of runs.
This is a weekly monitor of market stress built from public, sourced observations. It is not investment advice. It never states a crash date, a probability, or "weeks until" anything.
How to read the report
- Level says where an indicator sits against its own history: Normal, Elevated, or High.
- Trend says which way it moved over roughly the last four weeks: Improving, Flat, or Deteriorating.
- The two are separate. A High reading that eased slightly is still High.
- Signals are scored. Context rows are shown with values and changes but no color or status.
Dates
- Cutoff: the Friday before the Monday report, 23:59 UTC. Only observations dated on or before the cutoff are used. The cutoff is printed in the footer and stored with the run.
- Run id: ISO week of the Monday report date (for example
2026-W40). One email per run id. - Derived series (SOFR − IORB, SOFR99 − IORB, VIX − VIX3M, VIX9D − VIX) use the latest date where all inputs have an observation. Each series' own latest value is never mixed with another's.
- Weekly Fed data (H.4.1 reserves, TGA, total assets) posts Thursday for the prior Wednesday; NFCI posts Wednesday for the prior week. A Monday report therefore shows data several days old. That is normal, not overdue.
Data states
| State | Meaning | What is shown |
|---|---|---|
| ok | Current observation | Value, changes, status |
| overdue | Older than expected: daily > 5 business days before cutoff; weekly > 14 days; quarterly > 120 days after quarter end | Last verified value with its date, labeled overdue |
| fetch_failed | HTTP or parse error, unexpected CSV header, unrecognized units, or no shared dates for a derived series | No value |
| insufficient_history | Signal with under 2 years of observations | Value only, no percentile or status |
| unavailable | No free, redistributable source | Listed on this page only |
Nothing is filled, estimated, or interpolated.
Changes
- 1w change (
chg_1w): latest value minus the value in the previous report that was actually sent. This is what the reader saw last week, even if the source later revised history. - 4w change (
chg_4w): latest value minus the observation nearest to 28 days before the cutoff. Used for Trend. - Units: rate spreads in basis points; indices in index points; VIX measures in points; balances in $ billions and percent; the CRE delinquency rate in percentage points.
Scoring (signals only)
- Percentile (
pct): rank of the latest value within a trailing 10 year window, or the full history if shorter. The window length is printed next to the percentile. Oriented so higher = more stress (all current signals: higher value = more stress). Ties use mid-rank. - Level from percentile: Normal below 60, Elevated 60 to under 85, High 85 and above. Why: 60/85 split the distribution so roughly 40% of history reads Elevated or High and about 15% reads High. This is a judgment call meant to flag unusual readings without flagging most weeks.
- Funding spreads use fixed levels instead of percentiles. For SOFR − IORB and SOFR 99th percentile − IORB, the Level comes only from the fixed basis-point thresholds below. The percentile is still shown, marked "(context)". Why: IORB only exists from July 2021, so the whole comparison window sits in a period of abundant reserves when SOFR ran 8 to 11 bps below IORB. Against that history, a spread of 0 bps ranks near the 90th percentile even though it is not a stress reading. A percentile can't tell "less negative than usual" from "strained".
| Indicator | Normal | Elevated | High | Reason |
|---|---|---|---|---|
| SOFR − IORB | ≤ +10 bps | > +10 through +25 bps | > +25 bps | Repo trading well above the rate the Fed pays on reserves suggests reserves are becoming scarce; a spread above +25 bps has marked acute funding strains (for example, September 2019, measured then against IOER, the predecessor of IORB). |
| SOFR 99th pct − IORB | ≤ +20 bps | > +20 through +40 bps | > +40 bps | The 99th percentile normally sits about 10 bps above median SOFR (90th percentile of that gap: about 16 bps), so +20 is the tail equivalent of SOFR's +10. From 2021 to Sep 2026 it exceeded +40 bps only on recognized pressure days (Sep 19 and Sep 30, 2024; Oct 31, 2025). Month end and quarter end spikes to around +35 bps are routine and stay Elevated rather than High. |
- Absolute triggers (other signals) can only raise a Level, never lower it:
| Trigger | Result | Reason |
|---|---|---|
| VIX ≥ 25 | at least Elevated | 25 is a widely watched level where option markets price clearly above-average turbulence. |
| VIX ≥ 35 | High | Readings this high have historically coincided with acute stress episodes. |
| VIX − VIX3M > 0 for 5+ consecutive sessions | High | Near term implied volatility above three month implied (inverted term structure) is rare; a full week of it indicates persistent, not one-day, stress. |
| NFCI > 0 | at least Elevated | By construction, positive NFCI means financial conditions tighter than average. |
| STLFSI4 > 1 | High | By construction, zero is average stress; one standard deviation above average is a clear stress reading. |
- Low variance guard. If the trailing window's standard deviation is below a floor, the history is too flat for a percentile to mean anything. The percentile is shown grayed as "pct n/a (flat history)" and is not used for Level; only the fixed thresholds and absolute triggers apply. Floors: rate spreads 3 bps, VIX measures 1 point, index series 0.05 points. Why: a percentile must never contradict the badge. The funding spreads already use fixed levels (above), which is the stronger fix for them; the guard covers every other signal.
- Trend: from the 4 week change in the stress direction. Flat if the change is smaller than 10% of the window's standard deviation; otherwise Deteriorating (toward more stress) or Improving. Why 10%: small enough that meaningful moves register, large enough that week-to-week noise in slow indices reads as Flat.
Section assessment
For Liquidity, Credit and Volatility:
- Level = the highest Level among the section's signals. No averaging and no weight redistribution. Overdue signals still count toward the highest Level so stale data never hides stress, but they do not count as measured.
- Coverage is printed as "N of M signals measured" (ok signals).
- If fewer than half of a section's signals are ok, the section shows Insufficient data this week instead of a Level.
- Trend is magnitude aware, so one small move cannot set a whole section: Deteriorating if any signal's four week move is clear (at least half a standard deviation of its window) or more than half the signals are deteriorating; Improving by the same rule; otherwise Flat. Displayed in plain words: Steady, Slight increase in pressure, Rising pressure, Slight easing, Easing.
- Both are attributed: the report prints which signal set the Level and which set the Trend ("level set by SOFR minus IORB, trend set by SOFR 99th percentile minus IORB"), once if the same signal sets both.
Regime word
Set by code, never by the model:
Level and direction are kept apart:
- Stressed: any section High, or NFCI above zero, or STLFSI4 above one.
- Tightening: an Elevated section that is also rising, or two or more sections clearly rising.
- Mostly normal: a section is Elevated but nothing is tightening. The dashboard headline adds the count, for example "Mostly normal, with one elevated signal".
- Calm: everything Normal.
The email subject uses this word.
There is no overall composite of our own. The two Fed built indices (NFCI and STLFSI4) are the headline.
Why the flag
When a section is Elevated or High, a short callout explains it, written by code from the data: which indicator set it, how unusual the reading is (its percentile in the comparison history, or its fixed threshold), its trend, and how many of the section's other indicators are Normal.
What this means
Two or three plain sentences at the top of the email and the dashboard: what is normal, what is flagged and why, and whether anything is building. Written by the model with the same claim checks, plus a plain language rule (no terms like configuration, transmitting, tail or regime). If it fails, a templated summary is used.
The Reading
The Reading is written by Claude (model claude-sonnet-5) from the computed fields only. It never sees raw observations, the web, or earlier runs beyond the week over week changes already in the data.
- Order: Liquidity, Credit, Volatility, Market structure, then the Cash Window Projection that synthesizes them, then a Watch list.
- Claims: every factual statement carries claims that name an indicator or section by id (level is, trend is, rose, fell, above or below zero, percentile above or below, near its next threshold, data state, which signal set a section's level or its trend). Code checks each claim against the computed data before sending.
- No numbers from the model: a sentence with any digit in it is dropped. Every number shown, including in the footnotes, is rendered by code.
- Dropped sentences: any wrong claim, unknown id, digit, or banned word (crash, fuse, imminent, inevitable, collapse, guaranteed, certain) drops the sentence; it is never edited. Sentences with no claims are limited to a quarter of each paragraph.
- Fallback: if more than a third of a paragraph's sentences fail, that paragraph is replaced by the templated version. If the projection fails, or two or more section paragraphs fail, the whole Reading is templated. The Data check says what happened. The email always sends.
- Projection rule: the projection may only cite indicators that survived checking in the four paragraphs above it.
- Citations: each claim becomes a numbered mark linking to that indicator on the dashboard; the Sources block lists label, value, date, percentile, Level and Trend, all from computed data.
- Watch list: two to four indicators, with the distance to the next threshold added by code. A threshold is "near" when the distance is within 20% of its magnitude (or of the window's standard deviation, for thresholds at zero); a consecutive session trigger is near when one session away.
- Templated fallback (also the first report's movers): per section "Level and Trend, level set by, trend set by, coverage"; market structure as four week moves; projection as NFCI versus average, highest section, and the regime word.
- Interpretations the numbers do not establish (demand, cushions, warnings, fear, sentiment, "building toward", "doing the work") are rejected in code and the sentence is dropped.
- Wording rules: slight moves are described as slight; no claims about duration (the data is the latest value and its recent change); no superlatives without a percentile claim; no causes the numbers do not show.
- The model's raw response is stored with each run so a bad Reading can be diagnosed later.
Worth watching this week
One item per report, chosen by code and written by the model with the same claim checks (no digits, no figures in words, banned words, claims verified).
Divergence first. Code scans for things that do not fit:
| Pattern | Detected when |
|---|---|
| Sections disagree | one section is Elevated or High while another is Normal and Improving |
| Level versus trend | a signal is Normal but Deteriorating with a four week move in the top quarter of its history, or High but Improving |
| Median versus tail | SOFR minus IORB is Flat while the SOFR ninety ninth percentile spread is Deteriorating, or the reverse |
| Composites disagree | the Chicago Fed and St. Louis Fed indices are on different Levels |
| Implied versus realized | the VIX minus realized volatility gap is in the top or bottom tenth of its history |
Candidates are ranked by how many indicators they involve, then by size. The model is told whether the divergence is small, moderate or large and is asked not to overstate a small one.
Otherwise a connection: a curated explainer of how two or three of the indicators relate (for example tax day and bank reserves, or month end repo pressure). An entry with a relevant date in the coming week is preferred; otherwise the entry whose indicators moved most over four weeks. An entry used in the last six reports is skipped. Explainer sentences for a connection may carry no claims.
If any part of the block fails its checks, the block is left out entirely; there is no templated version.
Other commentary
- What moved: the three signals with the largest change in stress units (change divided by the window's standard deviation, so basis points and index points can be compared), shown in their own units. On the first report this uses four week changes and says so.
- Data check: one line per indicator not in the ok state, plus any Reading fallbacks.
Schedule
The report runs Mondays at 12:00 UTC: 5:00am in Las Vegas while it is on daylight time (4:00am after daylight saving ends), 2:00am in Hawaii. Data is cut off at the prior Friday.
Indicators and sources
All FRED series units are read from FRED series metadata at run time and printed. Source links are on every indicator name in the email.
Headline
| Indicator | Source | Notes |
|---|---|---|
| Chicago Fed National Financial Conditions Index | FRED NFCI | Weekly. Positive = tighter than average. |
| St. Louis Fed Financial Stress Index | FRED STLFSI4 | Weekly. Zero = average stress. |
A. Liquidity / Funding
| Role | Indicator | Source |
|---|---|---|
| Signal | SOFR minus IORB (same date) | SOFR, IORB. IORB starts July 2021, so this window is about 5 years, labeled. |
| Signal | SOFR 99th percentile minus IORB | SOFR99, IORB |
| Context | Overnight reverse repo balance | RRPONTSYD |
| Context | Reserve balances | WRESBAL |
| Context | Treasury General Account | WTREGEN |
| Context | Fed total assets | WALCL |
B. Credit
| Role | Indicator | Source |
|---|---|---|
| Signal | NFCI credit subindex | NFCICREDIT |
| Signal | NFCI risk subindex | NFCIRISK |
| Signal | NFCI leverage subindex | NFCILEVERAGE |
| Signal | Moody's Baa yield minus 10yr Treasury | BAA10Y. FRED license: citation required (not restricted). |
| Context | Bank CRE loan delinquency rate | DRCRELEXFACBS. Quarterly, lags about 6 weeks; the last published quarter is shown and is not flagged stale for being quarterly. |
C. Volatility
| Role | Indicator | Source |
|---|---|---|
| Signal | VIX close | CBOE VIX CSV. Taken from CBOE directly: FRED's copy (VIXCLS) runs several days behind. |
| Signal | VIX minus VIX3M (same date) | VIXCLS, CBOE VIX3M CSV. Positive = inverted term structure; consecutive inverted sessions are counted. |
| Signal | VIX9D minus VIX (same date) | CBOE VIX9D CSV, VIXCLS |
D. Market structure (context only, never scored)
| Indicator | Source | Label |
|---|---|---|
| VIX minus trailing 21-session realized volatility | VIXCLS, FRED SP500 | Gap between recent realized volatility and implied volatility. Realized volatility = sample standard deviation of the latest 21 daily log returns of the S&P 500 close × √252 × 100 (needs 22 closes). Compared with VIX on the same observation date; both inputs are printed under the row. This is not a measure of CTA or volatility control positioning. |
| CBOE SKEW | CBOE SKEW CSV | Relative pricing of out of the money puts. No status color. |
CBOE CSV header rows are checked on every fetch; an unexpected header is a fetch failure.
Not measured (unavailable)
| Concept | Why |
|---|---|
| ICE BofA corporate spreads (BAMLH0A0HYM2, BAMLC0A4CBBB, BAMLH0A1HYBB, BAMLH0A3HYC) | FRED now carries only 3 years of history and marks them copyright pre-approval (internal use only). Replaced by Fed produced NFCI subindices and Moody's Baa − 10y. |
| Dealer gamma exposure | Paid dataset (options dealer positioning). |
| Futures order book depth | Paid exchange market data. |
| ETF / NAV dislocations | Requires intraday ETF and NAV data from paid vendors. |
| Systematic (CTA, vol control) positioning | Only available as paid model estimates. |
| Money market fund assets (ICI), Treasury issuance | Deferred from v1. |
Licensing note
FRED marks SP500 as copyright pre-approval. This is a private report for one reader, and the owner chose to keep it (2026-09-28). The index level itself is never displayed; only the derived realized volatility is. The audit reports this as an accepted restriction.
Audit
scripts/audit.ts checks every series for history start, units, frequency, last observation, publication lag, and license before it is allowed into the scored set. Series with a redistribution restriction or under 5 years of history are removed; windows shorter than 10 years are labeled with their actual length. The latest output is in audit.md in the repository.
Storage
Every run is stored (raw observations used, computed fields, per-source fetch status, and the exact email HTML) before it is sent. A run whose id already has a sent timestamp is never sent again.