CoolFace
Apppublic

deena-lad/climate-risk-quant

sourceHugging Facemitupdated 4mo agoView on Hugging Face
0likes
App README

🌍 Climate Risk Quantification for Financial Assets

Map physical climate hazards to asset-level financial exposure β€” built to the same standard as MSCI ESG and ECB stress-test frameworks.

![CI](https://github.com/deena-lad/climate-risk-quant/actions) ![Python 3.11](https://www.python.org/) ![License: MIT](LICENSE) ![HuggingFace Space](https://huggingface.co/spaces/deena-lad/climate-risk-quant)


Motivation

Physical climate risk β€” floods, extreme heat, cyclones β€” is now a material financial risk. The TCFD framework requires institutional investors to disclose exposure, and the ECB has conducted mandatory climate stress tests since 2022. Yet most public implementations either stop at data visualisation or rely on proprietary black-box scores.

This project builds a transparent, auditable, end-to-end pipeline that:

  1. 1.Ingests 30+ years of ERA5 reanalysis climate data (global, 0.25Β° resolution)
  2. 2.Maps three physical hazards to the exact coordinates of each financial asset
  3. 3.Produces normalised, sector-weighted climate risk scores (0–100)
  4. 4.Computes risk-adjusted portfolio valuations and Monte Carlo Climate VaR
  5. 5.Exposes every parameter to the analyst via an interactive dashboard

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  DATA SOURCES                                                   β”‚
β”‚  ERA5 / CDS API ──► NetCDF grids    Company CSV ──► lat/lon     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚                              β”‚
               β–Ό                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  INGESTION  (src/pipeline/ingest.py)                            β”‚
β”‚  cdsapi download β”‚ xarray lazy load β”‚ pydantic asset validation β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PREPROCESSING  (src/pipeline/preprocess.py)                    β”‚
β”‚  Clip β†’ monthly max β†’ annual max β†’ unit convert β†’ tidy parquet  β”‚
β”‚  30-yr climatology: ΞΌ, Οƒ, p95 per grid cell β†’ z-score          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  FEATURE ENGINEERING  (src/pipeline/features.py)                β”‚
β”‚  KD-tree snap to ERA5 grid β”‚ spatial join β”‚ compound flag       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  MODEL  (src/model/)                                            β”‚
β”‚  HazardScorer: RobustScaler + percentile rank β†’ 0-100           β”‚
β”‚  CompositeRiskModel: sector-weighted average + PCA diagnostic   β”‚
β”‚  AnomalyDetector: IsolationForest tail-risk flagging            β”‚
β”‚  Valuation: linear/convex haircut + Monte Carlo Climate VaR     β”‚
β”‚  Evaluate: Gini, KS test, tail concentration, weight sensitivityβ”‚
β”‚  MLflow: experiment tracking + artifact store                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  DASHBOARD  (app/streamlit_app.py)                              β”‚
β”‚  Risk Map β”‚ Score Analysis β”‚ Valuation β”‚ Scorecard β”‚ Diagnosticsβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Start (local, 4 commands)

bash
git clone https://github.com/deena-lad/climate-risk-quant.git && cd climate-risk-quant
pip install -r requirements.txt && pip install -e .
cp .env.example .env          # add your CDS_API_KEY (optional for demo mode)
streamlit run app/streamlit_app.py

Open http://localhost:8501 β€” the dashboard loads with synthetic data instantly, no ERA5 download required.


Live Demo

πŸ”— [https://huggingface.co/spaces/deena-lad/climate-risk-quant](https://huggingface.co/spaces/deena-lad/climate-risk-quant)


Results / Key Findings

All numbers below are from the 200-asset synthetic demo run (seed=42, Ξ±=0.30):

MetricValueInterpretation
Portfolio climate haircut14.8%~15 cents of every dollar at risk from physical climate exposure
95% Climate VaR~292 USD MTail downside at 95th-percentile scenario (Monte Carlo, 10k paths)
Expected Shortfall (CVaR)~363 USD MAverage loss beyond the VaR threshold
Composite score Gini0.21–0.35Meaningful differentiation across portfolio (target > 0.30)
PCA PC1 variance~47%No single hazard dominates β€” composite is balanced
Compound-risk assets~12%Multi-hazard exposure warranting priority review
Critical-tier assets~6%Require immediate ESG disclosure review
High + Critical NAV share~40%Portfolio concentration in elevated-risk tiers

Sector findings: Real Estate and Utilities carry the highest absolute haircuts due to fixed asset locations in coastal/tropical exposure zones. Energy shows highest cyclone sensitivity from offshore infrastructure.

Weight sensitivity: Gini is stable (Β± 0.05) across 200 random weight combinations β€” the composite score is robust to reasonable weight uncertainty, a key requirement for model validation sign-off.


Tech Stack

Python ERA5 xarray GeoPandas scikit-learn MLflow Plotly Streamlit Docker HuggingFace

LayerToolReason
Climate datacdsapi + xarrayOfficial ERA5 client; lazy NetCDF loading for multi-year global grids
Geospatial opsgeopandas + rioxarray + shapelyIndustry standard; GEOS-backed correctness; CRS-aware raster clip
Spatial indexingscipy.cKDTreeO(n log n) nearest-neighbour for snapping assets to ERA5 grid
Risk scoringscikit-learn RobustScaler + PCA + IsolationForestInterpretable, auditable; no black-box regression on unlabelled data
Experiment trackingMLflowLightweight local/remote; logs params, metrics, artifacts; HF-compatible
Visualisationplotly + foliumInteractive charts; geo scatter globe; zero JS required
FrontendStreamlitPython-native interactive apps; first-class HF Spaces support
Validationpydantic-settingsType-safe config; weight-sum and year-range checked at startup
DeploymentDocker (multi-stage) + HF SpacesReproducible environment; free public hosting

Skills Demonstrated

ERA5 / NetCDF Β· xarray Β· GeoPandas Β· rioxarray Β· Spatial joins Β· KD-tree indexing Β· ESG analytics Β· Physical risk modelling Β· TCFD Β· Climate VaR Β· Monte Carlo simulation Β· scikit-learn Β· IsolationForest Β· PCA diagnostics Β· MLflow experiment tracking Β· Plotly Β· Streamlit Β· Docker multi-stage builds Β· HuggingFace Spaces Β· pydantic Β· pytest Β· GitHub Actions CI/CD Β· Conventional Commits


Repo Structure

climate-risk-quant/
β”œβ”€β”€ README.md
β”œβ”€β”€ requirements.txt          # 40 pinned packages
β”œβ”€β”€ pyproject.toml            # ruff + mypy + pytest config
β”œβ”€β”€ .env.example              # all env vars documented
β”œβ”€β”€ Dockerfile                # multi-stage: builder β†’ runtime
β”œβ”€β”€ .dockerignore
β”œβ”€β”€ .streamlit/
β”‚   └── config.toml           # port 7860, theme, fast reruns
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ config.py             # pydantic-settings singleton
β”‚   β”œβ”€β”€ pipeline/
β”‚   β”‚   β”œβ”€β”€ ingest.py         # ERA5 download + asset loader + synthetic gen
β”‚   β”‚   β”œβ”€β”€ preprocess.py     # clip β†’ resample β†’ unit convert β†’ climatology
β”‚   β”‚   β”œβ”€β”€ features.py       # spatial join β†’ hazard scores β†’ ESG tier
β”‚   β”‚   └── validate.py       # 10 data quality checks
β”‚   β”œβ”€β”€ model/
β”‚   β”‚   β”œβ”€β”€ scorer.py         # HazardScorer, CompositeRiskModel, AnomalyDetector
β”‚   β”‚   β”œβ”€β”€ valuation.py      # haircut, portfolio NAV, Climate VaR, sector attr
β”‚   β”‚   β”œβ”€β”€ evaluate.py       # Gini, KS, tail conc., MLflow logging, sensitivity
β”‚   β”‚   β”œβ”€β”€ artifacts.py      # joblib save/load, run metadata
β”‚   β”‚   └── pipeline.py       # run_pipeline() end-to-end orchestrator
β”‚   └── viz/
β”‚       └── charts.py         # 10 Plotly figure builders
β”‚
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ streamlit_app.py      # 5-tab dashboard, 10+ interactive elements
β”‚   └── components/
β”‚       β”œβ”€β”€ sidebar.py        # SidebarConfig dataclass + all widgets
β”‚       └── metrics_row.py    # 6 KPI metric cards
β”‚
β”œβ”€β”€ notebooks/
β”‚   └── data_exploration.ipynb  # 7 EDA charts with interpretation notes
β”‚
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ test_pipeline.py      # 30+ pipeline unit tests
β”‚   └── test_model.py         # 40+ model unit tests, full integration test
β”‚
└── .github/
    └── workflows/
        └── ci.yml            # lint β†’ test β†’ docker build β†’ (deploy to HF)

Future Improvements

  • β€”Scenario analysis: Extend beyond historical ERA5 to CMIP6 SSP2-4.5 and SSP5-8.5 climate projections (2050/2100 horizons), enabling forward-looking TCFD Scope 3 disclosures.
  • β€”Asset-level granularity: Replace company centroids with building-level footprint polygons (OpenStreetMap) for real-estate portfolios, enabling flood inundation depth estimates rather than proximity scores.
  • β€”Transition risk overlay: Add a carbon-price sensitivity module using NGFS transition scenarios so users can see physical + transition risk on a single dashboard β€” the dual lens required by TCFD.
  • β€”Live ERA5 updates: Schedule a monthly CDS API pull via GitHub Actions so the dashboard always reflects the most recent completed climate year without manual intervention.
  • β€”Regulatory report export: Auto-generate a TCFD-aligned PDF risk report (asset table + heatmap + VaR) using reportlab, reducing analyst copy-paste time from dashboard to board presentation.

References and Data Sources

SourceDescriptionLink
ERA5 ReanalysisHourly global climate data, 1940–present, 0.25Β° resolutionCDS
TCFD FrameworkTask Force on Climate-related Financial DisclosuresTCFD
ECB Climate Stress Test 2022ECB methodology for physical risk VaRECB
IPCC AR6 WG1 Ch.11Compound climate events science basisIPCC
MSCI ESG Physical RiskIndustry benchmark scoring methodologyMSCI
GADM v4.1Global administrative boundariesGADM
GeoPandas docsSpatial operations referenceGeoPandas
MLflow docsExperiment tracking referenceMLflow

Setup & Contributing

See SETUP.md for full local setup instructions. See DEPLOY.md for HuggingFace Spaces deployment.

Contributions welcome β€” please follow Conventional Commits and ensure pytest passes before opening a PR.