Asteria Investment Office — Portfolio-Risk Evaluation Skill
This skill distills reusable execution knowledge for solving Asteria Investment
Office portfolio-risk tasks. The test task gives you a prompt, an
input/payloads/answer_template.json, and one or more request payloads. You
also have the remote Asteria environment (the current book of record) and this
skill. Use the environment as the source of truth and follow the SOPs below.
The tasks draw on three workflows that may also be combined:
- Workflow A — Energy / fixed-income credit trade strategy. Build a BUY/SELL
bond package under credit-risk constraints, then report post-trade metrics,
constraint booleans, and sales positioning.
- Workflow B — International equity correlation review. Compute pair
correlations from monthly index levels, flag concentration, and propose sleeve
actions.
- Workflow C — Cross-asset active allocation view refresh. Translate macro
signal scores into active views (UW/N/OW) with conviction, change vs prior
quarter, rationale, plus risk overlay and committee fields.
0. Golden rules (read first)
- Environment is the current book of record. The remote service at
<remote-env-url> is authoritative for current holdings, holding
quantities, issuer status, market signals, index levels, policies, prior
views, and macro signals. Local input/payloads/*.json files are intake
context that may be stale (old worksheet dates, stale marks, stale
quantities, old shortlists). When a local payload conflicts with the
environment, prefer the environment unless the task prompt explicitly
overrides. Set data_precedence accordingly
(current_environment_over_stale_payload when a conflict exists;
no_conflict_found only when nothing stale is present). Watch for explicit
"stale" markers in payloads (snapshot_date, stale_*, memo_as_of_date,
stale_local_note, "Operations has not reconciled..." comments) — these
signal you must refresh from the environment.
- Always read
answer_template.json first. It defines required keys,
allowed enum values, field precision, ordering rules, and list lengths.
Conform exactly: a field declared precision: 1 must be rounded to 1
decimal; a list declared length: 2 must have exactly 2 items; an enum must
use one of the allowed values verbatim.
as_of_date is the environment's current date, NOT the payload's request
date or memo date. Read it from GET /api/policies (top-level as_of_date)
or any GET /api/portfolios/<id> (as_of_date). All train examples use the
environment as_of_date (e.g. 2026-05-29), not the older memo/snapshot dates.
- Correlations are never precomputed. Compute them yourself from
GET /api/index-levels (monthly simple returns, see Workflow B).
- Be precise about ordering. Lists are sorted as the template declares:
trade packages by
instrument_id ascending (or SELL-before-BUY then
instrument_id), index sets/pair_ids alphabetically, allocation rows in the
request payload's focus order, sleeve_actions by sleeve name ascending.
1. Environment endpoints (GET only)
Base URL: <remote-env-url>. Call with curl or Python urllib.
| Endpoint |
Use |
GET /api/catalog |
All available portfolio ids, policy ids, index ids, issuer ids, bond ids, opportunity sets. |
GET /api/policies |
Constraints + thresholds. Returns top-level policy_id (e.g. POLICY_SET_2026_05), as_of_date, and sub-policies credit_default, credit_risk_reduction, correlation, allocation_mapping, multi_asset, multi_asset_risk. |
GET /api/portfolios |
Portfolio summaries. |
GET /api/portfolios/<id> |
One portfolio: objective, constraints, current holdings (each with instrument_id, quantity_usd_m, sleeve, notes), market_value_usd_m, as_of_date. |
GET /api/instruments/bonds |
Full bond universe. Filter with ?candidate=true, ?rating_bucket=HY, etc. Each bond has instrument_id, issuer_id, sector, subsector, rating, rating_bucket (IG/HY), modified_duration_years, yield_to_maturity_pct, coupon_pct, maturity, energy_linked (bool), candidate (bool), recommended_theme_tags. |
GET /api/issuers |
Issuer master: issuer_id, sector, subsector, rating_bucket, watchlist (bool), outlook, issuer_name. |
GET /api/market/energy |
Energy market signals + pitch_themes. Used for sales positioning in energy trade tasks. |
GET /api/indices |
Index metadata (level_start_date, level_end_date, region, currency). |
GET /api/index-levels |
Object keyed by index_id, each a list of {date, level} monthly observations. |
GET /api/index-levels/<id> |
Monthly levels for one index. |
GET /api/allocation/opportunity-sets |
Opportunity-set taxonomy: opportunity_set, asset_class (Equities/Duration/Credit/Currency), display_order. |
GET /api/allocation/prior-views |
List of prior active views. Each entry has opportunity_set, quarter (the effective quarter), previous_quarter (when set), view, conviction. Two cohorts exist: a Q_{n-1}→Q_n cohort (the prior standing view) and a Q_n→Q_{n+1} cohort. |
GET /api/macro-signals |
List of macro signal scores. Each has opportunity_set, quarter, score (signed float), rationale_code, drivers. |
2. Cross-cutting conventions
Precision (follow answer_template.json declarations)
notional_usd_m / quantity_usd_m → 1 decimal.
post_trade_metrics numeric fields (*_usd_m with precision 2, hy_allocation_pct, weighted_modified_duration_years, weighted_yield_to_maturity_pct) → 2 decimals.
correlation → 3 decimals.
signal_score → 3 decimals.
total_market_value_usd_m → 2 decimals (per template).
- Booleans are
true/false. Round half-up to the declared precision; JSON drops
trailing zeros naturally (e.g. 5.80 renders as 5.8).
Policy snapshot (GET /api/policies)
Key fields the SOPs reference (concrete values observed; re-read at test time in
case the environment changed):
policy_id (top-level): the policy-set id used as the allocation policy_id
field (e.g. POLICY_SET_2026_05).
credit_default / credit_risk_reduction: 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. credit_risk_reduction adds
target_hy_reduction_pct: 4.0 (the minimum HY reduction a rebalance must
achieve). Portfolios opt into one of these via constraints.policy_id.
correlation: correlation_high_threshold: 0.8, correlation_low_threshold: 0.2, review_window_start, review_window_end.
allocation_mapping: view_score_thresholds (OW_min: 0.35, UW_max: -0.35,
neutral_between: [-0.35, 0.35]), conviction_thresholds (HIGH_abs_min: 0.7,
MEDIUM_abs_min: 0.35, LOW_abs_below: 0.35), view_rank (UW=-1, N=0, OW=1).
Which policy a portfolio uses
Read GET /api/portfolios/<id> → constraints.policy_id. A pure credit sleeve
uses POL_CREDIT_DEFAULT; a risk-reduction rotation uses
POL_CREDIT_RISK_REDUCTION (which adds the target_hy_reduction_pct floor).
Multi-asset sleeves compose: POL_MULTI_ASSET_DEFAULT references credit +
correlation + allocation; POL_MULTI_ASSET_RISK references correlation +
credit-risk-reduction.
3. Workflow A — Energy / fixed-income credit trade strategy
Applies to: standalone energy sleeve trades and fixed-income risk-reduction
rotations. Two sub-shapes exist:
- Income-add shape (two BUY tickets):
trade_package (BUY list),
post_trade_metrics, constraint_checks, sales_positioning,
data_precedence.
- Rotation shape (SELL+BUY):
rotation.trades (SELL before BUY),
risk_metrics, exception_flags, watchlist_handling, risk_note_code.
Step-by-step SOP
- Read the portfolio
GET /api/portfolios/<portfolio_id>. Capture current
holdings (instrument_id, quantity_usd_m), market_value_usd_m,
as_of_date, and constraints (duration band, HY cap, target HY reduction).
Use these environment quantities, not any stale quantities in the local
memo. (Example pattern: a local memo may show a holding at 10.0 while the
environment shows 12.0 — trade off the 12.0.)
- Read all bonds
GET /api/instruments/bonds (and ?candidate=true for the
buy universe). Read all issuers GET /api/issuers to get the
watchlist flag per issuer (join on issuer_id).
- Apply exclusion filters to candidate buys:
- Watchlist avoidance: drop any bond whose issuer
watchlist == true.
This is a hard exclusion (the desk is "sensitive to headline carry from
watchlisted issuers"). Candidates flagged with theme tags like
WATCHLIST_RISK / REFINANCING_RISK or refining issuers (negative
refining signal) are exclude-by-default.
- Duration-band eligibility: keep only bonds whose
modified_duration_years is within the policy band [3.0, 5.0]
(inclusive). Long-dated distractors (dur 5.8, 6.7, 5.9) and very short
ones (dur 2.3) are excluded. For an income-add where the package must
keep the portfolio weighted duration in band, individual buy durations
should sit inside the band.
- HY capacity: HY buys are allowed only if post-trade HY % stays under
the cap (20%). Do not blind-buy the highest-YTM HY name if it breaches the
cap or concentrates a watchlisted issuer.
- Sector/energy linkage: for an energy sleeve task, buy bonds with
energy_linked == true. (energy_linked is a bond-level field, not the
issuer sector; e.g. a utilities-issuer power bond can be energy-linked.)
- Issuer concentration: no single issuer should exceed
issuer_concentration_limit_pct (12%) of post-trade market value.
- Select the package honoring the desk's stated preferences (e.g. LNG
exporters / gas demand / non-watchlist carry) and the "improve carry" goal
(higher YTM helps). A robust pattern for a two-ticket income add:
- Pick one IG bond that matches the preferred theme (LNG/gas) for ballast +
client pitch suitability.
- Pick one higher-carry non-watchlist energy-linked bond (often HY) that
lifts portfolio YTM while keeping HY % well under cap and adding issuer +
subsector diversification.
- Verify post-trade weighted duration stays inside the band and that new
issuers/subsectors increase diversification.
For a rotation (risk-reduction) task:
- Sell watchlist holdings first (entire positions); the watchlist sells are
reported in
watchlist_sell_ids.
- Sell additional HY as needed to bring post-trade HY % under cap and meet
target_hy_reduction_pct. Prefer keeping higher-carry non-watchlist HY and
trimming lower-carry HY when both are eligible, so carry is preserved.
- Fund duration ballast by buying IG candidates from the shortlist that are
non-watchlist and inside the duration band; weight notionals toward longer
duration to replace duration lost when selling short-duration HY.
- Keep the package roughly self-funding (total sell notional ≈ total buy
notional) so total market value is preserved.
- Order the trades as the template declares. Income-add: ascending by
instrument_id. Rotation: SELL before BUY, then instrument_id
ascending within each action. Notional field name follows the template
(notional_usd_m for income-add; quantity_usd_m for rotation).
Post-trade metrics computation (mechanical, market-value-weighted)
Build the post-trade book = current holdings adjusted by trades (SELL subtracts,
BUY adds; use environment quantities). Then:
total_market_value_usd_m = sum of post-trade quantity_usd_m.
hy_allocation_pct = (sum of HY quantity_usd_m) / total_market_value × 100,
rounded to 2 decimals. (HY defined by bond rating_bucket == "HY".)
weighted_modified_duration_years = Σ(quantity · modified_duration_years) /
total_market_value, rounded to 2.
weighted_yield_to_maturity_pct = Σ(quantity · yield_to_maturity_pct) /
total_market_value, rounded to 2.
For rotation outputs:
post_trade_hy_allocation_pct = post-trade HY % (2 decimals).
post_trade_duration_years = post-trade weighted modified duration (2 dp).
hy_reduction_pct_points = pre_trade_HY% − post_trade_HY% (2 dp, can be
large when a watchlist HY block is removed).
post_trade_watchlist_exposure_usd_m = sum of post-trade quantities whose
issuer watchlist == true (1 decimal; should be 0.0 if all watchlist names
were sold).
- Pre-trade HY % is computed the same way on the un-adjusted holdings.
Verify every metric by recomputing; do not trust stale worksheet snapshots.
Constraint-check derivation (booleans)
hy_cap_pass = post-trade HY % ≤ max_hy_allocation_pct (20.0).
duration_band_pass = post-trade weighted duration within
duration_band_years (inclusive [3.0, 5.0]).
issuer_diversification_pass / selected_issuer_diversification_pass = no
single issuer exceeds issuer_concentration_limit_pct (12%) of post-trade
MV (and, for "selected" variants, the newly selected issuers are distinct from
each other / add diversification).
subsector_diversification_pass / selected_subsector_diversification_pass =
the holding set (or selected buys) spans at least
subsector_min_count_for_diversified (2) subsectors.
watchlist_avoidance_pass = no bought bond has a watchlisted issuer (and for
rotation, watchlist_exposure_cleared = post-trade watchlist exposure == 0).
target_hy_reduction_met (rotation only) = hy_reduction_pct_points ≥
target_hy_reduction_pct (4.0 for credit_risk_reduction policy).
Sales positioning (energy income-add)
Read GET /api/market/energy. Pick the strongest positive signal that matches
your selected bonds' theme tags:
- LNG export pull (highest positive score) →
theme: lng_export_tailwind.
- Midstream defensive carry →
midstream_stability.
- Oil discipline / oversupply caution →
oil_oversupply_caution.
- Watchlist yield avoidance →
avoid_watchlist_yield_trap.
- Selective transition/renewables →
transition_bond_selectivity.
target_segment follows the client context in the desk request (e.g.
"multi-asset income update" → multi_asset_income; insurance / pension /
private-bank / endowment contexts map to their respective enum). When
refining/refiner signals are negative and watchlisted, do NOT pick a refining
theme; the refining signal being negative + AVOID_REFINING_WATCHLIST pitch theme
reinforces watchlist avoidance.
risk_note_code (rotation)
Pick the dominant driver of the rotation:
watchlist_concentration (when the main issue was a watchlisted holding),
hy_cap_pressure (when HY cap drove sells), duration_preservation (when the
rotation's point was keeping duration in band), carry_tradeoff (when selling
carry to reduce risk), no_action (when nothing needed fixing). If watchlist
exposure was the trigger, watchlist_concentration is correct.
4. Workflow B — International equity correlation review
No correlations are precomputed. Compute from GET /api/index-levels.
Correlation method (confirmed deterministic)
- Take the review window from the request payload
(
review_window.level_start_date / level_end_date) — these match the
policy correlation.review_window_start/end for the default window. For each
index in the requested index_universe, extract the monthly levels in date
order across the window.
- Compute monthly simple returns:
r[t] = level[t] / level[t-1] − 1 for
consecutive observations. A window with 12 month-end levels yields 11
return observations (review_window.return_observations = 11).
- For each unordered pair of indices, compute the Pearson correlation of
their return series:
corr = cov(r_x, r_y) / (σ_x · σ_y) (population, dividing by n).
extreme_pairs.highest_positive = the pair with max correlation;
extreme_pairs.lowest = the pair with min correlation (most negative).
pair_id lists the two index ids sorted alphabetically. Round correlation
to 3 decimals.
index_set = the requested universe, sorted ascending alphabetically.
Concentration flags
Using the policy thresholds correlation_high_threshold (0.8) and
correlation_low_threshold (0.2):
high_threshold_breached = any pair correlation ≥ 0.8.
china_asia_dependence_flag = true when IDX_CHINA correlation with the
Asia/EM/developed block (IDX_EM, IDX_AC_ASIA_PAC_EX_JP, IDX_ACWI_IMI,
IDX_WORLD, IDX_EAFE, IDX_EM_EX_CHINA, IDX_INDIA) is ≥ 0.8 — i.e. China is
tightly coupled to the regional/global complex.
primary_code:
CHINA_ASIA_DEPENDENCE when China↔Asia/EM pairs drive the concentration
(CIO memos flag ASIA_BETA_OVERLAP / CHINA_DEDICATED_SLEEVE).
GLOBAL_DEVELOPED_OVERLAP when the dominant high-correlation cluster is
developed/global (IDX_WORLD, IDX_EAFE, IDX_ACWI_IMI) rather than China.
NO_MATERIAL_CONCENTRATION when no pair breaches the high threshold.
Diversification candidates
From the template's allowed candidate set, select indices that diversify the
concentration center (typically China). Rank allowed candidates by their
correlation with the concentration center ascending (most negative / lowest
first). Include those that meaningfully diversify:
- Indices with negative correlation to the center (e.g. IDX_LATAM, which is
negatively correlated to everything) always qualify.
- Indices that structurally remove the concentration (e.g. IDX_EM_EX_CHINA)
qualify as the "ex-China" diversifier.
- Exclude an allowed candidate whose correlation with the concentration
center breaches the high threshold in a way that adds to (not relieves) the
concentration (e.g. IDX_INDIA when it is ≥0.8 with China does not diversify
China risk). Sort the resulting list ascending alphabetically.
Sleeve actions
Produce the number of sleeve-action objects the template requires, ordered
ascending by sleeve name. Map from the review's conclusions:
trim the concentrated sleeve (the source of concentration, e.g. the China
dedicated sleeve).
add the best diversifier sleeve (e.g. Latin America).
hold / monitor / hedge / rotate for other sleeves as appropriate.
target_index_id and action must use the template's allowed enums.
5. Workflow C — Cross-asset active allocation view refresh
This is largely deterministic — derive views mechanically from macro signal
scores and the allocation_mapping policy. Do not editorialize.
Per opportunity set (one allocation_views row)
- prior_view = the view from
GET /api/allocation/prior-views for the entry
where quarter == target_quarter AND previous_quarter == prior_quarter.
(This is the standing view set last quarter for the current quarter.)
- signal_score =
GET /api/macro-signals score for
(opportunity_set, quarter == target_quarter) (round to 3 decimals if the
template exposes it).
- rationale_code = that macro-signal entry's
rationale_code (verbatim
from the environment — e.g. CHINA_DEPENDENCE, INDIA_OFFSET,
LATAM_DIVERSIFIER, DURATION_SUPPORT, HY_VALUATION_RISK, EUROPE_RECOVERY,
JAPAN_POLICY_RISK, DOLLAR_DEFENSIVE, NEUTRAL_BALANCE, RATE_CUT_SUPPORT,
CREDIT_SPREAD_RISK, GROWTH_IMPROVES).
- view from
signal_score via allocation_mapping.view_score_thresholds:
- score ≥
OW_min (0.35) → OW
- score ≤
UW_max (−0.35) → UW
- otherwise →
N
- conviction from
|signal_score| via conviction_thresholds:
- |score| ≥
HIGH_abs_min (0.7) → HIGH
MEDIUM_abs_min (0.35) ≤ |score| < 0.7 → MEDIUM
- |score| <
LOW_abs_below (0.35) → LOW
- change from
view_rank (UW=−1, N=0, OW=1): compare rank(new_view) vs
rank(prior_view):
- new > prior →
UP
- new < prior →
DOWN
- equal →
UNCHANGED
- asset_class = the
asset_class from
GET /api/allocation/opportunity-sets for that opportunity set (Equities /
Duration / Credit / Currency).
- Row order = the request payload's
focus_opportunity_sets order (not
alphabetical). required_length matches the focus list size.
- policy_id = the top-level
policy_id from GET /api/policies
(e.g. POLICY_SET_2026_05). target_quarter / prior_quarter = the request
values. as_of_date = environment as_of_date.
Risk overlay (judgment, but patterned)
Choose overlay_code / primary_action from the macro picture formed by the
rows (paired enums: DURATION_QUALITY_TILT↔tilt_to_duration_quality,
CREDIT_RISK_REDUCTION↔trim_credit_beta, EQUITY_BETA_EXTENSION↔add_cyclical_equity_beta,
CURRENCY_DEFENSIVE_HEDGE↔add_currency_hedge, NO_OVERLAY↔hold_policy_weights):
- If duration/quality is supported (U.S. Treasuries OW, DURATION_SUPPORT) and
HY is a risk (Corporate HY UW, HY_VALUATION_RISK) →
DURATION_QUALITY_TILT / tilt_to_duration_quality.
- If HY/credit spread risk dominates →
CREDIT_RISK_REDUCTION /
trim_credit_beta.
- If defensive currency is the theme →
CURRENCY_DEFENSIVE_HEDGE /
add_currency_hedge.
- If cyclical growth recovery dominates with no risk offset →
EQUITY_BETA_EXTENSION / add_cyclical_equity_beta.
- If views net to roughly neutral with no material risk →
NO_OVERLAY /
hold_policy_weights.
rationale_codes = the rationale codes driving the overlay choice, ordered by
business priority (highest priority first). Favor the risk-reduction and
duration-support rationales first (e.g. DURATION_SUPPORT, HY_VALUATION_RISK,
CHINA_DEPENDENCE), then growth/diversifier codes. Keep the list to the 2–4 most
material drivers.
6. Combined workflow — correlation + allocation (committee JSON)
Some tasks (multi-asset committee files) combine B and C: a small index
sub-universe correlation summary plus allocation views for the matching sleeves.
correlation_summary (length 2, ordered [highest_concentration, best_diversifier]): compute Pearson over the requested index subset;
highest_concentration = the max-correlation pair; best_diversifier =
the min (most negative) pair. Each pair sorted alphabetically;
correlation rounded to 3 decimals.
target_sleeve_actions: one row per requested opportunity set, in the
template's item_order. Map view → action: OW → add, UW → trim. For a
currency sleeve (USD) that is downgraded from OW toward neutral, use hedge
(reduce the defensive long). Stable OW → add/hold; stable N → hold/
monitor.
allocation_views: same deterministic computation as Workflow C (prior_view,
signal_score, view, change, conviction, rationale_code), in the template's
item_order.
rebalance_trigger:
correlation_cap_breach when a high-threshold pair breach drove the review
(most common when the correlation summary shows a pair ≥ 0.8).
hy_cap_pressure / duration_drift / watchlist_concentration for those
drivers; committee_review as the generic catch-all.
portfolio_risk_concentration_flag: true when the correlation summary or
constraint picture shows material concentration (a pair ≥ high threshold, or a
China/Asia dependence flag).
next_step:
approve_with_monitoring when actions reduce concentration but residual
monitoring is warranted (common when the package trims the concentrated
sleeve and adds diversifiers without a hard breach remaining).
approve_rotation when the proposed rotation fully resolves the issue.
defer_pending_risk_review when material exceptions remain unresolved.
reject_constraint_breach when a constraint breach cannot be fixed.
7. Common misjudgments to avoid
- Trusting stale holding quantities / marks. The local memo is intake
context. A "stale_exception_board" with
stale_quantity_usd_m or a
"stale_holding_snapshot" is explicitly unreliable — recompute off the
environment portfolio. Sell quantities must equal the environment's current
holding quantity when exiting a position.
- Buying a watchlisted issuer. Always join bonds → issuers and check
watchlist. High-carry HY names (yield 9–11%) are often traps: a watchlisted
refiner or E&P issuer with a HIGH_CARRY/WATCHLIST_RISK tag should be excluded
even if it maximizes YTM.
- Duration-ineligible distractors. Long-dated IG names (dur 5.8–6.7) and
very short HY names (dur 2.3) sit outside the [3.0, 5.0] band. Do not pick
them as buys; flag the band as the reason.
- Misreading
energy_linked. It is a bond-level boolean, not the issuer's
sector label. Filter buys for the task's sector requirement using this flag.
- Forgetting the HY cap after adds. A high-YTM HY buy can push post-trade HY
% over 20%; recompute and choose a smaller notional or an IG alternative.
- Computing correlations wrong. Use simple monthly returns
(level[t]/level[t-1]−1), not log returns or level changes; use Pearson
(cov/σσ); the number of return observations is one fewer than the number of
levels. Round to 3 decimals; sort pair ids alphabetically.
- Inventing allocation views. Views/conviction/rationale are computed from
macro-signals + the allocation_mapping thresholds + prior-views — not
judgment. A score of 0.732 is OW/HIGH/INDIA_OFFSET, not a subjective call.
- Wrong
change. change compares the new view rank to the prior standing
view (from prior-views for the target quarter), not to a guessed baseline.
OW(1)→UW(−1) is DOWN; N(0)→OW(1) is UP; OW→OW is UNCHANGED.
- Wrong as_of_date. Use the environment's current as_of_date, not the
request_date / memo_as_of_date / snapshot_date in the local payload.
- Wrong trade ordering. Income-add packages sort by instrument_id ascending;
rotation packages put SELL before BUY then instrument_id ascending within each
action.
- Including rationale_code not in the enum. Only use rationale codes that
appear in the template's
allowed_values and are returned by the macro-signals
endpoint verbatim.
- Over- or under-rounding. Match each field's declared precision exactly.
8. Final output checklist
Before emitting the JSON:
1---2name: fewshot-attempt-01-113description: Asteria Investment Office — Portfolio-Risk Evaluation Skill4---5# Asteria Investment Office — Portfolio-Risk Evaluation Skill67This skill distills reusable execution knowledge for solving Asteria Investment8Office portfolio-risk tasks. The test task gives you a prompt, an9`input/payloads/answer_template.json`, and one or more request payloads. You10also have the remote Asteria environment (the current book of record) and this11skill. Use the environment as the source of truth and follow the SOPs below.1213The tasks draw on three workflows that may also be combined:1415- **Workflow A — Energy / fixed-income credit trade strategy.** Build a BUY/SELL16 bond package under credit-risk constraints, then report post-trade metrics,17 constraint booleans, and sales positioning.18- **Workflow B — International equity correlation review.** Compute pair19 correlations from monthly index levels, flag concentration, and propose sleeve20 actions.21- **Workflow C — Cross-asset active allocation view refresh.** Translate macro22 signal scores into active views (UW/N/OW) with conviction, change vs prior23 quarter, rationale, plus risk overlay and committee fields.2425---2627## 0. Golden rules (read first)28291. **Environment is the current book of record.** The remote service at30 `<remote-env-url>` is authoritative for current holdings, holding31 quantities, issuer status, market signals, index levels, policies, prior32 views, and macro signals. Local `input/payloads/*.json` files are **intake33 context** that may be stale (old worksheet dates, stale marks, stale34 quantities, old shortlists). When a local payload conflicts with the35 environment, **prefer the environment** unless the task prompt explicitly36 overrides. Set `data_precedence` accordingly37 (`current_environment_over_stale_payload` when a conflict exists;38 `no_conflict_found` only when nothing stale is present). Watch for explicit39 "stale" markers in payloads (`snapshot_date`, `stale_*`, `memo_as_of_date`,40 `stale_local_note`, "Operations has not reconciled..." comments) — these41 signal you must refresh from the environment.422. **Always read `answer_template.json` first.** It defines required keys,43 allowed enum values, field precision, ordering rules, and list lengths.44 Conform exactly: a field declared `precision: 1` must be rounded to 145 decimal; a list declared `length: 2` must have exactly 2 items; an enum must46 use one of the allowed values verbatim.473. **`as_of_date` is the environment's current date**, NOT the payload's request48 date or memo date. Read it from `GET /api/policies` (top-level `as_of_date`)49 or any `GET /api/portfolios/<id>` (`as_of_date`). All train examples use the50 environment as_of_date (e.g. `2026-05-29`), not the older memo/snapshot dates.514. **Correlations are never precomputed.** Compute them yourself from52 `GET /api/index-levels` (monthly simple returns, see Workflow B).535. **Be precise about ordering.** Lists are sorted as the template declares:54 trade packages by `instrument_id` ascending (or SELL-before-BUY then55 instrument_id), index sets/pair_ids alphabetically, allocation rows in the56 request payload's focus order, sleeve_actions by sleeve name ascending.5758---5960## 1. Environment endpoints (GET only)6162Base URL: `<remote-env-url>`. Call with `curl` or Python `urllib`.6364| Endpoint | Use |65|---|---|66| `GET /api/catalog` | All available portfolio ids, policy ids, index ids, issuer ids, bond ids, opportunity sets. |67| `GET /api/policies` | **Constraints + thresholds.** Returns top-level `policy_id` (e.g. `POLICY_SET_2026_05`), `as_of_date`, and sub-policies `credit_default`, `credit_risk_reduction`, `correlation`, `allocation_mapping`, `multi_asset`, `multi_asset_risk`. |68| `GET /api/portfolios` | Portfolio summaries. |69| `GET /api/portfolios/<id>` | One portfolio: objective, `constraints`, current `holdings` (each with `instrument_id`, `quantity_usd_m`, sleeve, notes), `market_value_usd_m`, `as_of_date`. |70| `GET /api/instruments/bonds` | Full bond universe. Filter with `?candidate=true`, `?rating_bucket=HY`, etc. Each bond has `instrument_id`, `issuer_id`, `sector`, `subsector`, `rating`, `rating_bucket` (IG/HY), `modified_duration_years`, `yield_to_maturity_pct`, `coupon_pct`, `maturity`, `energy_linked` (bool), `candidate` (bool), `recommended_theme_tags`. |71| `GET /api/issuers` | Issuer master: `issuer_id`, `sector`, `subsector`, `rating_bucket`, **`watchlist` (bool)**, `outlook`, `issuer_name`. |72| `GET /api/market/energy` | Energy market signals + `pitch_themes`. Used for sales positioning in energy trade tasks. |73| `GET /api/indices` | Index metadata (`level_start_date`, `level_end_date`, region, currency). |74| `GET /api/index-levels` | Object keyed by `index_id`, each a list of `{date, level}` monthly observations. |75| `GET /api/index-levels/<id>` | Monthly levels for one index. |76| `GET /api/allocation/opportunity-sets` | Opportunity-set taxonomy: `opportunity_set`, `asset_class` (Equities/Duration/Credit/Currency), `display_order`. |77| `GET /api/allocation/prior-views` | List of prior active views. Each entry has `opportunity_set`, `quarter` (the effective quarter), `previous_quarter` (when set), `view`, `conviction`. Two cohorts exist: a Q_{n-1}→Q_n cohort (the prior standing view) and a Q_n→Q_{n+1} cohort. |78| `GET /api/macro-signals` | List of macro signal scores. Each has `opportunity_set`, `quarter`, `score` (signed float), `rationale_code`, `drivers`. |7980---8182## 2. Cross-cutting conventions8384### Precision (follow `answer_template.json` declarations)85- `notional_usd_m` / `quantity_usd_m` → **1 decimal**.86- `post_trade_metrics` numeric fields (`*_usd_m` with precision 2, `hy_allocation_pct`, `weighted_modified_duration_years`, `weighted_yield_to_maturity_pct`) → **2 decimals**.87- `correlation` → **3 decimals**.88- `signal_score` → **3 decimals**.89- `total_market_value_usd_m` → **2 decimals** (per template).90- Booleans are `true`/`false`. Round half-up to the declared precision; JSON drops91 trailing zeros naturally (e.g. `5.80` renders as `5.8`).9293### Policy snapshot (`GET /api/policies`)94Key fields the SOPs reference (concrete values observed; re-read at test time in95case the environment changed):96- `policy_id` (top-level): the policy-set id used as the allocation `policy_id`97 field (e.g. `POLICY_SET_2026_05`).98- `credit_default` / `credit_risk_reduction`: `duration_band_years: [3.0, 5.0]`,99 `max_hy_allocation_pct: 20.0`, `issuer_concentration_limit_pct: 12.0`,100 `subsector_min_count_for_diversified: 2`. `credit_risk_reduction` adds101 `target_hy_reduction_pct: 4.0` (the minimum HY reduction a rebalance must102 achieve). Portfolios opt into one of these via `constraints.policy_id`.103- `correlation`: `correlation_high_threshold: 0.8`, `correlation_low_threshold:104 0.2`, `review_window_start`, `review_window_end`.105- `allocation_mapping`: `view_score_thresholds` (`OW_min: 0.35`, `UW_max: -0.35`,106 `neutral_between: [-0.35, 0.35]`), `conviction_thresholds` (`HIGH_abs_min: 0.7`,107 `MEDIUM_abs_min: 0.35`, `LOW_abs_below: 0.35`), `view_rank` (`UW=-1, N=0, OW=1`).108109### Which policy a portfolio uses110Read `GET /api/portfolios/<id>` → `constraints.policy_id`. A pure credit sleeve111uses `POL_CREDIT_DEFAULT`; a risk-reduction rotation uses112`POL_CREDIT_RISK_REDUCTION` (which adds the `target_hy_reduction_pct` floor).113Multi-asset sleeves compose: `POL_MULTI_ASSET_DEFAULT` references credit +114correlation + allocation; `POL_MULTI_ASSET_RISK` references correlation +115credit-risk-reduction.116117---118119## 3. Workflow A — Energy / fixed-income credit trade strategy120121Applies to: standalone energy sleeve trades and fixed-income risk-reduction122rotations. Two sub-shapes exist:123124- **Income-add shape (two BUY tickets):** `trade_package` (BUY list),125 `post_trade_metrics`, `constraint_checks`, `sales_positioning`,126 `data_precedence`.127- **Rotation shape (SELL+BUY):** `rotation.trades` (SELL before BUY),128 `risk_metrics`, `exception_flags`, `watchlist_handling`, `risk_note_code`.129130### Step-by-step SOP1311321. **Read the portfolio** `GET /api/portfolios/<portfolio_id>`. Capture current133 holdings (`instrument_id`, `quantity_usd_m`), `market_value_usd_m`,134 `as_of_date`, and `constraints` (duration band, HY cap, target HY reduction).135 **Use these environment quantities, not any stale quantities in the local136 memo.** (Example pattern: a local memo may show a holding at 10.0 while the137 environment shows 12.0 — trade off the 12.0.)1382. **Read all bonds** `GET /api/instruments/bonds` (and `?candidate=true` for the139 buy universe). **Read all issuers** `GET /api/issuers` to get the140 `watchlist` flag per issuer (join on `issuer_id`).1413. **Apply exclusion filters to candidate buys:**142 - **Watchlist avoidance:** drop any bond whose issuer `watchlist == true`.143 This is a hard exclusion (the desk is "sensitive to headline carry from144 watchlisted issuers"). Candidates flagged with theme tags like145 `WATCHLIST_RISK` / `REFINANCING_RISK` or refining issuers (negative146 refining signal) are exclude-by-default.147 - **Duration-band eligibility:** keep only bonds whose148 `modified_duration_years` is within the policy band **[3.0, 5.0]**149 (inclusive). Long-dated distractors (dur 5.8, 6.7, 5.9) and very short150 ones (dur 2.3) are excluded. For an income-add where the *package* must151 keep the *portfolio* weighted duration in band, individual buy durations152 should sit inside the band.153 - **HY capacity:** HY buys are allowed only if post-trade HY % stays under154 the cap (20%). Do not blind-buy the highest-YTM HY name if it breaches the155 cap or concentrates a watchlisted issuer.156 - **Sector/energy linkage:** for an *energy* sleeve task, buy bonds with157 `energy_linked == true`. (`energy_linked` is a bond-level field, not the158 issuer `sector`; e.g. a utilities-issuer power bond can be energy-linked.)159 - **Issuer concentration:** no single issuer should exceed160 `issuer_concentration_limit_pct` (12%) of post-trade market value.1614. **Select the package** honoring the desk's stated preferences (e.g. LNG162 exporters / gas demand / non-watchlist carry) and the "improve carry" goal163 (higher YTM helps). A robust pattern for a two-ticket income add:164 - Pick one IG bond that matches the preferred theme (LNG/gas) for ballast +165 client pitch suitability.166 - Pick one higher-carry non-watchlist energy-linked bond (often HY) that167 lifts portfolio YTM while keeping HY % well under cap and adding issuer +168 subsector diversification.169 - Verify post-trade weighted duration stays inside the band and that new170 issuers/subsectors increase diversification.171 For a **rotation (risk-reduction)** task:172 - Sell watchlist holdings first (entire positions); the watchlist sells are173 reported in `watchlist_sell_ids`.174 - Sell additional HY as needed to bring post-trade HY % under cap and meet175 `target_hy_reduction_pct`. Prefer keeping higher-carry non-watchlist HY and176 trimming lower-carry HY when both are eligible, so carry is preserved.177 - Fund duration ballast by buying IG candidates from the shortlist that are178 non-watchlist and inside the duration band; weight notionals toward longer179 duration to replace duration lost when selling short-duration HY.180 - Keep the package roughly self-funding (total sell notional ≈ total buy181 notional) so total market value is preserved.1825. **Order the trades** as the template declares. Income-add: ascending by183 `instrument_id`. Rotation: **SELL before BUY**, then `instrument_id`184 ascending within each action. Notional field name follows the template185 (`notional_usd_m` for income-add; `quantity_usd_m` for rotation).186187### Post-trade metrics computation (mechanical, market-value-weighted)188189Build the post-trade book = current holdings adjusted by trades (SELL subtracts,190BUY adds; use environment quantities). Then:191192- `total_market_value_usd_m` = sum of post-trade `quantity_usd_m`.193- `hy_allocation_pct` = (sum of HY `quantity_usd_m`) / total_market_value × 100,194 rounded to 2 decimals. (HY defined by bond `rating_bucket == "HY"`.)195- `weighted_modified_duration_years` = Σ(quantity · modified_duration_years) /196 total_market_value, rounded to 2.197- `weighted_yield_to_maturity_pct` = Σ(quantity · yield_to_maturity_pct) /198 total_market_value, rounded to 2.199200For rotation outputs:201- `post_trade_hy_allocation_pct` = post-trade HY % (2 decimals).202- `post_trade_duration_years` = post-trade weighted modified duration (2 dp).203- `hy_reduction_pct_points` = pre_trade_HY% − post_trade_HY% (2 dp, can be204 large when a watchlist HY block is removed).205- `post_trade_watchlist_exposure_usd_m` = sum of post-trade quantities whose206 issuer `watchlist == true` (1 decimal; should be 0.0 if all watchlist names207 were sold).208- Pre-trade HY % is computed the same way on the un-adjusted holdings.209210**Verify every metric by recomputing; do not trust stale worksheet snapshots.**211212### Constraint-check derivation (booleans)213214- `hy_cap_pass` = post-trade HY % ≤ `max_hy_allocation_pct` (20.0).215- `duration_band_pass` = post-trade weighted duration within216 `duration_band_years` (inclusive [3.0, 5.0]).217- `issuer_diversification_pass` / `selected_issuer_diversification_pass` = no218 single issuer exceeds `issuer_concentration_limit_pct` (12%) of post-trade219 MV (and, for "selected" variants, the newly selected issuers are distinct from220 each other / add diversification).221- `subsector_diversification_pass` / `selected_subsector_diversification_pass` =222 the holding set (or selected buys) spans at least223 `subsector_min_count_for_diversified` (2) subsectors.224- `watchlist_avoidance_pass` = no bought bond has a watchlisted issuer (and for225 rotation, `watchlist_exposure_cleared` = post-trade watchlist exposure == 0).226- `target_hy_reduction_met` (rotation only) = `hy_reduction_pct_points` ≥227 `target_hy_reduction_pct` (4.0 for credit_risk_reduction policy).228229### Sales positioning (energy income-add)230231Read `GET /api/market/energy`. Pick the strongest positive signal that matches232your selected bonds' theme tags:233- LNG export pull (highest positive score) → `theme: lng_export_tailwind`.234- Midstream defensive carry → `midstream_stability`.235- Oil discipline / oversupply caution → `oil_oversupply_caution`.236- Watchlist yield avoidance → `avoid_watchlist_yield_trap`.237- Selective transition/renewables → `transition_bond_selectivity`.238239`target_segment` follows the client context in the desk request (e.g.240"multi-asset income update" → `multi_asset_income`; insurance / pension /241private-bank / endowment contexts map to their respective enum). When242refining/refiner signals are negative and watchlisted, do NOT pick a refining243theme; the refining signal being negative + AVOID_REFINING_WATCHLIST pitch theme244reinforces watchlist avoidance.245246### `risk_note_code` (rotation)247248Pick the dominant driver of the rotation:249`watchlist_concentration` (when the main issue was a watchlisted holding),250`hy_cap_pressure` (when HY cap drove sells), `duration_preservation` (when the251rotation's point was keeping duration in band), `carry_tradeoff` (when selling252carry to reduce risk), `no_action` (when nothing needed fixing). If watchlist253exposure was the trigger, `watchlist_concentration` is correct.254255---256257## 4. Workflow B — International equity correlation review258259No correlations are precomputed. Compute from `GET /api/index-levels`.260261### Correlation method (confirmed deterministic)2622631. Take the review window from the request payload264 (`review_window.level_start_date` / `level_end_date`) — these match the265 policy `correlation.review_window_start/end` for the default window. For each266 index in the requested `index_universe`, extract the monthly levels in date267 order across the window.2682. Compute **monthly simple returns**: `r[t] = level[t] / level[t-1] − 1` for269 consecutive observations. A window with 12 month-end levels yields **11270 return observations** (`review_window.return_observations` = 11).2713. For each unordered pair of indices, compute the **Pearson correlation** of272 their return series:273 `corr = cov(r_x, r_y) / (σ_x · σ_y)` (population, dividing by n).2744. `extreme_pairs.highest_positive` = the pair with max correlation;275 `extreme_pairs.lowest` = the pair with min correlation (most negative).276 `pair_id` lists the two index ids **sorted alphabetically**. Round correlation277 to **3 decimals**.2785. `index_set` = the requested universe, sorted ascending alphabetically.279280### Concentration flags281282Using the policy thresholds `correlation_high_threshold (0.8)` and283`correlation_low_threshold (0.2)`:284- `high_threshold_breached` = **any** pair correlation ≥ 0.8.285- `china_asia_dependence_flag` = true when `IDX_CHINA` correlation with the286 Asia/EM/developed block (IDX_EM, IDX_AC_ASIA_PAC_EX_JP, IDX_ACWI_IMI,287 IDX_WORLD, IDX_EAFE, IDX_EM_EX_CHINA, IDX_INDIA) is ≥ 0.8 — i.e. China is288 tightly coupled to the regional/global complex.289- `primary_code`:290 - `CHINA_ASIA_DEPENDENCE` when China↔Asia/EM pairs drive the concentration291 (CIO memos flag ASIA_BETA_OVERLAP / CHINA_DEDICATED_SLEEVE).292 - `GLOBAL_DEVELOPED_OVERLAP` when the dominant high-correlation cluster is293 developed/global (IDX_WORLD, IDX_EAFE, IDX_ACWI_IMI) rather than China.294 - `NO_MATERIAL_CONCENTRATION` when no pair breaches the high threshold.295296### Diversification candidates297298From the template's allowed candidate set, select indices that **diversify the299concentration center** (typically China). Rank allowed candidates by their300correlation with the concentration center ascending (most negative / lowest301first). Include those that meaningfully diversify:302- Indices with **negative** correlation to the center (e.g. IDX_LATAM, which is303 negatively correlated to everything) always qualify.304- Indices that structurally remove the concentration (e.g. IDX_EM_EX_CHINA)305 qualify as the "ex-China" diversifier.306- **Exclude** an allowed candidate whose correlation with the concentration307 center breaches the high threshold in a way that adds to (not relieves) the308 concentration (e.g. IDX_INDIA when it is ≥0.8 with China does not diversify309 China risk). Sort the resulting list ascending alphabetically.310311### Sleeve actions312313Produce the number of sleeve-action objects the template requires, ordered314**ascending by sleeve name**. Map from the review's conclusions:315- `trim` the concentrated sleeve (the source of concentration, e.g. the China316 dedicated sleeve).317- `add` the best diversifier sleeve (e.g. Latin America).318- `hold` / `monitor` / `hedge` / `rotate` for other sleeves as appropriate.319`target_index_id` and `action` must use the template's allowed enums.320321---322323## 5. Workflow C — Cross-asset active allocation view refresh324325This is **largely deterministic** — derive views mechanically from macro signal326scores and the `allocation_mapping` policy. Do not editorialize.327328### Per opportunity set (one allocation_views row)3293301. **prior_view** = the view from `GET /api/allocation/prior-views` for the entry331 where `quarter == target_quarter` AND `previous_quarter == prior_quarter`.332 (This is the standing view set last quarter for the current quarter.)3332. **signal_score** = `GET /api/macro-signals` score for334 `(opportunity_set, quarter == target_quarter)` (round to 3 decimals if the335 template exposes it).3363. **rationale_code** = that macro-signal entry's `rationale_code` (verbatim337 from the environment — e.g. CHINA_DEPENDENCE, INDIA_OFFSET,338 LATAM_DIVERSIFIER, DURATION_SUPPORT, HY_VALUATION_RISK, EUROPE_RECOVERY,339 JAPAN_POLICY_RISK, DOLLAR_DEFENSIVE, NEUTRAL_BALANCE, RATE_CUT_SUPPORT,340 CREDIT_SPREAD_RISK, GROWTH_IMPROVES).3414. **view** from `signal_score` via `allocation_mapping.view_score_thresholds`:342 - score ≥ `OW_min` (0.35) → `OW`343 - score ≤ `UW_max` (−0.35) → `UW`344 - otherwise → `N`3455. **conviction** from `|signal_score|` via `conviction_thresholds`:346 - |score| ≥ `HIGH_abs_min` (0.7) → `HIGH`347 - `MEDIUM_abs_min` (0.35) ≤ |score| < 0.7 → `MEDIUM`348 - |score| < `LOW_abs_below` (0.35) → `LOW`3496. **change** from `view_rank` (UW=−1, N=0, OW=1): compare `rank(new_view)` vs350 `rank(prior_view)`:351 - new > prior → `UP`352 - new < prior → `DOWN`353 - equal → `UNCHANGED`3547. **asset_class** = the `asset_class` from355 `GET /api/allocation/opportunity-sets` for that opportunity set (Equities /356 Duration / Credit / Currency).3578. **Row order** = the request payload's `focus_opportunity_sets` order (not358 alphabetical). `required_length` matches the focus list size.3599. **policy_id** = the top-level `policy_id` from `GET /api/policies`360 (e.g. `POLICY_SET_2026_05`). `target_quarter` / `prior_quarter` = the request361 values. `as_of_date` = environment as_of_date.362363### Risk overlay (judgment, but patterned)364365Choose `overlay_code` / `primary_action` from the macro picture formed by the366rows (paired enums: DURATION_QUALITY_TILT↔tilt_to_duration_quality,367CREDIT_RISK_REDUCTION↔trim_credit_beta, EQUITY_BETA_EXTENSION↔add_cyclical_equity_beta,368CURRENCY_DEFENSIVE_HEDGE↔add_currency_hedge, NO_OVERLAY↔hold_policy_weights):369- If duration/quality is supported (U.S. Treasuries OW, DURATION_SUPPORT) **and**370 HY is a risk (Corporate HY UW, HY_VALUATION_RISK) →371 `DURATION_QUALITY_TILT` / `tilt_to_duration_quality`.372- If HY/credit spread risk dominates → `CREDIT_RISK_REDUCTION` /373 `trim_credit_beta`.374- If defensive currency is the theme → `CURRENCY_DEFENSIVE_HEDGE` /375 `add_currency_hedge`.376- If cyclical growth recovery dominates with no risk offset →377 `EQUITY_BETA_EXTENSION` / `add_cyclical_equity_beta`.378- If views net to roughly neutral with no material risk → `NO_OVERLAY` /379 `hold_policy_weights`.380381`rationale_codes` = the rationale codes driving the overlay choice, ordered by382**business priority** (highest priority first). Favor the risk-reduction and383duration-support rationales first (e.g. DURATION_SUPPORT, HY_VALUATION_RISK,384CHINA_DEPENDENCE), then growth/diversifier codes. Keep the list to the 2–4 most385material drivers.386387---388389## 6. Combined workflow — correlation + allocation (committee JSON)390391Some tasks (multi-asset committee files) combine B and C: a small index392sub-universe correlation summary plus allocation views for the matching sleeves.393394- `correlation_summary` (length 2, ordered `[highest_concentration,395 best_diversifier]`): compute Pearson over the requested index subset;396 `highest_concentration` = the **max**-correlation pair; `best_diversifier` =397 the **min** (most negative) pair. Each `pair` sorted alphabetically;398 correlation rounded to 3 decimals.399- `target_sleeve_actions`: one row per requested opportunity set, in the400 template's `item_order`. Map view → action: OW → `add`, UW → `trim`. For a401 currency sleeve (USD) that is downgraded from OW toward neutral, use `hedge`402 (reduce the defensive long). Stable OW → `add`/`hold`; stable N → `hold`/403 `monitor`.404- `allocation_views`: same deterministic computation as Workflow C (prior_view,405 signal_score, view, change, conviction, rationale_code), in the template's406 `item_order`.407- `rebalance_trigger`:408 - `correlation_cap_breach` when a high-threshold pair breach drove the review409 (most common when the correlation summary shows a pair ≥ 0.8).410 - `hy_cap_pressure` / `duration_drift` / `watchlist_concentration` for those411 drivers; `committee_review` as the generic catch-all.412- `portfolio_risk_concentration_flag`: true when the correlation summary or413 constraint picture shows material concentration (a pair ≥ high threshold, or a414 China/Asia dependence flag).415- `next_step`:416 - `approve_with_monitoring` when actions reduce concentration but residual417 monitoring is warranted (common when the package trims the concentrated418 sleeve and adds diversifiers without a hard breach remaining).419 - `approve_rotation` when the proposed rotation fully resolves the issue.420 - `defer_pending_risk_review` when material exceptions remain unresolved.421 - `reject_constraint_breach` when a constraint breach cannot be fixed.422423---424425## 7. Common misjudgments to avoid426427- **Trusting stale holding quantities / marks.** The local memo is intake428 context. A "stale_exception_board" with `stale_quantity_usd_m` or a429 "stale_holding_snapshot" is explicitly unreliable — recompute off the430 environment portfolio. Sell quantities must equal the environment's current431 holding quantity when exiting a position.432- **Buying a watchlisted issuer.** Always join bonds → issuers and check433 `watchlist`. High-carry HY names (yield 9–11%) are often traps: a watchlisted434 refiner or E&P issuer with a HIGH_CARRY/WATCHLIST_RISK tag should be excluded435 even if it maximizes YTM.436- **Duration-ineligible distractors.** Long-dated IG names (dur 5.8–6.7) and437 very short HY names (dur 2.3) sit outside the [3.0, 5.0] band. Do not pick438 them as buys; flag the band as the reason.439- **Misreading `energy_linked`.** It is a bond-level boolean, not the issuer's440 sector label. Filter buys for the task's sector requirement using this flag.441- **Forgetting the HY cap after adds.** A high-YTM HY buy can push post-trade HY442 % over 20%; recompute and choose a smaller notional or an IG alternative.443- **Computing correlations wrong.** Use **simple monthly returns**444 (level[t]/level[t-1]−1), not log returns or level changes; use **Pearson**445 (cov/σσ); the number of return observations is one fewer than the number of446 levels. Round to 3 decimals; sort pair ids alphabetically.447- **Inventing allocation views.** Views/conviction/rationale are *computed* from448 macro-signals + the allocation_mapping thresholds + prior-views — not449 judgment. A score of 0.732 is OW/HIGH/INDIA_OFFSET, not a subjective call.450- **Wrong `change`.** `change` compares the new view rank to the **prior standing451 view** (from prior-views for the target quarter), not to a guessed baseline.452 OW(1)→UW(−1) is DOWN; N(0)→OW(1) is UP; OW→OW is UNCHANGED.453- **Wrong as_of_date.** Use the environment's current as_of_date, not the454 request_date / memo_as_of_date / snapshot_date in the local payload.455- **Wrong trade ordering.** Income-add packages sort by instrument_id ascending;456 rotation packages put SELL before BUY then instrument_id ascending within each457 action.458- **Including rationale_code not in the enum.** Only use rationale codes that459 appear in the template's `allowed_values` and are returned by the macro-signals460 endpoint verbatim.461- **Over- or under-rounding.** Match each field's declared precision exactly.462463---464465## 8. Final output checklist466467Before emitting the JSON:468469- [ ] Every required top-level key present; no extra narrative outside the JSON470 (unless the prompt allows it — most say "Return only JSON").471- [ ] `as_of_date` = environment as_of_date; `portfolio_id` / `task_id` /472 `target_quarter` / `prior_quarter` / `review_quarter` match the template's473 `required_value`.474- [ ] All enums use allowed values verbatim; all booleans are real booleans.475- [ ] Every numeric field rounded to its declared precision.476- [ ] Lists sorted and sized per template (`ordering`, `length`,477 `required_length`, `item_order`).478- [ ] `trade_package` / `rotation.trades` ordering correct (instrument_id asc;479 SELL-before-BUY where applicable).480- [ ] Post-trade metrics recomputed from environment holdings + trades (not the481 stale snapshot); constraint booleans derived from recomputed metrics vs482 policy thresholds.483- [ ] Watchlist joins performed; no watchlisted issuer bought; watchlist sells484 reported correctly.485- [ ] Correlations computed from monthly simple returns (Pearson, 3 decimals).486- [ ] Allocation views computed from macro-signals + allocation_mapping policy +487 prior-views; `change` vs the matching prior-views cohort.488- [ ] `data_precedence` reflects whether a local payload was stale vs the489 environment.490- [ ] Validate the final object against `answer_template.json` field-by-field.