Files
worldmonitor/docs/api/EconomicService.openapi.json
Elie Habib 45f5e5a457 feat(forecast): AI Forecasts prediction module (#1579)
* feat(forecast): add AI Forecasts prediction module (Pro-tier)

MiroFish-inspired prediction engine that generates structured forecasts
across 6 domains (conflict, market, supply chain, political, military,
infrastructure) using existing WorldMonitor data streams.

- Proto definitions for ForecastService with GetForecasts RPC
- Dedicated seed script (seed-forecasts.mjs) with 6 domain detectors,
  cross-domain cascade resolver, prediction market calibration, and
  trend detection via prior snapshot comparison
- Premium-gated RPC handler (PREMIUM_RPC_PATHS enforcement)
- Lazy-loaded ForecastPanel with domain filters, probability bars,
  trend arrows, signal evidence, and cascade links
- Health monitoring integration (seed-meta freshness tracking)
- Refresh scheduler with API key guard

* test(forecast): add 47 unit tests for forecast detectors and utilities

Covers forecastId, normalize, resolveCascades, calibrateWithMarkets,
computeTrends, and smoke tests for all 6 domain detectors. Exports
testable functions from seed script with direct-run guard.

* fix(forecast): domain mismatch 'infra' vs 'infrastructure', add panel category

- Seed script used 'infra' but ForecastPanel filtered on 'infrastructure',
  causing Infra tab to show zero results
- Added 'forecast' to intelligence category in PANEL_CATEGORY_MAP

* fix(forecast): move CSS to one-time injection, improve type safety

- P2: Move style block from setContent to one-time document.head injection
  to prevent CSS accumulation on repeated renders
- P3: Replace +toFixed(3) with Math.round for readability in seed script
- P3: Use Forecast type instead of any[] in RPC handler filter

* fix(forecast): handle sebuf proto data shapes from Redis

Detectors now normalize CII scores from server-side proto format
(combinedScore, TREND_DIRECTION_RISING, region) to uniform shape.
Outage severity handles proto enum format (SEVERITY_LEVEL_HIGH).
Added confidence floor of 0.3 for single-source predictions.

Verified against live Redis: 2 predictions generated (Iran infra
shutdown, IL political instability).

* feat(forecast): unlock AI Forecasts on web, lock desktop only (trial)

- Remove forecast RPC from PREMIUM_RPC_PATHS (web access is free)
- Panel locked on desktop only (same as oref-sirens/telegram-intel)
- Remove API key guards from data-loader and refresh scheduler
- Web users get full access during trial period

* chore: regenerate proto types with make generate

Re-ran make generate after rebasing on main. Plugin v0.7.0 dropped
@ts-nocheck from output, added it back to all 50 generated files.
Fixed 4 type errors from proto codegen changes:
- MarketSource enum -> string union type
- TemporalAnomalyProto -> TemporalAnomaly rename
- webcam lastUpdated number -> string

* fix(forecast): use chokepoints v4 key, include ciiContribution in unrest

- P1: Switch chokepoints input from stale v2 to active v4 Redis key,
  matching bootstrap.js and cache-keys.ts
- P2: Add ciiContribution to unrest component fallback chain in
  normalizeCiiEntry so political detector reads the correct sebuf field

* feat(forecast): Phase 2 LLM scenario enrichment + confidence model

MiroFish-inspired enhancements:
- LLM scenario narratives via Groq/OpenRouter (narrative-only, no numeric
  adjustment). Evidence-grounded prompts with mandatory signal citation
  and few-shot examples from MiroFish's SECTION_SYSTEM_PROMPT_TEMPLATE.
- Top-4 predictions batched into single LLM call for cost efficiency.
- News context from newsInsights attached to all predictions for LLM
  prompt grounding (NOT in signals, cannot affect confidence).
- Deterministic confidence model: source diversity via SIGNAL_TO_SOURCE
  mapping (deduplicates cii+cii_delta, theater+indicators) + calibration
  agreement from prediction market drift. Floor 0.2, ceiling 1.0.
- Output validation: rejects scenarios without signal references.
- Truncated JSON repair for small model output.
- Structured JSON logging for LLM calls.
- Redis cache for LLM scenarios (1h TTL).
- 23 new tests (70 total), all passing.
- Live-tested: OpenRouter gemini-2.5-flash produces evidence-grounded
  scenario narratives from real WorldMonitor data.

* feat(forecast): Phase 3 multi-perspective scenarios, projections, data-driven cascades

MiroFish-inspired enhancements:
- Multi-perspective LLM analysis: top-2 predictions get strategic,
  regional, and contrarian viewpoints via combined LLM call
- Probability projections: domain-specific decay curves (h24/d7/d30)
  anchored to timeHorizon so probability equals projections[timeHorizon]
- Data-driven cascade rules: moved from hardcoded array to JSON config
  (scripts/data/cascade-rules.json) with schema validation, named
  predicate evaluators, unknown key rejection, and fallback to defaults
- 4 new cascade paths: infrastructure->supply_chain, infrastructure->market
  (both requiresSeverity:total), conflict->political, political->market
- Proto: added Perspectives and Projections messages to Forecast
- ForecastPanel: renders projections row and conditional perspectives toggle
- 89 tests (19 new), all passing
- Live-tested: OpenRouter produces perspectives from real data

* feat(forecast): Phase 4 data utilization + entity graph

Fixes data gaps that prevented 4 of 6 detectors from firing:
- Input normalizers: chokepoint v4 shape + GPS hexes-to-zones mapping
- Chokepoint warm-ping (production-only, requires WM_API_BASE_URL)
- Lowered CII conflict threshold from 70 to 60, gated on level=high|critical

4 new standalone detectors:
- UCDP conflict zones (10+ events per country)
- Cyber threat concentration (5+ threats per country)
- GPS jamming in maritime shipping zones (5 regions)
- Prediction markets as signals (60-90% probability markets)

Entity-relationship graph (file-based, 38 nodes):
- Countries, theaters, commodities, chokepoints, alliances
- Alias table resolves both ISO codes and display names
- Graph cascade discovery links predictions across entities

Result: 51 predictions (up from 1-2), spanning conflict, infrastructure,
and supply chain domains. 112 tests, all passing.

* fix(forecast): redis cache format, signal source mapping, type safety

Fresh-eyes audit fixes:
- BUG: redisSet used wrong Upstash API format (POST body with {value,ex}
  instead of command array ['SET',key,value,'EX',ttl]). LLM cache writes
  were silently failing, causing fresh LLM calls every run.
- BUG: prediction_market signal type missing from SIGNAL_TO_SOURCE,
  inflating confidence for market-derived predictions.
- CLEANUP: Remove unnecessary (f as any) casts in ForecastPanel since
  generated Forecast type already has projections/perspectives fields.
- CLEANUP: Bump health maxStaleMin from 60 to 90 to avoid false STALE
  alerts when LLM calls add latency to seed runs.

* feat(forecast): headline-entity matching with news corroboration signals

Uses entity graph aliases to match headlines to predictions by
country/theater (excludes commodity/infrastructure nodes to prevent
false positives). Predictions with matching headlines get a
news_corroboration signal visible in the panel.

Also fixes buildUserPrompt to merge unique headlines from ALL
predictions in the LLM batch (was only reading preds[0].newsContext).

Live-tested: 13 of 51 predictions now have corroborating headlines
(Iran, Israel, Syria, Ukraine, etc). 116 tests, all passing.

* feat(forecast): add country-codes.json for headline-entity matching

56 countries with ISO codes, full names, and scoring keywords (extracted
from src/config/countries.ts + UCDP-relevant additions). Used by
attachNewsContext for richer headline matching via getSearchTermsForRegion
which combines country-codes + entity graph + keyword aliases.

14/57 predictions now have news corroboration (limited by headline
coverage, not matching quality: only 8 headlines currently available).

* feat(forecast): read 300 headlines from news digest instead of 8

Read news:digest:v1:full:en (300 headlines across 16 categories) instead
of just news:insights:v1 topStories (8 headlines). Fallback to topStories
if digest is unavailable.

Result: news corroboration jumped from 25% to 64% (38/59 predictions).

* fix(forecast): handle parenthetical country names in headline matching

Strip suffixes like '(Zaire)', '(Burma)', '(Soviet Union)' from UCDP
region names before matching against country-codes.json. Also use
includes() for reverse name lookup to catch partial matches.

Corroboration: 64% -> 69% (41/59). Remaining 18 unmatched are countries
with no current English-language news coverage.

* fix(forecast): cache validated LLM output, add digest test, log cache errors

Fresh-eyes audit fixes:
- Combined LLM cache now stores only validated items (was caching raw
  unvalidated output, serving potentially invalid scenarios on cache hit)
- redisSet logs warnings on failure (was silently swallowing all errors)
- Added digest-based test for attachNewsContext (primary path was untested)
- Fixed test arity: attachNewsContext(preds, news, digest) with 3 params

* fix(forecast): remove dead confidenceFromSources, reduce warm-ping timeout

- P2: Remove confidenceFromSources (dead code, computeConfidence overwrites
  all initial confidence values). Inline the formula in original detectors.
- P3: Reduce warm-ping timeout from 30s to 15s (non-critical step)
- P3: Add trial status comment on forecast panel config

* fix(forecast): resolve ISO codes to country names, fix market detector, safe pre-push

P1 fixes from code review:
- CII ISO codes (IL, IR) now resolved to full country names (Israel, Iran)
  via country-codes.json. Prevents substring false positives (IL matching
  Chile) in event correlation. Uses word-boundary regex for matching.
- Market detector CII-to-theater mapping now uses entity graph traversal
  instead of broken theater-name substring matching. Iran correctly maps
  to Middle East theater via graph links.
- Pre-push hook no longer runs destructive git checkout on proto freshness
  failure. Reports mismatch and exits without modifying worktree.
2026-03-15 01:42:04 +04:00

1 line
23 KiB
JSON

{"components":{"schemas":{"BisCreditToGdp":{"description":"BisCreditToGdp represents total credit as percentage of GDP from BIS.","properties":{"countryCode":{"description":"ISO 2-letter country code.","type":"string"},"countryName":{"description":"Country or region name.","type":"string"},"creditGdpRatio":{"description":"Total credit as percentage of GDP.","format":"double","type":"number"},"date":{"description":"Date as YYYY-QN.","type":"string"},"previousRatio":{"description":"Previous quarter ratio.","format":"double","type":"number"}},"type":"object"},"BisExchangeRate":{"description":"BisExchangeRate represents effective exchange rate indices from BIS.","properties":{"countryCode":{"description":"ISO 2-letter country code.","type":"string"},"countryName":{"description":"Country or region name.","type":"string"},"date":{"description":"Date as YYYY-MM.","type":"string"},"nominalEer":{"description":"Nominal effective exchange rate index.","format":"double","type":"number"},"realChange":{"description":"Percentage change from previous period (real).","format":"double","type":"number"},"realEer":{"description":"Real effective exchange rate index.","format":"double","type":"number"}},"type":"object"},"BisPolicyRate":{"description":"BisPolicyRate represents a central bank policy rate from BIS.","properties":{"centralBank":{"description":"Central bank name (e.g. \"Federal Reserve\").","type":"string"},"countryCode":{"description":"ISO 2-letter country code (US, GB, JP, etc.)","type":"string"},"countryName":{"description":"Country or region name.","type":"string"},"date":{"description":"Date as YYYY-MM.","type":"string"},"previousRate":{"description":"Previous period rate percentage.","format":"double","type":"number"},"rate":{"description":"Current policy rate percentage.","format":"double","type":"number"}},"type":"object"},"EnergyCapacitySeries":{"properties":{"data":{"items":{"$ref":"#/components/schemas/EnergyCapacityYear"},"type":"array"},"energySource":{"type":"string"},"name":{"type":"string"}},"type":"object"},"EnergyCapacityYear":{"properties":{"capacityMw":{"format":"double","type":"number"},"year":{"format":"int32","type":"integer"}},"type":"object"},"EnergyPrice":{"description":"EnergyPrice represents a current energy commodity price from EIA.","properties":{"change":{"description":"Percentage change from previous period.","format":"double","type":"number"},"commodity":{"description":"Energy commodity identifier.","minLength":1,"type":"string"},"name":{"description":"Human-readable name (e.g., \"WTI Crude Oil\", \"Henry Hub Natural Gas\").","type":"string"},"price":{"description":"Current price in USD.","format":"double","type":"number"},"priceAt":{"description":"Price date, as Unix epoch milliseconds.. Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"unit":{"description":"Unit of measurement (e.g., \"$/barrel\", \"$/MMBtu\").","type":"string"}},"required":["commodity"],"type":"object"},"Error":{"description":"Error is returned when a handler encounters an error. It contains a simple error message that the developer can customize.","properties":{"message":{"description":"Error message (e.g., 'user not found', 'database connection failed')","type":"string"}},"type":"object"},"FearGreedHistoryEntry":{"description":"FearGreedHistoryEntry is a single day's Fear \u0026 Greed index reading.","properties":{"date":{"description":"Date string (YYYY-MM-DD).","type":"string"},"value":{"description":"Index value (0-100).","format":"int32","maximum":100,"minimum":0,"type":"integer"}},"type":"object"},"FearGreedSignal":{"description":"FearGreedSignal tracks the Crypto Fear \u0026 Greed index.","properties":{"history":{"items":{"$ref":"#/components/schemas/FearGreedHistoryEntry"},"type":"array"},"status":{"description":"Classification label (e.g., \"Extreme Fear\", \"Greed\").","type":"string"},"value":{"description":"Current index value (0-100).","format":"int32","type":"integer"}},"type":"object"},"FieldViolation":{"description":"FieldViolation describes a single validation error for a specific field.","properties":{"description":{"description":"Human-readable description of the validation violation (e.g., 'must be a valid email address', 'required field missing')","type":"string"},"field":{"description":"The field path that failed validation (e.g., 'user.email' for nested fields). For header validation, this will be the header name (e.g., 'X-API-Key')","type":"string"}},"required":["field","description"],"type":"object"},"FlowStructureSignal":{"description":"FlowStructureSignal compares BTC vs QQQ 5-day returns.","properties":{"btcReturn5":{"description":"BTC 5-day return percentage.","format":"double","type":"number"},"qqqReturn5":{"description":"QQQ 5-day return percentage.","format":"double","type":"number"},"status":{"description":"\"PASSIVE GAP\", \"ALIGNED\", or \"UNKNOWN\".","type":"string"}},"type":"object"},"FredObservation":{"description":"FredObservation represents a single data point from a FRED economic series.","properties":{"date":{"description":"Observation date as YYYY-MM-DD string.","type":"string"},"value":{"description":"Observation value.","format":"double","type":"number"}},"type":"object"},"FredSeries":{"description":"FredSeries represents a FRED time series with metadata.","properties":{"frequency":{"description":"Data frequency (e.g., \"Monthly\", \"Quarterly\").","type":"string"},"observations":{"items":{"$ref":"#/components/schemas/FredObservation"},"type":"array"},"seriesId":{"description":"Series identifier (e.g., \"GDP\", \"UNRATE\", \"CPIAUCSL\").","minLength":1,"type":"string"},"title":{"description":"Series title.","type":"string"},"units":{"description":"Unit of measurement.","type":"string"}},"required":["seriesId"],"type":"object"},"GetBisCreditRequest":{"description":"GetBisCreditRequest requests credit-to-GDP ratio data.","type":"object"},"GetBisCreditResponse":{"description":"GetBisCreditResponse contains BIS credit-to-GDP data.","properties":{"entries":{"items":{"$ref":"#/components/schemas/BisCreditToGdp"},"type":"array"}},"type":"object"},"GetBisExchangeRatesRequest":{"description":"GetBisExchangeRatesRequest requests effective exchange rates.","type":"object"},"GetBisExchangeRatesResponse":{"description":"GetBisExchangeRatesResponse contains BIS effective exchange rate data.","properties":{"rates":{"items":{"$ref":"#/components/schemas/BisExchangeRate"},"type":"array"}},"type":"object"},"GetBisPolicyRatesRequest":{"description":"GetBisPolicyRatesRequest requests central bank policy rates.","type":"object"},"GetBisPolicyRatesResponse":{"description":"GetBisPolicyRatesResponse contains BIS policy rate data.","properties":{"rates":{"items":{"$ref":"#/components/schemas/BisPolicyRate"},"type":"array"}},"type":"object"},"GetEnergyCapacityRequest":{"properties":{"energySources":{"items":{"description":"Energy source codes to query (e.g., \"SUN\", \"WND\", \"COL\").\n Empty returns all tracked sources (SUN, WND, COL).","type":"string"},"type":"array"},"years":{"description":"Number of years of historical data. Default 20 if not set.","format":"int32","type":"integer"}},"type":"object"},"GetEnergyCapacityResponse":{"properties":{"series":{"items":{"$ref":"#/components/schemas/EnergyCapacitySeries"},"type":"array"}},"type":"object"},"GetEnergyPricesRequest":{"description":"GetEnergyPricesRequest specifies which energy commodities to retrieve.","properties":{"commodities":{"items":{"description":"Optional commodity filter. Empty returns all tracked commodities.","type":"string"},"type":"array"}},"type":"object"},"GetEnergyPricesResponse":{"description":"GetEnergyPricesResponse contains energy price data.","properties":{"prices":{"items":{"$ref":"#/components/schemas/EnergyPrice"},"type":"array"}},"type":"object"},"GetFredSeriesBatchRequest":{"description":"GetFredSeriesBatchRequest looks up multiple FRED series in a single call.","properties":{"limit":{"description":"Maximum number of observations per series. Defaults to 120.","format":"int32","type":"integer"},"seriesIds":{"items":{"description":"FRED series IDs (e.g., \"WALCL\", \"FEDFUNDS\"). Max 10.","maxItems":10,"minItems":1,"type":"string"},"maxItems":10,"minItems":1,"type":"array"}},"type":"object"},"GetFredSeriesBatchResponse":{"description":"GetFredSeriesBatchResponse contains the requested FRED series data.","properties":{"fetched":{"description":"Number of series successfully fetched.","format":"int32","type":"integer"},"requested":{"description":"Number of series requested.","format":"int32","type":"integer"},"results":{"additionalProperties":{"$ref":"#/components/schemas/FredSeries"},"description":"Map of series_id -\u003e FRED series for found series.","type":"object"}},"type":"object"},"GetFredSeriesRequest":{"description":"GetFredSeriesRequest specifies which FRED series to retrieve.","properties":{"limit":{"description":"Maximum number of observations to return. Defaults to 120.","format":"int32","type":"integer"},"seriesId":{"description":"FRED series ID (e.g., \"GDP\", \"UNRATE\", \"CPIAUCSL\").","minLength":1,"type":"string"}},"required":["seriesId"],"type":"object"},"GetFredSeriesResponse":{"description":"GetFredSeriesResponse contains the requested FRED series data.","properties":{"series":{"$ref":"#/components/schemas/FredSeries"}},"type":"object"},"GetMacroSignalsRequest":{"description":"GetMacroSignalsRequest requests the current macro signal dashboard.","type":"object"},"GetMacroSignalsResponse":{"description":"GetMacroSignalsResponse contains the full macro signal dashboard with 7 signals and verdict.","properties":{"bullishCount":{"description":"Number of bullish signals.","format":"int32","type":"integer"},"meta":{"$ref":"#/components/schemas/MacroMeta"},"signals":{"$ref":"#/components/schemas/MacroSignals"},"timestamp":{"description":"ISO 8601 timestamp of computation.","type":"string"},"totalCount":{"description":"Total number of evaluated signals (excluding UNKNOWN).","format":"int32","type":"integer"},"unavailable":{"description":"True when upstream data is unavailable (fallback result).","type":"boolean"},"verdict":{"description":"Overall verdict: \"BUY\", \"CASH\", or \"UNKNOWN\".","type":"string"}},"type":"object"},"HashRateSignal":{"description":"HashRateSignal tracks Bitcoin hash rate momentum.","properties":{"change30d":{"description":"Hash rate change over 30 days as percentage.","format":"double","type":"number"},"status":{"description":"\"GROWING\", \"DECLINING\", \"STABLE\", or \"UNKNOWN\".","type":"string"}},"type":"object"},"LiquiditySignal":{"description":"LiquiditySignal tracks JPY 30d rate of change as a liquidity proxy.","properties":{"sparkline":{"items":{"description":"Last 30 JPY close prices.","format":"double","type":"number"},"type":"array"},"status":{"description":"\"SQUEEZE\", \"NORMAL\", or \"UNKNOWN\".","type":"string"},"value":{"description":"JPY 30d ROC percentage, absent if unavailable.","format":"double","type":"number"}},"type":"object"},"ListWorldBankIndicatorsRequest":{"description":"ListWorldBankIndicatorsRequest specifies filters for retrieving World Bank data.","properties":{"countryCode":{"description":"Optional country filter (ISO 3166-1 alpha-2).","type":"string"},"cursor":{"description":"Cursor for next page.","type":"string"},"indicatorCode":{"description":"World Bank indicator code (e.g., \"NY.GDP.MKTP.CD\").","minLength":1,"type":"string"},"pageSize":{"description":"Maximum items per page.","format":"int32","type":"integer"},"year":{"description":"Optional year filter. Defaults to latest available.","format":"int32","type":"integer"}},"required":["indicatorCode"],"type":"object"},"ListWorldBankIndicatorsResponse":{"description":"ListWorldBankIndicatorsResponse contains World Bank indicator data.","properties":{"data":{"items":{"$ref":"#/components/schemas/WorldBankCountryData"},"type":"array"},"pagination":{"$ref":"#/components/schemas/PaginationResponse"}},"type":"object"},"MacroMeta":{"description":"MacroMeta contains supplementary chart data.","properties":{"qqqSparkline":{"items":{"description":"Last 30 QQQ close prices for sparkline.","format":"double","type":"number"},"type":"array"}},"type":"object"},"MacroRegimeSignal":{"description":"MacroRegimeSignal compares QQQ vs XLP 20-day rate of change.","properties":{"qqqRoc20":{"description":"QQQ 20d ROC percentage.","format":"double","type":"number"},"status":{"description":"\"RISK-ON\", \"DEFENSIVE\", or \"UNKNOWN\".","type":"string"},"xlpRoc20":{"description":"XLP 20d ROC percentage.","format":"double","type":"number"}},"type":"object"},"MacroSignals":{"description":"MacroSignals contains all 7 individual signal computations.","properties":{"fearGreed":{"$ref":"#/components/schemas/FearGreedSignal"},"flowStructure":{"$ref":"#/components/schemas/FlowStructureSignal"},"hashRate":{"$ref":"#/components/schemas/HashRateSignal"},"liquidity":{"$ref":"#/components/schemas/LiquiditySignal"},"macroRegime":{"$ref":"#/components/schemas/MacroRegimeSignal"},"priceMomentum":{"$ref":"#/components/schemas/PriceMomentumSignal"},"technicalTrend":{"$ref":"#/components/schemas/TechnicalTrendSignal"}},"type":"object"},"PaginationResponse":{"description":"PaginationResponse contains pagination metadata returned alongside list results.","properties":{"nextCursor":{"description":"Cursor for fetching the next page. Empty string indicates no more pages.","type":"string"},"totalCount":{"description":"Total count of items matching the query, if known. Zero if the total is unknown.","format":"int32","type":"integer"}},"type":"object"},"PriceMomentumSignal":{"description":"PriceMomentumSignal uses the Mayer Multiple (price/SMA200) as a market-adaptive signal.","properties":{"status":{"description":"\"STRONG\", \"MODERATE\", \"WEAK\", or \"UNKNOWN\".","type":"string"}},"type":"object"},"ResultsEntry":{"properties":{"key":{"type":"string"},"value":{"$ref":"#/components/schemas/FredSeries"}},"type":"object"},"TechnicalTrendSignal":{"description":"TechnicalTrendSignal evaluates BTC price vs moving averages and VWAP.","properties":{"btcPrice":{"description":"Current BTC price.","format":"double","type":"number"},"mayerMultiple":{"description":"Mayer multiple (BTC price / SMA200).","format":"double","type":"number"},"sma200":{"description":"200-day simple moving average.","format":"double","type":"number"},"sma50":{"description":"50-day simple moving average.","format":"double","type":"number"},"sparkline":{"items":{"description":"Last 30 BTC close prices.","format":"double","type":"number"},"type":"array"},"status":{"description":"\"BULLISH\", \"BEARISH\", \"NEUTRAL\", or \"UNKNOWN\".","type":"string"},"vwap30d":{"description":"30-day volume-weighted average price.","format":"double","type":"number"}},"type":"object"},"ValidationError":{"description":"ValidationError is returned when request validation fails. It contains a list of field violations describing what went wrong.","properties":{"violations":{"description":"List of validation violations","items":{"$ref":"#/components/schemas/FieldViolation"},"type":"array"}},"required":["violations"],"type":"object"},"WorldBankCountryData":{"description":"WorldBankCountryData represents a World Bank indicator value for a country.","properties":{"countryCode":{"description":"ISO 3166-1 alpha-2 country code.","minLength":1,"type":"string"},"countryName":{"description":"Country name.","type":"string"},"indicatorCode":{"description":"World Bank indicator code (e.g., \"NY.GDP.MKTP.CD\").","minLength":1,"type":"string"},"indicatorName":{"description":"Indicator name.","type":"string"},"value":{"description":"Indicator value.","format":"double","type":"number"},"year":{"description":"Data year.","format":"int32","type":"integer"}},"required":["countryCode","indicatorCode"],"type":"object"}}},"info":{"title":"EconomicService API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/api/economic/v1/get-bis-credit":{"get":{"description":"GetBisCredit retrieves credit-to-GDP ratio data from BIS.","operationId":"GetBisCredit","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBisCreditResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetBisCredit","tags":["EconomicService"]}},"/api/economic/v1/get-bis-exchange-rates":{"get":{"description":"GetBisExchangeRates retrieves effective exchange rates from BIS.","operationId":"GetBisExchangeRates","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBisExchangeRatesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetBisExchangeRates","tags":["EconomicService"]}},"/api/economic/v1/get-bis-policy-rates":{"get":{"description":"GetBisPolicyRates retrieves central bank policy rates from BIS.","operationId":"GetBisPolicyRates","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBisPolicyRatesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetBisPolicyRates","tags":["EconomicService"]}},"/api/economic/v1/get-energy-capacity":{"get":{"description":"GetEnergyCapacity retrieves installed capacity data (solar, wind, coal) from EIA.","operationId":"GetEnergyCapacity","parameters":[{"description":"Energy source codes to query (e.g., \"SUN\", \"WND\", \"COL\").\n Empty returns all tracked sources (SUN, WND, COL).","in":"query","name":"energy_sources","required":false,"schema":{"type":"string"}},{"description":"Number of years of historical data. Default 20 if not set.","in":"query","name":"years","required":false,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetEnergyCapacityResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetEnergyCapacity","tags":["EconomicService"]}},"/api/economic/v1/get-energy-prices":{"get":{"description":"GetEnergyPrices retrieves current energy commodity prices from EIA.","operationId":"GetEnergyPrices","parameters":[{"description":"Optional commodity filter. Empty returns all tracked commodities.","in":"query","name":"commodities","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetEnergyPricesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetEnergyPrices","tags":["EconomicService"]}},"/api/economic/v1/get-fred-series":{"get":{"description":"GetFredSeries retrieves time series data from the Federal Reserve Economic Data.","operationId":"GetFredSeries","parameters":[{"description":"FRED series ID (e.g., \"GDP\", \"UNRATE\", \"CPIAUCSL\").","in":"query","name":"series_id","required":false,"schema":{"type":"string"}},{"description":"Maximum number of observations to return. Defaults to 120.","in":"query","name":"limit","required":false,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetFredSeriesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetFredSeries","tags":["EconomicService"]}},"/api/economic/v1/get-fred-series-batch":{"post":{"description":"GetFredSeriesBatch retrieves multiple FRED series in a single call.","operationId":"GetFredSeriesBatch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetFredSeriesBatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetFredSeriesBatchResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetFredSeriesBatch","tags":["EconomicService"]}},"/api/economic/v1/get-macro-signals":{"get":{"description":"GetMacroSignals computes 7 macro signals from 6 upstream sources with BUY/CASH verdict.","operationId":"GetMacroSignals","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMacroSignalsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetMacroSignals","tags":["EconomicService"]}},"/api/economic/v1/list-world-bank-indicators":{"get":{"description":"ListWorldBankIndicators retrieves development indicator data from the World Bank.","operationId":"ListWorldBankIndicators","parameters":[{"description":"World Bank indicator code (e.g., \"NY.GDP.MKTP.CD\").","in":"query","name":"indicator_code","required":false,"schema":{"type":"string"}},{"description":"Optional country filter (ISO 3166-1 alpha-2).","in":"query","name":"country_code","required":false,"schema":{"type":"string"}},{"description":"Optional year filter. Defaults to latest available.","in":"query","name":"year","required":false,"schema":{"format":"int32","type":"integer"}},{"description":"Maximum items per page.","in":"query","name":"page_size","required":false,"schema":{"format":"int32","type":"integer"}},{"description":"Cursor for next page.","in":"query","name":"cursor","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWorldBankIndicatorsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"ListWorldBankIndicators","tags":["EconomicService"]}}}}