Asteria Investment Office — Institutional Portfolio-Risk Skill
Reusable workflow rules for solving Asteria portfolio-risk tasks against the shared
remote environment. Apply the SOP below per task type. All numeric output must follow
the precision declared in each task's input/payloads/answer_template.json.
0. Environment (the authoritative book of record)
Base URL: <remote-env-url> — GET endpoints only. The environment is the
current book of record. Every local payload in input/payloads/ (desk requests,
meeting memos, committee packets, stale snapshots, exception boards) is possibly-stale
intake context; when it conflicts with the environment, prefer the environment.
Key endpoints:
GET /api/catalog — all portfolio ids, policy ids, index ids, issuer ids, bond ids, opportunity sets.
GET /api/policies — portfolio constraints, correlation thresholds, allocation-mapping rules. Top-level as_of_date and policy_id (policy set) plus sub-policies.
GET /api/portfolios and GET /api/portfolios/<portfolio_id> — objective, constraints, current holdings (each holding instrument_id, quantity_usd_m = market value USD millions, sleeve).
GET /api/instruments/bonds (all) and GET /api/instruments/bonds?candidate=true — bond universe. Each bond: instrument_id, issuer_id, rating_bucket (IG/HY), modified_duration_years, yield_to_maturity_pct, coupon_pct, energy_linked, candidate, sector, subsector, recommended_theme_tags.
GET /api/issuers — issuer_id, sector, subsector, rating_bucket, watchlist (boolean), outlook, tags.
GET /api/market/energy — current oil/gas/LNG/refining/renewables signals (signal_id, direction, score, summary) and pitch_themes.
GET /api/indices — index metadata; GET /api/index-levels and /api/index-levels/<index_id> — monthly levels. No correlations are precomputed; compute them yourself from levels.
GET /api/allocation/opportunity-sets — taxonomy mapping opportunity_set → asset_class (Equities / Duration / Credit / Currency) and display_order.
GET /api/allocation/prior-views — prior active allocation views (multi-quarter).
GET /api/macro-signals — macro/asset signal scores and rationale codes (multi-quarter; see Workflow C filter rule).
Use curl or urllib/requests. Do not attempt to read source code, env files, or
any local env/ directory.
1. Universal conventions
- as_of_date: use the environment's
as_of_date (top-level of /api/policies, also on each portfolio). It is the recommendation date.
- Numeric precision (from each field's template, never assume):
notional_usd_m, quantity_usd_m, post_trade_watchlist_exposure_usd_m → 1 decimal.
- Post-trade metrics (market value, HY %, duration, YTM, reduction pct points) → 2 decimals.
- Correlations,
signal_score → 3 decimals.
- Ordering rules (strict, verified):
trade_package (BUY/SELL tickets): ascending by instrument_id.
rotation.trades: SELL before BUY, then instrument_id ascending within each action.
index_set, pair_id lists: ascending alphabetical by index id using ASCII code points — the underscore _ (0x5F) sorts AFTER uppercase letters (A–Z are 0x41–0x5A). Therefore IDX_ACWI_IMI sorts BEFORE IDX_AC_ASIA_PAC_EX_JP. Always mirror the order shown in /api/catalog and in the request payload's index universe.
sleeve_actions: ascending by the sleeve field (the sleeve name string, e.g. "China" < "Latin America").
allocation_views / target_sleeve_actions: follow the request payload's focus_opportunity_sets / opportunity-set order exactly.
correlation_summary: item order [highest_concentration, best_diversifier].
diversification_candidates: ascending alphabetical (fixed by template).
- Data precedence: when a local payload snapshot (stale holding worksheet, stale exception board quantities, stale desk notes) conflicts with the current environment, set any
data_precedence field to current_environment_over_stale_payload. Use current environment quantities for all computations.
2. Credit policies (read from /api/policies)
Two credit sub-policies share most fields:
POL_CREDIT_DEFAULT: duration_band_years [3.0, 5.0], max_hy_allocation_pct 20.0, issuer_concentration_limit_pct 12.0, subsector_min_count_for_diversified 2, target_hy_reduction_pct 0.0.
POL_CREDIT_RISK_REDUCTION: same but target_hy_reduction_pct 4.0 (use when a task targets an HY reduction of ≥4 ppt).
The portfolio record's constraints.policy_id tells you which applies. Multi-asset
portfolios use POL_MULTI_ASSET_DEFAULT (bundles allocation mapping, correlation
default, credit default) or POL_MULTI_ASSET_RISK (uses correlation default +
credit risk reduction; committee escalation at two_or_more_material_exceptions).
Correlation policy: correlation_high_threshold 0.8, correlation_low_threshold 0.2,
review_window_start/review_window_end.
3. Workflow A — Energy / fixed-income trade strategy
For BUY/SELL bond tickets under credit-risk constraints (e.g. PF-EN-ALTA income
sleeve, PF-FI-LUMEN rotation).
Step-by-step
GET /api/portfolios/<id> for current holdings + constraints + as_of_date.
GET /api/instruments/bonds (all — held bonds are NOT in the ?candidate=true list) and ?candidate=true for eligible new buys. GET /api/issuers for watchlist flags. GET /api/market/energy for signals/themes. GET /api/policies for the credit policy.
- Compute pre-trade metrics from current holdings (market-value-weighted):
HY% = sum(qty where rating_bucket==HY) / total_mv * 100
weighted modified duration = sum(qty * modified_duration_years) / total_mv
weighted YTM = sum(qty * yield_to_maturity_pct) / total_mv
- Build the trade package, then recompute the same metrics on the post-trade book (held − sells + buys). Round to template precision.
Selection rules
- Carry improvement = maximize YTM within constraints. For an income BUY package, pick the IG anchor on the strongest positive energy signal (e.g. LNG export pull) and the carry ticket as the highest-YTM eligible bond that is: energy-linked, non-watchlist, modified duration inside the
[3.0, 5.0] band, and from a new issuer/subsector. Do NOT pick a lower-YTM "story" bond if a higher-YTM in-band non-watchlist bond clears every constraint.
- Watchlist avoidance is a hard constraint. Never BUY a bond whose
issuer_id has watchlist == true. The energy watchlist issuers are the high-carry traps (e.g. shale E&P, refining). A non-watchlist HY carry bond is acceptable; a watchlist HY bond is not, regardless of yield.
- Duration-band distractors: exclude bonds with
modified_duration_years outside [3.0, 5.0] — long-dated LNG/oil bonds (dur > 5) and very-short HY (dur < 3) are traps. Also confirm the post-trade weighted duration stays in band.
- Issuer concentration limit 12%: a candidate whose issuer is already held can blow past 12% post-trade — exclude or size down. Prefer new issuers.
- Diversification of the package: the selected buys must span ≥2 distinct issuers AND ≥2 distinct subsectors (set
selected_issuer_diversification_pass and selected_subsector_diversification_pass accordingly).
- HY cap: post-trade
hy_allocation_pct must be ≤ 20.0.
- sales_positioning.theme: map to the single strongest energy signal / pitch theme (e.g. LNG export tailwind). A "selectivity/transition" theme that overlaps the narrative is NOT correct when a clear commodity-tailwind theme dominates.
target_segment follows the request's client context (e.g. multi-asset income → multi_asset_income).
Rotation variant (SELL + BUY to cut HY/watchlist, keep duration)
- The desk memo's
stale_exception_board and candidate_shortlist are prior-week context; reconcile every quantity and issuer status to the current environment.
- SELL the watchlist bond(s) first (clears watchlist exposure to 0). SELL enough HY to get post-trade
hy_allocation_pct ≤ 20.0 and meet the target_hy_reduction_pct (≥4 ppt for the risk-reduction policy). BUY eligible IG candidates (non-watchlist, duration in band) to redeploy the proceeds (rotation keeps total MV constant).
watchlist_sell_ids = only the watchlist-flagged bonds you sold (ascending). buys_avoid_watchlist = true iff no buy issuer is watchlisted.
- The sell side drives correctness: which HY you sell (and the resulting HY% / reduction ppt) are the primary differentiators. Get the sell side right (clear watchlist to 0, land HY under cap and ≥ target reduction, keep duration in band); buy sizing and the
risk_note_code are secondary. Pick the HY to keep (if any) deliberately — do not assume the highest-carry HY is always retained; weigh stability and subsector too.
risk_metrics: post_trade_hy_allocation_pct (2), post_trade_duration_years (2), hy_reduction_pct_points = pre HY% − post HY% (2), post_trade_watchlist_exposure_usd_m (1).
exception_flags: hy_cap_pass, duration_band_pass, target_hy_reduction_met, watchlist_exposure_cleared.
trades ordering: SELL before BUY, then instrument_id ascending within each action.
4. Workflow B — International equity correlation review
For pair-correlation analysis across an index universe (e.g. PF-INT-NEXVEN).
Step-by-step
- Read the request's
review_window, index_universe, and CIO concern memo. GET /api/policies for correlation thresholds + window. GET /api/portfolios/<id> for sleeve names. GET /api/index-levels for monthly levels.
- Filter each index's levels to
[review_window_start, review_window_end] (the policy's review_window_start/_end). There should be 12 monthly levels → 11 monthly simple returns r_t = level_t / level_{t-1} − 1.
- Compute Pearson correlation over the 11 returns for every pair. Round to 3 decimals. (Pair id lists are 2 index ids sorted ascending alphabetically by ASCII — see §1.)
extreme_pairs: highest_positive = the pair with the max correlation; lowest = the pair with the min (most negative) correlation.
index_set: the request universe, ascending ASCII alphabetical (ACWI before AC_ASIA_PAC_EX_JP — underscore sorts after letters; mirror /api/catalog).
review_window: level_start_date, level_end_date, return_observations (= 11).
Concentration & sleeve actions
high_threshold_breached = true if any pair correlation > 0.8.
china_asia_dependence_flag = true if the China/Asia-Pacific overlap pair exceeds the high threshold.
primary_code: choose CHINA_ASIA_DEPENDENCE when the review's CIO memo concerns Asia-beta overlap / China-dedicated sleeves — even if the single highest pair in the universe is a global-developed overlap. The memo's stated concern drives the primary code, not the raw maximum pair. Use GLOBAL_DEVELOPED_OVERLAP only when the concentration is unambiguously a developed/global block overlap with no Asia/China concern. Use NO_MATERIAL_CONCENTRATION only when no pair breaches.
diversification_candidates: the fixed template list (e.g. [IDX_EM_EX_CHINA, IDX_INDIA, IDX_LATAM]), ascending.
sleeve_actions: 2 items, ascending by sleeve name. Trim the concentrated sleeve (e.g. China) and add the best diversifier (the index with the most negative correlation to the concentration, typically LATAM). Use the portfolio's sleeve names (e.g. "China", "Latin America") as the sleeve field; target_index_id from the allowed index set.
5. Workflow C — Cross-asset active allocation view updates
For active allocation views + risk overlay (e.g. Q2 2026 refresh, PF-MA-HELIO committee file).
Critical: filter macro-signals to the TARGET quarter
GET /api/macro-signals returns records for multiple quarters (e.g. both Q2_2026 and
Q3_2026 in the same list). You MUST keep only the records whose quarter == target_quarter.
Do NOT build a dict keyed by opportunity_set without filtering by quarter — duplicate
opportunity sets across quarters will silently overwrite and you will use next-quarter
scores, producing wrong views. Filter explicitly:
signal = next(x for x in macro if x['opportunity_set']==opp and x['quarter']==target_quarter)
Deriving each allocation row
Per opportunity set in the request's focus order:
asset_class: from /api/allocation/opportunity-sets taxonomy (Equities / Duration / Credit / Currency).
signal_score: the filtered macro signal score (3 decimals).
rationale_code: the filtered macro signal rationale_code (one of the 12 enums).
view from POL_ALLOCATION_MAPPING thresholds: OW if score ≥ 0.35; UW if score ≤ −0.35; else N.
conviction from |score|: HIGH if ≥ 0.7; MEDIUM if ≥ 0.35; else LOW.
prior_view: from /api/allocation/prior-views — the record with previous_quarter == prior_quarter and quarter == target_quarter (its view is the prior-quarter view). For target Q2_2026 / prior Q1_2026, use the records where previous_quarter=Q1_2026, quarter=Q2_2026.
change: compare view ranks (OW=1, N=0, UW=−1): UP if new > prior, DOWN if new < prior, else UNCHANGED.
- Order rows by the request payload's
focus_opportunity_sets order.
Cross-check
Sanity-check your derived view + conviction against the prior-views record whose
previous_quarter == target_quarter (the next-quarter refresh record) — the
target-quarter views should match that record's view/conviction when signals are
stable. If they disagree, you likely filtered the wrong quarter.
Lineage & overlay
task_id, target_quarter, prior_quarter: use the template's required values.
as_of_date: environment as_of_date.
policy_id: the allocation-mapping policy id (POL_ALLOCATION_MAPPING) that defines the view/conviction thresholds. (If a multi-asset policy id is expected instead, the portfolio's constraints.policy_id is the fallback — resolve from the portfolio record.)
risk_overlay: {overlay_code, primary_action, rationale_codes}. The overlay is evaluated as a unit; pick the single overlay that matches the dominant portfolio-level action implied by the views (e.g. duration-up + credit-down → duration-quality tilt; broad equity growth → equity-beta extension; currency defensiveness → currency hedge; balanced/no single dominant risk → no overlay). rationale_codes in business-priority order (highest priority first), drawn from the same 12-code enum. Choose deliberately — a mismatched overlay bundle earns no partial credit.
6. Workflow D — Combined correlation + allocation committee file
(e.g. PF-MA-HELIO: link non-US equity correlation findings to active allocation views
for a subset of opportunity sets.)
correlation_summary: 2 items [highest_concentration, best_diversifier], each
{pair_role, pair (2 index ids, sorted alphabetical ASCII), correlation (3 decimals, Pearson of monthly simple returns over the policy correlation window)}.
highest_concentration = max-correlation pair; best_diversifier = most-negative
(min) correlation pair.
allocation_views: as Workflow C (filter macro to target quarter; include
prior_view, signal_score, view, change, conviction, rationale_code),
ordered by the request's opportunity-set list.
target_sleeve_actions: one per opportunity set in the request's order, each
{opportunity_set, action}. Action reflects view + correlation finding: trim a sleeve
that is UW and/or part of the highest-concentration pair; add a sleeve that is OW
and/or the best diversifier; hold a sleeve that is unchanged. For a currency sleeve
downgraded OW→N on a neutral signal, prefer trim (implement the downgrade) over
hold.
rebalance_trigger: correlation_cap_breach when any reviewed pair exceeds the 0.8
high threshold. Other triggers (hy_cap_pressure, duration_drift,
watchlist_concentration, committee_review) apply only when those conditions are
present in the portfolio.
portfolio_risk_concentration_flag: true when any reviewed pair > 0.8.
next_step: with a live concentration breach (flag = true) and a remedying rotation,
approve_with_monitoring is preferred over approve_rotation. Use
defer_pending_risk_review only at two_or_more_material_exceptions; use
reject_constraint_breach only when the rotation cannot clear the breach.
7. Confirmed pass/fail & common pitfalls
- Multi-quarter macro signals: always filter
/api/macro-signals to quarter == target_quarter before deriving views. This is the single most impactful allocation pitfall.
- ASCII alphabetical ordering of index ids: underscore sorts after uppercase letters (
IDX_ACWI_IMI before IDX_AC_ASIA_PAC_EX_JP). Mirror /api/catalog.
- Carry maximization within constraints: for income BUY packages, the highest-YTM in-band non-watchlist eligible bond is preferred over a lower-YTM "story" bond.
- Watchlist = hard no-buy: never buy a watchlisted issuer, however high the yield.
- Duration band [3.0, 5.0]: exclude per-bond distractors outside the band AND keep the post-trade weighted duration inside it.
- HY cap 20%: post-trade HY% must be ≤ 20.0; for the risk-reduction policy also reduce by ≥ 4 ppt.
- Data precedence: current environment over any stale local payload snapshot/board/notes.
- No precomputed correlations: compute Pearson from monthly simple returns of consecutive index levels (12 levels → 11 returns).
- Held bonds are not in
?candidate=true: fetch the full bond list to get held-instrument details (duration, YTM, rating).
- Sell side drives rotation correctness: in HY/watchlist-reduction rotations, which HY you sell (and the resulting HY% and reduction ppt) is the differentiator; buy sizing and the qualitative note are secondary. Clear watchlist to 0 and land HY under cap.
- Overlay / sleeve-action sets are all-or-nothing: a partial match earns no credit — commit to the single most defensible overlay/action set rather than hedging.
- next_step with a live breach: prefer
approve_with_monitoring over approve_rotation.
8. Output discipline
Return a single JSON object conforming to input/payloads/answer_template.json. Match
every field name, enum value, ordering rule, and precision exactly. Include only the
fields the template requires. Do not add narrative outside the JSON. Round every
number to the field's declared precision. Re-derive all metrics from current
environment data, never from stale local payloads.
1---2name: reflect-3-attempt-01-173description: Asteria Investment Office — Institutional Portfolio-Risk Skill4---5# Asteria Investment Office — Institutional Portfolio-Risk Skill67Reusable workflow rules for solving Asteria portfolio-risk tasks against the shared8remote environment. Apply the SOP below per task type. All numeric output must follow9the precision declared in each task's `input/payloads/answer_template.json`.1011## 0. Environment (the authoritative book of record)1213Base URL: `<remote-env-url>` — GET endpoints only. The environment is the14**current book of record**. Every local payload in `input/payloads/` (desk requests,15meeting memos, committee packets, stale snapshots, exception boards) is possibly-stale16intake context; when it conflicts with the environment, prefer the environment.1718Key endpoints:19- `GET /api/catalog` — all portfolio ids, policy ids, index ids, issuer ids, bond ids, opportunity sets.20- `GET /api/policies` — portfolio constraints, correlation thresholds, allocation-mapping rules. Top-level `as_of_date` and `policy_id` (policy set) plus sub-policies.21- `GET /api/portfolios` and `GET /api/portfolios/<portfolio_id>` — objective, constraints, current holdings (each holding `instrument_id`, `quantity_usd_m` = market value USD millions, `sleeve`).22- `GET /api/instruments/bonds` (all) and `GET /api/instruments/bonds?candidate=true` — bond universe. Each bond: `instrument_id`, `issuer_id`, `rating_bucket` (IG/HY), `modified_duration_years`, `yield_to_maturity_pct`, `coupon_pct`, `energy_linked`, `candidate`, `sector`, `subsector`, `recommended_theme_tags`.23- `GET /api/issuers` — `issuer_id`, `sector`, `subsector`, `rating_bucket`, `watchlist` (boolean), `outlook`, `tags`.24- `GET /api/market/energy` — current oil/gas/LNG/refining/renewables signals (`signal_id`, `direction`, `score`, `summary`) and `pitch_themes`.25- `GET /api/indices` — index metadata; `GET /api/index-levels` and `/api/index-levels/<index_id>` — monthly levels. **No correlations are precomputed; compute them yourself from levels.**26- `GET /api/allocation/opportunity-sets` — taxonomy mapping `opportunity_set` → `asset_class` (Equities / Duration / Credit / Currency) and `display_order`.27- `GET /api/allocation/prior-views` — prior active allocation views (multi-quarter).28- `GET /api/macro-signals` — macro/asset signal scores and rationale codes (multi-quarter; see Workflow C filter rule).2930Use `curl` or `urllib`/`requests`. Do not attempt to read source code, env files, or31any local `env/` directory.3233## 1. Universal conventions3435- **as_of_date**: use the environment's `as_of_date` (top-level of `/api/policies`, also on each portfolio). It is the recommendation date.36- **Numeric precision** (from each field's template, never assume):37 - `notional_usd_m`, `quantity_usd_m`, `post_trade_watchlist_exposure_usd_m` → 1 decimal.38 - Post-trade metrics (market value, HY %, duration, YTM, reduction pct points) → 2 decimals.39 - Correlations, `signal_score` → 3 decimals.40- **Ordering rules** (strict, verified):41 - `trade_package` (BUY/SELL tickets): ascending by `instrument_id`.42 - `rotation.trades`: SELL before BUY, then `instrument_id` ascending within each action.43 - `index_set`, `pair_id` lists: ascending alphabetical by index id using **ASCII code points** — the underscore `_` (0x5F) sorts AFTER uppercase letters (A–Z are 0x41–0x5A). Therefore `IDX_ACWI_IMI` sorts BEFORE `IDX_AC_ASIA_PAC_EX_JP`. Always mirror the order shown in `/api/catalog` and in the request payload's index universe.44 - `sleeve_actions`: ascending by the `sleeve` field (the sleeve name string, e.g. "China" < "Latin America").45 - `allocation_views` / `target_sleeve_actions`: follow the request payload's `focus_opportunity_sets` / opportunity-set order exactly.46 - `correlation_summary`: item order `[highest_concentration, best_diversifier]`.47 - `diversification_candidates`: ascending alphabetical (fixed by template).48- **Data precedence**: when a local payload snapshot (stale holding worksheet, stale exception board quantities, stale desk notes) conflicts with the current environment, set any `data_precedence` field to `current_environment_over_stale_payload`. Use current environment quantities for all computations.4950## 2. Credit policies (read from `/api/policies`)5152Two credit sub-policies share most fields:53- `POL_CREDIT_DEFAULT`: `duration_band_years [3.0, 5.0]`, `max_hy_allocation_pct 20.0`, `issuer_concentration_limit_pct 12.0`, `subsector_min_count_for_diversified 2`, `target_hy_reduction_pct 0.0`.54- `POL_CREDIT_RISK_REDUCTION`: same but `target_hy_reduction_pct 4.0` (use when a task targets an HY reduction of ≥4 ppt).5556The portfolio record's `constraints.policy_id` tells you which applies. Multi-asset57portfolios use `POL_MULTI_ASSET_DEFAULT` (bundles allocation mapping, correlation58default, credit default) or `POL_MULTI_ASSET_RISK` (uses correlation default +59credit risk reduction; committee escalation at `two_or_more_material_exceptions`).6061Correlation policy: `correlation_high_threshold 0.8`, `correlation_low_threshold 0.2`,62`review_window_start`/`review_window_end`.6364## 3. Workflow A — Energy / fixed-income trade strategy6566For BUY/SELL bond tickets under credit-risk constraints (e.g. PF-EN-ALTA income67sleeve, PF-FI-LUMEN rotation).6869### Step-by-step701. `GET /api/portfolios/<id>` for current holdings + constraints + as_of_date.712. `GET /api/instruments/bonds` (all — held bonds are NOT in the `?candidate=true` list) and `?candidate=true` for eligible new buys. `GET /api/issuers` for watchlist flags. `GET /api/market/energy` for signals/themes. `GET /api/policies` for the credit policy.723. Compute pre-trade metrics from current holdings (market-value-weighted):73 - `HY% = sum(qty where rating_bucket==HY) / total_mv * 100`74 - `weighted modified duration = sum(qty * modified_duration_years) / total_mv`75 - `weighted YTM = sum(qty * yield_to_maturity_pct) / total_mv`764. Build the trade package, then recompute the same metrics on the post-trade book (held − sells + buys). Round to template precision.7778### Selection rules79- **Carry improvement = maximize YTM within constraints.** For an income BUY package, pick the IG anchor on the strongest positive energy signal (e.g. LNG export pull) and the carry ticket as the **highest-YTM** eligible bond that is: energy-linked, non-watchlist, modified duration inside the `[3.0, 5.0]` band, and from a new issuer/subsector. Do NOT pick a lower-YTM "story" bond if a higher-YTM in-band non-watchlist bond clears every constraint.80- **Watchlist avoidance is a hard constraint.** Never BUY a bond whose `issuer_id` has `watchlist == true`. The energy watchlist issuers are the high-carry traps (e.g. shale E&P, refining). A non-watchlist HY carry bond is acceptable; a watchlist HY bond is not, regardless of yield.81- **Duration-band distractors**: exclude bonds with `modified_duration_years` outside `[3.0, 5.0]` — long-dated LNG/oil bonds (dur > 5) and very-short HY (dur < 3) are traps. Also confirm the **post-trade weighted** duration stays in band.82- **Issuer concentration limit 12%**: a candidate whose issuer is already held can blow past 12% post-trade — exclude or size down. Prefer new issuers.83- **Diversification of the package**: the selected buys must span ≥2 distinct issuers AND ≥2 distinct subsectors (set `selected_issuer_diversification_pass` and `selected_subsector_diversification_pass` accordingly).84- **HY cap**: post-trade `hy_allocation_pct` must be ≤ 20.0.85- **sales_positioning.theme**: map to the single strongest energy signal / pitch theme (e.g. LNG export tailwind). A "selectivity/transition" theme that overlaps the narrative is NOT correct when a clear commodity-tailwind theme dominates. `target_segment` follows the request's client context (e.g. multi-asset income → `multi_asset_income`).8687### Rotation variant (SELL + BUY to cut HY/watchlist, keep duration)88- The desk memo's `stale_exception_board` and `candidate_shortlist` are prior-week context; reconcile every quantity and issuer status to the current environment.89- SELL the watchlist bond(s) first (clears watchlist exposure to 0). SELL enough HY to get post-trade `hy_allocation_pct` ≤ 20.0 and meet the `target_hy_reduction_pct` (≥4 ppt for the risk-reduction policy). BUY eligible IG candidates (non-watchlist, duration in band) to redeploy the proceeds (rotation keeps total MV constant).90- `watchlist_sell_ids` = only the watchlist-flagged bonds you sold (ascending). `buys_avoid_watchlist = true` iff no buy issuer is watchlisted.91- **The sell side drives correctness**: which HY you sell (and the resulting HY% / reduction ppt) are the primary differentiators. Get the sell side right (clear watchlist to 0, land HY under cap and ≥ target reduction, keep duration in band); buy sizing and the `risk_note_code` are secondary. Pick the HY to keep (if any) deliberately — do not assume the highest-carry HY is always retained; weigh stability and subsector too.92- `risk_metrics`: `post_trade_hy_allocation_pct` (2), `post_trade_duration_years` (2), `hy_reduction_pct_points` = pre HY% − post HY% (2), `post_trade_watchlist_exposure_usd_m` (1).93- `exception_flags`: `hy_cap_pass`, `duration_band_pass`, `target_hy_reduction_met`, `watchlist_exposure_cleared`.94- `trades` ordering: SELL before BUY, then `instrument_id` ascending within each action.9596## 4. Workflow B — International equity correlation review9798For pair-correlation analysis across an index universe (e.g. PF-INT-NEXVEN).99100### Step-by-step1011. Read the request's `review_window`, `index_universe`, and CIO concern memo. `GET /api/policies` for correlation thresholds + window. `GET /api/portfolios/<id>` for sleeve names. `GET /api/index-levels` for monthly levels.1022. Filter each index's levels to `[review_window_start, review_window_end]` (the policy's `review_window_start`/`_end`). There should be 12 monthly levels → **11 monthly simple returns** `r_t = level_t / level_{t-1} − 1`.1033. Compute **Pearson correlation** over the 11 returns for every pair. Round to **3 decimals**. (Pair id lists are 2 index ids sorted ascending alphabetically by ASCII — see §1.)1044. `extreme_pairs`: `highest_positive` = the pair with the max correlation; `lowest` = the pair with the min (most negative) correlation.1055. `index_set`: the request universe, ascending ASCII alphabetical (ACWI before AC_ASIA_PAC_EX_JP — underscore sorts after letters; mirror `/api/catalog`).1066. `review_window`: `level_start_date`, `level_end_date`, `return_observations` (= 11).107108### Concentration & sleeve actions109- `high_threshold_breached = true` if any pair correlation > 0.8.110- `china_asia_dependence_flag = true` if the China/Asia-Pacific overlap pair exceeds the high threshold.111- `primary_code`: choose `CHINA_ASIA_DEPENDENCE` when the review's CIO memo concerns Asia-beta overlap / China-dedicated sleeves — even if the single highest pair in the universe is a global-developed overlap. The memo's stated concern drives the primary code, not the raw maximum pair. Use `GLOBAL_DEVELOPED_OVERLAP` only when the concentration is unambiguously a developed/global block overlap with no Asia/China concern. Use `NO_MATERIAL_CONCENTRATION` only when no pair breaches.112- `diversification_candidates`: the fixed template list (e.g. `[IDX_EM_EX_CHINA, IDX_INDIA, IDX_LATAM]`), ascending.113- `sleeve_actions`: 2 items, ascending by sleeve name. Trim the concentrated sleeve (e.g. China) and add the best diversifier (the index with the most negative correlation to the concentration, typically LATAM). Use the portfolio's sleeve names (e.g. "China", "Latin America") as the `sleeve` field; `target_index_id` from the allowed index set.114115## 5. Workflow C — Cross-asset active allocation view updates116117For active allocation views + risk overlay (e.g. Q2 2026 refresh, PF-MA-HELIO committee file).118119### Critical: filter macro-signals to the TARGET quarter120`GET /api/macro-signals` returns records for **multiple quarters** (e.g. both Q2_2026 and121Q3_2026 in the same list). You MUST keep only the records whose `quarter == target_quarter`.122Do NOT build a dict keyed by `opportunity_set` without filtering by quarter — duplicate123opportunity sets across quarters will silently overwrite and you will use next-quarter124scores, producing wrong views. Filter explicitly:125```126signal = next(x for x in macro if x['opportunity_set']==opp and x['quarter']==target_quarter)127```128129### Deriving each allocation row130Per opportunity set in the request's focus order:131- `asset_class`: from `/api/allocation/opportunity-sets` taxonomy (Equities / Duration / Credit / Currency).132- `signal_score`: the filtered macro signal `score` (3 decimals).133- `rationale_code`: the filtered macro signal `rationale_code` (one of the 12 enums).134- `view` from `POL_ALLOCATION_MAPPING` thresholds: `OW` if score ≥ 0.35; `UW` if score ≤ −0.35; else `N`.135- `conviction` from `|score|`: `HIGH` if ≥ 0.7; `MEDIUM` if ≥ 0.35; else `LOW`.136- `prior_view`: from `/api/allocation/prior-views` — the record with `previous_quarter == prior_quarter` and `quarter == target_quarter` (its `view` is the prior-quarter view). For target Q2_2026 / prior Q1_2026, use the records where `previous_quarter=Q1_2026, quarter=Q2_2026`.137- `change`: compare view ranks (`OW`=1, `N`=0, `UW`=−1): `UP` if new > prior, `DOWN` if new < prior, else `UNCHANGED`.138- Order rows by the request payload's `focus_opportunity_sets` order.139140### Cross-check141Sanity-check your derived `view` + `conviction` against the prior-views record whose142`previous_quarter == target_quarter` (the next-quarter refresh record) — the143target-quarter views should match that record's view/conviction when signals are144stable. If they disagree, you likely filtered the wrong quarter.145146### Lineage & overlay147- `task_id`, `target_quarter`, `prior_quarter`: use the template's required values.148- `as_of_date`: environment `as_of_date`.149- `policy_id`: the allocation-mapping policy id (`POL_ALLOCATION_MAPPING`) that defines the view/conviction thresholds. (If a multi-asset policy id is expected instead, the portfolio's `constraints.policy_id` is the fallback — resolve from the portfolio record.)150- `risk_overlay`: `{overlay_code, primary_action, rationale_codes}`. The overlay is evaluated as a unit; pick the single overlay that matches the dominant portfolio-level action implied by the views (e.g. duration-up + credit-down → duration-quality tilt; broad equity growth → equity-beta extension; currency defensiveness → currency hedge; balanced/no single dominant risk → no overlay). `rationale_codes` in business-priority order (highest priority first), drawn from the same 12-code enum. Choose deliberately — a mismatched overlay bundle earns no partial credit.151152## 6. Workflow D — Combined correlation + allocation committee file153154(e.g. PF-MA-HELIO: link non-US equity correlation findings to active allocation views155for a subset of opportunity sets.)156157- `correlation_summary`: 2 items `[highest_concentration, best_diversifier]`, each158 `{pair_role, pair (2 index ids, sorted alphabetical ASCII), correlation (3 decimals,159 Pearson of monthly simple returns over the policy correlation window)}`.160 `highest_concentration` = max-correlation pair; `best_diversifier` = most-negative161 (min) correlation pair.162- `allocation_views`: as Workflow C (filter macro to target quarter; include163 `prior_view`, `signal_score`, `view`, `change`, `conviction`, `rationale_code`),164 ordered by the request's opportunity-set list.165- `target_sleeve_actions`: one per opportunity set in the request's order, each166 `{opportunity_set, action}`. Action reflects view + correlation finding: trim a sleeve167 that is UW and/or part of the highest-concentration pair; add a sleeve that is OW168 and/or the best diversifier; hold a sleeve that is unchanged. For a currency sleeve169 downgraded OW→N on a neutral signal, prefer `trim` (implement the downgrade) over170 `hold`.171- `rebalance_trigger`: `correlation_cap_breach` when any reviewed pair exceeds the 0.8172 high threshold. Other triggers (`hy_cap_pressure`, `duration_drift`,173 `watchlist_concentration`, `committee_review`) apply only when those conditions are174 present in the portfolio.175- `portfolio_risk_concentration_flag`: `true` when any reviewed pair > 0.8.176- `next_step`: with a live concentration breach (flag = true) and a remedying rotation,177 `approve_with_monitoring` is preferred over `approve_rotation`. Use178 `defer_pending_risk_review` only at `two_or_more_material_exceptions`; use179 `reject_constraint_breach` only when the rotation cannot clear the breach.180181## 7. Confirmed pass/fail & common pitfalls182183- **Multi-quarter macro signals**: always filter `/api/macro-signals` to `quarter == target_quarter` before deriving views. This is the single most impactful allocation pitfall.184- **ASCII alphabetical ordering** of index ids: underscore sorts after uppercase letters (`IDX_ACWI_IMI` before `IDX_AC_ASIA_PAC_EX_JP`). Mirror `/api/catalog`.185- **Carry maximization within constraints**: for income BUY packages, the highest-YTM in-band non-watchlist eligible bond is preferred over a lower-YTM "story" bond.186- **Watchlist = hard no-buy**: never buy a watchlisted issuer, however high the yield.187- **Duration band [3.0, 5.0]**: exclude per-bond distractors outside the band AND keep the post-trade weighted duration inside it.188- **HY cap 20%**: post-trade HY% must be ≤ 20.0; for the risk-reduction policy also reduce by ≥ 4 ppt.189- **Data precedence**: current environment over any stale local payload snapshot/board/notes.190- **No precomputed correlations**: compute Pearson from monthly simple returns of consecutive index levels (12 levels → 11 returns).191- **Held bonds are not in `?candidate=true`**: fetch the full bond list to get held-instrument details (duration, YTM, rating).192- **Sell side drives rotation correctness**: in HY/watchlist-reduction rotations, which HY you sell (and the resulting HY% and reduction ppt) is the differentiator; buy sizing and the qualitative note are secondary. Clear watchlist to 0 and land HY under cap.193- **Overlay / sleeve-action sets are all-or-nothing**: a partial match earns no credit — commit to the single most defensible overlay/action set rather than hedging.194- **next_step with a live breach**: prefer `approve_with_monitoring` over `approve_rotation`.195196## 8. Output discipline197198Return a single JSON object conforming to `input/payloads/answer_template.json`. Match199every field name, enum value, ordering rule, and precision exactly. Include only the200fields the template requires. Do not add narrative outside the JSON. Round every201number to the field's declared precision. Re-derive all metrics from current202environment data, never from stale local payloads.