AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Parallax Client Review

skill-bencharoenwong-parallax-workflows-parallax-client-review · by bencharoenwong

RIA/wealth advisor client portfolio review: full analysis, redundancy, health flags, macro context, per-holding drill-down, prioritized recommendations, and AI assessment via Parallax MCP tools. Holdings as [{symbol, weight}]. NOT for fund manager briefs (use /parallax-morning-brief), not for quick stock checks (use /parallax-should-i-buy).

No reviews yet
0 installs
0 views
view→install

Install

$ agentstack add skill-bencharoenwong-parallax-workflows-parallax-client-review

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-bencharoenwong-parallax-workflows-parallax-client-review)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
yesterday

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Parallax Client Review? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Client Portfolio Review

When not to use

  • Fund manager morning brief → use /parallax-morning-brief
  • Single stock analysis → use /parallax-should-i-buy

Gotchas

  • JIT-load _parallax/parallax-conventions.md for fallback patterns and parallel execution
  • JIT-load _parallax/house-view/loader.md FIRST; if active view present, follow §2 (validation), §3 (multipliers), §4 (conflict resolution), §5 (output rendering), §6 (audit). The view shapes the suitability assessment AND the recommendations: holdings misaligned with view get higher priority for trimming, view-aligned holdings get implicit support.
  • When active view is present, use the view-aware disclaimer per loader.md §5 rule 5; otherwise use the standard disclaimer
  • JIT-load references/recommendation-matrix.md for priority classification and drill-down criteria
  • JIT-load _parallax/client-policy/policy-loader.md when policy= is present. Fallback ladder per that file. S1/S2 math via adaptation.py only — never inline.
  • Policy sections never precede the first render-gate anchor. Render them in the body, after House View Alignment, per policy-loader.md §7 placement rule.
  • No policy= supplied means today's behavior, unchanged. No policy sections, no disclosure line.
  • Holdings in RIC format, weights sum to ~1.0
  • analyze_portfolio called twice — once for performance/returns fields, once for concentration/attribution fields (split to manage response size). Call shape: portfolio=[{date: , symbol: , weight: }, ...] with fields=[...], subject to the discovered live schema. The date is an as-of date over a price history, not a snapshot key: a deployed connector may need a date early enough that each holding has sufficient prior price history, 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. The parameters holdings and lens do not exist in the deployed schema. Fallback (on MCP schema validation error OR truncated/oversized response): check_portfolio_redundancy (concentration) + quick_portfolio_scores (factor tilt) + individual get_stock_outlook with aspect="risk_return" (performance).
  • Per-holding drill-down capped at 8 holdings to manage latency
  • Mixed-exchange portfolios may need split scoring (see shared conventions)
  • Output should be presentation-ready for client meetings
  • LANGUAGE HAND-OFF — if lang= is present and ≠ en, the terminal Translate step is mandatory. Route zh-CN/zh-TW/zh-HK/translate-chinese-finance, th/translate-thai-finance, using the delimited routing-directive block (never a prose sentence the translator could echo). Unsupported values → English output with the standard warning footer.
  • get_assessment prompt should incorporate all findings including macro, flags, and recommendations
  • Pre-Render calls _parallax/white-label/rm_consumer.pyload_rm_branding_context("portfolio review", audience=). This executable visual-only seam owns source redaction, voice isolation, branding fallback, AND the §13.1 audience-mode resolution — pass the parsed audience= flag (or None) into the call and read resolved_audience off the returned context; never inspect the branding mapping directly for this. Use its header_lines and about_lines exactly once. The state mapping follows _parallax/white-label/integration-pattern.md §5 + §7. The context's about_lines already carry the §13.4 mode line and any unrecognized-value notice — do not append a second mode line.
  • Branding Header uses **** portfolio review (skill-specific framing), not the generic **** report template in integration-pattern.md §5. Intentional divergence — do not "fix" to match the generic template.

Presentation-ready portfolio review with health flags and prioritized recommendations for wealth advisors.

Usage

/parallax-client-review [{"symbol":"AAPL.O","weight":0.25},{"symbol":"BRK-B.N","weight":0.20}] client="conservative retiree, income focus, 10yr horizon"
/parallax-client-review [{"symbol":"AAPL.O","weight":0.25},{"symbol":"BRK-B.N","weight":0.20}] client="conservative retiree, income focus, 10yr horizon" lang=zh-CN register=retail policy="/path/to/client-policy.yaml"
/parallax-client-review [{"symbol":"AAPL.O","weight":0.25},{"symbol":"BRK-B.N","weight":0.20}] audience=client_safe

Optional: append lang= (en default; zh-CN, zh-TW, zh-HK, th) and register=retail after the existing parameters. register=retail is passed only when translation is requested; absent means institutional register.

Optional audience= argument: client_safe | internal_analyst; precedence follows parallax-conventions.md §13.1.

Optional: append policy= after the existing parameters. policy= accepts a file path or inline YAML/JSON describing the client's strategic allocation, bands, and tactical budget. Absent means the review runs exactly as today. Do not add an invocation k= flag. The locked spec allows a firm-level default and a per-mandate override only; both live in the policy artifact's adaptation block.

Note on benchmark= parameter. The current workflow body does NOT consume a benchmark= parameter — the example above intentionally omits it. If a future revision adds benchmark-relative attribution: pass benchmarks as plain ETF tickers (e.g., SPY, QQQ, EWJ) and route through etf_daily_price — NOT export_price_series. The two endpoints are separate. Equity tickers (with RIC suffix like .O or .N) go through export_price_series; ETFs (plain ticker) go through etf_daily_price. Mixing them silently fails-empty.

Workflow

Execute using mcp__claude_ai_Parallax__* tools. JIT-load _parallax/parallax-conventions.md for execution mode, fallback patterns, and macro reasoning. JIT-load _parallax/house-view/loader.md for active-view validation and integration. JIT-load references/recommendation-matrix.md for priority system.

Batch 0 — Tool Loading & Active House View

Call ToolSearch with query "+Parallax" to load the deferred MCP tool schemas before the first mcp__claude_ai_Parallax__* call.

Per loader.md §1-§2. If view present, capture tilt vector, excludes, basis_statement (used to frame Suitability Assessment in firm voice). The view's tilts become an additional layer of recommendation justification: when trimming, "view tilts UW [sector]" is cited alongside health flags. If validation fails or no view present, run review without view.

Batch 0.5 — Client policy load (only if policy= present)

JIT-load _parallax/client-policy/policy-loader.md. Write inline policy input to a private mktemp file first. Then, in one Bash step:

python3 "/../_parallax/client-policy/adaptation.py" --policy "$POLICY" --json

Capture errors, fallback_tier, resolved_k, k_source, policy_hash, and data_quality. Blocking errors mean tier no_policy for the rest of the run; dimension-scoped errors (missing basis, total-basis without equity weight) drop only that dimension to multiplier_fallback — policy-loader.md §2 defines the classes. Render the error list in Policy Data Quality and continue the review unchanged. Exposure-dependent S1/S2 runs after Batch A, in Batch C.

Batch A — Portfolio-level analysis (parallel)

| Tool | Parameters | Notes | |---|---|---| | analyze_portfolio | portfolio=[{date: , symbol: , weight: }], fields=["performance_metrics","rolling_metrics","drawdown_analysis","portfolio_summary","time_period_returns"] | Returns/risk metrics. Build portfolio array from provided holdings; use an as-of date with sufficient prior price history per the Gotchas call-shape note; state the date used. | | analyze_portfolio | portfolio=[{date: , symbol: , weight: }], fields=["concentration_metrics","sector_allocation","company_contribution"] | Concentration and attribution analysis. Two separate calls with distinct field subsets to stay under the 180K-char response ceiling. | | check_portfolio_redundancy | holdings | Overlap detection. When analyze_portfolio.sector_allocation also came back, cross-check the two sector breakdowns per Batch C step 3 before rendering either. | | get_peer_snapshot | per holding | Primary scoring source for PARALLAX_LOADER_V2=1. Aggregate scores client-side per loader.md §3b. Distinguish a timeout from an empty response: a timeout leaves the holding unscored, per existing handling. A successful call that carries no data for the listing is a coverage gap, not a timeout — fall back to get_company_info plus get_score_analysis for that holding, label every figure derived this way a profile-derived score, and name every profile-derived holding in the report. Same-scale guard: before ranking or combining a profile-derived score with a peer/batch composite score, cross-check it against the holding's latest get_score_analysis row. If the two agree (same composite field, same 1–10 scale), the profile-derived score is same-scale by construction and renders alongside the composite normally, tagged profile-derived. If they disagree, or only one source returned, do NOT blend a guessed value into the ranking: flag the holding's score unverified — not combined with the composite ranking in Factor Analysis and Per-Holding Analysis instead. | | get_company_info | per holding (parallel) | Ground-truth oracle per loader.md §5 rule 3 (required universally). Records expected_name. | | list_macro_countries | — | Check market coverage | | quick_portfolio_scores | holdings | Legacy/V1 path only. Do NOT use if PARALLAX_LOADER_V2=1 and view active. A successful call with no data for a listing (distinct from a timeout) is the same coverage-gap case as the get_peer_snapshot row above — fall back to get_company_info plus get_score_analysis for that holding, label it a profile-derived score, and apply the same same-scale guard before combining it into any ranking. |

After Batch A: cross-check returned names against get_company_info names per loader.md §5 rule 3. For PARALLAX_LOADER_V2=1, any mismatch in get_peer_snapshot is flagged ⚠ MISMATCH and excluded from aggregate calculations. For V1, any mismatch in quick_portfolio_scores is re-scored individually and flagged as UNTRUSTED for the batch factor profile.

Batch B — Macro context (concurrent with Batch A)

Derive home markets from RIC suffixes (available from the input holdings before any call — no Batch A output is required). Fire macro_analyst with component="tactical" for each unique covered market (cap 3) in the SAME tool-call turn as the Batch A calls, rather than as a separate stage after them. Batch C still requires BOTH Batch A and these macro calls to have completed.

Batch C — Health flags + drill-down (after A + B)

  1. Evaluate the 5 health flags per holding — binding flag conditions in references/recommendation-matrix.md (same taxonomy and threshold values as parallax-portfolio-checkup/references/health-flags.md, whose canonical portfolio-level weighted-average definitions apply to portfolio-checkup, not here): Low Score (holding total score ≤5.0), Concentration (holding weight >15%, or holding among the top-3 when their combined weight >45%), Redundancy (holding is part of a redundant pair), Value Trap (holding value score ≤3.0), Macro Misalignment (holding's sector has a negative tactical outlook). Per-holding flag counts drive priority and drill-down selection.
  2. House-view alignment (if view active): add View Misalignment (>25% off view-tilted target) and View Excluded (on tilts.excludes) as additional flags. Surface a portfolio-level "view alignment score" (% of weight in view-aligned positions).
  3. Flag redundancy as low-confidence if coverage /../parallax/coveragecheck.py" --portfolio-sectors "$PORTFOLIOSECTORS" --redundancy-sectors "$REDUNDANCYSECTORS" --holdings "$HOLDINGS" — and render from its JSON verdict. Tag every --holdings entry with its sector from the analyzeportfolio sector mapping (per-holding backstop as usual): the live redundancy payload is aggregate-only, and without sector tags the helper cannot infer absent holdings and resolves absentholdingsbasis to notcomputable. Cite the operator-agnostic-helper rationale by reference to parallax/house-view/loader.md §2 ("Why the helper, not inline math") — the same argument applies here: the coverage arithmetic must be identical across operator LLMs. On coveragelimited, label the redundancy figures **coverage-limited** in Concentration & Redundancy, name the divergingsectors and any absentholdings, and prefer the analyzeportfolio sector weights for every rendered concentration figure. absentholdings is basis-qualified via absentholdingsbasis: when it is sectorinference, note that the absent-holdings list is inferred from diverging sectors (the redundancy payload carried no per-holding data) rather than confirmed directly; when it is notcomputable, render nothing about absent holdings at all — do not claim absence and do not claim confirmed coverage. On consistent`, render normally.
  4. Assign health status: Healthy (0) · Monitor (1-2) · Attention (3+). View Excluded counts as Attention regardless of other flags.
  5. Select up to 8 holdings for drill-down per references/recommendation-matrix.md: weight >10%, any flag (including View flags), or macro-misaligned. Prioritize by flag count then weight.

For each drill-down holding (parallel):

| Tool | Parameters | |---|---| | get_score_analysis | symbol, weeks as int 26 (26-week lookback, non-default — see conventions §0.2) | | get_stock_outlook | symbol, aspect="risk_return" | | get_peer_snapshot | symbol |

Ground-truth re-verification per drill-down holding (per loader.md §5 rule 3): cross-check get_peer_snapshot.target_company against the get_company_info.name already captured in Batch A. If mismatch at drill-down time (can occur when the Batch A and Batch C queries bind differently), flag ⚠ MISMATCH and extract the queried stock's scores from the get_peer_snapshot.comparison[] row whose symbol matches the queried symbol, not from the target_company field.

News (selective, async): get_news_synthesis for holdings >10% weight AND flagged, or in sectors with active macro developments. Cap at 5.

Client policy S1 + S2 (only if policy= present and tier is not no_policy)

  1. Assemble current sleeve exposures: region from RIC suffixes per _parallax/parallax-conventions.md §1; sector from the Batch A analyze_portfolio sector_allocation block, with get_peer_snapshot / get_company_info as a per-holding backstop. Region is therefore classified by listing venue, not by issuer domicile or economic exposure; ADR/fund look-through is not applied in phase 1, and the Policy Data Quality intro states this basis. Renormalize each dimension's weights over MAPPED holdings only and record coverage plus every unmapped holding. Carry each holding's isin when one is available. Payload shape is pinned in policy-loader.md §3; these clauses of it bind the producer and are stated identically in adaptation.py's module docstring and policy-loader.md §3 — one of the three copies that must agree:
  • basis must be "sleeve" — Phase 1 accepts sleeve-relative exposures only; the producer converts before calling. A missing or non-"sleeve" basis is rejected at the CLI (exit 2, naming the file and the offending value), the same operator-mistake class as a non-object payload; no conversion is implemented in Phase 1.
  • The whole payload is checked by validate_exposures against this contract (known dimensions, finite weights in [0, 1], per-dimension sums

Source & license

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

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

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.