- 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>
74 lines
2.6 KiB
Python
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()
|