ADR-021: Currency-Aware Path-Profile (Date-Aligned FX Conversion)
Date: 2026-06-25 Status: accepted Continuation: ADR-020 (Concentration Default Off) has no direct relationship; works in conjunction with the path-profile system (core/regime_probability.py) + historical backfill (scripts/backfill_history.py).
Context
The committee's path-profile (forward path distribution after selling/holding, fed to CIO for TRIM buyback points + downside metrics) has always been calculated in the asset's quote currency: GC=F is quoted in USD/oz, so regime classification, forward return distribution, and downside percentiles are all for USD Gold. CNY conversion is only done at the display layer (Zheshang Accumulated Gold ¥/g).
However, the user actually holds Zheshang Accumulated Gold priced in ¥/g — the risk they bear is XAU/CNY (= XAU/USD × USDCNY). FX changes the picture: during gold price declines, the Renminbi often strengthens, amplifying losses for CNY holders. Measured (downtrend):
| 90d | p_below | 20th Percentile Downside |
|---|---|---|
| USD Denomination (Original Committee) | 0.37 | −5.1% |
| CNY Denomination (Actual User) | 0.40 | −5.9% |
→ The USD denomination systematically underestimates the downside for CNY holders.
Why can't we simply "build an XAU/CNY series and run it": The Renminbi only truly floated in 2005 (USDCNY data from 2001, pegged at 8.28 before 2005), so the continuous XAU/CNY market history is only ~20 years → downtrend 90d independent samples are only ~5 (effective_n=5), which is both insufficient and biased (those few samples all come from the CNY bull market). The direct series method cannot solve this.
Decision: Date-Aligned FX Conversion to Synthesize Holding Currency Distribution
Instead of waiting for the scarce "merged history", take one sample-rich base currency leg and convert it daily using the actual exchange rate at that time:
- Base Currency Gold Leg: XAU/USD downtrend forward distribution — 57 years after backfill (ADR-020 / backfill_history), 90d
effective_n=16. Thick enough, forms the main distribution. - FX Leg: USDCNY forward returns — available in MarketStore from 2001+. No separate sampling, aligned by date to the base currency leg.
Synthesis: For each regime day d, take the actual FX forward return occurring within the same calendar window, point-by-point: r_cny(d) = (1 + r_usd(d)) × (1 + r_fx(d)) − 1, stacking into the CNY denomination distribution.
Why Date-Aligned Instead of "Pairwise Random Combination" (MC Convolution)
An early implementation used Monte Carlo to independently pair samples from two bags (200k random draws of r_usd × random draws of r_fx). Code review identified three flaws:
- Inconsistent Leg Spans: The FX leg used
shift(-d)(trading days), while the base currency leg used calendar day forwards — in a 90d window, FX actually spanned ~126 calendar days, systematically deepening the rolled downside. - Ignored Point-in-Time: The FX leg read the full history, inconsistent with the
asofcutoff of the base currency leg → walk-forward would look ahead. - Independent Pairing Averaged Out Co-movement: The fact that gold declines often coincide with Renminbi strengthening (amplifying losses for CNY holders) is the very motivation for this ADR. Independent random pairing precisely averages out this correlation — underestimating the very correlated drawdown it aims to warn about.
Switching to date-aligned (inner join of base currency leg and FX leg by date, pointwise multiplication) fixes all three at once: same searchsorted calendar day → consistent spans; both legs filtered by the same asof → zero lookahead; uses actual same-window co-movement instead of random pairing → preserves the real tail of "gold down × CNY strong". No randomness → naturally deterministic, no seed needed.
Sample count = regime days of base currency leg ∩ days with FX forward returns ≈ base currency leg samples (FX covers the entire period). The bottleneck remains the base currency leg, thickened by the 57-year backfill — not relying on a "16×68 Cartesian product" to inflate independent samples (which is a product of the independence assumption that doesn't hold).
Implementation (Opt-In, Zero Change to Default USD Behavior)
get_path_profile(asset, regime, …, convert_ccy=None): DefaultNone→ completely unchanged. Whenconvert_ccy="CNY"is set, the main windows remain in the quote currency (text/prices), while the date-aligned converted holding currency distribution is attached tocurrency_overlay(calculated within the same frame, single data load — no longer requiring a secondaryget_path_profileread like the early version). Both conditional and unconditional distributions are converted, socalibrate_profileshrinkage stays within the same currency.asofis passed down to the FX leg, ensuring zero lookahead.effective_nstill takes the value from the base currency (USD) leg (the bottleneck leg); after conversion,fx_aligned_n(intersection sample count) is stored separately as metadata.quote_currency_iso(asset)covers major exchange suffixes (.AX/.HK/.SS/.SZ/.L/.T/.TO/.PA… + crypto -USD/-EUR); defaults to USD for unknown suffixes, butcurrency_overlayis only attached when the FX series actually exists — missing labels won't fabricate a fake conversion.convert_ccy_for(asset, holding_cost_ccy): Returns the currency to convert to when the holding denomination currency ≠ the asset's quote currency (quote_currency_iso), otherwise None. Example: GC=F (quoted in USD) + Zheshang Accumulated Gold (cost_currency CNY) → "CNY"; 510300.SS (quoted in CNY) + CNY holding → None.- Wiring:
core/runner/session.py(Direct/Web/Cron) andcore/runner/coordinator.py(Coordinator) both automatically passconvert_ccybased on the holdingcost_currency(if holding query fails → falls back to base currency, does not affect the main reentry flow). - Output: Structured
path_profilewithcurrency_overlayattached (holding currency distribution); a line with ⚠ holding currency denomination downside (compared to the quote currency denomination, e.g., USD/AUD/HKD — not hardcoded to USD) is appended at the end of the reentry text for the CIO.
v1 Approximations (Explicitly Documented, Refinable Later)
- The FX leg's forward returns themselves are not secondarily filtered by gold-regime: Date-alignment already implicitly selects "the actual FX co-movement on those specific regime days", but the FX series itself is not separately conditioned on regime — CNY is a managed float with much lower volatility than gold, and secondary conditioning would thin the sample too much. Left as a refinement. (The early version's flaw of "independent MC pairing averaging out co-movement" is already fixed by date-alignment, so this is no longer an approximation item.)
- The path "shape" (when it bottoms/retraces) still follows the base currency, only the terminal risk numbers are converted — the shape is dominated by gold, FX is just a terminal minor correction.
- Prices remain in the asset's quote currency (USD/oz), consistent with the rest of the committee's briefs (Quant's MA, Risk, etc.), avoiding mixed currencies within a single brief; holding currency information is provided as "percentage metrics + ⚠ warning".
Consequences
- The committee for CNY-denominated holdings (Zheshang Accumulated Gold) now sees the real currency downside (deeper), no longer underestimated by the USD denomination.
- Samples are thickened by the 57-year base currency leg (90d eff_n=16), date-alignment preserves the real gold-FX co-movement, bypassing the "long XAU/CNY history doesn't exist" problem.
- Fully opt-in: Assets without holding currency mismatch (e.g., 510300.SS quoted in CNY + CNY holding, pure USD assets) have
convert_ccy=None, zero change to USD/base currency behavior. - Dependency: The thickness of the base currency leg relies on the long historical backfill from ADR-020/backfill_history (57 years of USD Gold); if the base currency leg is still thin, lever-1 shrinkage (
calibrate_profile, disabled by default pending fit) can be applied.