A deterministic daily regime snapshot in Python.
The snapshot on the QISAgent homepage is generated by the pipeline documented below — in full, because a data utility an institution cannot inspect is a data utility an institution cannot trust. Use it as a template for any rules-based monitoring job: pull public data, apply pre-committed rules, publish a signed artifact, fail loudly.
Design principles
- Deterministic. Same inputs, same output. No model calls, no sampled text — the "signal" sentence is templated from computed values, so the artifact is auditable line-by-line.
- Fail loudly, publish nothing partial. If any required series is missing, the run exits non-zero and yesterday's artifact stands. A stale-but-honest snapshot beats a fresh-but-broken one.
- Same-origin delivery. The Action commits
snapshot.jsoninto the site repository; Cloudflare Pages redeploys automatically and the widget fetches from its own origin — no CORS, no third-party runtime dependency, and a baked-in fallback means the widget can never render an error state.
Signal definitions
| Field | Rule |
|---|---|
| Index moves | One-day close-to-close change of SPY and QQQ (broad-market and Nasdaq-100 proxies). |
| Volatility level | Latest ^VIX close. |
| Regime read | Threshold rule on VIX: below 20 → Risk-On; 20–30 → Neutral; above 30 → Risk-Off. Deliberately coarse and pre-committed — the point is consistency, not cleverness. |
| Leading factor | Highest trailing ~21-trading-day return among the factor ETFs MTUM (momentum), VLUE (value), QUAL (quality), USMV (low volatility). |
The generator
The complete script ships in this repository as daily_snapshot.py. Core structure:
def market_regime(vix_level: float) -> str:
if vix_level < 20: return "Risk-On"
if vix_level < 30: return "Neutral"
return "Risk-Off"
def main() -> None:
sp500_chg = pct_change_1d("SPY")
nasdaq_chg = pct_change_1d("QQQ")
vix = latest_level("^VIX")
factor, factor_ret = leading_factor() # max trailing-1M among MTUM/VLUE/QUAL/USMV
payload = {
"generated_at_utc": now_utc_iso(),
"sp500_change_pct": round(sp500_chg, 2),
"nasdaq_change_pct": round(nasdaq_chg, 2),
"vix_level": round(vix, 1),
"top_factor": factor,
"market_regime": market_regime(vix),
"daily_signal": build_signal_text(sp500_chg, vix, factor),
}
json.dump(payload, open("snapshot.json", "w"), indent=2)
if __name__ == "__main__":
try:
main()
except Exception as exc: # fail loudly — never publish a partial file
sys.exit(f"Snapshot generation failed: {exc}")
Scheduling with GitHub Actions
A workflow runs the script each U.S. trading weekday and commits the artifact only when it changed:
on:
schedule:
- cron: "0 13 * * 1-5" # 13:00 UTC ≈ market-open hour, US Eastern
workflow_dispatch: {}
permissions: { contents: write }
jobs:
snapshot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install yfinance pandas
- run: python daily_snapshot.py
- run: |
git config user.name "qis-agent-bot"
git config user.email "[email protected]"
git add snapshot.json
git diff --staged --quiet || git commit -m "Daily snapshot: $(date -u +%F)"
git push
Because the site deploys from this repository, the commit itself is the publication step. Total infrastructure cost: zero.
Extending the template
Natural upgrades, in order of value: (1) add cross-asset breadth — rates, credit, and dollar-index reads; (2) compute realized-vs-implied volatility spread as a VRP indicator; (3) swap yfinance for a production data vendor from the Atlas; (4) attach drift and data-integrity checks so the pipeline monitors its own inputs. Each is an additive, auditable rule — never a model guess.