Tideline
Methodology & Audit
Tideline is a public macro regime tracker with one narrow predictive claim and one descriptive panel. Every test, every iteration, every external review is documented here. The brand is honesty, not alpha.
What Tideline claims
Two outputs. They mean different things and should not be conflated:
Zone 0 — Faber Trend Signal (predictive)
- Rule:
SPY > 200DMA AND 50DMA > 200DMA→GREEN; both bearish →CAUTION; mixed →NEUTRAL - Sample: 1997-01-02 to 2026-04-22, 7,153 trading days, n=1,541 CAUTION days
- Claim: when CAUTION fires, SPY 20-day DOWN rate has been 46.0% vs 36.9% unconditional baseline (+9.1pp edge)
- Robustness: block bootstrap (L=60, 10,000 iterations) 95% CI
[37.7%, 55.8%]— lower bound exceeds baseline - Framing: risk-management signal, not return forecast. Faber (2007) A Quantitative Approach to Tactical Asset Allocation.
CAUTION. The edge is small (≈9pp) and barely clears the bootstrap-corrected baseline. Nothing else is claimed.
Zone 1 — 4-State Regime (descriptive only)
- States:
EASY·NORMAL·ELEVATED·STRESS - Inputs: BAA10Y credit spread, VIX, 3m–10y curve, Chicago Fed NFCI
- Thresholds: rolling 5-year percentile (1,260 trading days), no point cutoffs
- Behavior: ~14.9 transitions/year; reacts to real stress events within 0–3 trading days
- Out-of-sample test: correctly flagged
STRESSwithin 3 days on 5/5 events not used to design the rule (US downgrade 2011, China devaluation 2015, yen intervention 2022, UK gilt crisis 2022, SVB 2023)
STRESS describes current macro conditions, not what SPY will do next. In our backtest, average forward returns in STRESS are slightly positive (mean reversion after stress) — same pattern observed across 5 separate tests of the underlying composite.
Architecture
- Python workers fetch data on a 5-minute cron (GitHub Actions, public repo).
- Rule
v1.pyevaluates state machine on the full 1997-present panel. - Output JSON + decision-log CSV uploaded to Cloudflare R2 (zero egress, viral-safe).
- Static frontend (this page) fetches from R2 — no backend, no database.
- Every payload includes
rule_sha256— SHA256 ofrule/v1.pyat compute time. If the rule is ever modified, the hash changes and any reader can detect it.
Data sources (all free)
- FRED: BAA10Y (credit spread), VIXCLS, T10Y3M (curve), NFCI (Chicago Fed)
- Yahoo Finance: SPY (close, 50/200-day MAs)
Important caveats (read these)
FRED data forward-fill
FRED publishes most series at T+1 (next business day). When Tideline computes today's row, yesterday's BAA10Y/VIX/curve values are forward-filled to today. This is the only honest behavior given FRED's release schedule — there's no look-ahead, just lag.
NFCI release lag (T+5)
NFCI for the week ending Friday is released the following Wednesday. The state
machine enforces this 5-day lag explicitly in code. When you see NFCI in
the components panel, it's the value as of 5 trading days ago — what would have been
available at the time of our state computation.
Decision log drift on FRED revisions
Every state on the dashboard is recomputed from current FRED data each run. If FRED retroactively revises a historical value (this happens occasionally, e.g., NFCI), past decision-log entries can shift slightly. In our audit, a +50bp revision to a single historical BAA10Y day caused 1 state-day to flip over a 29-year panel. Small but real. The log is "current vintage truth," not appended-immutable. We may persist transitions append-only in a future version.
What we tested and rejected
Tideline went through 11 documented research entries. Several initial designs failed
and were killed honestly. The full chronological log lives in the project repository at
workers/backtest/research_log.md. Highlights:
- Failed: 4-factor stress composite as a directional predictor for SPY (5 separate null-tests; composite has zero incremental information after standard controls)
- Failed: 6-target tournament with 4-factor composite (only TLT-direction passed marginally; not the public claim)
- Surviving: Faber/Golden-cross binary signal — block-bootstrap CI clears baseline by ~0.8pp
- Iterated: 4-state regime went through v2 → v3 → v4. v2 had bugs (stuck in STRESS 49% of days), v3 partial, v4 passes all 9 historical stress events. Documented.
Integrity checks
- Every payload carries
rule_sha256. Compute SHA256 ofworkers/rule/v1.pyin the repo and verify it matches. - Every percentile uses
shift(1).rolling(window).quantile()— today is excluded from its own threshold by construction. - NFCI is shifted by 5 trading days before any rule evaluates it.
- State machine bit-identical to the validated research artifact (7,376/7,376 days match).
- Public Faber claim reproduces from live data to the basis point: 46.0% / +9.1pp / CI [37.7%, 55.8%].
Disclaimer
Tideline is a descriptive macro regime tracker. It is not investment advice and does not recommend any trade. Past performance does not predict future results. The historical edge described above is small, statistically marginal, and based on a single 29-year sample. Signal decay is plausible — the underlying rule has been public since the 1970s.
Decision log
Every state transition Tideline has ever assigned, machine-readable. Download CSV.
Full research log
Every test run, every external review, every iteration that got us here. Failures included.
Rendered verbatim from workers/backtest/research_log.md.