event-calendar · git:20261105.16f88fa · 2026-11-05 · sha256 94995601c950d01b

event-calendar git:20261105.16f88faA

Immutable. This exact content never changes and is served at /api/v1/blob/94995601c950d01b.

---
name: event-calendar
description: Audit and refresh the desks' scheduled-event calendar (services/intel/data/scheduled_events.json) - elections, jobs/CPI/PCE/GDP, FOMC, OPEC+, IEA/OPEC reports, funding deadlines, Congress, holidays, contract expiries, China data. Run when the desk brief warns "event calendar ends", monthly, or before any election or big political date.
---

# event-calendar

Operator, 2026-11-05, after the 11-03 midterms dropped Brent 9% with no
election on the calendar: "I NEED YOU TO AUDIT THE EVENT CALENDAR AND TAKE
THEM INTO ACCOUNT WHEN YOU MAKE DECISIONS... ELECTIONS, BILLS, ALL THAT".

What uses the calendar:
- **Thesis:** every row becomes an intel catalyst
  (`services/intel/sources/catalysts.py::scheduled_catalysts`).
- **Desk brief:** the "Events next 72h" line.
- **Blackout:** impact-3 rows block new entries
  (`services/strategies/event_blackout.py::event_blackout`). They are enforced
  in `place_bracket` and `trigger_watch.py`.
- **Generated, not in the file:** FOMC decisions, CPI, OPEC+ meetings, EIA,
  API, Baker Hughes and CFTC come from `services/intel/calendar_us.py`. Update
  those tables there (FOMC_DECISION_DATES_*, CPI_RELEASE_DATES,
  OPEC_PLUS_MEETINGS), not in the JSON.

## Steps

1. **Read the file** and note `valid_through`.
2. **Research forward** to at least 90 days past today. Use one
   general-purpose agent with web search, and official sources first:
   - US data: bls.gov, bea.gov.
   - Central bank: federalreserve.gov.
   - Oil: opec.org, iea.org, eia.gov, ice.com, cmegroup.com.
   - Politics: congress.gov, state election sites. Also look for scheduled
     Iran/Hormuz talks, sanctions deadlines and IAEA board dates.

   Every row needs: date, at_utc (or null for all day or unknown), event,
   kind, coins (oil and/or metals; "silver" is the old name of the metals group and still accepted), impact (1 to 3), verified, source.
   Unverified dates stay `verified: false`. Never guess a time.
3. **Impact 3 is for events that can move price 2% or more within hours.**
   Those block entries. Keep it to:
   - FOMC
   - CPI
   - OPEC+ ministerials
   - national elections or referendums
   - a dated war, sanctions or peace deadline

   Jobs, PCE, IEA and OPEC monthly reports, funding deadlines and holidays
   are 2. Elections get their own window: `before_h` from 24h before the
   polls close, `after_h` until results are in.
4. **Update the file.** Bump `refreshed` and `valid_through`. Keep past rows:
   they are the record the what-if eval replays.
5. **Check:**
   - `.venv/bin/python -m pytest -q services/intel/tests/test_scheduled_events.py`
   - `python3 -m services.strategies.evals.event_direction_test` (what a
     short or long through each past event did; information only)
6. **Commit and push.** Say "reconnect pathiel MCP (/mcp) in the desk loops
   window" so place_bracket and the brief reload the file.

## Scan (every 12 hours)

`/event-calendar scan`. The desk-loops cron runs it at 17 past, every 12
hours (operator, 2026-11-05: "a scan every 12 hours to get new events and
wire them into our trading decision"). It adds to the overlay at
`$PATHIEL_STATE_DIR/intel/scanned_events.json`; it never edits
`services/intel/data/scheduled_events.json`. A curated row wins on a
(date, kind, title) conflict; the brief shows scanned rows with a `(scan)`
tag. Keep it cheap: one script, one agent, no more.

1. **Deterministic half.** `python3 scripts/event_scan.py` (no LLM):
   congress.gov committee meetings and hearings for the next 21 days
   (Foreign Relations, Energy and Natural Resources, Foreign Affairs, Energy
   and Commerce, Armed Services, Appropriations always; any committee whose
   title matches Iran, Hormuz, oil, sanctions, OPEC, war powers, NDAA...),
   new actions on the watched bills (H.R. 1422 and S. 556, the Iran-oil
   sanctions bills, plus bills on a kept markup's agenda), and Federal
   Register documents with an effective date or comment deadline in the
   window. It prints and logs one `EVENT-SCAN ...` line. Without
   `CONGRESS_API_KEY` it says `skipped=congress_...` and still runs the
   Federal Register. `--dry-run` prints without writing.
2. **Research half.** ONE general-purpose research agent with web search,
   for the next 21 days from today: dated Iran/Hormuz talks and deadlines,
   US-Russia-Ukraine talks, OPEC+ meetings not in `calendar_us.py`,
   Congress floor schedules (cloture votes, NDAA, appropriations, sanctions
   bills), sanctions deadlines (snapback, waivers, general licenses expiring),
   summits (G7, G20, NATO, Trump travel), Fed speakers (Powell, Waller,
   Williams), China data (NBS, trade, PBoC). It returns rows only: date,
   at_utc (or null), event, kind, coins, impact, verified, source, note.
   Official page first; otherwise `verified: false`. No row without a source.
3. **Write each row** with `scripts/event_add.py` (the one writer; it
   validates, dedups on date + kind + title, and refuses impact 3):

       python3 scripts/event_add.py --json '{"date": "2026-11-18", "at_utc": null, "event": "...", "kind": "iran_talks", "coins": ["oil", "silver"], "impact": 2, "verified": false, "source": "https://..."}'

   Impact 3 only for FOMC, CPI, an OPEC+ ministerial, a national election,
   or a dated war/sanctions/peace deadline; pass `--allow-impact-3` for
   exactly those and say so in the note. Everything else is 1 or 2. Never
   `--allow-impact-3` for a hearing, a report, a speech or a summit.
   Skip anything `calendar_us.py` generates (FOMC decisions, CPI, OPEC+
   meetings, EIA, API, Baker Hughes, CFTC). `python3 scripts/event_add.py
   --list` shows the overlay; `--prune-before <DATE>` drops old rows.
4. **End with one line** appended to `/tmp/oil-loop/progress.log`:
   `EVENT-SCAN-RESEARCH <utc> added=N updated=M refused=R` (what the agent
   found, what event_add took). Say nothing else in the loops window
   unless a row is impact 3 or a new blackout starts within 24h: then
   one sentence to the operator.
5. **Promote, do not edit.** A scanned row the operator wants permanent
   moves into the curated file in a normal refresh (steps above), with a
   commit; the scan then reports it as `curated` and stops carrying it.

## Rules

- Unscheduled headlines (a deal, a strike) are the thesis's job (news,
  Polymarket), not this file's.
- A blackout never touches an open position, its stop, target or a close.
- Do not change BEFORE_H/AFTER_H or BLACKOUT_IMPACT without the operator's go.