analyze_report
Analyze investment portfolios with comprehensive risk assessment, diversification scoring, stress testing, and rebalancing recommendations to optimize performance and manage exposure.
Instructions
Full report — all 8 modules in one call: risk + rebalance + diversification + stress_test (10 scenarios) + var_cvar + factor_exposure + correlation + market_regime. Includes ExecutiveSummary: overall score (0-100), grade (A-F), key strengths, key risks, top recommendations. Most cost-effective option for comprehensive analysis. Costs $0.10 USDC.
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/index.ts:610-643 (handler)The handler for the '/analyze/report' endpoint, which maps to the "analyze_report" tool/feature. It validates the request, handles payment, checks for cached results, and calls generateFullReport.
app.post('/analyze/report', validate, charge('0.05'), async (c) => { try { const portfolio = c.get('portfolio') // Cache: return previously computed result for identical portfolios (30s TTL). // Uses stableJSON() instead of JSON.stringify() to ensure key order in the // input object does not produce different cache keys for identical portfolios. if (c.env.CACHE) { const keyBytes = await crypto.subtle.digest( 'SHA-256', new TextEncoder().encode(stableJSON(portfolio)), ) const cacheKey = 'report:' + Array.from(new Uint8Array(keyBytes)) .map((b) => b.toString(16).padStart(2, '0')) .join('') const cached = await c.env.CACHE.get(cacheKey).catch(() => null) if (cached) { c.res.headers.set('X-Cache', 'HIT') return c.json({ ...JSON.parse(cached), ...{ warnings: c.get('warnings') ?? [], request_id: c.get('requestId') ?? 'unknown' } }) } const result = generateFullReport(portfolio, c.get('returnSeries')) await c.env.CACHE.put(cacheKey, JSON.stringify(result), { expirationTtl: 30 }).catch(() => undefined) c.res.headers.set('X-Cache', 'MISS') return c.json(withMeta(c, result)) } return c.json(withMeta(c, generateFullReport(portfolio, c.get('returnSeries')))) } catch (err) { console.error(err) return internalError(c) } }) - src/analyze/full-report.ts:225-254 (handler)The core implementation logic for generating the full report, which aggregates data from various other analyze modules.
export function generateFullReport( portfolio: Portfolio, returnSeries?: Record<string, number[]>, ): FullReport { const risk = analyzeRisk(portfolio, returnSeries) const rebalance = analyzeRebalance(portfolio) const diversification = analyzeDiversification(portfolio) const stressTest = analyzeStressTest(portfolio, returnSeries) const varCvar = analyzeVaR(portfolio) const factorExposure = analyzeFactorExposure(portfolio, returnSeries) const correlation = analyzeCorrelation(portfolio, returnSeries) const marketRegime = analyzeMarketRegime(portfolio) const executiveSummary = computeExecutiveSummary( risk, diversification, correlation, factorExposure, stressTest, portfolio, ) return { risk, rebalance, diversification, stressTest, varCvar, factorExposure, correlation, marketRegime, executiveSummary, generatedAt: new Date().toISOString(), version: '3.1.0', } }