A quantitative geopolitical risk dashboard that combines financial market signals, news analytics (GDELT), OSINT-verified conflict events, and optional ground-truth event data (ACLED) to produce probabilistic assessments of Iran-Israel conflict escalation — along with actionable portfolio regime guidance.
Disclaimer: This tool is for informational and research purposes only. Nothing here constitutes financial, investment, legal, or professional advice. See the full Disclaimer section before use.
- Overview
- Key Features
- Scheduled Execution (GitHub Actions)
- Quick Start (Google Colab)
- Local Installation
- Configuration
- How It Works
- Signal Reference
- Market Tickers
- Output Files
- Example Output
- Understanding the Output
- Contributing
- Disclaimer
- License
The Iran Conflict Escalation Dashboard monitors 17 financial market instruments (including a physical tanker-equity Hormuz basket), up to 4 GDELT news query streams (with automatic RSS fallback), and a GitHub-hosted OSINT attack-wave database to generate a daily composite escalation score. A hybrid heuristic + Random Forest ML layer converts that score into three mutually exclusive probability states:
| State | Description |
|---|---|
| Escalation | Active conflict intensification likely |
| Stabilization | No dominant directional signal |
| De-escalation | Active conflict reduction or negotiation underway |
A parallel duration model estimates the most probable conflict timeline if escalation occurs:
| Scenario | Timeframe |
|---|---|
| Short conventional war | 2–4 weeks |
| Extended conflict | 1–3 months |
| Long proxy / hybrid conflict | 6m+ |
Both outputs are combined to generate a portfolio regime recommendation — a plain-language description of risk posture adjustments appropriate to each state.
- Zero-cost by default — Market data via
yfinance, news data via the free GDELT API, OSINT data from a public GitHub database. No paid subscriptions required. - GDELT integration — Queries conflict volume and sentiment tone across Iran/Israel, Strait of Hormuz, proxy attacks, and ceasefire/negotiation signals.
- RSS fallback — If GDELT rate-limits, the model automatically falls back to Google News RSS feeds via
feedparserso news signals are never completely dark. - OSINT event layer — Pulls a GitHub-hosted database of OSINT-verified Iran-Israel operations, contributing munitions-use and air-defense failure signals to the model.
- Optional ACLED support — Plug in ACLED credentials for ground-truth conflict event counts that feed directly into the model.
- Hybrid ML + heuristic model — A
scikit-learnRandom Forest classifier is trained on OSINT-verified operation days and its escalation probability is blended with the weighted heuristic score. - Temporal ML holdout — The Random Forest enforces a strict 60-day train/test split and reports holdout ROC-AUC separately, so overfitting on thin event history is visible rather than hidden.
- Empirical weight optimization — Market signal weights are derived from logistic regression on OSINT ground truth, replacing circular heuristic weighting.
- Causal / confirming signal taxonomy — Signals are tagged as
causal(drive the escalation score and regime trigger) orconfirming(market-return based, shown as a separate corroboration layer). This breaks the XLE → overweight XLE circular reference. - 37-signal composite model — Weighted, z-scored, and normalized across market, news, event, and physical-market dimensions. Explicitly covers the tripartite Iran-Israel-US conflict with dedicated signals for US strikes, American casualties, Israeli airstrikes, Hezbollah operations, CENTCOM force posture, Israeli Shekel, crude oil volatility, Wikipedia edit velocity, and GDELT structured conflict event counts.
- Physical Hormuz signals — Tanker-equity basket (FRO / STNG / DHT) and Brent-WTI spread added as forward-looking physical market proxies for Strait of Hormuz stress, supplementing GDELT tone signals.
- Sub-sector regime guidance — Regime recommendations broken to sub-sector level (upstream E&P, midstream, refining, defense primes, defense services) rather than generic ETF direction.
- OSINT lead/lag validation — Cross-correlation of OSINT operation days vs. market escalation score at lags −10 to +10 days. Result is typically near-coincident (lag=0 ± a few days); supports use as a real-time monitoring framework rather than a standalone timing signal.
- Iran Conflict Escalation Index (ICEI) — A 0–100 index mapped from the raw escalation score, with bootstrap confidence intervals from the last 30 trading days. Interpretive range guide in the PDF: 0–30 low pressure, 30–50 below-neutral, 50–70 mixed/stabilization, 70+ elevated escalation.
- Walk-forward out-of-sample backtest — A rolling evaluation where the model is tested on held-out 1-month windows it never trained on, giving a true out-of-sample AUC separate from the in-sample weight-optimization AUC. Both AUCs are reported in the validation metrics table.
- Backtest validation — ROC-AUC computed on the full market signal layer vs. OSINT-verified operation days (non-circular).
- Signal Coverage — Tracks the fraction of core signal families currently returning live data; displayed in the dashboard output as "Signal Coverage" (data availability, not forecast certainty).
- Regime-change alerting — The GitHub Actions daily run detects when the regime flips (e.g. Stabilization → Escalation) or when ICEI crosses key thresholds (30 / 70), and posts an alert comment to a pinned GitHub issue. Subscribe to the issue to receive email notifications.
- Data availability dashboard — Real-time layer status (Live / Partial / Down) for market, news, and event data sources.
- Crypto risk-off signal — Bitcoin price action added as an additional cross-asset risk indicator.
- Softmax probability outputs — Smooth probability distributions rather than hard threshold triggers.
- Automated PDF report — ReportLab-generated multi-page report with charts, probability tables, sub-sector guidance, regime change triggers, and ICEI interpretive range guide.
- Regime Change Triggers — The PDF includes a model-native table of conditions that would argue for a regime reassessment (e.g. sustained ICEI above 70, P(Escalation) exceeding P(Stabilization) for consecutive runs). Thresholds are directional guides, not calibrated confidence bounds.
- CSV exports — Full historical timeseries, latest-day snapshot, ICEI history, and data availability summary for downstream analysis.
- Interactive charts — Plotly-based visualizations for exploration in Colab/Jupyter.
- Google Drive persistence — All outputs (PDF, CSVs, charts) are written to a
IranDashboard/folder in your Google Drive so they survive Colab runtime disconnects. Uses the same Google auth as BigQuery — no extra credentials required. Controlled byUSE_DRIVEin Section 2b. - Colab-native — Designed to run top-to-bottom in Google Colab with no local setup required.
- Exponential backoff — Robust HTTP retry logic for GDELT queries in shared runtime environments.
A .github/workflows/daily_run.yml workflow runs the dashboard automatically twice daily — 08:00 and 15:30 CST (14:00 and 21:30 UTC) — every day and saves outputs as downloadable GitHub Actions artifacts (retained for 30 days). It can also be triggered manually from the Actions tab at any time.
To change the schedule, edit the cron: lines near the top of the workflow file. Times are in UTC; CST = UTC−6, CDT (summer) = UTC−5.
- Executes the notebook headlessly via papermill
- Falls back to GDELT REST API + RSS if no BigQuery credentials are configured
- Uploads the PDF report, CSVs, and charts as a run artifact
- Commits a lightweight
latest/snapshot (latest-day CSV + chart) back to the repo
Yes. Your API keys, email password, and GCP credentials are stored in GitHub's encrypted secrets vault — they are never written to any file in the repository. The workflow YAML only references them by name (e.g. ${{ secrets.ANTHROPIC_API_KEY }}); GitHub substitutes the real value at runtime and automatically masks it in all log output. Even if someone forks your public repo, they get the code but not your secrets. As long as you never paste a real key directly into a .yml file or notebook cell, nothing sensitive is exposed.
- Fork this repository to your own GitHub account (click Fork in the top-right corner of the repo page). This gives you your own copy where you can configure secrets and the workflow will run under your account.
- Go to your fork → Settings → Secrets and variables → Actions → New repository secret
- Add the following secrets:
Required for email digest:
| Secret | Value | Purpose |
|---|---|---|
ANTHROPIC_API_KEY |
sk-ant-... |
Claude generates the plain-English briefing |
MAIL_USERNAME |
you@gmail.com |
Sender address (Gmail recommended) |
MAIL_PASSWORD |
16-char app password | Gmail: myaccount.google.com/apppasswords (requires 2FA) |
MAIL_TO |
you@gmail.com,colleague@firm.com |
Comma-separated distribution list — everyone here gets every daily email |
MAIL_SERVER |
smtp.gmail.com |
SMTP server (default: Gmail) |
MAIL_PORT |
465 |
SMTP port (default: 465 SSL) |
Optional — for BigQuery GDELT (higher signal quality):
| Secret | Value | Purpose |
|---|---|---|
GCP_SERVICE_ACCOUNT_KEY |
JSON contents of a GCP service account key | Enables BigQuery GDELT |
GCP_PROJECT_ID |
Your GCP project ID | Required alongside the key above |
Creating a service account key: In the GCP Console, go to IAM & Admin → Service Accounts → Create Service Account, grant it the
BigQuery Job UserandBigQuery Data Viewerroles on thegdelt-bqproject, then create a JSON key. Paste the full JSON as theGCP_SERVICE_ACCOUNT_KEYsecret.
- Push any change to
main(or trigger manually from the Actions tab) — the workflow will run, generate a Claude summary, and email the distribution list.
What the daily email contains:
- A 250–350 word plain-English analyst briefing written by Claude, interpreting the regime, ICEI reading, probability split, and positioning implications
- A key metrics table (Regime, ICEI, P(Escalation), Escalation Score, Signal Coverage)
- Subject line is prefixed
⚠️ ALERT —on regime-change days - A link to the full PDF report and chart artifacts in GitHub Actions
Distribution list management: Edit the MAIL_TO secret to add or remove addresses. No code changes needed — just comma-separate the addresses.
pip install papermill ipykernel
python -m ipykernel install --user --name python3
# Without BigQuery (REST API fallback):
papermill conflict_escalation_dashboard_ml_pdf_v5.ipynb output.ipynb \
-p USE_DRIVE False -p OUTPUT_DIR outputs/ \
-p ENABLE_BIGQUERY False
# With BigQuery (service account via env var):
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json
papermill conflict_escalation_dashboard_ml_pdf_v5.ipynb output.ipynb \
-p USE_DRIVE False -p OUTPUT_DIR outputs/ \
-p ENABLE_BIGQUERY True -p GCP_PROJECT_ID your-project-idThe fastest way to run this dashboard is directly in Google Colab — no installation required.
-
Click the badge at the top of this README:
-
In the Colab menu, select Runtime → Run all (or press
Ctrl+F9). -
The notebook will:
- Install all required packages
- Pull live market data from Yahoo Finance
- Query the GDELT API for news signals (with RSS fallback)
- Pull the OSINT attack-wave database from GitHub
- Run the conflict escalation model
- Generate the ICEI with bootstrap confidence intervals
- Generate and save a PDF report + CSV exports
-
First run: Two Google sign-in popups will appear — one for Google Drive (Section 2b) and one for BigQuery (Section 5). Click through both with the same Google account. After that, outputs are saved automatically to My Drive → IranDashboard/.
Tip: Because outputs persist to Google Drive, you do not need to download files immediately. They will be waiting for you in your Drive after any runtime disconnect.
ACLED (optional): If you want ground-truth conflict event data, set your ACLED credentials before running Section 7. See Configuration.
- Python 3.8 or higher
- pip
# 1. Clone the repository
git clone https://github.com/SecondOrderEdge/Iran_Conflict_Dashboard.git
cd Iran_Conflict_Dashboard
# 2. (Recommended) Create a virtual environment
python -m venv venv
source venv/bin/activate # macOS / Linux
# venv\Scripts\activate # Windows
# 3. Install dependencies
pip install -r requirements.txt
# 4. Launch Jupyter
jupyter notebook conflict_escalation_dashboard_ml_pdf_v5.ipynbAll parameters are set in Section 3 of the notebook. Key variables:
| Variable | Default | Description |
|---|---|---|
USE_DRIVE |
True |
Persist all outputs to Google Drive (survives Colab disconnects). Set False to write to the local Colab filesystem instead. |
DRIVE_FOLDER |
IranDashboard |
Folder name inside My Drive where all outputs are saved. Created automatically on first run. |
LOOKBACK_DAYS |
90 |
Days of market history to display |
WARMUP_DAYS |
120 |
Pre-calculation buffer for rolling indicators. Set to 120 to support 120-day level signals. |
NEWS_LOOKBACK_DAYS |
30 |
Days of GDELT news history |
N_BOOTSTRAP |
500 |
Bootstrap resamples for ICEI confidence intervals |
VALIDATION_START |
2024-01-01 |
Start date for backtest / weight-optimization window |
ENABLE_GDELT |
True |
Enable/disable GDELT v2 REST API (used only if BigQuery is off or fails) |
ENABLE_BIGQUERY |
True |
Use BigQuery for GDELT instead of the rate-limited REST API (recommended) |
GCP_PROJECT_ID |
"" |
Your own Google Cloud project ID (required when ENABLE_BIGQUERY = True). Each user must supply their own — see GDELT setup below. |
ENABLE_OSINT |
True |
Enable/disable GitHub-hosted OSINT event database |
ENABLE_WEIGHT_OPTIMIZATION |
True |
Derive market signal weights via logistic regression on OSINT ground truth |
ENABLE_ACLED |
False |
Enable/disable ACLED conflict event data |
ACLED_EMAIL |
"" |
ACLED API email (env: ACLED_EMAIL) |
ACLED_PASSWORD |
"" |
ACLED API password (env: ACLED_PASSWORD) |
ACLED provides free access to conflict event data for researchers. To use it:
- Register for a free account at acleddata.com
- Set
ENABLE_ACLED = Truein Section 3 - Provide credentials via environment variables (recommended) or directly in the config cell:
# Option A: Environment variables (recommended)
import os
os.environ["ACLED_EMAIL"] = "your_email@example.com"
os.environ["ACLED_PASSWORD"] = "your_acled_api_key"
# Option B: Direct assignment (do NOT commit credentials to git)
ACLED_EMAIL = "your_email@example.com"
ACLED_PASSWORD = "your_acled_api_key"Security note: Never hard-code credentials in a notebook you plan to commit or share publicly.
The dashboard supports two GDELT access modes. Each user must choose one — there are no shared credentials.
BigQuery gives direct SQL access to the full GDELT dataset with no rate limits. Every user needs their own free Google Cloud project. The repository owner's project cannot be shared — each person brings their own.
Why your own project? The GDELT dataset is fully public. You are not paying to access the data — you are only paying for the compute to run the query. That cost is effectively zero for this notebook (well within Google's 1 TB/month free tier). There is no way to share this across users without exposing billing credentials, so each user sets up their own in ~10 minutes.
One-time setup per user:
- Go to console.cloud.google.com and sign in with any Google account
- Click the project dropdown → New Project → name it anything (e.g.
gdelt-dashboard) → Create - Go to APIs & Services → Enable APIs & Services → search
BigQuery API→ Enable - In Section 3 of the notebook, set:
ENABLE_BIGQUERY = True GCP_PROJECT_ID = "your-project-id" # ← replace with YOUR project ID, not the repo owner's
- Run the notebook — a Google sign-in popup appears in Colab on the first run. Click through once and you're done.
Important:
GCP_PROJECT_IDmust be your own project ID. Using someone else's project ID will fail with a permissions error. Your project ID is visible in the GCP Console header after you create it.
If you do not want to set up a GCP project, set ENABLE_BIGQUERY = False in Section 3. The notebook will query the free GDELT v2 REST API instead. This API is rate-limited and shared public infrastructure — it will frequently return empty responses during busy periods, falling back automatically to RSS-derived signals. Signal quality will be lower and less reliable than Option A.
ENABLE_BIGQUERY = False # no GCP account needed
ENABLE_GDELT = True # use REST API with RSS fallbackThe OSINT layer pulls a public SQLite database of OSINT-verified Iran-Israel operations from GitHub (OSINT_DB_URL in Section 3). No credentials required. Set ENABLE_OSINT = False to skip this layer.
┌──────────────┬──────────────────────────────────┬─────────────┬──────────────┐
│ Yahoo Finance│ News (priority order) │ OSINT DB │ ACLED │
│ (yfinance) │ 1. BigQuery GKG (recommended) │ (GitHub) │ (optional) │
│ │ 2. GDELT v2 REST API (fallback) │ │ │
│ 17 tickers │ 3. RSS via feedparser (fallback) │ Munitions & │ Ground-truth │
│ 90-day OHLC │ │ intercept │ event counts │
└──────┬───────┴──────────────┬───────────────────┴──────┬──────┴──────┬───────┘
│ │ │ │
└──────────────────────┴──────────────────────┴─────────────┘
│
build_indicator_table()
│
build_signal_table() ← 26 normalized signals
│
┌───────────────┴───────────────┐
│ Heuristic weighted score │
│ + Random Forest ML blend │
│ (trained on OSINT ground │
│ truth, non-circular) │
└───────────────┬───────────────┘
│
softmax → [p_deesc, p_stab, p_esc]
│
┌───────────────┴───────────────┐
│ ICEI (0–100 index) │
│ + portfolio regime │
│ + duration model │
└───────────────────────────────┘
Signal Construction
Scan report · 2026-09-29
- ✓ Prohibited terms or links
- ✓ Repository eligibility
- ✓ slopscore.md paperwork
- ✓ Content policy
- ✓ Risk review
From the balcony · 0 of 4 clapped
Princess, Crusoe, Schnitzel and Cap'm Slop read it and passed. Their reasons are on the balcony, with every other verdict.
Critics are accounts on this site with no GitHub account behind them. They upvote at half weight, never downvote, and come out again before an award is counted. Who they are.
0 comments
log in to comment.