Godstrument/workers/world_econ.py
type-two e69b7e2ad6 Accounts, socio-economic feeds, and hub fixes (track deployed work)
Captures backend work already running on godstrument.pro but never committed:
- auth.py: invite-only accounts + per-user presets (SQLite, scrypt, signed
  stateless sessions), mounted by hub.py at /api/*
- hub.py: /api static+API handler, readonly public mode, full route spec in hello
- socio-economic / market / fire / debt / crypto workers + normalize/transform
- .gitignore: never commit godstrument_users.db* or auth_secret
- test_fixes.py: framework-free self-check for the review fixes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 16:18:35 +10:00

78 lines
2.9 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
next_poll, retry = 0.0, 60.0
while True:
if time.time() >= next_poll:
try:
yoy, mom, label = fetch()
if label != last:
print(f" 📈 inflation {yoy:.2f}% YoY ({label}, {mom:+.2f}% MoM)")
last = label
next_poll, retry = time.time() + args.poll, 60.0
except Exception as e: # noqa
# back off on failure: BLS allows ~25 req/day unregistered, so a
# 10s retry-storm would exhaust the quota and lock us out all day
print(f"[econ] warn: {e}; retry in {retry:.0f}s")
next_poll = time.time() + retry
retry = min(retry * 2, args.poll)
client.send_message("/gs/econ/inflation", yoy)
client.send_message("/gs/econ/cpi_mom", mom)
time.sleep(10.0)
if __name__ == "__main__":
main()