fed-debt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fed_debt-0.1.0/LICENSE +21 -0
- fed_debt-0.1.0/PKG-INFO +108 -0
- fed_debt-0.1.0/README.md +81 -0
- fed_debt-0.1.0/pyproject.toml +45 -0
- fed_debt-0.1.0/setup.cfg +4 -0
- fed_debt-0.1.0/src/fed_debt/__init__.py +7 -0
- fed_debt-0.1.0/src/fed_debt/__main__.py +6 -0
- fed_debt-0.1.0/src/fed_debt/api.py +137 -0
- fed_debt-0.1.0/src/fed_debt/cli.py +116 -0
- fed_debt-0.1.0/src/fed_debt/ticker.py +163 -0
- fed_debt-0.1.0/src/fed_debt.egg-info/PKG-INFO +108 -0
- fed_debt-0.1.0/src/fed_debt.egg-info/SOURCES.txt +14 -0
- fed_debt-0.1.0/src/fed_debt.egg-info/dependency_links.txt +1 -0
- fed_debt-0.1.0/src/fed_debt.egg-info/entry_points.txt +2 -0
- fed_debt-0.1.0/src/fed_debt.egg-info/requires.txt +7 -0
- fed_debt-0.1.0/src/fed_debt.egg-info/top_level.txt +1 -0
fed_debt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xorn the first
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fed_debt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fed-debt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Check the current US federal debt to the penny
|
|
5
|
+
Author-email: xorn the first <x0rn@gitlab.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/x0rn/fed-debt
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/x0rn/fed-debt
|
|
9
|
+
Project-URL: Bug Tracker, https://gitlab.com/x0rn/fed-debt/-/issues
|
|
10
|
+
Keywords: treasury,debt,us-debt,finance
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
18
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: httpx
|
|
23
|
+
Requires-Dist: rich
|
|
24
|
+
Provides-Extra: ticker
|
|
25
|
+
Requires-Dist: windows-curses; sys_platform == "win32" and extra == "ticker"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# fed-debt πΊπΈ
|
|
29
|
+
|
|
30
|
+
Check the current US federal debt, to the penny.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
$ fed-debt
|
|
34
|
+
$39,828,435,390,891.55
|
|
35
|
+
|
|
36
|
+
$ fed-debt --verbose
|
|
37
|
+
US Federal Debt β 2026-08-04
|
|
38
|
+
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ
|
|
39
|
+
β Metric β Amount β
|
|
40
|
+
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββ€
|
|
41
|
+
β Total Public Debt Outstanding β $39,828,435,390,891.55 β
|
|
42
|
+
β Debt Held by the Public β $32,100,703,256,953.12 β
|
|
43
|
+
β Intragovernmental Holdings β $7,727,732,133,938.43 β
|
|
44
|
+
β Debt per US Citizen β $118,890.85 β
|
|
45
|
+
ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
### From PyPI (recommended)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install fed-debt
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or with ticker support (requires `windows-curses` on Windows):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install "fed-debt[ticker]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### From source
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://gitlab.com/x0rn/fed-debt.git
|
|
66
|
+
cd fed-debt
|
|
67
|
+
py -m pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or for the ticker:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
py -m pip install -e ".[ticker]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
| Command | What it does |
|
|
79
|
+
|------------------------|-------------------------------------------------|
|
|
80
|
+
| `fed-debt` | Prints the latest total debt to the penny |
|
|
81
|
+
| `fed-debt --verbose` | Detailed breakdown table with per-citizen debt |
|
|
82
|
+
| `fed-debt --ticker` | Opens a small live-updating ticker window |
|
|
83
|
+
| `fed-debt --version` | Prints version number |
|
|
84
|
+
| `fed-debt --help` | Shows usage |
|
|
85
|
+
|
|
86
|
+
### Ticker
|
|
87
|
+
|
|
88
|
+
The `--ticker` flag opens a thin, 3-line curses window that shows a
|
|
89
|
+
**continuously incrementing** debt counter. Instead of polling the API
|
|
90
|
+
every few seconds (wasteful β the data only updates once a day at
|
|
91
|
+
3:00 PM ET), it:
|
|
92
|
+
|
|
93
|
+
1. Fetches the latest official reading on startup
|
|
94
|
+
2. Estimates the per-second growth from recent history (~$1.3B/day)
|
|
95
|
+
3. Interpolates the current value in real-time
|
|
96
|
+
|
|
97
|
+
Press `q` to quit, `r` to force a re-sync with the Treasury API.
|
|
98
|
+
|
|
99
|
+
## Data Source
|
|
100
|
+
|
|
101
|
+
U.S. Treasury Fiscal Data β [Debt to the Penny](https://fiscaldata.treasury.gov/datasets/debt-to-the-penny)
|
|
102
|
+
endpoint, updated daily at 3:00 PM ET.
|
|
103
|
+
|
|
104
|
+
## Future
|
|
105
|
+
|
|
106
|
+
- `--chart` β terminal-based historical charts (via `plotext`)
|
|
107
|
+
- `--since` β see the debt on a specific past date
|
|
108
|
+
- GUI/web backend using the same `api.py` module
|
fed_debt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# fed-debt πΊπΈ
|
|
2
|
+
|
|
3
|
+
Check the current US federal debt, to the penny.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
$ fed-debt
|
|
7
|
+
$39,828,435,390,891.55
|
|
8
|
+
|
|
9
|
+
$ fed-debt --verbose
|
|
10
|
+
US Federal Debt β 2026-08-04
|
|
11
|
+
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ
|
|
12
|
+
β Metric β Amount β
|
|
13
|
+
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββ€
|
|
14
|
+
β Total Public Debt Outstanding β $39,828,435,390,891.55 β
|
|
15
|
+
β Debt Held by the Public β $32,100,703,256,953.12 β
|
|
16
|
+
β Intragovernmental Holdings β $7,727,732,133,938.43 β
|
|
17
|
+
β Debt per US Citizen β $118,890.85 β
|
|
18
|
+
ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
### From PyPI (recommended)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install fed-debt
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or with ticker support (requires `windows-curses` on Windows):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install "fed-debt[ticker]"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### From source
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://gitlab.com/x0rn/fed-debt.git
|
|
39
|
+
cd fed-debt
|
|
40
|
+
py -m pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or for the ticker:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
py -m pip install -e ".[ticker]"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
| Command | What it does |
|
|
52
|
+
|------------------------|-------------------------------------------------|
|
|
53
|
+
| `fed-debt` | Prints the latest total debt to the penny |
|
|
54
|
+
| `fed-debt --verbose` | Detailed breakdown table with per-citizen debt |
|
|
55
|
+
| `fed-debt --ticker` | Opens a small live-updating ticker window |
|
|
56
|
+
| `fed-debt --version` | Prints version number |
|
|
57
|
+
| `fed-debt --help` | Shows usage |
|
|
58
|
+
|
|
59
|
+
### Ticker
|
|
60
|
+
|
|
61
|
+
The `--ticker` flag opens a thin, 3-line curses window that shows a
|
|
62
|
+
**continuously incrementing** debt counter. Instead of polling the API
|
|
63
|
+
every few seconds (wasteful β the data only updates once a day at
|
|
64
|
+
3:00 PM ET), it:
|
|
65
|
+
|
|
66
|
+
1. Fetches the latest official reading on startup
|
|
67
|
+
2. Estimates the per-second growth from recent history (~$1.3B/day)
|
|
68
|
+
3. Interpolates the current value in real-time
|
|
69
|
+
|
|
70
|
+
Press `q` to quit, `r` to force a re-sync with the Treasury API.
|
|
71
|
+
|
|
72
|
+
## Data Source
|
|
73
|
+
|
|
74
|
+
U.S. Treasury Fiscal Data β [Debt to the Penny](https://fiscaldata.treasury.gov/datasets/debt-to-the-penny)
|
|
75
|
+
endpoint, updated daily at 3:00 PM ET.
|
|
76
|
+
|
|
77
|
+
## Future
|
|
78
|
+
|
|
79
|
+
- `--chart` β terminal-based historical charts (via `plotext`)
|
|
80
|
+
- `--since` β see the debt on a specific past date
|
|
81
|
+
- GUI/web backend using the same `api.py` module
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fed-debt"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Check the current US federal debt to the penny"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "xorn the first", email = "x0rn@gitlab.com"},
|
|
15
|
+
]
|
|
16
|
+
keywords = ["treasury", "debt", "us-debt", "finance"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Topic :: Office/Business :: Financial",
|
|
25
|
+
"Intended Audience :: End Users/Desktop",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
dependencies = [
|
|
29
|
+
"httpx",
|
|
30
|
+
"rich",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
ticker = ["windows-curses; sys_platform == 'win32'"]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://gitlab.com/x0rn/fed-debt"
|
|
38
|
+
Repository = "https://gitlab.com/x0rn/fed-debt"
|
|
39
|
+
"Bug Tracker" = "https://gitlab.com/x0rn/fed-debt/-/issues"
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
fed-debt = "fed_debt.cli:main"
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.packages.find]
|
|
45
|
+
where = ["src"]
|
fed_debt-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Fetch US federal debt data from the Treasury Fiscal Data API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
|
|
11
|
+
BASE_URL = (
|
|
12
|
+
"https://api.fiscaldata.treasury.gov/services/api/fiscal_service"
|
|
13
|
+
)
|
|
14
|
+
ENDPOINT = "/v2/accounting/od/debt_to_penny"
|
|
15
|
+
|
|
16
|
+
# Seconds in a full day (used for real-time ticker interpolation)
|
|
17
|
+
SECONDS_PER_DAY = 86_400
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclasses.dataclass(frozen=True)
|
|
21
|
+
class DebtSnapshot:
|
|
22
|
+
"""A single official debt reading from the Treasury."""
|
|
23
|
+
|
|
24
|
+
record_date: datetime.date
|
|
25
|
+
tot_pub_debt_out_amt: float
|
|
26
|
+
debt_held_public_amt: float
|
|
27
|
+
intragov_hold_amt: float
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def debt_per_citizen(self) -> float:
|
|
31
|
+
from fed_debt import US_POPULATION
|
|
32
|
+
return self.tot_pub_debt_out_amt / US_POPULATION
|
|
33
|
+
|
|
34
|
+
def format_dollar(self, value: float) -> str:
|
|
35
|
+
"""Format a dollar amount with commas and 2 decimal places."""
|
|
36
|
+
return f"${value:,.2f}"
|
|
37
|
+
|
|
38
|
+
def total_dollar(self) -> str:
|
|
39
|
+
return self.format_dollar(self.tot_pub_debt_out_amt)
|
|
40
|
+
|
|
41
|
+
def public_dollar(self) -> str:
|
|
42
|
+
return self.format_dollar(self.debt_held_public_amt)
|
|
43
|
+
|
|
44
|
+
def intragov_dollar(self) -> str:
|
|
45
|
+
return self.format_dollar(self.intragov_hold_amt)
|
|
46
|
+
|
|
47
|
+
def per_citizen_dollar(self) -> str:
|
|
48
|
+
return self.format_dollar(self.debt_per_citizen)
|
|
49
|
+
|
|
50
|
+
def __str__(self) -> str:
|
|
51
|
+
return self.total_dollar()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DebtClient:
|
|
55
|
+
"""Client for the Treasury Fiscal Data API."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, timeout: float = 15.0) -> None:
|
|
58
|
+
self._client = httpx.Client(timeout=timeout)
|
|
59
|
+
|
|
60
|
+
def close(self) -> None:
|
|
61
|
+
self._client.close()
|
|
62
|
+
|
|
63
|
+
def __enter__(self) -> DebtClient:
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self, *args: Any) -> None:
|
|
67
|
+
self.close()
|
|
68
|
+
|
|
69
|
+
# ------------------------------------------------------------------
|
|
70
|
+
# Public methods
|
|
71
|
+
# ------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
def latest(self) -> DebtSnapshot:
|
|
74
|
+
"""Fetch the single most recent debt record."""
|
|
75
|
+
raw = self._fetch(params={
|
|
76
|
+
"sort": "-record_date",
|
|
77
|
+
"page[size]": 1,
|
|
78
|
+
})
|
|
79
|
+
return self._parse_row(raw["data"][0])
|
|
80
|
+
|
|
81
|
+
def last_n(self, n: int) -> list[DebtSnapshot]:
|
|
82
|
+
"""Fetch the *n* most recent debt records (newest first)."""
|
|
83
|
+
raw = self._fetch(params={
|
|
84
|
+
"sort": "-record_date",
|
|
85
|
+
"page[size]": n,
|
|
86
|
+
})
|
|
87
|
+
return [self._parse_row(row) for row in raw["data"]]
|
|
88
|
+
|
|
89
|
+
# ------------------------------------------------------------------
|
|
90
|
+
# Internal helpers
|
|
91
|
+
# ------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
def _fetch(self, params: dict[str, Any]) -> dict[str, Any]:
|
|
94
|
+
url = BASE_URL + ENDPOINT
|
|
95
|
+
resp = self._client.get(url, params=params)
|
|
96
|
+
resp.raise_for_status()
|
|
97
|
+
return resp.json()
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _parse_row(row: dict[str, str]) -> DebtSnapshot:
|
|
101
|
+
return DebtSnapshot(
|
|
102
|
+
record_date=datetime.date.fromisoformat(row["record_date"]),
|
|
103
|
+
tot_pub_debt_out_amt=float(row["tot_pub_debt_out_amt"]),
|
|
104
|
+
debt_held_public_amt=float(row["debt_held_public_amt"]),
|
|
105
|
+
intragov_hold_amt=float(row["intragov_hold_amt"]),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# ------------------------------------------------------------------
|
|
109
|
+
# Ticker helpers: compute a per-second growth rate
|
|
110
|
+
# ------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
def daily_increase(self, n_days: int = 2) -> float:
|
|
113
|
+
"""Return the average *daily* increase over the last *n_days*.
|
|
114
|
+
|
|
115
|
+
Fetches ``n_days + 1`` records so we can compute deltas between
|
|
116
|
+
consecutive business-day readings, then returns the mean daily
|
|
117
|
+
change in dollars.
|
|
118
|
+
"""
|
|
119
|
+
records = self.last_n(n_days + 1)
|
|
120
|
+
if len(records) < 2:
|
|
121
|
+
return 0.0
|
|
122
|
+
deltas = []
|
|
123
|
+
for i in range(len(records) - 1):
|
|
124
|
+
delta = (
|
|
125
|
+
records[i].tot_pub_debt_out_amt
|
|
126
|
+
- records[i + 1].tot_pub_debt_out_amt
|
|
127
|
+
)
|
|
128
|
+
# Only count positive deltas (the debt grows)
|
|
129
|
+
if delta > 0:
|
|
130
|
+
deltas.append(delta)
|
|
131
|
+
if not deltas:
|
|
132
|
+
return 0.0
|
|
133
|
+
return sum(deltas) / len(deltas)
|
|
134
|
+
|
|
135
|
+
def per_second_increase(self, n_days: int = 5) -> float:
|
|
136
|
+
"""Return the average *per-second* increase in dollars."""
|
|
137
|
+
return self.daily_increase(n_days) / SECONDS_PER_DAY
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Main CLI entry point for fed-debt."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from typing import NoReturn
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
from .api import DebtClient
|
|
11
|
+
from .ticker import run_ticker
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog="fed-debt",
|
|
17
|
+
description="Show the current US federal debt, to the penny.",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--verbose", "-v",
|
|
21
|
+
action="store_true",
|
|
22
|
+
help="Show a detailed breakdown including public vs intragovernmental "
|
|
23
|
+
"holdings and debt per citizen.",
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--ticker", "-t",
|
|
27
|
+
action="store_true",
|
|
28
|
+
help="Open a small, continuously updating debt ticker window.",
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
"--version",
|
|
32
|
+
action="store_true",
|
|
33
|
+
help="Print the version number and exit.",
|
|
34
|
+
)
|
|
35
|
+
return parser
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def print_verbose(snapshot: "DebtSnapshot") -> None:
|
|
39
|
+
"""Print a Rich-formatted detail table."""
|
|
40
|
+
try:
|
|
41
|
+
from rich.console import Console
|
|
42
|
+
from rich.table import Table
|
|
43
|
+
except ImportError:
|
|
44
|
+
# Fallback: plain-text output
|
|
45
|
+
print(f"Record date: {snapshot.record_date}")
|
|
46
|
+
print(f"Total debt: {snapshot.total_dollar()}")
|
|
47
|
+
print(f"Held by the public: {snapshot.public_dollar()}")
|
|
48
|
+
print(f"Intragovernmental: {snapshot.intragov_dollar()}")
|
|
49
|
+
print(f"Per US citizen: {snapshot.per_citizen_dollar()}")
|
|
50
|
+
print()
|
|
51
|
+
print(
|
|
52
|
+
"Data from the U.S. Treasury Fiscal Data API. "
|
|
53
|
+
"Updated at end of each business day with the "
|
|
54
|
+
"prior business day's figures."
|
|
55
|
+
)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
console = Console()
|
|
59
|
+
table = Table(title=f"US Federal Debt β {snapshot.record_date}")
|
|
60
|
+
|
|
61
|
+
table.add_column("Metric", style="cyan", no_wrap=True)
|
|
62
|
+
table.add_column("Amount", style="bold green", justify="right")
|
|
63
|
+
|
|
64
|
+
table.add_row("Total Public Debt Outstanding", snapshot.total_dollar())
|
|
65
|
+
table.add_row("Debt Held by the Public", snapshot.public_dollar())
|
|
66
|
+
table.add_row("Intragovernmental Holdings", snapshot.intragov_dollar())
|
|
67
|
+
table.add_row("Debt per US Citizen", snapshot.per_citizen_dollar())
|
|
68
|
+
|
|
69
|
+
console.print(table)
|
|
70
|
+
console.print(
|
|
71
|
+
"[dim]Data from the U.S. Treasury Fiscal Data API. "
|
|
72
|
+
"Updated at end of each business day with the prior "
|
|
73
|
+
"business day's figures.[/dim]"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def print_brief(snapshot: "DebtSnapshot") -> None:
|
|
78
|
+
"""Print just the dollar amount, with optional Rich styling."""
|
|
79
|
+
try:
|
|
80
|
+
from rich.console import Console
|
|
81
|
+
console = Console()
|
|
82
|
+
console.print(f"[bold green]{snapshot.total_dollar()}[/bold green]")
|
|
83
|
+
except ImportError:
|
|
84
|
+
print(snapshot.total_dollar())
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def main(argv: list[str] | None = None) -> int:
|
|
88
|
+
parser = build_parser()
|
|
89
|
+
args = parser.parse_args(argv)
|
|
90
|
+
|
|
91
|
+
if args.version:
|
|
92
|
+
print(f"fed-debt v{__version__}")
|
|
93
|
+
return 0
|
|
94
|
+
|
|
95
|
+
if args.ticker:
|
|
96
|
+
return run_ticker()
|
|
97
|
+
|
|
98
|
+
# Normal mode: fetch and display the latest debt
|
|
99
|
+
try:
|
|
100
|
+
client = DebtClient()
|
|
101
|
+
snapshot = client.latest()
|
|
102
|
+
client.close()
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
print(f"Error fetching debt data: {exc}", file=sys.stderr)
|
|
105
|
+
return 1
|
|
106
|
+
|
|
107
|
+
if args.verbose:
|
|
108
|
+
print_verbose(snapshot)
|
|
109
|
+
else:
|
|
110
|
+
print_brief(snapshot)
|
|
111
|
+
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
sys.exit(main())
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Curses-based live debt ticker window.
|
|
2
|
+
|
|
3
|
+
Usage
|
|
4
|
+
-----
|
|
5
|
+
from fed_debt.ticker import run_ticker
|
|
6
|
+
run_ticker() # opens the curses window
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import datetime
|
|
12
|
+
import time
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .api import DebtClient, DebtSnapshot
|
|
16
|
+
|
|
17
|
+
# ββ constants ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
18
|
+
REFRESH_INTERVAL = 1.0 # seconds between display updates
|
|
19
|
+
SYNC_INTERVAL = 3600.0 # re-fetch official data every hour
|
|
20
|
+
SECONDS_PER_DAY = 86_400.0
|
|
21
|
+
|
|
22
|
+
# Coordinates of the three lines in the ticker window
|
|
23
|
+
LINE_DATE = 0
|
|
24
|
+
LINE_VALUE = 1
|
|
25
|
+
LINE_HELP = 2
|
|
26
|
+
|
|
27
|
+
WINDOW_HEIGHT = 3
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _format_float(value: float) -> str:
|
|
31
|
+
"""Format a large dollar figure with commas and two decimals."""
|
|
32
|
+
return f"${value:,.2f}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _compute_current(
|
|
36
|
+
snapshot: DebtSnapshot,
|
|
37
|
+
per_second: float,
|
|
38
|
+
elapsed: float,
|
|
39
|
+
) -> float:
|
|
40
|
+
"""Interpolate the current debt based on the last official snapshot.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
snapshot :
|
|
45
|
+
The most recent official reading.
|
|
46
|
+
per_second :
|
|
47
|
+
Estimated per-second increase (dollars).
|
|
48
|
+
elapsed :
|
|
49
|
+
Number of seconds since the official snapshot's record date
|
|
50
|
+
(specifically, since midnight of that date β a reasonable
|
|
51
|
+
approximation).
|
|
52
|
+
"""
|
|
53
|
+
return snapshot.tot_pub_debt_out_amt + (per_second * elapsed)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _seconds_since_midnight(d: datetime.date) -> float:
|
|
57
|
+
"""How many seconds have passed since midnight UTC of *d*."""
|
|
58
|
+
then = datetime.datetime(
|
|
59
|
+
d.year, d.month, d.day, tzinfo=datetime.timezone.utc,
|
|
60
|
+
)
|
|
61
|
+
now = datetime.datetime.now(datetime.timezone.utc)
|
|
62
|
+
return (now - then).total_seconds()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def run_ticker() -> int:
|
|
66
|
+
"""Open the curses debt-ticker and block until the user quits.
|
|
67
|
+
|
|
68
|
+
Returns 0 on clean exit, 1 on error.
|
|
69
|
+
"""
|
|
70
|
+
# ββ bootstrap data βββββββββββββββββββββββββββββββββββββββββββββ
|
|
71
|
+
try:
|
|
72
|
+
client = DebtClient()
|
|
73
|
+
snapshot = client.latest()
|
|
74
|
+
per_second = client.per_second_increase(n_days=5)
|
|
75
|
+
client.close()
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
print(f"Error fetching initial debt data: {exc}")
|
|
78
|
+
return 1
|
|
79
|
+
|
|
80
|
+
last_sync = time.monotonic()
|
|
81
|
+
# Estimate seconds since midnight of the record date
|
|
82
|
+
elapsed = _seconds_since_midnight(snapshot.record_date)
|
|
83
|
+
|
|
84
|
+
# ββ curses setup ββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
85
|
+
import curses
|
|
86
|
+
|
|
87
|
+
def _main(stdscr: Any) -> None:
|
|
88
|
+
nonlocal snapshot, per_second, last_sync, elapsed
|
|
89
|
+
|
|
90
|
+
curses.curs_set(0) # hide cursor
|
|
91
|
+
stdscr.nodelay(True) # non-blocking getch()
|
|
92
|
+
stdscr.timeout(int(REFRESH_INTERVAL * 1000))
|
|
93
|
+
|
|
94
|
+
while True:
|
|
95
|
+
now = time.monotonic()
|
|
96
|
+
|
|
97
|
+
# ββ periodic re-sync with the API βββββββββββββββββββββ
|
|
98
|
+
if now - last_sync >= SYNC_INTERVAL:
|
|
99
|
+
try:
|
|
100
|
+
cli = DebtClient()
|
|
101
|
+
fresh = cli.latest()
|
|
102
|
+
fresh_ps = cli.per_second_increase(n_days=5)
|
|
103
|
+
cli.close()
|
|
104
|
+
# Only update if the record date actually changed
|
|
105
|
+
if fresh.record_date != snapshot.record_date:
|
|
106
|
+
snapshot = fresh
|
|
107
|
+
per_second = fresh_ps
|
|
108
|
+
elapsed = _seconds_since_midnight(
|
|
109
|
+
snapshot.record_date,
|
|
110
|
+
)
|
|
111
|
+
last_sync = now
|
|
112
|
+
except Exception:
|
|
113
|
+
pass # keep going with stale data
|
|
114
|
+
|
|
115
|
+
# ββ compute interpolated value βββββββββββββββββββββββββ
|
|
116
|
+
elapsed = _seconds_since_midnight(snapshot.record_date)
|
|
117
|
+
current = _compute_current(snapshot, per_second, elapsed)
|
|
118
|
+
|
|
119
|
+
# ββ render βββββββββββββββββββββββββββββββββββββββββββββ
|
|
120
|
+
height, width = stdscr.getmaxyx()
|
|
121
|
+
|
|
122
|
+
# Date line
|
|
123
|
+
date_str = f" Record date: {snapshot.record_date} "
|
|
124
|
+
stdscr.addstr(LINE_DATE, 0, date_str[: width - 1])
|
|
125
|
+
|
|
126
|
+
# Value line (centred if possible)
|
|
127
|
+
val_str = _format_float(current)
|
|
128
|
+
x = max(0, (width - len(val_str)) // 2)
|
|
129
|
+
stdscr.addstr(LINE_VALUE, x, val_str[: width - x - 1])
|
|
130
|
+
|
|
131
|
+
# Help line
|
|
132
|
+
help_str = " [q] quit [r] refresh "
|
|
133
|
+
stdscr.addstr(LINE_HELP, 0, help_str[: width - 1])
|
|
134
|
+
|
|
135
|
+
stdscr.refresh()
|
|
136
|
+
|
|
137
|
+
# ββ keyboard handling ββββββββββββββββββββββββββββββββββ
|
|
138
|
+
key = stdscr.getch()
|
|
139
|
+
if key in (ord("q"), ord("Q")):
|
|
140
|
+
break
|
|
141
|
+
if key in (ord("r"), ord("R")):
|
|
142
|
+
# Force re-sync now
|
|
143
|
+
try:
|
|
144
|
+
cli = DebtClient()
|
|
145
|
+
fresh = cli.latest()
|
|
146
|
+
fresh_ps = cli.per_second_increase(n_days=5)
|
|
147
|
+
cli.close()
|
|
148
|
+
if fresh.record_date != snapshot.record_date:
|
|
149
|
+
snapshot = fresh
|
|
150
|
+
per_second = fresh_ps
|
|
151
|
+
elapsed = _seconds_since_midnight(
|
|
152
|
+
snapshot.record_date,
|
|
153
|
+
)
|
|
154
|
+
last_sync = now
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
curses.wrapper(_main)
|
|
160
|
+
except KeyboardInterrupt:
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
return 0
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fed-debt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Check the current US federal debt to the penny
|
|
5
|
+
Author-email: xorn the first <x0rn@gitlab.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://gitlab.com/x0rn/fed-debt
|
|
8
|
+
Project-URL: Repository, https://gitlab.com/x0rn/fed-debt
|
|
9
|
+
Project-URL: Bug Tracker, https://gitlab.com/x0rn/fed-debt/-/issues
|
|
10
|
+
Keywords: treasury,debt,us-debt,finance
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
18
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: httpx
|
|
23
|
+
Requires-Dist: rich
|
|
24
|
+
Provides-Extra: ticker
|
|
25
|
+
Requires-Dist: windows-curses; sys_platform == "win32" and extra == "ticker"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# fed-debt πΊπΈ
|
|
29
|
+
|
|
30
|
+
Check the current US federal debt, to the penny.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
$ fed-debt
|
|
34
|
+
$39,828,435,390,891.55
|
|
35
|
+
|
|
36
|
+
$ fed-debt --verbose
|
|
37
|
+
US Federal Debt β 2026-08-04
|
|
38
|
+
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββ
|
|
39
|
+
β Metric β Amount β
|
|
40
|
+
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββ€
|
|
41
|
+
β Total Public Debt Outstanding β $39,828,435,390,891.55 β
|
|
42
|
+
β Debt Held by the Public β $32,100,703,256,953.12 β
|
|
43
|
+
β Intragovernmental Holdings β $7,727,732,133,938.43 β
|
|
44
|
+
β Debt per US Citizen β $118,890.85 β
|
|
45
|
+
ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββ
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
### From PyPI (recommended)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install fed-debt
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or with ticker support (requires `windows-curses` on Windows):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install "fed-debt[ticker]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### From source
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://gitlab.com/x0rn/fed-debt.git
|
|
66
|
+
cd fed-debt
|
|
67
|
+
py -m pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or for the ticker:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
py -m pip install -e ".[ticker]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
| Command | What it does |
|
|
79
|
+
|------------------------|-------------------------------------------------|
|
|
80
|
+
| `fed-debt` | Prints the latest total debt to the penny |
|
|
81
|
+
| `fed-debt --verbose` | Detailed breakdown table with per-citizen debt |
|
|
82
|
+
| `fed-debt --ticker` | Opens a small live-updating ticker window |
|
|
83
|
+
| `fed-debt --version` | Prints version number |
|
|
84
|
+
| `fed-debt --help` | Shows usage |
|
|
85
|
+
|
|
86
|
+
### Ticker
|
|
87
|
+
|
|
88
|
+
The `--ticker` flag opens a thin, 3-line curses window that shows a
|
|
89
|
+
**continuously incrementing** debt counter. Instead of polling the API
|
|
90
|
+
every few seconds (wasteful β the data only updates once a day at
|
|
91
|
+
3:00 PM ET), it:
|
|
92
|
+
|
|
93
|
+
1. Fetches the latest official reading on startup
|
|
94
|
+
2. Estimates the per-second growth from recent history (~$1.3B/day)
|
|
95
|
+
3. Interpolates the current value in real-time
|
|
96
|
+
|
|
97
|
+
Press `q` to quit, `r` to force a re-sync with the Treasury API.
|
|
98
|
+
|
|
99
|
+
## Data Source
|
|
100
|
+
|
|
101
|
+
U.S. Treasury Fiscal Data β [Debt to the Penny](https://fiscaldata.treasury.gov/datasets/debt-to-the-penny)
|
|
102
|
+
endpoint, updated daily at 3:00 PM ET.
|
|
103
|
+
|
|
104
|
+
## Future
|
|
105
|
+
|
|
106
|
+
- `--chart` β terminal-based historical charts (via `plotext`)
|
|
107
|
+
- `--since` β see the debt on a specific past date
|
|
108
|
+
- GUI/web backend using the same `api.py` module
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/fed_debt/__init__.py
|
|
5
|
+
src/fed_debt/__main__.py
|
|
6
|
+
src/fed_debt/api.py
|
|
7
|
+
src/fed_debt/cli.py
|
|
8
|
+
src/fed_debt/ticker.py
|
|
9
|
+
src/fed_debt.egg-info/PKG-INFO
|
|
10
|
+
src/fed_debt.egg-info/SOURCES.txt
|
|
11
|
+
src/fed_debt.egg-info/dependency_links.txt
|
|
12
|
+
src/fed_debt.egg-info/entry_points.txt
|
|
13
|
+
src/fed_debt.egg-info/requires.txt
|
|
14
|
+
src/fed_debt.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fed_debt
|