# PulsePup Dashboard — Complete User Guide

Last audited: 2026-08-20

> **Static snapshot:** This hosted copy contains bundled historical data only. It does not connect to PostgreSQL, Massive, or a live API. Update it by building and uploading a new package.

## Hosted static snapshot runtime

This `/pulsepup/` package is a self-contained browser site, not a running dashboard server.

- The browser reads one same-folder asset: `static_snapshot.json`.
- The `/api/...` names described below are internal route keys inside that JSON file; the hosted browser does **not** request `/api/...` URLs.
- No PostgreSQL connection, Massive connection, live API, username, host, password, Seahorse lookup, scheduler, or external chart-data request is present or required.
- The `STATIC SNAPSHOT` badge means every rank, state, quantitative finding, delayed-context value, and chart bar is frozen at the package cutoff shown on the page.
- The `FROZEN` or `SNAPSHOT` delayed-context labels are historical context captured at package build time. They are not current quotes and cannot update themselves.
- Clicking Refresh only rereads the bundled JSON file and rerenders the same snapshot. It does not fetch new market data or start a calculation.
- To update the hosted dashboard, build a new package from the local reviewed dashboard and upload the replacement files into the existing `/pulsepup/` folder.

The source-lineage references to PostgreSQL, Massive, credentials, and API endpoints in the sections below describe how the frozen snapshot was produced and audited. They do not describe connections made by this uploaded package.

This guide defines every decision-facing part of the dashboard: what it measures, how it is calculated, how to interpret it, what it does **not** mean, where its data came from, and what to do when an acceptance gate fails.

The dashboard is medium-horizon decision support for a monitored stock universe. It does not place orders, issue buy/sell instructions, provide price targets, or convert a score into a probability of profit.

## 1. Recommended reading order

For any stock, read the dashboard in this order:

1. **Ticker and data cutoff** — confirm the stock and final trading session being described.
2. **State plus quantitative status** — distinguish a supported state from a mixed candidate or unavailable assessment.
3. **Aligned price chart** — verify the price path, moving averages, volume, final date, and final close used by the state.
4. **Exact support rule** — read the state-specific mandatory and minimum-pass conditions.
5. **Definition findings** — verify the exact structural rule that produced the candidate.
6. **Independent confirmation findings** — inspect both passing and failing advanced tests.
7. **Opportunity score and input audit** — understand relative ranking separately from state support.
8. **Additional structure and valuation context** — inspect direct daily features and maintained metadata.
9. **Delayed timing context** — use only as delayed entry-timing context; never treat it as a rank input.
10. **Diagnostics and automation** — verify the data pipeline, lineage, and freshness.

## 2. Data cutoff and provenance

The displayed score, state, quantitative findings, and graph must refer to one coherent observation cutoff.

- The current score comes from the latest valid `COMPLETED` Massive score run.
- That score run is bound by `source_feature_build_id` to one successful adjusted-daily feature build.
- The selected feature row must have the exact `feature_date` stored on the score.
- Per-stock quantitative inputs must have the same immutable point-in-time cutoff.
- The chart reads Massive adjusted one-day OHLCV from canonical PostgreSQL table `market_data_hist_raw`.
- The chart's final date must equal the feature date.
- The chart's final close must match the feature close within the configured tolerance.
- Re-running the deterministic state classifier on that feature row must reproduce the displayed structural candidate.

If any of these gates fails, the chart returns no bars and displays the exact reason. It never substitutes synthetic prices, demo data, a different cutoff, or an external chart.

### Data mode

- **database data** means the browser is reading canonical PostgreSQL outputs built from Massive data.
- **demo data** means a committed offline teaching fixture. Demo values are not current provider observations.

Neither label means the delayed intraday panel is real-time.

## 3. Structural candidate versus quantitative status

The persisted daily classifier first produces a **structural candidate**. Independent quantitative evidence then decides whether the UI may present that candidate as supported.

### Status meanings

| Status | Exact meaning |
|---|---|
| `QUANT SUPPORTED` | Every structural definition test passes, every mandatory confirmation passes, the minimum number of confirmation tests passes, and all evidence uses the aligned cutoff. |
| `MIXED QUANT EVIDENCE` | The structural label remains a candidate because a definition, mandatory confirmation, or required confirmation count did not pass. All contradictions remain visible. |
| `QUANT UNAVAILABLE` | The canonical feature row, current quantitative panel, aligned cutoff, or other required evidence input is unavailable. Unknown is never converted to zero. |

A supported k-of-n rule may contain a failed **non-mandatory** finding. The exact support rule printed above the findings explains why. A mandatory finding can never be outvoted by optional findings.

### Per-stock evidence versus the cohort forecast model

State support and return-model research are deliberately separate:

- **Per-stock descriptive evidence** aligns one stock with its configured benchmark and uses 314–380 adjusted daily observations ending exactly on the score's feature date. It calculates 20/60/120/252-session log momentum, 60-session benchmark-residual momentum from a trailing market model, and EWMA volatility. A SHA-256 source fingerprint and immutable feature-build ID make the carrier auditable.
- **Cohort expected-return research** keeps its stricter complete common-grid, walk-forward, embargo, calibration, and publication gates. A newer listing may make that whole cohort ineligible without making another stock's historical descriptive statistics unknowable.

The dashboard never relabels per-stock descriptive evidence as a forecast. If the cohort model is unavailable or rejected, the state can still be `SUPPORTED` or `MIXED` from current descriptive evidence, while the forecast line remains explicitly `UNAVAILABLE` or `REJECTED_EXCLUDED`.

Every expected confirmation row is retained. If an input is absent, its observed value says `unavailable`, the finding fails, and it remains in the denominator. The dashboard never improves a result by hiding a missing test.

### PASS and FAIL

Each evidence row prints four separate facts:

- **Observed** — the actual value at the cutoff.
- **Criterion** — the fixed comparison, including exact `>`, `≥`, `<`, or `≤` boundaries.
- **PASS/FAIL** — the direct result of that comparison.
- **Source** — canonical daily feature row, point-in-time quantitative panel, or causal HMM regime filter.

The row also states why the metric matters, its units, and its calculation method. The popup repeats these definitions without hiding failed observations.

## 4. Exact structural-state rules and quantitative support

Rules are evaluated in the priority order below. The first matching structural rule produces the candidate. Quantitative support is evaluated afterward and never changes the persisted score formula.

### 1. Failed Trend

Structural definition:

- distance from SMA 150 is at most `-8.0%`, **or** close is below `92%` of SMA 150; **and**
- fitted 60-session slope is below `0.00%` per session.

Quantitative support:

- all three 60/120/252-session observations must be available, and negative momentum on at least two is mandatory; and
- at least one of these also passes: negative benchmark-residual momentum or `P(Risk Off) ≥ 50%`;
- at least two confirmation findings pass in total.

### 2. Correction

Structural definition:

- distance from SMA 50 is at most `-5.0%`, **or**
- drawdown from the trailing 252-session high is at most `-18.0%`.

Quantitative support:

- both 20- and 60-session observations must be available, and at least one must be negative; this finding is mandatory; and
- at least one of these also passes: negative benchmark-residual momentum or EWMA conditional volatility of at least `45%` annualized;
- at least two confirmation findings pass in total.

High volatility by itself cannot certify a directional Correction.

### 3. Extended Trend

Structural definition:

- distance above SMA 20 is at least `12.0%`, **or**
- price is within `2.0%` of the trailing high and the 20-session return is at least `10.0%`.

Quantitative support:

- all four 20/60/120/252-session observations are available and at least three are positive; and
- benchmark-residual momentum is positive;
- both available confirmation findings pass.

Extended describes measured stretch. It is not a forecast that price must fall.

### 4. Early Breakout

Structural definition:

- distance from the trailing high is at least `-4.0%`;
- 20-session return is above `0.0%`; and
- latest volume is at or above the 65th percentile of the latest 20 sessions.

Quantitative support requires at least two of:

- positive 20-session advanced momentum;
- positive 60-session advanced momentum;
- positive benchmark-residual momentum.

### 5. Healthy Trend

Structural definition:

- `close ≥ SMA 20 ≥ SMA 50 ≥ SMA 150`;
- fitted 20-session slope is above `0.00%`; and
- fitted 60-session slope is at least `0.00%`.

Quantitative support:

- all four 20/60/120/252-session observations are available and at least three are positive; and
- benchmark-residual momentum is positive;
- both findings are mandatory.

### 6. Base Formation

Structural definition:

- absolute fitted 20-session slope is at most `0.08%` per session;
- absolute distance from SMA 50 is at most `6.0%`; and
- 252-session drawdown is more than `-18.0%` and at most `-3.0%`.

Quantitative support requires at least two of:

- absolute 20-session log return at most `log(1.08)` (about ±8% simple return);
- absolute 60-session log return at most `log(1.15)` (about ±15% simple return);
- absolute benchmark-residual momentum at most `0.75` residual-volatility units.

### 7. Transition

Structural definition:

- exactly zero of the six named structural rules above passes.

Quantitative support:

- all four 20/60/120/252-session momentum observations must be available and the set must contain at least one positive and one negative horizon; and
- either 20- and 60-session momentum have opposite signs or raw 60-session momentum and benchmark-residual momentum have opposite signs;
- at least two conflict findings pass in total.

Transition is therefore a positive claim about measured disagreement, not an unexplained fallback label.

## 5. Quantitative metric glossary

### Advanced log momentum

For horizon `h`, the calculation is:

`log(latest adjusted close / adjusted close h sessions earlier)`

The UI converts the result to the equivalent simple return with `exp(log return) - 1`. The sign test is still performed in log-return space. Horizons are 20, 60, 120, and 252 trading sessions.

### Benchmark-residual momentum

1. Align stock and mapped-benchmark daily returns.
2. Fit stock return to benchmark return over 252 sessions using ordinary least squares.
3. Sum the latest 60 residual returns.
4. Divide by expected 60-session residual volatility.

The result is in **residual-volatility units**. Positive means stock-specific strength after removing the benchmark-associated move; negative means stock-specific weakness. It is not a percentage and is not the same as simple relative strength.

### EWMA conditional volatility

Exponentially weighted variance gives more weight to recent point-in-time daily log returns, then annualizes volatility using 252 trading sessions. It measures recent variability, not direction and not a guaranteed future range.

### Fitted trend slope

For 20 or 60 closes:

1. fit an ordinary least-squares line against session index;
2. divide the fitted price change per session by the mean close;
3. multiply by 100.

Units are percentage points per trading session.

### 20-session volume percentile

`100 × count(latest-20 volumes ≤ current volume) / 20`

The value ranges from 5 to 100 because the current session is included.

### Moving-average distance

`100 × (latest close / SMA − 1)`

Positive is above the average and negative is below it.

### 252-session drawdown / distance from high

`100 × (latest close / highest close in 252 sessions − 1)`

Zero means at the high. Values below zero show the percentage loss from that high.

## 6. Causal HMM market regime

The HMM panel is **market-level context shared by all stocks at the same cutoff**. It is not the selected stock's structural state.

The three-state diagonal-Gaussian hidden Markov model uses market momentum/return, EWMA volatility, universe breadth, and average correlation. It is fit only on the historical prefix available through the displayed cutoff. The browser shows filtered probabilities for:

- `RISK_OFF` — learned latent state with the weakest return/risk profile;
- `NEUTRAL` — learned middle state;
- `RISK_ON` — learned state with the strongest return/risk profile.

The probabilities should sum to approximately 100% after rounding. They are probabilities of latent market-state membership, not probabilities that the selected stock will rise or fall. The regime is only counted in state support when a displayed evidence finding explicitly says so.

## 7. Forward-model boundary

Descriptive state evidence is separate from forecasts.

- `PUBLISHED_CURRENT` means an explicitly promoted, current model exists. Its output remains outcome context and does not define the structural state.
- `REJECTED_EXCLUDED` means walk-forward publication gates rejected the model. Expected returns and gain probabilities are excluded.
- `SHADOW_EXCLUDED` means a non-published challenger exists. Its outputs are excluded.
- `UNAVAILABLE` means no current forward-model evidence exists.

Rejected, stale, and shadow predictions cannot secretly contribute to a state, score, or rank.

## 8. Opportunity score

The Opportunity score is a relative 0–100 rank input:

`Opportunity = 0.35 × Mispricing + 0.30 × Trend + 0.20 × Participation + 0.15 × Readiness`

Every component and sub-score is clamped to 0–100. The composite is rounded to two decimal places. The ranking sorts descending by composite and breaks exact ties deterministically by ticker symbol.

An Opportunity score of 82 does **not** mean an 82% chance of profit, an 82% expected return, or a price target.

### Mispricing component

Mispricing is the unweighted mean of available sub-scores:

- lower-positive Forward P/E percentile within the active universe;
- lower-positive Price/Book percentile within the active universe;
- long-trend discount: `clamp(70 − 2 × max(distance150, 0) + 0.8 × max(−distance150, 0))`;
- pullback quality: `clamp(100 − 4 × |drawdown + 10|)`;
- range value: `clamp(100 − 3.5 × |distance_from_high + 8|)`.

Missing valuation ratios are omitted from the mean rather than set to zero. This is a comparative formula, not a fundamental fair-value model.

### Trend component

Trend is the unweighted mean of available sub-scores:

- moving-average alignment: 100 for `close ≥ SMA20 ≥ SMA50 ≥ SMA150`, 80 for `close ≥ SMA50 ≥ SMA150`, 60 for `close ≥ SMA150`, otherwise 20;
- `clamp(50 + 200 × slope20)`;
- `clamp(50 + 200 × slope60)`;
- `clamp(50 + 1.5 × return60)`;
- `clamp(100 + 2.5 × drawdown)`;
- when available, `clamp(50 + 2 × relative_strength60)`.

### Participation component

Participation is the unweighted mean of:

- 20-session volume percentile;
- `clamp(50 + volume_acceleration_percent)`;
- breakout-location confirmation: `clamp(100 + 8 × distance_from_high)` when within 10% of the trailing high, otherwise 20.

### Readiness component

Readiness is the unweighted mean of:

- support score: `clamp(100 − 12 × |support_proximity|)`, or fallback `clamp(100 − 10 × min(|distance20|, |distance50|))`;
- resistance room: `clamp(8 × |resistance_proximity|)`, or fallback `clamp(7 × |distance_from_high|)`;
- volatility score: `clamp(100 − 1.8 × realized_volatility20)`;
- drawdown score: `clamp(100 − 5 × |drawdown + 8|)`.

The selected-stock panel prints the actual component values, weighted equation, and raw input audit.

## 9. Rank and rotation

- **Rank** is position inside the active monitored universe, not the entire stock market.
- **Δ rank** compares the current completed score run with the prior completed score run. Positive means movement toward rank 1.
- **Rotation pulse** orders names by absolute rank movement.
- **Leader/Middle/Laggard** are display thirds of currently ranked names; they are not structural states or recommendations.
- A stock can move in rank because its own score changed, other stocks changed, or both.
- The Find box filters the existing 40-name ranked table by ticker or company name. It never searches outside the approved universe and never reranks the rows.

## 10. Aligned price chart

The selected-stock panel automatically loads an embedded chart. The ticker and **Open large chart** action open the full-screen interactive view.

The chart contains:

- adjusted daily open, high, low, and close;
- adjusted daily volume;
- SMA 20, SMA 50, and SMA 150;
- provider aggregate VWAP when present;
- transaction count when present;
- visible-period high reference for Early Breakout and Extended Trend.

Pointer or touch movement selects the nearest real bar. With keyboard focus:

- Left/Right moves one bar;
- Home selects the first visible bar;
- End selects the final bar.

The tooltip reports date, cursor price, OHLC, volume, VWAP, and each available moving average.

### Chart failure reason codes

| Reason | Meaning | Required response |
|---|---|---|
| `canonical_history_store_unavailable` | PostgreSQL canonical-history read failed. | Check database diagnostics and process credential. |
| `no_valid_stored_history` | No adjusted Massive one-day rows passed validation. | Repair/backfill canonical daily history. |
| `no_valid_history` | Legacy provider read-through returned no valid adjusted daily rows. | Check provider symbol coverage and retry. |
| `source_alignment_date_mismatch` | Final chart date differs from the score-bound feature date. | Reconcile history and rebuild features/scores. |
| `source_alignment_close_mismatch` | Final chart close differs from the score-bound feature close beyond tolerance. | Reconcile adjusted revisions and rebuild. |
| `canonical_state_feature_mismatch` | The displayed state cannot be reproduced from score-bound features. | Rebuild the inconsistent score snapshot. |
| `canonical_as_of_unavailable` | No score-bound feature date exists. | Repair score/feature lineage. |
| `canonical_as_of_invalid` | The score-bound feature date is malformed. | Repair the malformed record and rebuild. |
| `canonical_close_unavailable` | No valid score-bound close exists. | Repair the feature row and rebuild. |
| `insufficient_warmup_history` | Fewer than 150 valid sessions remain. | Backfill enough adjusted history. |
| `history_disabled` | Chart output is disabled by configuration. | Enable history and restart. |
| `history_request_failed` | Browser could not complete the local API read. | Check local server reachability and retry. |
| `demo_mode_no_real_history` | Demo fixture contains no real provider history. | Use database mode. |
| `missing_api_key` | Legacy provider read-through was requested without its server key. | Start through the Seahorse launcher; normal stored charts do not need this path. |
| `provider_unavailable` | Legacy provider read-through failed after bounded retries. | Check provider availability and retry. |
| `state_changed_during_request` | Ranking state changed while the chart was loading. | Refresh before comparing the row and graph. |
| `empty_chart_domain` | No finite accepted prices remained for an axis. | Validate stored OHLCV rows. |

`missing_api_key` and `provider_unavailable` remain documented for legacy provider read-through. Normal database-mode chart reads use stored PostgreSQL history and do not require a browser or per-chart provider credential.

## 11. Additional structure measurements

- **20d return** — simple return over 20 trading sessions.
- **60d relative strength** — stock 60-session return minus mapped-benchmark 60-session return, in percentage points.
- **vs. SMA 50** — signed percentage distance from intermediate average.
- **52w drawdown** — signed loss from the 252-session high.
- **20d volatility** — population standard deviation of 20 simple daily returns, annualized by `sqrt(252)`.
- **Volume percentile** — rank of current daily volume inside the latest 20 sessions.

These are explanatory features, not a second hidden score.

## 12. Benchmark and valuation context

- **Benchmark** is the maintained comparison ticker used for 60-session relative strength and advanced residual momentum.
- **Sector proxy** is contextual metadata and is not a score by itself.
- **Forward P/E** is price relative to expected earnings from maintained metadata.
- **Price/Book** is price relative to reported book value from maintained metadata.

Missing is unknown, not zero. Ratio comparability varies by industry.

## 13. Universe breadth

The production manifest is [`docs/initial_pool.csv`](initial_pool.csv): exactly 40 Nasdaq- or NYSE-listed **operating-company equities**. It contains no ETFs, funds, notes, warrants, units, rights, or test issues. Every row records its exchange, security type, AI-link category, plain-language AI rationale, listing-verification date, and source. The full inclusion/removal audit is in [`AI Equity Universe — 40-name production manifest`](ai_equity_universe_40.md).

`SOXX`, `IGV`, and `QQQ` may appear as internal benchmark series. They are not active universe members, do not receive states or ranks, and cannot appear as dashboard stock rows.

Breadth describes only the active monitored universe.

- **Above SMA 20/50/150** counts names whose current close is above each average.
- **Advancing/Declining** uses positive/negative 20-session return; zero or missing is neither.
- **Participation average** is the mean Participation component, not raw market volume.
- **States represented** counts distinct current structural labels.

## 14. Frozen timing context

This panel contains the delayed intraday context captured in `static_snapshot.json` when the package was built. It is not a live feed and is not refreshed by the browser.

- **Context flag** — the captured `CONFIRM`, `VETO`, or `NEUTRAL` value.
- **Captured delayed close and minute OHLC** — historical values from the snapshot cutoff.
- **Session VWAP, short return, volume ratio, range position, and freshness** — explanatory fields retained from the build-time context.
- **SNAPSHOT / FROZEN** — explicit proof that the values are historical and not current.

The panel can explain the captured timing context. It cannot change state, component scores, composite score, or rank.

## 15. Dashboard automation

Automation is disabled in the hosted static package. The status card is retained so the dashboard documents this explicitly.

- **Coordinator** — `disabled` because no server process is running in the browser package.
- **Daily refresh** — not run by this website.
- **Delayed context ingestion** — not run by this website.
- **Refresh control** — rereads the local snapshot only.

## 16. Data diagnostics

The hosted package intentionally reports a degraded or warning-level operational status because it has no database, provider, scheduler, or live freshness channel. This is an expected static-package condition, not a claim that the bundled ranks or charts are missing.

The diagnostic cards identify the snapshot mode, frozen delayed context, data cutoff, and lineage retained in the JSON asset. They do not perform a server health check.

## 17. Controls and accessibility

- Every `?` button supports pointer hover, touch/click, Enter, Space, and keyboard focus.
- Escape closes a help popup and the chart dialog.
- The chart dialog traps Tab focus and restores focus to the opening control when closed.
- The chart supports pointer, touch, and keyboard inspection.
- The row loads stock detail; the ticker button opens the large chart.
- State and theme filters change visibility only; they never recalculate canonical data.
- Refresh reloads read-only APIs and browser chart cache; it does not launch a data build.
- Reduced-motion browser preferences disable nonessential animation.

## 18. Hosted package credentials and updates

This uploaded package needs no credentials. Do not enter or add a username, host, password, PostgreSQL credential, Massive key, Seahorse lookup, or server launcher for it.

The only browser data asset is the local `static_snapshot.json` file. To update the dashboard, generate a new reviewed snapshot package locally and upload its contents into the existing `/pulsepup/` folder through cPanel.

## 19. Static asset reference

The package contains:

- `index.html` — the PulsePup dashboard shell.
- `static/dashboard.js` and `static/dashboard.css` — the chart, evidence, help-popup, and responsive UI.
- `static_snapshot.json` — the frozen 40-name data snapshot and 260-bar-per-name histories.
- `dashboard-user-guide.html` — this guide.
- `pulsepup-mark.svg` and `favicon.svg` — the PulsePup logo assets.
- `.htaccess` — directory index and directory-listing protection only.

## 20. Source-lineage API reference (not used by hosted package)

The browser reads these primary endpoints:

- `GET /api/summary`
- `GET /api/rankings`
- `GET /api/symbol/{symbol}`
- `GET /api/symbol/{symbol}/structure-history`
- `GET /api/breadth`
- `GET /api/rotation`
- `GET /api/delayed-context/{symbol}`
- `GET /api/automation/status`
- `GET /api/diagnostics`

Interactive OpenAPI documentation is available at `/docs`. The dashboard API is read-only.

> **Hosted runtime reminder:** The endpoint names in the source-lineage reference are not requested by this package. The browser reads only the local snapshot asset.