Mark a book of positions to current fair value and flag any name where the mark is low-confidence (stale, wide-spread, illiquid). Two modes: delayed REST snapshots for EOD reporting, live WebSocket stream for intraday risk. Emits a marked-positions table plus an exception block per flagged mark. Use when an operator hands over a CSV and asks "what's this book worth right now" or "which marks am I not sure about."
Scanned 9/6/2026
Install to Claude Code
npx -y skills add rgourley/quant-garage --skill portfolio-mark --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Portfolio Mark?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/rgourley-portfolio-mark)More formats (shields.io, HTML) on the badges page.
---
name: portfolio-mark
description: Mark a book of positions to current fair value and flag any name where the mark is low-confidence (stale, wide-spread, illiquid). Two modes: delayed REST snapshots for EOD reporting, live WebSocket stream for intraday risk. Emits a marked-positions table plus an exception block per flagged mark. Use when an operator hands over a CSV and asks "what's this book worth right now" or "which marks am I not sure about."
---
# portfolio-mark
You hand over a position CSV. The skill marks every line to current
fair value, computes book value and (if cost basis is in the input)
unrealized P&L, and flags any mark where confidence is medium or low
so the operator can override before the number lands in a report.
This is the "what is this book worth right now" workflow. Pricing
services do it; this skill does the same thing without the seat fee
and with the per-position confidence rating that internal pricing
desks usually leave implicit.
## When to invoke
- Operator says "mark this book" or "what's this portfolio worth right
now" or hands over a CSV asking for current value
- A risk team needs intraday marks for a Greek aggregation or var run
- An ops team is generating an EOD statement and wants to flag thin
names before publishing the NAV
- A PM is wiring up a live dashboard and needs a streaming mark per
symbol
## Modes
### Delayed mode (default)
REST snapshot per position. Walks the 4-step fallback chain
(`snapshot.ticker.lastTrade.p` → `min.c` → `day.c` → `prevDay.c`) via
`lib/quant_garage/snapshot.py::resolve_price` and emits the timestamp of
whichever field won. Works on any paid Stocks plan; on Free Basic the
rate limit caps batch size to ~5 positions per minute but the
methodology is identical.
Use this for end-of-day reporting, weekly statements, and any context
where a 15-minute lag on the underlying is acceptable. Cheaper
operationally; no socket to manage; one report per run.
### Live mode
WebSocket subscribes to one or more channels for every position.
Listens for `--listen` seconds (default 30), accumulates the most
recent mark per symbol, and emits the same marked-positions table
plus an optional "live tape" trailer showing the last few ticks per
symbol.
Live mode reads from the **business cluster**
(`wss://business.polygon.io/stocks`) and prefers, in order:
1. `T.{ticker}` (tick trades; requires Stocks Advanced + signed
real-time agreement)
2. `AM.{ticker}` (per-minute aggregates; available on Stocks Business
without the real-time addendum)
3. `FMV.{ticker}` (Business-tier Fair Market Value stream)
If `T.{ticker}` returns `not authorized`, the skill resubscribes to
`AM.{ticker}` automatically and notes the downgrade in the rendered
output. Quote channel `Q.{ticker}` is subscribed in parallel so bid/ask
flow from the stream without a REST round-trip. See [`references/live-vs-delayed.md`](./references/live-vs-delayed.md)
for the tier matrix and which channels each plan actually delivers
(the published Massive docs and the lived entitlement behavior diverge,
see notes there).
## What you need
- A position CSV with at minimum `ticker,shares`. Optional columns:
`cost_basis` (per-share, for P&L), `as_of_date` (informational).
- `MASSIVE_API_KEY` exported. Delayed mode runs on Stocks Starter or
higher (Stocks Basic works with rate-limit pain). Live mode needs
Stocks Advanced for `T.{ticker}` ticks, or Stocks Business for the
`AM.{ticker}` and `FMV.{ticker}` fallback channels.
## What you get back
Two output layers from one analysis.
**Layer 1: canonical JSON** matching [`output-schema.json`](./output-schema.json).
Per-position fields: mark, mark_source (which step in the fallback
chain won), confidence (high/medium/low), as_of timestamp, bid/ask if
available, spread in basis points, and (if cost basis given) unrealized
P&L. Per-flagged-mark fields: reason codes, detail text, source
endpoint. UIs and downstream agents consume this.
**Layer 2: rendered hybrid output**. A marked-positions table at the
top, a FLAGGED exception block at the bottom for any mark that wasn't
high-confidence. Optional "Live tape" trailer in live mode. See
[`references/rendering.md`](./references/rendering.md) for the format
rules. Claude Code users read this.
## How it works (delayed mode)
1. Read every row of the CSV. Group by symbol; warn on duplicates but
keep them separate (different lots).
2. For each unique symbol, GET the snapshot endpoint. Walk the
fallback chain per
[`references/snapshot-fallback-chain.md`](./references/snapshot-fallback-chain.md)
and record which step produced the mark.
3. Compute confidence per
[`references/confidence-scoring.md`](./references/confidence-scoring.md):
recency of the chosen mark, bid-ask spread in bps, and average daily
volume. High = top-decile ADV and last trade within 60s and spread
<10bps; Medium = mid ADV and trade within 5min and spread 10-50bps;
Low = anything thinner or staler.
4. Compute book value (sum of `shares * mark`). If cost basis is in
the input, compute unrealized P&L per
[`references/book-value-and-pnl.md`](./references/book-value-and-pnl.md).
5. Emit JSON and rendered markdown. Anything below `high` confidence
appears in the FLAGGED block.
## How it works (live mode)
1. Open one WebSocket to the business cluster.
2. Auth, then subscribe to the preferred channel for every symbol
in the book. If `T.{ticker}` returns `not authorized`, resubscribe
to `AM.{ticker}` and note the downgrade.
3. Listen for `--listen` seconds. Maintain per-symbol state: last
mark, last update timestamp, trade count, recent ticks for the tape.
See [`references/websocket-mark-updates.md`](./references/websocket-mark-updates.md)
for the message-handling pattern (move work off the receive thread,
resubscribe on reconnect, backpressure-aware drain loop).
4. On disconnect within the listen window, resubscribe to the full
set per the
[`massive-websockets`](../massive-websockets) foundation. Log the
gap and continue.
5. At the end of the window, emit the same marked-positions table.
For symbols that received zero ticks during the window, fall back
to a one-shot REST snapshot (with `mark_source: "snapshot.*"` so
the operator sees which symbols never streamed).
## Foundations used
- [`massive-api-patterns`](../massive-api-patterns) for REST auth,
rate limiting, and the snapshot fallback chain
- [`massive-websockets`](../massive-websockets) for the live socket
flow (auth, subscribe, reconnect, backpressure)
This is the first skill in the suite that exercises the
`massive-websockets` foundation end-to-end. Any gotchas discovered
during the build are added to that foundation's SKILL.md.
## Endpoints used
Delayed mode:
- `GET /v2/snapshot/locale/us/markets/stocks/tickers/{ticker}`: mark,
bid/ask, recent OHLC. The single REST call per symbol.
- `GET /v2/aggs/ticker/{ticker}/range/1/day/{from}/{to}`: ~22 sessions
of daily aggregates for the 30-day ADV bucketing in confidence
scoring. Cached per ticker per run.
Live mode (in addition to the REST snapshot for fallback):
- `wss://business.polygon.io/stocks` channels: `T.{ticker}`,
`AM.{ticker}`, `FMV.{ticker}` (subscribe-time fallback in that
order), plus `Q.{ticker}` for bid/ask.
## Doesn't handle (yet)
- Options OCC marks (the skill is equity-only for v1; options chains
use a different snapshot endpoint and a different live channel under
`wss://socket.polygon.io/options`. Extending to options is a clean
follow-up; the fallback chain and confidence model carry over.)
- FX-denominated positions. The mark is in USD; non-USD positions
need an FX overlay outside the scope of v1.
- Long/short with separate margin treatment. The skill flips the sign
on `unrealized_pnl_usd` for negative-share positions but doesn't
compute margin requirements.
- Multi-account roll-up. One CSV in, one report out.
Add these in a PR if you need them. The patterns are clean extensions
of the existing chain + confidence logic.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!