analyze_stress
Stress test investment portfolios across 10 macro scenarios including market crashes, rate hikes, and geopolitical shocks to identify P&L impacts and worst/best performing assets.
Instructions
Stress test across 10 macro scenarios using real portfolio volatility data: market_crash (-40%), crypto_winter (-70%), stablecoin_depeg, high_volatility, recovery_bull (+50%), rate_hike_shock, recession, stagflation, geopolitical_shock, dollar_rally. All shocks are asset-class and sector-aware. Returns P&L per scenario with worst/best asset. Payment: $0.03 USDC on Tempo chain.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| holdings | Yes | ||
| profile | No | Risk profile — affects rebalance targets and scoring. Default: balanced. | |
| benchmarkReturn | No | Annual benchmark return for Sharpe calculation, e.g. 0.08 = 8%. Default: 0.08. | |
| riskFreeRate | No | Annual risk-free rate for Sortino and VaR excess return, e.g. 0.05 = 5%. Default: 0.05. | |
| rebalanceMethod | No | Portfolio construction method for rebalance recommendations. Default: profile. | |
| marketIndicators | No | Optional macro indicators — improves market regime detection confidence to HIGH when 3+ provided. |
Implementation Reference
- src/analyze/stress-test.ts:267-300 (handler)The `analyzeStressTest` function calculates the impact of various market scenarios on a given portfolio, considering holdings, betas, and asset classes.
export function analyzeStressTest( portfolio: Portfolio, returnSeries?: Record<string, number[]>, ): StressTestResult { // Pre-compute effective beta for each holding (real OLS or class default) const holdingBetas = portfolio.holdings.map((h) => resolveHoldingBeta(h, returnSeries)) const scenarios: ScenarioResult[] = SCENARIOS.map((spec) => { const assetChanges = portfolio.holdings.map((h, idx) => ({ asset: h.asset, change: spec.calcAssetChange(h, holdingBetas[idx]), })) const portfolioChange = portfolio.holdings.reduce( (acc, h, i) => acc + h.weight * assetChanges[i].change, 0, ) const portfolioValue = Math.round(BASE_VALUE * (1 + portfolioChange)) const worstAsset = assetChanges.reduce((min, a) => (a.change < min.change ? a : min)) const bestAsset = assetChanges.reduce((max, a) => (a.change > max.change ? a : max)) return { name: spec.name, description: spec.description, portfolioValue, change: Number(portfolioChange.toFixed(4)), worstAsset: { asset: worstAsset.asset, change: Number(worstAsset.change.toFixed(4)) }, bestAsset: { asset: bestAsset.asset, change: Number(bestAsset.change.toFixed(4)) }, } }) return { baseValue: BASE_VALUE, scenarios } }