# Parallax Scenario Analysis

> React to a news event or scenario: assess portfolio exposure, macro regime shift, sector impact, identify most-exposed holdings, find rotation candidates, and output an information-framed action analysis via Parallax MCP tools. Also surfaces under PM/RIA vocabulary: 'stress book', 'scenario suite', 'stress test' (e.g., rates +100bps, USD shock, 2008 / 2020 / 2022 replays). Requires a portfolio an…

- **Type:** Skill
- **Install:** `agentstack add skill-bencharoenwong-parallax-workflows-parallax-scenario-analysis`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [bencharoenwong](https://agentstack.voostack.com/s/bencharoenwong)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [bencharoenwong](https://github.com/bencharoenwong)
- **Source:** https://github.com/bencharoenwong/parallax-workflows/tree/main/plugin/skills/parallax-scenario-analysis

## Install

```sh
agentstack add skill-bencharoenwong-parallax-workflows-parallax-scenario-analysis
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Scenario Analysis

## When not to use

- Routine morning brief → use /parallax-morning-brief
- General macro outlook → use /parallax-macro-outlook
- Single stock analysis → use /parallax-deep-dive
- Portfolio rebalancing without a trigger event → use /parallax-rebalance

## Gotchas

- JIT-load _parallax/parallax-conventions.md for RIC resolution, parallel execution, and fallback patterns
- get_assessment with a scenario-focused prompt is the core analytical engine — feed it everything from prior steps
- get_news_synthesis may already surface the event — check before assuming the user's framing is complete
- Score changes haven't happened yet if the event is breaking — use get_assessment for forward-looking analysis
- For historical events, get_score_analysis can show what actually moved
- This skill produces FORWARD-LOOKING analysis — always caveat uncertainty
- 78 billable tokens at 10 equity holdings, including the 10 classification probes (see `_parallax/token-costs.md`); scales roughly linearly per holding — the >20-holding cap in Phase 2 bounds large books.
- JIT-load `_parallax/house-view/loader.md` if an active CIO view is present; portfolio-level skill, so apply §3 (multipliers) to replacement-candidate scoring + §4 (the user-supplied scenario is sovereign per §4 — if scenario CONTRADICTS view tilts, surface "scenario contradicts house view" banner; the Action Plan acts on the scenario, not the view) + §5 (preamble + view-aware rendering) + §6 (audit). For an ALIGNED scenario, the Action Plan is more aggressive; for a CONTRADICTING scenario, the conflict is surfaced and the user's stated framing wins.
- When active view is present, use the view-aware disclaimer per loader.md §5 rule 5; otherwise use the standard disclaimer.
- JIT-load `_parallax/white-label/integration-pattern.md` before the Pre-Render step. Loader call is `load_visual_branding()` (7-key visual subset; voice structurally excluded — `branding["voice"]` raises `KeyError`). Apply §5 (Branding Header) and §7 (About This Report) in Output Format.

Something happened (or might happen). What's exposed? What shifts? What do I do?

## Usage

```
/parallax-scenario-analysis "China announces 25% tariff on US semiconductors" portfolio=[{"symbol":"NVDA.O","weight":0.15},{"symbol":"AAPL.O","weight":0.20},{"symbol":"TSM.N","weight":0.10},{"symbol":"JPM.N","weight":0.15},{"symbol":"XOM.N","weight":0.20},{"symbol":"JNJ.N","weight":0.20}]
/parallax-scenario-analysis "Fed cuts rates 50bps emergency" portfolio=[holdings]
/parallax-scenario-analysis "Oil spikes to $120 on Middle East escalation" portfolio=[holdings]
```

## Workflow

Call `ToolSearch` with query `"+Parallax"` to load the deferred MCP tool schemas before the first `mcp__claude_ai_Parallax__*` call. Execute using `mcp__claude_ai_Parallax__*` tools. JIT-load `_parallax/parallax-conventions.md` for execution mode, RIC resolution, and fallback patterns.

### Pre-Workflow — Load Active House View

Per `_parallax/house-view/loader.md` §1 and §2: load and validate any active house view BEFORE running the workflow. If view present, capture the load preamble for rendering at the top of Output Format per §5.1, capture the tilt vector + excludes, and check whether the user-supplied scenario aligns with or contradicts the view per §4. If no active view (or validation failure): run the workflow normally with the standard disclaimer.

### Phase 1: Understand the Event (parallel)

Fire all three simultaneously. `get_news_synthesis` is async (30-90s) and NEVER gates Phase 2, Phase 3, or the assessment: per conventions §5, insert its section when it resolves, or render a "news: pending" placeholder — do not wait on it.

| Tool | Parameters | Notes |
|---|---|---|
| `get_news_synthesis` | 2-3 most affected sectors | What market already knows |
| `get_telemetry` | fields: regime_tag, signals, commentary.headline, commentary.mechanism, divergences | Starting macro environment |
| `macro_analyst` | relevant countries/regions; component "tactical" if macro event | Positioning implications |

### Phase 2: Assess Portfolio Exposure

Phase 2 is staged into two parallel turns: **2a classification + ground-truth** must complete before **2b per-equity scoring + sector** can fire, because the asset-class decision in 2a determines which holdings are eligible for 2b's `get_score_analysis` (factor scores are equity-only). Within each sub-phase every call is independent and parallel-safe per `_parallax/parallax-conventions.md` §3.

**Fan-out cap for large portfolios**: for portfolios with >20 holdings, restrict 2b's `get_score_analysis` and the cost-bearing 2a per-holding fan-outs to the top 20 holdings by weight plus any holding already flagged (⚠ MISMATCH or otherwise). The classification fan-out remains uncapped. Render a degraded-coverage note in Output Format listing the symbols skipped by the cap.

#### Phase 2a — Classification + ground-truth (parallel, single tool-call turn)

| Tool | Parameters | Notes |
|---|---|---|
| `analyze_portfolio` | `portfolio=[{date: , symbol: , weight: }]`, `fields=["concentration_metrics","sector_allocation","company_contribution"]` | Sector and concentration exposure. Supply a `date` per holding only when the discovered live schema requires one; it is an as-of date over a price history, not a snapshot key, so today's date can return empty or fail validation. Read the live schema's own description first. When the schema gives no guidance, date every holding at the most recent completed trading day at least 5 calendar days in the past, one date for all holdings, and state the date used in the report — live schema still wins over this default. `analyze_portfolio` returns **no** factor-exposure block; `factor_exposures` is not a field, and `fields` is a direct passthrough, so an invalid name is not rejected — the block is simply absent and the call still reports success. Check `result._meta.invalid_fields` after the call and treat any entry there as a caller error, not as unavailable data. Per-holding factor data comes from Phase 2b `get_score_analysis` instead. The parameters `holdings` and `lens` do not exist in the deployed schema. WARNING: may exceed 180K chars — fall back to `check_portfolio_redundancy` if truncated or on MCP schema validation error. Server-side ETF handling: ETFs included here without per-skill branching. |
| `etf_profile` | per holding, plain ticker — **all N calls fan out in parallel within 2a** | **Asset-class oracle.** Non-error response → ETF; error response (`{"error": "No profile data found"}`) → equity. 1 token per call per `_parallax/token-costs.md`. Mirrors `explain-portfolio` Step 1a. |
| `get_company_info` | per holding — **all N calls fan out in parallel within 2a** | **Ground-truth name oracle** for cross-validation per conventions §2. Per-holding only — do NOT use comma-joined: comma-joined calls fail-empty on partial coverage, which is risky for arbitrary user-supplied portfolios where any single unresolved RIC silently zeroes the entire batch. |
| `build_stock_universe` | theme describing what benefits from this scenario (e.g., "domestic US semiconductor manufacturers" if China tariffs hit imports) | **Beneficiary discovery (Phase 3 step 7), hoisted here** — the theme derives from the scenario text alone, needs no Phase 2 output, so it fires in the 2a turn to overlap the rest of Phase 2. If the theme is genuinely not derivable from the scenario, defer it to after Phase 2b instead. |

**Gate between 2a and 2b** — perform the following checks on 2a results before firing 2b:

1. **Asset-class routing**: partition holdings into `equities` (etf_profile error) and `etfs` (etf_profile non-error). Phase 2b's `get_score_analysis` runs ONLY on the `equities` set.
2. **Name cross-validation** (per conventions §2): for each holding, find the `analyze_portfolio` `company_contribution[]` row whose `ric` equals that holding's RIC and compare the row's `name` against `get_company_info.name`, normalizing both sides first per conventions §2 step 2 — `Apple Inc` and `Apple Inc.` are the same company and must not flag ⚠ MISMATCH. `analyze_portfolio` has no top-level identity field and no peer rollup — `company_contribution[]` is the per-holding identity 2a requests, so it is the only one available at this gate. A holding with no `company_contribution` row, or whose `get_company_info` call fails or returns empty, has no name pair to compare and therefore cannot be identity-checked at all — record it as UNCHECKED and surface it in the Ground-truth Integrity table rather than treating a missing comparison as a pass. Do not defer such a holding to 2b's `get_score_analysis` `data[0].symbol` as a substitute: per conventions §2 step 2 the RIC is the input to both calls, so it agrees by construction and confirms nothing. UNCHECKED is not ⚠ MISMATCH — the holding stays in 2b and in the assessment prompt, and the output states that its identity was never confirmed. Mismatches are flagged ⚠ MISMATCH and the holding is **fully excluded** from downstream analysis — both from 2b's `get_score_analysis` and from Phase 3's `get_assessment` prompt construction. Do not include mismatched holdings with empty profiles; that produces hallucinated factor profiles in the assessor's output.

#### Phase 2b — Per-equity score trajectories (parallel, single tool-call turn)

| Tool | Parameters | Notes |
|---|---|---|
| `get_score_analysis` | per **equity** holding (excluding ETFs from 2a and ⚠ MISMATCH holdings), 4-8 weeks — **all N calls fan out in parallel within 2b** | Current factor trajectories. Skipped holdings are surfaced explicitly in output (see Output Format). |
| `get_peer_snapshot` | top 5 beneficiary candidates from the 2a `build_stock_universe` result — **fire in this 2b turn alongside `get_score_analysis`** | **Beneficiary scoring (Phase 3 step 8), hoisted here** — candidate symbols come from the universe build (different symbols than the score fan-out), so the two are independent and batch together. |

Then call `get_assessment` with a prompt that:
   - Describes the scenario
   - Lists each **equity, non-mismatched** holding with its sector and factor profile
   - For ETF holdings: lists symbol + sector exposure from `analyze_portfolio` only (no per-holding factor profile)
   - Explicitly excludes ⚠ MISMATCH holdings from the prompt entirely
   - Asks: "Rank these holdings from most-exposed to least-exposed to this scenario. For each, explain the transmission mechanism (direct revenue impact, supply chain, regulatory, sentiment)."

### Phase 3: Sector Rotation & Replacement Candidates (steps 7-8 overlap Phase 2 — see above)
7. **Identify Beneficiaries** — `build_stock_universe` is fired in the Phase 2a turn (theme is scenario-derived; see the 2a table).
8. **Score Beneficiaries** — `get_peer_snapshot` for the top 5 universe candidates is fired in the Phase 2b turn (see the 2b table).
9. **Compare Replacements** — For the top 2-3 replacement candidates, call `get_financials` with statement "summary" to verify they're fundamentally sound, not just thematically relevant.

### Phase 4: Action Plan
10. **Synthesize** — Call `get_assessment` with a comprehensive prompt incorporating:
    - The scenario and its transmission mechanisms
    - Portfolio exposure ranking from Phase 2
    - Macro regime and tactical outlook
    - Replacement candidates and their scores
    - Active house view if present (basis_statement + relevant tilts + alignment-vs-contradiction flag with the user-supplied scenario)
    - Ask: "Given this scenario, which holdings' exposure the scenario most affects and what adjustment candidates follow, framed as information. Prioritize by urgency and magnitude of exposure."

11. **Audit log** — Append entry per loader.md §6.1 if a view was loaded.

### Pre-Render — Load white-label branding

Load `_parallax/white-label/integration-pattern.md` §2 and compute `white_label_active` + `client_name` per that section. Apply §5 (Branding Header) and §7 (About This Report) when composing the Output Format. The loader returns exactly seven keys; any other access (e.g. `branding["voice"]`) raises `KeyError` — structurally enforced by `loader.py`.

## Output Format

- **House View Preamble** (only if view active) — render per loader.md §5 rule 1 (banner from Pre-Workflow + low-confidence warnings). If user-supplied scenario CONTRADICTS view tilts, append a "Scenario contradicts house view" line stating which tilt(s) disagree — per loader.md §4, the user's scenario is sovereign. Per loader.md §5.1 the preamble goes at the very top — it precedes the Branding Header.
- **Branding Header** (only if `white_label_active` AND `client_name != ""`) — single line immediately below the House View Preamble (or at the very top if no view): `**** scenario analysis`. Logo handling per integration-pattern.md §5: empty path → text only; URL → embed; absolute local (`/` or `~`) → skip embed and append `Logo on file: ` to About This Report.
- **Scenario Summary** (what happened, why it matters — 2-3 sentences)
- **Ground-truth Integrity** *(only render if Phase 2a flagged any mismatches OR recorded any holding UNCHECKED OR ETFs were excluded from per-position trajectory)* — table: `symbol`, `returned_name`, `expected_name`, status (⚠ MISMATCH / UNCHECKED / ETF — sector exposure only / TRUSTED). Mismatched holdings were fully excluded from the assessment prompt; ETF holdings are present in `analyze_portfolio` sector exposure but not in per-position factor trajectories. An UNCHECKED row fills whichever name is missing with `no row` (absent from `company_contribution[]`) or `no oracle` (`get_company_info` failed or returned empty); it was NOT excluded from the assessment, so state plainly that its identity could not be confirmed.
- **Macro Regime Impact** (how this shifts the current regime, which factors are affected)
- **Exposure Heat Map** (table: each equity holding ranked by exposure level — High/Medium/Low — with transmission mechanism. ETF holdings appear with sector-level exposure only; mismatched holdings are excluded.)
- **Most Exposed** (the 2-3 holdings at greatest risk, with specific reasoning)
- **Least Affected** (safe positions — brief explanation why)
- **Sector Rotation Thesis** (what benefits from this scenario)
- **Replacement Candidates** (table: symbol, name, sector, total score, why it fits)
- **Action Analysis** (prioritized, per conventions §12: exposure-reduction candidates, addition candidates, hold rationale — weight arithmetic framed as analysis, not instructions). Render the informational preface and action-label framing per `parallax-conventions.md` §12.
- **What to Watch** (2-3 signals that would confirm or invalidate this thesis)
- **Confidence & Caveats** (how certain is this analysis, what could go wrong with the rotation)
- **About This Report** (always present): one line stating branding state per integration-pattern.md §7 markdown column (render per table; do not collapse). If a logo was skipped per the Branding Header rule, append `Logo on file: ` as a second About This Report line.

**AI-interaction disclosure (required regardless of view state):** Render `parallax-conventions.md §9.2` immediately above the disclaimer below.

If active view: use the view-aware disclaimer per loader.md §5 rule 5. Otherwise:

*"This is scenario-based analysis, not investment advice. Forward-looking assessments are inherently uncertain."*

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [bencharoenwong](https://github.com/bencharoenwong)
- **Source:** [bencharoenwong/parallax-workflows](https://github.com/bencharoenwong/parallax-workflows)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-bencharoenwong-parallax-workflows-parallax-scenario-analysis
- Seller: https://agentstack.voostack.com/s/bencharoenwong
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
