Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MacroScope economic weather bulletin: country observatory, five-front shock graph, bounded outcomes, and a decision record

MacroScope

An AI Economic Scenario Observatory that keeps observed data, scenario assumptions, and model prose in three visibly separate lanes.

Python 3.8+ stdlib only tests 30 World Bank API BYOK MIT license

Quick start · Five-minute demo · How it works · 简体中文


MacroScope is a localhost web application for policy teams, economic researchers, risk analysts, and finance practitioners who need to turn country indicators into an auditable scenario briefing. It loads the World Bank Indicators API without a key, accepts CSV/JSON when official access is unavailable or the analyst has better data, measures trend/volatility/gaps/regime, propagates five macro shocks through a versioned graph, and exports both a readable briefing draft and a machine-readable decision record.

AI is optional and structurally bounded: it may extract policy text or explain saved result IDs. It does not calculate the country indicators, regime, shock outcomes, uncertainty bands, or assumption flips. A server-side grounding check withholds AI output that introduces unsupported numbers or evidence IDs.

Method boundary: MacroScope is a scenario observatory, not an econometric forecast, investment signal, or policy recommendation engine. Every graph coefficient is an illustrative, unvalidated editorial assumption with per-edge provenance metadata—not a fitted causal estimate. P10/P90 are bounded assumption-propagation intervals, not empirical probabilities.

MacroScope shock-map workspace with five shock controls, bounded GDP/inflation/current-account results, contribution bars, and assumption flips
Real local app screenshot · Germany fixture · graph 2026.1 · Offline Mock available but not needed for these numbers

MacroScope workflow from country observatory through shock bulletin and briefing room
Real local-browser workflow · no external AI provider required

Contents

Problem

Country-macro work often breaks its evidence chain in one of three places:

  1. an indicator dashboard shows the latest value but hides missing years and vintage mismatch;
  2. a scenario spreadsheet mixes observed values with coefficients and subjective assumptions;
  3. an AI-generated memo sounds coherent while quietly adding a number that never existed in the source.

MacroScope preserves those boundaries in the interface and in every export:

Layer What belongs there What does not belong there
Observation Source values, trend slope, change volatility, gaps, regime evidence IDs Interpolated missing years or model prose
Scenario Named shock inputs, graph version/fingerprint, coefficient ranges, bounded outcomes, flip points Claims that the interval is a probability forecast
AI annotation Policy-text extraction and plain-language explanation of saved IDs New indicators, recalculation, unsupported numbers, trading advice
Decision record Human owner, review date, chosen next action, provenance, model limits Automatic approval or recommendation

Why Existing Approaches Are Not Enough

Capability Generic indicator dashboard Spreadsheet scenario Unbounded AI chat MacroScope
No-key official country ingest Sometimes No No Yes
CSV and JSON fallback Rarely Yes Manual Yes
Explicit gap ledger; no interpolation Rarely Manual No Yes
Versioned shock graph + fingerprint No Manual No Yes
Reproducible bounded uncertainty No Varies No Yes
Assumption-flip thresholds No Manual Usually prose Yes
Numeric grounding for AI output N/A N/A Usually no Yes
Key-free local history and resume No File-based Varies Yes
Briefing + decision-record export Dashboard only Manual Prose only Yes

What This Project Does

MacroScope converts an official or imported country-indicator station into an auditable observation, a versioned scenario bulletin, assumption-flip thresholds, and a human-owned decision record. AI remains a bounded annotation layer.

Key Features

  • Country station ingestion: fetches nine World Bank indicator families concurrently without an API key, while retaining URL, indicator code, and retrieval time in the source log.
  • Real fallback path: imports wide CSV, long CSV, canonical JSON, indicator maps, and compact year maps. Six realistic country fixtures plus one malformed edge case run offline.
  • Economic weather observatory: calculates least-squares trend, sample volatility of annual changes, latest/previous values, gap counts, coverage, core-series sufficiency, and a transparent monitoring regime.
  • Five-front shock graph: propagates energy, policy-rate, external-demand, FX, and fiscal shocks into GDP growth, inflation, and current-account outcomes.
  • Illustrative graph provenance: ships 2025.4 and 2026.1 scenario-assumption vintages. Each edge records an assumption ID, unit, horizon, sign rationale, review date, and source-status note; each run stores full ranges and a stable SHA-256-derived fingerprint. The 2026.1 fingerprint is c671234d91816130. Traceability does not economically validate the coefficient.
  • Bounded uncertainty: draws reproducible triangular paths from the user input band and each edge's low/central/high coefficient. The UI and export label the result as an assumption interval.
  • Assumption flips: solves the shock level that would move an outcome across a stated watch threshold while other central contributions stay fixed.
  • Grounded AI desk: supports policy extraction and deterministic-result explanation through Offline Mock, OpenAI, Anthropic, Gemini, DeepSeek, Moonshot Kimi, Zhipu GLM, or a custom endpoint.
  • Operational handoff: exports a Markdown briefing, JSON decision record, or CSV outcome table with source log, quality label, graph fingerprint, assumptions, interval, flips, and limits.

Why this is useful

The product is designed for a real meeting sequence rather than a one-off chart:

  1. an analyst opens an official or internal country station;
  2. the observatory exposes stale series and internal gaps before anyone discusses a scenario;
  3. the team selects a graph vintage and moves five explicit shock fronts;
  4. the nearest assumption flips reveal which exposure deserves primary-source verification;
  5. AI can extract the communiqué or explain saved result IDs, but a numeric whitelist blocks unsupported prose;
  6. a named human saves the next action and review date into a reusable decision record.

Removing AI still leaves the full numeric observatory, scenario, flip analysis, and exports. Losing official API access still leaves CSV/JSON ingestion and the offline fixture set. The cooperating subsystems produce useful partial value instead of collapsing around one mechanism.

Demo / Screenshots

Five-minute demo

  1. Start the server and open http://127.0.0.1:8000.
  2. Under Station intake, choose Germany / energy squeeze and click Open.
  3. Inspect the observed regime, indicator traces, coverage, and missing-weather ledger.
  4. Open Shock map. Keep graph 2026.1, set an energy shock, rate move, and external-demand shock, then issue the bulletin.
  5. Inspect GDP/inflation/current-account intervals, edge contributions, and nearest assumption flips.
  6. Open Policy desk, keep Offline Mock, paste a policy statement, and run extraction or explanation.
  7. Open Briefing room, add an owner/review date, then export Markdown, JSON, or CSV.
  8. Refresh the page, open Archive, and resume the checkpoint. The provider key will not be restored because it was never written there.
Mobile observatory preview

MacroScope responsive mobile observatory hero

Use Cases

  • Country-risk and policy-monitoring meeting preparation.
  • Economic scenario exercises with visible coefficient and data-vintage assumptions.
  • Teaching trend, volatility, missingness, conditional scenarios, and uncertainty boundaries.
  • Producing a source-labeled briefing draft that a human analyst can review and own.

Bundled fixtures:

Fixture Shape Purpose
argentina_fx_stress.csv wide CSV high inflation, FX depreciation, fiscal/external strain
germany_energy_squeeze.csv wide CSV energy exposure and weak growth
kenya_gap_case.csv sparse wide CSV missing years and uneven series coverage
china_demand_transition.json indicator map multi-series demand transition
indonesia_resilient_mix.json long records negative/no-urgent-issue style case with mixed structure
united_states_tightening.json nested series rate and fiscal/current-account watch
malformed_bad_year.csv malformed CSV responsible import rejection

Quick Start

Requirements

  • Python 3.8 or newer
  • A modern browser
  • No package installation, database server, Node.js, or API key

Run

git clone https://github.com/ZhenyuanPAN822/macroscope.git
cd macroscope
python server.py

Expected output:

MacroScope / Economic Weather Bureau
  serving ...\repo\web
  open    http://127.0.0.1:8000/
  Ctrl-C to stop

Choose a different port if 8000 is already occupied:

python server.py --port 8049

The durable, key-free SQLite history defaults to ~/.macroscope/tasks.sqlite3. Override it without changing source code:

$env:MACROSCOPE_DATA_DIR = "D:\private\macroscope-data"
python server.py

Example Input / Output

Input paths

World Bank Indicators API

Enter an ISO-style country code and date range. MacroScope requests these canonical series when available:

MacroScope key World Bank code Display unit
gdp_growth NY.GDP.MKTP.KD.ZG % y/y
inflation FP.CPI.TOTL.ZG % y/y
unemployment SL.UEM.TOTL.ZS % labor force
policy_rate FR.INR.RINR real interest-rate proxy, %
fx_rate PA.NUS.FCRF local currency per USD
fiscal_balance GC.BAL.CASH.GD.ZS % GDP
exports_growth NE.EXP.GNFS.KD.ZG % y/y
current_account BN.CAB.XOKA.GD.ZS % GDP
energy_imports EG.IMP.CONS.ZS % energy use

The API path is partial by design: missing or unavailable series stay in source_errors and reduce the data-quality label. MacroScope never fills a gap with an invented value.

Wide CSV

country_code,country_name,year,gdp_growth,inflation,unemployment,policy_rate,fx_rate
DEU,Germany,2023,-0.3,5.9,3.1,0.6,0.92
DEU,Germany,2024,-0.2,2.3,3.4,1.4,0.92

Long CSV

country_code,country_name,year,indicator,value
IDN,Indonesia,2024,gdp_growth,5.0
IDN,Indonesia,2024,inflation,2.3

JSON indicator map

{
  "country": {"code": "CHN", "name": "China"},
  "indicators": {
    "gdp_growth": [[2022, 3.0], [2023, 5.2], [2024, 5.0]],
    "inflation": [[2022, 2.0], [2023, 0.2], [2024, 0.2]]
  }
}

Aliases and World Bank codes are accepted. Unknown columns are ignored; invalid years and non-finite values are rejected. Duplicate country-year-indicator observations use the last value and create a quality warning.

How It Works

MacroScope architecture: source stations feed a deterministic observatory and versioned shock graph; a separately grounded AI desk annotates exports

flowchart LR
    A[World Bank API] --> C[Canonical country dataset]
    B[CSV / JSON fallback] --> C
    C --> D[Quality + gap ledger]
    C --> E[Trend + volatility]
    D --> F[Observed regime]
    E --> F
    F --> G[Versioned shock graph]
    G --> H[Bounded intervals]
    G --> I[Assumption flips]
    J[Policy text] --> K[Bounded AI desk]
    H --> K
    I --> K
    K --> L[Grounding validator]
    H --> M[Briefing + decision record]
    I --> M
    L --> M
    M --> N[(Key-free SQLite history)]
Loading

Observation method

  • Trend: ordinary least-squares slope over the latest configurable window (default eight available observations).
  • Volatility: sample standard deviation of annualized changes; gaps longer than one year are divided by elapsed years rather than mislabeled year-over-year moves.
  • Gaps: missing integer years between the dataset's minimum and maximum year. No interpolation.
  • Data readiness: a completeness/freshness grade using the requested-series denominator, core-series presence, source failures, configurable as-of year, absolute staleness, and vintage alignment. It is not a confidence score about the economy or scenario.
  • Regime: transparent thresholds across growth, price, financial, external, and fiscal pressure. Each rationale carries an OBS.* evidence ID.

Shock method

Raw shock inputs are normalized into domain units—for example, 100 basis points for the policy-rate front and 10% for energy/FX. Each graph edge stores central, low, and high coefficients, lag label, transmission channel, and explicit illustrative-assumption provenance. A seeded simulation draws triangular values from the input band and coefficient ranges. It does not fit coefficients to the loaded country history. GDP, inflation, and current-account baselines retain their own years; excessive vintage gaps are surfaced rather than silently blended.

An assumption flip solves the driver value needed to cross a watch threshold while holding other central contributions fixed. This makes the model challengeable: the user can see which assumption would reverse the status and verify that exposure outside MacroScope.

AI and BYOK boundary

Supported provider presets

Provider Adapter Default endpoint family
Offline Mock local normalized response mock://offline
OpenAI OpenAI-compatible chat completions https://api.openai.com/v1
Anthropic native Messages API https://api.anthropic.com/v1
Google Gemini native generateContent https://generativelanguage.googleapis.com/v1beta
DeepSeek OpenAI-compatible https://api.deepseek.com/v1
Moonshot Kimi OpenAI-compatible https://api.moonshot.cn/v1
Zhipu GLM OpenAI-compatible https://open.bigmodel.cn/api/paas/v4
Custom / relay chosen adapter user-supplied base URL + model

Provider defaults are editable because model names and endpoints evolve. Test key requires an adapter-specific probe marker instead of accepting any HTTP 200 object, and surfaces invalid-key, missing-model, balance/quota, rate-limit, timeout, network, bad-response, and upstream errors. Provider responses are capped at 2 MiB; redirects are disabled; DNS is resolved and pinned while TLS verifies the original hostname.

What AI receives

  • Extract mode: only the pasted policy text and a strict schema requiring measures, dates, institutions, direct quotes, ambiguities, and confidence. Every direct_quote must be an exact normalized substring of that policy text.
  • Explain mode: a compact deterministic bundle containing source-backed observations, regime evidence, graph version/fingerprint, shocks, outcomes, intervals, and flip IDs; optional policy text is clearly separated.

Numeric grounding

The server validates mode-specific JSON schemas, verifies direct quotes / quoted evidence against source text, extracts numeric tokens and OBS.* / SERIES.* / SCENARIO.* / EDGE.* / FLIP.* IDs, and compares them with the supplied sources. Unsupported output returns HTTP 422 and is not displayed as a valid annotation. This is a strict provenance guard, not a proof that every sentence is economically correct.

Task resilience

Long and repeated work is represented as a local task state machine:

DRAFT → INGESTING → DATA_READY → OBSERVING → OBSERVATORY_READY
      → SIMULATING → SCENARIO_READY → AI_PENDING → COMPLETE

Interrupted provider work can become AI_INTERRUPTED, a grounding rejection becomes PARTIAL, and a user cancellation becomes CANCELLED. The interface shows phase and progress, uses AbortController to stop waiting, preserves completed stages, and lets the user resume a saved bundle from Archive after refresh.

Task history stores sanitized country data, assumptions, deterministic results, AI annotations, token usage, progress, and errors. The recursive sanitizer removes key-like fields before SQLite persistence.

Exports

Export File Use
Briefing draft macroscope-briefing-draft.md readable meeting brief with evidence IDs and method limits
Decision record macroscope-decision-record.json auditable archive or downstream workflow input
Outcome table macroscope-outcomes.csv portable baseline/central/P10/P90/threshold table

The decision record includes the human status, owner, review date, chosen next action, country/source log, data readiness (completeness, freshness, as-of date, and source failures), regime, illustrative graph version/fingerprint/provenance, shocks, each outcome's baseline year and vintage gap, bounded outcomes, flip points, alerts, optional AI annotations, token ledger, and consolidated limitations.

See the checked-in example briefing and example decision record for the Germany fixture.

HTTP API

Method Path Purpose
GET /api/health local readiness and AI/numeric boundary
GET /api/session per-process CSRF token and network-mode metadata
GET /api/catalog indicator metadata and default graph
GET /api/graph?version=2026.1 inspect a graph vintage
GET /api/world-bank/country/CHN?start=2015&end=2024 no-key official country ingest
POST /api/ingest normalize CSV/JSON fallback
POST /api/observe trends, volatility, gaps, quality, regime
POST /api/scenario graph propagation, intervals, flips
POST /api/validate provider/model test
POST /api/ai grounded extraction or explanation
POST /api/export Markdown, JSON, or CSV artifact
GET/POST/PUT/DELETE /api/tasks[/<id>] history, progress, resume, cancel, cleanup

All endpoints are same-origin and localhost-oriented. Request bodies are size-limited to 8 MiB. Static serving blocks path traversal.

Project Structure

repo/
├── app/
│   ├── http_app.py          # stdlib HTTP API and static server
│   ├── world_bank.py        # no-key official data client + partial failure ledger
│   ├── provider_gateway.py  # OpenAI/Anthropic/Gemini adapters
│   ├── security.py          # URL safety, key validation, redaction
│   ├── mock_provider.py     # offline extraction/explanation demo
│   └── task_store.py        # key-free SQLite checkpoints
├── domain/
│   ├── schema.py            # canonical indicators and aliases
│   ├── ingest.py            # CSV/JSON normalization and quality
│   ├── observatory.py       # trends, volatility, gaps, regime
│   ├── transmission.py      # versioned graph, intervals, flip points
│   ├── policy_ai.py         # AI prompts and numeric grounding
│   └── exports.py           # briefing and decision artifacts
├── fixtures/                # six country stations + malformed edge case
├── web/                     # warm editorial weather-bureau UI
├── tests/                   # 30 unit, integrity, security, and integration tests
├── scripts/smoke_test.py    # offline end-to-end smoke test
└── server.py                # Python 3.8-compatible entry point

Tests

Run the full suite:

python -m unittest discover -s tests -v

Run the offline end-to-end smoke test:

python scripts/smoke_test.py

Compile every Python module under Python 3.8:

python -m compileall -q app domain tests scripts server.py

The 30 tests cover CSV/JSON import, multi-country rejection, missing-year and annualized-gap handling, readiness/freshness/as-of logic, malformed/zero/unknown-version/out-of-range inputs, baseline years and vintage gaps, illustrative graph fingerprints/provenance, reproducible uncertainty, assumption flips, strict AI schemas and quote substrings, secret allowlisting, Host/Origin/CSRF/network-mode boundaries, DNS-pinned no-redirect provider calls, response caps, CAS cancellation, archive hydration, World Bank normalization, the HTTP pipeline, offline AI, and exports.

Security and privacy

  • API keys are entered as masked browser fields and held in memory by default.
  • API keys are page-memory only and clear on reload. Optional tab-session persistence stores provider/protocol/base URL/model metadata in sessionStorage, never the key; localStorage is not used.
  • The backend receives the key only for the selected provider request and never writes it to SQLite or exports.
  • Request bodies and headers are never logged.
  • Remote custom endpoints require HTTPS; loopback HTTP is allowed for local relays. DNS is pinned, private/link-local/metadata targets are rejected, redirects are disabled, and responses are capped.
  • Provider error text is length-limited and credential-like strings are redacted.
  • Local writes require the random token from /api/session, X-MacroScope-CSRF, a valid loopback Host, same-origin request context, and JSON where applicable. Non-loopback binding is rejected unless --unsafe-network and an authentication token of at least 16 characters are supplied.
  • A same-origin Content Security Policy blocks external scripts, object embedding, framing, and cross-origin connections.
  • The app is local-first, but a real provider call sends the bounded prompt and policy text to that provider. Review the provider's data policy before using sensitive text.

Explicit network-mode startup:

python server.py --host 0.0.0.0 --unsafe-network --auth-token "replace-with-at-least-16-characters"

Limitations

  • World Bank series have country-specific lags, definitions, revisions, and gaps. The real-interest-rate series is a policy/financial-conditions proxy, not a harmonized central-bank policy-rate feed.
  • Regime thresholds are transparent monitoring rules, not estimated recession or crisis probabilities.
  • Shock coefficients are versioned scenario assumptions shipped with the repository. They are not country-calibrated causal estimates.
  • Bounded P10/P90 output reflects selected input and coefficient ranges; it is not a confidence interval derived from historical forecast errors.
  • Assumption flips hold other central contributions fixed and do not model nonlinear feedback, time-varying parameters, or endogenous policy response.
  • Numeric grounding blocks new numeric tokens and evidence IDs; it does not independently fact-check qualitative model prose.
  • Client cancellation stops the browser wait and marks the checkpoint cancelled. A request already received by an external provider may still finish upstream, but its response is discarded if the task remains cancelled.
  • The server is designed for one trusted local user, not as a hardened multi-tenant internet service.

Roadmap

  • User-editable graph versions with signed manifests and diff view
  • Country-specific coefficient packs backed by cited research
  • Quarterly/monthly frequency adapters and revision-vintage comparison
  • Multi-country spillover layer and trade-weighted external-demand fronts
  • Optional time-series forecast evaluation with proper backtesting
  • Signed decision-record bundles and organization review workflow

License

MIT. See LICENSE.


English · 简体中文
Observe first. Disturb assumptions second. Let AI write last.

Releases

Packages

Contributors

Languages