Godstrument/workers/world_econ.py
monsterrobotparty a039ee7d7c Add dark/yang data, concept groups, named templates, editing console, Strudel kit
- world_debt.py + world_econ.py: US national debt + inflation (the dark half)
- concept groups: "the shadow" / "the bright" — group sources by feeling
- named templates: hub save_patch/load_patch + console TEMPLATES panel
- editing console: runtime add/remove/set route + make_group; drag-to-patch,
  shift-click inspector, cmd-select grouping in the viz
- planetary orbital LFOs per group; almanac + ephemeris workers
- Web MIDI (out + learn) in the console; strudel/godstrument.md starter kit

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 02:25:19 +10:00

74 lines
2.6 KiB
Python

"""
world_econ.py — the cost of living. Inflation as pressure.
Pulls US CPI (consumer price index) from the Bureau of Labor Statistics (no key,
but rate-limited — so it polls rarely; CPI is monthly data anyway) and computes
the year-over-year inflation rate. Inflation is a quiet, grinding pressure — it
never trends toward zero, it just erodes. Good as a slow "tension" source in the
dark half of a set.
Emits:
/gs/econ/inflation year-over-year CPI change, percent (~0..10)
/gs/econ/cpi_mom latest month-over-month change, percent
"""
from __future__ import annotations
import argparse
import json
import time
import urllib.request
from pythonosc.udp_client import SimpleUDPClient
URL = "https://api.bls.gov/publicAPI/v2/timeseries/data/CUUR0000SA0" # CPI-U all
UA = "Godstrument/1.0 (live instrument)"
def fetch():
req = urllib.request.Request(URL, headers={"User-Agent": UA})
with urllib.request.urlopen(req, timeout=30) as r:
rows = json.load(r)["Results"]["series"][0]["data"]
latest = rows[0]
idx = float(latest["value"])
# month-over-month (previous data point)
mom = ((idx / float(rows[1]["value"])) - 1) * 100 if len(rows) > 1 else 0.0
# year-over-year: same period one year earlier
yoy = 0.0
for row in rows:
if row["year"] == str(int(latest["year"]) - 1) and row["period"] == latest["period"]:
yoy = (idx / float(row["value"]) - 1) * 100
break
return yoy, mom, latest["periodName"] + " " + latest["year"]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--host", default="127.0.0.1")
ap.add_argument("--port", type=int, default=9000)
ap.add_argument("--poll", type=float, default=21600.0, help="re-query every Ns (BLS is rate-limited)")
args = ap.parse_args()
client = SimpleUDPClient(args.host, args.port)
print(f"[econ] emitting /gs/econ/inflation|cpi_mom (US CPI) "
f"(re-query every {args.poll:g}s)")
yoy, mom, last = 0.0, 0.0, None
last_poll = 0.0
while True:
try:
if time.time() - last_poll > args.poll or last is None:
yoy, mom, label = fetch()
last_poll = time.time()
if label != last:
print(f" 📈 inflation {yoy:.2f}% YoY ({label}, {mom:+.2f}% MoM)")
last = label
client.send_message("/gs/econ/inflation", yoy)
client.send_message("/gs/econ/cpi_mom", mom)
except Exception as e: # noqa
print(f"[econ] warn: {e}")
time.sleep(10.0)
if __name__ == "__main__":
main()