analyze_regime
Detect current market regimes (risk_on, stable_growth, recovery, high_volatility, risk_off, crisis) using macro indicators and portfolio signals. Assess portfolio alignment with detected regime and provide adjustment recommendations.
Instructions
Market regime detection from macro indicators + portfolio signals. Regimes: risk_on | stable_growth | recovery | high_volatility | risk_off | crisis. Returns regime, confidence (low/medium/high), individual signals, portfolio-fit score (0-100), and suggested adjustments. Provide marketIndicators for higher confidence. Costs $0.02 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/analyze/market-regime.ts:291-337 (handler)The main handler function `analyzeMarketRegime` that computes the market regime by aggregating weighted signals from both provided macro indicators and portfolio-derived metrics.
export function analyzeMarketRegime(portfolio: Portfolio): MarketRegimeResult { const indicators = portfolio.marketIndicators ?? {} const scoredSignals: ScoredSignal[] = [] // Macro signals (when provided) if (indicators.vix !== undefined) scoredSignals.push(vixSignal(indicators.vix)) if (indicators.yieldCurve !== undefined) scoredSignals.push(yieldCurveSignal(indicators.yieldCurve)) if (indicators.creditSpread !== undefined) scoredSignals.push(creditSpreadSignal(indicators.creditSpread)) if (indicators.inflationRate !== undefined) scoredSignals.push(inflationSignal(indicators.inflationRate)) if (indicators.gdpGrowth !== undefined) scoredSignals.push(gdpSignal(indicators.gdpGrowth)) // Portfolio-derived signals (always computed) scoredSignals.push(portfolioVolSignal(portfolio)) scoredSignals.push(cryptoConcentrationSignal(portfolio)) scoredSignals.push(defensiveRatioSignal(portfolio)) // Weighted average score const totalWeight = scoredSignals.reduce((acc, s) => acc + s.weight, 0) const weightedScore = scoredSignals.reduce((acc, s) => acc + s.score * s.weight, 0) / totalWeight // Confidence: low if we only have portfolio signals, high if macro + portfolio align const macroCount = scoredSignals.filter((s) => ['VIX Fear Index', 'Yield Curve (10Y-2Y)', 'IG Credit Spread', 'Inflation Rate', 'GDP Growth'].includes(s.name), ).length const confidence: MarketRegimeResult['confidence'] = macroCount >= 3 ? 'high' : macroCount >= 1 ? 'medium' : 'low' const portVol = Math.sqrt( portfolio.holdings.reduce((acc, h) => acc + h.weight ** 2 * h.volatility ** 2, 0), ) const regime = scoreToRegime(weightedScore, portVol) const fit = portfolioFitScore(regime, portfolio) // Strip internal score/weight from output const signals: RegimeSignal[] = scoredSignals.map(({ name, value, signal }) => ({ name, value, signal, })) return { regime, confidence, signals, portfolioFitScore: fit, recommendation: regimeDescription(regime), suggestedAdjustments: buildAdjustments(regime, portfolio), } } - src/index.ts:590-597 (registration)The tool `analyze/regime` is registered as a POST endpoint in the main application file, utilizing the `analyzeMarketRegime` handler.
app.post('/analyze/regime', validate, charge('0.01'), async (c) => { try { return c.json(withMeta(c, analyzeMarketRegime(c.get('portfolio')))) } catch (err) { console.error(err) return internalError(c) } })