File size: 4,803 Bytes
2c1c4d5
 
 
1320d43
 
 
d2180f1
 
2c1c4d5
 
 
 
 
7ac8c92
 
2c1c4d5
 
f9eb3ae
 
 
 
2c1c4d5
f9eb3ae
7ac8c92
d2180f1
1320d43
7ac8c92
36d8afc
 
1320d43
 
 
2c1c4d5
 
 
1320d43
 
2c1c4d5
 
1320d43
2c1c4d5
 
 
f9eb3ae
2c1c4d5
 
1320d43
f9eb3ae
d2180f1
 
 
 
2c1c4d5
 
 
f9eb3ae
 
2c1c4d5
 
f9eb3ae
1320d43
d2180f1
1320d43
 
d2180f1
1320d43
 
 
 
 
 
d2180f1
1320d43
 
d2180f1
1320d43
36d8afc
2c1c4d5
 
 
 
 
 
 
 
d2180f1
1320d43
2c1c4d5
2fbf000
f9eb3ae
2c1c4d5
2fbf000
7ac8c92
f9eb3ae
2c1c4d5
f9eb3ae
2c1c4d5
5819bad
2c1c4d5
 
2fbf000
5819bad
2c1c4d5
1320d43
d2180f1
1320d43
2c1c4d5
1320d43
 
 
2c1c4d5
f9eb3ae
 
2c1c4d5
 
f9eb3ae
2c1c4d5
2c56e40
5819bad
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# app.py
#
# Hugging Face Space – Live U.S. Debt Clock
# β€’ Pulls the last 40 Treasury β€œDebt to the Penny” points
# β€’ Derives a $/sec rate from point-0 and point-30 (or falls back to the last
#   two distinct points when needed)
# β€’ Projects the current debt from the timestamp (00:01 UTC) of the latest
#   record all the way to *now*β€”no 12-hour guard
#
# Endpoints
#   /            – simple text β€œrunning” check
#   /api/debt    – JSON { startingDebt, ratePerSecond, asOf }
#   /api/ping    – lightweight uptime probe
#
# ────────────────────────────────────────────────────────────

import os, time, threading, requests
from flask import Flask, jsonify

app = Flask(__name__)

# ─────────────────── runtime state ────────────────────
DEBT_STATE = {
    "debt_at_record": 0.0,   # value at latest record date
    "record_time":    0.0,   # epoch seconds for that record date (00:01 UTC)
    "rate_per_sec":   0.0,   # $/s derived from chosen points
    "last_refresh":   0.0
}

# ─────────────────── config ───────────────────────────
HORIZON = 30    # how many records back to measure the slope

API = (
    "https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v2/"
    "accounting/od/debt_to_penny"
    f"?fields=record_date,tot_pub_debt_out_amt&sort=-record_date&page[size]={HORIZON+10}"
    # 10 extra records just in case identical values need skipping
)

# ─────────────────── helpers ──────────────────────────
def load_debt_records():
    r = requests.get(API, timeout=15)
    r.raise_for_status()
    return [
        (item["record_date"], float(item["tot_pub_debt_out_amt"]))
        for item in r.json()["data"]
    ]   # newest first

def epoch_0001utc(date_str: str) -> float:
    """Convert YYYY-MM-DD to seconds since epoch at 00:01 UTC of that day."""
    tm = time.strptime(date_str, "%Y-%m-%d")
    return time.mktime(tm) + 60          # add 60 s β†’ 00:01 UTC

# ─────────────────── background refresher ────────────
def refresher():
    while True:
        try:
            recs = load_debt_records()             # newest first
            (d0_date, d0_val) = recs[0]

            # attempt the long-horizon slope
            candidate = recs[HORIZON] if len(recs) > HORIZON else None

            if candidate and candidate[1] != d0_val:
                (dk_date, dk_val) = candidate       # point-30
            else:
                # fall back to previous distinct point
                dk_date, dk_val = next(
                    (d for d in recs[1:] if d[1] != d0_val),
                    (None, None)
                )
                if dk_date is None:
                    raise ValueError("Could not find two distinct points")

            t0, tk = epoch_0001utc(d0_date), epoch_0001utc(dk_date)
            rate   = (d0_val - dk_val) / (t0 - tk)  # $/s (may be negative)

            DEBT_STATE.update(
                debt_at_record = d0_val,
                record_time    = t0,
                rate_per_sec   = rate,
                last_refresh   = time.time()
            )

            print(
                f"[refresh] latest={d0_date}  debt={d0_val:,.2f}  "
                f"rate={rate:,.2f}$/s  horizon={t0 - tk:.0f}s"
            )

        except Exception as e:
            print("Debt refresh error:", e)

        time.sleep(300)  # five-minute cycle

threading.Thread(target=refresher, daemon=True).start()

# ─────────────────── routes ──────────────────────────
@app.route("/")
def root():
    return "βœ… Debt clock running", 200

@app.route("/api/debt")
def api_debt():
    now     = time.time()
    elapsed = max(0.0, now - DEBT_STATE["record_time"])  # **no 12-h guard**
    current = DEBT_STATE["debt_at_record"] + DEBT_STATE["rate_per_sec"] * elapsed
    return jsonify(
        startingDebt  = current,
        ratePerSecond = DEBT_STATE["rate_per_sec"],
        asOf          = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(now))
    )

@app.route("/api/ping")
def api_ping():
    return {"status": "ok"}, 200

# ─────────────────── main ────────────────────────────
if __name__ == "__main__":
    port = int(os.environ.get("PORT", 7860))
    app.run(host="0.0.0.0", port=port)