Clarion value screener
Implements the two-stage pipeline from docs/ALLOCATION-POLICY.md. Stage 1 (cast the net + score + sector cap + watchlist) is the script's job. Stage 2 (filing-level deep dives + thesis starters) is delegated to clarion-single-stock-eval per top candidate.
When to use
User asks any of:
- "Run a value screen."
- "Screen the S&P 500 for new candidates."
- "Screen NVDA, AAPL, ADBE."
- "What names should I be looking at right now?"
Cadence guidance (from AWB):
- Monthly — full screen
- After market drawdowns — opportunistic
- After regime changes — especially Green→Orange or Orange→Red
Decision tree
Resolve scope. If the user named specific tickers, use --tickers. Otherwise, the chat agent should prepare a JSON input by:
Run the script.
# Tickers list mode (script fetches yfinance fundamentals)
python /home/workspace/clarion-intelligence-system/skills/clarion-value-screener/scripts/screen.py \
--tickers NVDA,AAPL,ADBE,LULU,AOS
# JSON input mode (chat agent prepared the candidates)
python ... screen.py --input ~/clarion/queue/screen-input.json
Read the watchlist file the script wrote. Path is printed at the end. The file is the structured output; pass it through to the user verbatim for the tables, then walk them through the [TODO] sections (Sniff Test, Passed On, Existing Theses Impact).
Fill the Sniff Test sections by running clarion-single-stock-eval on each top-cap candidate that isn't already covered by an active thesis. Use the eval output to write the sniff-test snapshot per the AWB watchlist examples.
Existing Theses Impact — list any top candidate that already has a thesis in ~/clarion/theses/. If so, summarize whether the screen confirms or challenges the existing thesis.
How to run
SCREEN=python /home/workspace/clarion-intelligence-system/skills/clarion-value-screener/scripts/screen.py
# Tickers mode (most common when user names a list)
$SCREEN --tickers NVDA,AAPL,ADBE,LULU,AOS
# JSON input mode (full S&P 500 screen via WebFetch by chat agent)
$SCREEN --input ~/clarion/queue/screen-input.json
# Override context
$SCREEN --tickers NVDA,AAPL --rf-rate-pct 4.45 --sp500-cape 35.2
# Custom universe label and target size
$SCREEN --tickers NVDA,AAPL,... --universe "Russell 1000" --top-size 15
JSON input schema
{
"screen_date": "2026-05-07",
"context": {
"regime_color": "orange",
"danger_state": false,
"rf_rate_pct": 4.45,
"hurdle_rate_pct": 10.45,
"sp500_cape": 35.2,
"sp500_trailing_pe": 28.4,
"implied_return_low_pct": 0.0,
"implied_return_high_pct": 3.0,
"universe": "S&P 500",
"notes": "Mega-cap led rally; verify breadth before sizing."
},
"candidates": [
{
"ticker": "NVDA",
"company": "NVIDIA Corp",
"sector": "Tech",
"pe": 35.2,
"pfcf": 40.0,
"roe": 0.55,
"roic": 0.42,
"op_margin": 0.62,
"profit_margin": 0.55,
"de": 0.25,
"insider_pct": -0.2,
"market_cap": 3000000000000,
"price": 140.50
}
]
}
Margin / yield / return fields are decimals (0.20 = 20%). Insider activity is a signed percent (positive = net buying). Any field can be omitted; the composite formula reduces its weight share when data is missing.
Voice
Lead with the regime + screening stance (one line). Then surface the top 3 by composite with the most interesting one-line take. Then point at the watchlist file for the full ranked table and detail.
When the script flags a candidate's contributing_weight as low (< 60), call it out — that means the score is based on partial data and shouldn't be over-weighted.
Hard rules
- Never fabricate fundamental data. If the screener site or yfinance doesn't return a metric, leave it None and let the contributing-weight surface the gap.
- Regime adjusts aggressiveness, not quality. In Red/Danger, deeper discounts required — never lower the quality bar.
- The screener finds candidates, not positions. The output is a watchlist with thesis starters. The principal reviews and decides what gets a full thesis.
- Document what you passed on. The "Passed On" section is as valuable as the winners.
- Stage 2 requires filings. Don't generate a thesis starter without indexed filings — run
clarion-sec-research index <TICKER> first if needed.
On error
SCREEN_ERROR: regime unavailable — yfinance cache empty for SPY/TLT/RSP. Run clarion-regime-check first.
SCREEN_ERROR: --tickers or --input required — supply one of them.
SCREEN_ERROR: input file not found — check the --input path.
_warning: yfinance fetch failed for <TICKER> — non-fatal; that ticker's score will be based on partial data. The script continues.
1---2name: clarion-value-screener3description: Run a value-quality screen and write a watchlist file. The script accepts either a list of tickers (fundamentals fetched from yfinance) or a JSON input file the chat agent prepared from a screener site (multpl.com, finviz, Yardeni, etc.). Computes the 8-factor composite score (P/E, P/FCF, ROE, ROIC, Operating Margin, D/E, Profit Margin, Insider) per docs/ALLOCATION-POLICY.md, applies regime-tightened thresholds, and produces a sector-capped Top-10 watchlist. Saves to ~/clarion/watchlists/sp500-screen-YYYY-MM-DD.md. Use when the user asks "run a value screen", "screen the S&P 500", "screen these tickers <list>", or after market drawdowns / regime changes. Requires clarion-setup to have been run.4---56# Clarion value screener78Implements the two-stage pipeline from [`docs/ALLOCATION-POLICY.md`](../../docs/ALLOCATION-POLICY.md). Stage 1 (cast the net + score + sector cap + watchlist) is the script's job. Stage 2 (filing-level deep dives + thesis starters) is delegated to `clarion-single-stock-eval` per top candidate.910## When to use1112User asks any of:13- "Run a value screen."14- "Screen the S&P 500 for new candidates."15- "Screen NVDA, AAPL, ADBE."16- "What names should I be looking at right now?"1718Cadence guidance (from AWB):19- **Monthly** — full screen20- **After market drawdowns** — opportunistic21- **After regime changes** — especially Green→Orange or Orange→Red2223## Decision tree24251. **Resolve scope.** If the user named specific tickers, use `--tickers`. Otherwise, the chat agent should prepare a JSON input by:26 - Running [`clarion-regime-check`](../clarion-regime-check) to confirm regime27 - Running [`clarion-expected-return-calc`](../clarion-expected-return-calc) to confirm hurdle28 - Using `WebFetch` on a screener site (multpl.com / Yardeni / finviz) with regime-appropriate filters per [`docs/ALLOCATION-POLICY.md`](../../docs/ALLOCATION-POLICY.md)29 - Building a JSON file with the resulting candidates (see schema below)30312. **Run the script.**3233 ```bash34 # Tickers list mode (script fetches yfinance fundamentals)35 python /home/workspace/clarion-intelligence-system/skills/clarion-value-screener/scripts/screen.py \36 --tickers NVDA,AAPL,ADBE,LULU,AOS3738 # JSON input mode (chat agent prepared the candidates)39 python ... screen.py --input ~/clarion/queue/screen-input.json40 ```41423. **Read the watchlist file the script wrote.** Path is printed at the end. The file is the structured output; pass it through to the user verbatim for the tables, then walk them through the [TODO] sections (Sniff Test, Passed On, Existing Theses Impact).43444. **Fill the Sniff Test sections** by running `clarion-single-stock-eval` on each top-cap candidate that isn't already covered by an active thesis. Use the eval output to write the sniff-test snapshot per the AWB watchlist examples.45465. **Existing Theses Impact** — list any top candidate that already has a thesis in `~/clarion/theses/`. If so, summarize whether the screen confirms or challenges the existing thesis.4748## How to run4950```bash51SCREEN=python /home/workspace/clarion-intelligence-system/skills/clarion-value-screener/scripts/screen.py5253# Tickers mode (most common when user names a list)54$SCREEN --tickers NVDA,AAPL,ADBE,LULU,AOS5556# JSON input mode (full S&P 500 screen via WebFetch by chat agent)57$SCREEN --input ~/clarion/queue/screen-input.json5859# Override context60$SCREEN --tickers NVDA,AAPL --rf-rate-pct 4.45 --sp500-cape 35.26162# Custom universe label and target size63$SCREEN --tickers NVDA,AAPL,... --universe "Russell 1000" --top-size 1564```6566### JSON input schema6768```json69{70 "screen_date": "2026-05-07",71 "context": {72 "regime_color": "orange",73 "danger_state": false,74 "rf_rate_pct": 4.45,75 "hurdle_rate_pct": 10.45,76 "sp500_cape": 35.2,77 "sp500_trailing_pe": 28.4,78 "implied_return_low_pct": 0.0,79 "implied_return_high_pct": 3.0,80 "universe": "S&P 500",81 "notes": "Mega-cap led rally; verify breadth before sizing."82 },83 "candidates": [84 {85 "ticker": "NVDA",86 "company": "NVIDIA Corp",87 "sector": "Tech",88 "pe": 35.2,89 "pfcf": 40.0,90 "roe": 0.55,91 "roic": 0.42,92 "op_margin": 0.62,93 "profit_margin": 0.55,94 "de": 0.25,95 "insider_pct": -0.2,96 "market_cap": 3000000000000,97 "price": 140.5098 }99 ]100}101```102103Margin / yield / return fields are decimals (0.20 = 20%). Insider activity is a signed percent (positive = net buying). Any field can be omitted; the composite formula reduces its weight share when data is missing.104105## Voice106107Lead with the **regime + screening stance** (one line). Then surface the **top 3 by composite** with the most interesting one-line take. Then point at the watchlist file for the full ranked table and detail.108109When the script flags a candidate's `contributing_weight` as low (< 60), call it out — that means the score is based on partial data and shouldn't be over-weighted.110111## Hard rules1121131. **Never fabricate fundamental data.** If the screener site or yfinance doesn't return a metric, leave it None and let the contributing-weight surface the gap.1142. **Regime adjusts aggressiveness, not quality.** In Red/Danger, deeper discounts required — never lower the quality bar.1153. **The screener finds candidates, not positions.** The output is a watchlist with thesis starters. The principal reviews and decides what gets a full thesis.1164. **Document what you passed on.** The "Passed On" section is as valuable as the winners.1175. **Stage 2 requires filings.** Don't generate a thesis starter without indexed filings — run `clarion-sec-research index <TICKER>` first if needed.118119## On error120121- **`SCREEN_ERROR: regime unavailable`** — yfinance cache empty for SPY/TLT/RSP. Run `clarion-regime-check` first.122- **`SCREEN_ERROR: --tickers or --input required`** — supply one of them.123- **`SCREEN_ERROR: input file not found`** — check the `--input` path.124- **`_warning: yfinance fetch failed for <TICKER>`** — non-fatal; that ticker's score will be based on partial data. The script continues.