macrotrace 0.2.0__tar.gz → 0.3.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.
- {macrotrace-0.2.0 → macrotrace-0.3.0}/.github/workflows/docs.yml +2 -2
- macrotrace-0.3.0/CHANGELOG.md +75 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/PKG-INFO +2 -6
- {macrotrace-0.2.0 → macrotrace-0.3.0}/README.md +1 -5
- macrotrace-0.3.0/macrotrace/_time.py +19 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/db.py +10 -15
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/mt/analysis.py +8 -3
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/mt/plotter.py +6 -15
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/mt/time_series.py +466 -83
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/sources/base.py +7 -1
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/sources/fred.py +13 -51
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/sources/ons.py +7 -2
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/sources/rtdsm.py +5 -16
- {macrotrace-0.2.0 → macrotrace-0.3.0}/scripts/backstop_ingest.py +5 -11
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/series/test_init.py +26 -7
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/series/test_series.py +354 -13
- macrotrace-0.3.0/tests/models/mt/series/test_window_source_local_bounds.py +124 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/test_analysis.py +49 -44
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/test_plotter.py +2 -4
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_dataset_manager.py +14 -36
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_release_manager.py +0 -25
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_tz_handling.py +0 -40
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_helpers.py +1 -9
- macrotrace-0.3.0/tests/test_time.py +40 -0
- macrotrace-0.2.0/CHANGELOG.md +0 -35
- {macrotrace-0.2.0 → macrotrace-0.3.0}/.github/workflows/ci.yml +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/.github/workflows/release.yml +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/.gitignore +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/.pre-commit-config.yaml +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/.python-version +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/LICENSE +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/__init__.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/_paths.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/cli.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/graphing.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/__init__.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/mt/__init__.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/mt/observation.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/models/mt/series_metadata.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/ons_cli/__init__.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/ons_cli/cli.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/ons_cli/common.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/ons_cli/tui.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/py.typed +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/sources/__init__.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/macrotrace/sources/example.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/pyproject.toml +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/assets/mt/time_series/expected_vm.csv +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/assets/mt/time_series/from_dataframe.csv +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/assets/mt/time_series/from_dataframe_with_tz.csv +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/series/test_db_path_forwarding.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/test_metadata.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/mt/utils.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/models/test_db_models.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/ons_cli/test_cli.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/ons_cli/test_common.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/ons_cli/test_root_cli.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/ons_cli/test_tui.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/ons_cli/utils.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/fixtures.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_api_client.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_dataset_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_observation_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_release_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_series_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_update_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_base_update_state.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/base/test_db_path_resolution.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/fixtures.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_api_client.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_observation_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_series_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/fred/test_fred_update_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/fixtures.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/test_ons_api_client.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/test_ons_dataset_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/test_ons_observation_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/test_ons_release_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/test_ons_series_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/ons/test_ons_update_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/fixtures.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_api_client.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_dataset_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_observation_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_release_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_series_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/sources/rtdsm/test_rtdsm_update_manager.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/test_package_init.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/tests/test_paths.py +0 -0
- {macrotrace-0.2.0 → macrotrace-0.3.0}/uv.lock +0 -0
|
@@ -37,7 +37,7 @@ jobs:
|
|
|
37
37
|
- name: Deploy dev docs (push to main)
|
|
38
38
|
if: github.ref == 'refs/heads/main'
|
|
39
39
|
run: |
|
|
40
|
-
uv run mike deploy --push --update-aliases dev
|
|
40
|
+
uv run mike deploy --push --update-aliases --prop-set hidden=true dev
|
|
41
41
|
if ! uv run mike list 2>/dev/null | grep -qE '^[0-9]'; then
|
|
42
42
|
uv run mike set-default --push dev
|
|
43
43
|
fi
|
|
@@ -49,7 +49,7 @@ jobs:
|
|
|
49
49
|
if [[ "$VERSION" =~ (rc|a|b|dev|alpha|beta) ]]; then
|
|
50
50
|
uv run mike deploy --push "$VERSION"
|
|
51
51
|
else
|
|
52
|
-
uv run mike deploy --push --update-aliases "$VERSION" latest
|
|
52
|
+
uv run mike deploy --push --update-aliases --alias-type copy "$VERSION" latest
|
|
53
53
|
uv run mike set-default --push latest
|
|
54
54
|
fi
|
|
55
55
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
|
|
4
|
+
versions follow [SemVer](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## 0.3.0 — 2026-06-15
|
|
7
|
+
|
|
8
|
+
- **Breaking:** Naive date inputs (`as_of`, the vintage/data windows,
|
|
9
|
+
`vintage_comparison`) are read on the source's clock instead of UTC.
|
|
10
|
+
This fixes same-day vintages being silently skipped for FRED,
|
|
11
|
+
which stamps releases at midnight US Central:
|
|
12
|
+
`as_of("2018-03-16")` now returns the 2018-03-16 vintage, and a
|
|
13
|
+
`data_end_date` on a period boundary no longer drops the final observation.
|
|
14
|
+
Timezone-aware datetimes still compare as exact instants.
|
|
15
|
+
- **Breaking:** Date strings must be `YYYY-MM-DD`; `datetime.date` objects
|
|
16
|
+
are now accepted. Free-text parsing is gone (along with the `dateutil`
|
|
17
|
+
dependency), so ambiguous formats like `"03/04/2018"` are rejected instead
|
|
18
|
+
of guessed.
|
|
19
|
+
- **Fixed:** `created_at` columns stamped each row with the process start
|
|
20
|
+
time instead of its actual creation time.
|
|
21
|
+
|
|
22
|
+
## 0.2.2 — 2026-06-12
|
|
23
|
+
|
|
24
|
+
- **Vintage matching:** `identify_vintage` now interprets a tz-naive index in
|
|
25
|
+
the source's native timezone (e.g. midnight US Central for FRED) instead of
|
|
26
|
+
UTC, so plain dates match FRED vintages.
|
|
27
|
+
- **Vintage matching:** Added a `decimals` argument that rounds both sides
|
|
28
|
+
before comparison, for matching data published at a fixed precision.
|
|
29
|
+
- **Vintage matching:** `VintageMatch.failure_reason` now reports why nothing
|
|
30
|
+
matched: timestamps no vintage contains (`"coverage"`) vs value
|
|
31
|
+
disagreements (`"values"`).
|
|
32
|
+
- **Vintage matching:** Numeric/positional indexes are rejected with a clear
|
|
33
|
+
error, and `pd.PeriodIndex` is supported.
|
|
34
|
+
- **Vintage matching:** When nothing matches, `VintageMatch.alignment_hint`
|
|
35
|
+
flags timestamps that would match under a wrong timezone localization, a
|
|
36
|
+
constant time shift, or a month-end vs month-start convention.
|
|
37
|
+
|
|
38
|
+
## 0.2.1 — 2026-06-11
|
|
39
|
+
|
|
40
|
+
- **Docs:** RTDSM is now listed as an available source on the documentation
|
|
41
|
+
homepage — it had been left under "Coming Soon" when 0.2.0 shipped.
|
|
42
|
+
- **Docs:** The version selector now shows the `latest` label next to the
|
|
43
|
+
release it points at, and the in-development `dev` build is hidden from
|
|
44
|
+
the selector (it is still reachable directly at `/dev/`).
|
|
45
|
+
|
|
46
|
+
## 0.2.0 — 2026-06-10
|
|
47
|
+
|
|
48
|
+
- **Sources:** Added the Federal Reserve Bank of Philadelphia's Real-Time
|
|
49
|
+
Data Set for Macroeconomists (RTDSM) — vintage-aware ingestion of 115 U.S.
|
|
50
|
+
macroeconomic series. Each series is parsed from the published full-history spreadsheet; an optional `series_key={"frequency": "Q" | "M"}` selects the vintage frequency, and a monthly refresh throttle avoids re-downloading the same series
|
|
51
|
+
within a calendar month as requested by the Philadelphia Federal Reserve Bank.
|
|
52
|
+
- **Vintage matching:** Added `MTTimeSeries.identify_vintage(...)`, which
|
|
53
|
+
recovers which release(s) an undated block of observations came from by
|
|
54
|
+
comparing it against every stored vintage. Returns a `VintageMatch`
|
|
55
|
+
(`matched`, `is_ambiguous`, `release_date` / `release_dates`) — useful for
|
|
56
|
+
pinning down the vintage behind replication-package data.
|
|
57
|
+
- **Time series:** Added `MTTimeSeries.to_series(...)`, the values-only,
|
|
58
|
+
date-indexed pandas `Series` counterpart to `to_dataframe` (supports the
|
|
59
|
+
`default`, `first_difference`, and `pct_change` modes). Exposed
|
|
60
|
+
`VintageMatch` at the package root.
|
|
61
|
+
|
|
62
|
+
## 0.1.0 — 2026-04-28
|
|
63
|
+
|
|
64
|
+
First public release.
|
|
65
|
+
|
|
66
|
+
- **Sources:** vintage-aware ingestion from FRED and ONS, with a local
|
|
67
|
+
SQLite store (`MacroTrace.db`) and shared request cache.
|
|
68
|
+
- **Time series:** `MTTimeSeries` with `as_of(...)`, vintage- and
|
|
69
|
+
data-window filtering, `from_dataframe`, and pandas / Darts export.
|
|
70
|
+
- **Analysis:** revision metrics, vintage comparison, decomposition
|
|
71
|
+
across vintages, biasedness regression, and revision autocorrelation.
|
|
72
|
+
- **Plotting:** Plotly-based vintage, revision, and decomposition plots
|
|
73
|
+
via `MTTimeSeriesPlotter`.
|
|
74
|
+
- **CLI / TUI:** `macrotrace ons explorer` and `macrotrace ons tui`
|
|
75
|
+
(the latter via the optional `ons-tui` extra).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: macrotrace
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A Python library for managing and analyzing macroeconomic time series data with vintage awareness.
|
|
5
5
|
Project-URL: Homepage, https://github.com/john-ramsey/macrotrace
|
|
6
6
|
Project-URL: Repository, https://github.com/john-ramsey/macrotrace
|
|
@@ -170,13 +170,9 @@ if match.is_ambiguous:
|
|
|
170
170
|
elif match.matched:
|
|
171
171
|
print(f"Matches the {match.release_date.date()} vintage")
|
|
172
172
|
else:
|
|
173
|
-
print("No matching vintage found")
|
|
173
|
+
print(f"No matching vintage found (failed on: {match.failure_reason})")
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
A match is ambiguous when the data is unchanged across consecutive vintages, so
|
|
177
|
-
the values alone cannot pin down a single release; `release_dates` lists every
|
|
178
|
-
consistent vintage in that case.
|
|
179
|
-
|
|
180
176
|
## Command-Line Tools
|
|
181
177
|
|
|
182
178
|
MacroTrace includes command-line tools for exploring ONS datasets:
|
|
@@ -129,13 +129,9 @@ if match.is_ambiguous:
|
|
|
129
129
|
elif match.matched:
|
|
130
130
|
print(f"Matches the {match.release_date.date()} vintage")
|
|
131
131
|
else:
|
|
132
|
-
print("No matching vintage found")
|
|
132
|
+
print(f"No matching vintage found (failed on: {match.failure_reason})")
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
A match is ambiguous when the data is unchanged across consecutive vintages, so
|
|
136
|
-
the values alone cannot pin down a single release; `release_dates` lists every
|
|
137
|
-
consistent vintage in that case.
|
|
138
|
-
|
|
139
135
|
## Command-Line Tools
|
|
140
136
|
|
|
141
137
|
MacroTrace includes command-line tools for exploring ONS datasets:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from datetime import datetime, tzinfo
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def ensure_timezone(dt: Optional[datetime], tz: tzinfo) -> Optional[datetime]:
|
|
6
|
+
"""
|
|
7
|
+
Return the datetime made aware in ``tz``, or None.
|
|
8
|
+
|
|
9
|
+
Naive datetimes keep their wall clock while aware ones are converted. Uses pytz's
|
|
10
|
+
``localize()`` when available so the real historical offset is picked
|
|
11
|
+
instead of pytz's first entry for the zone (LMT).
|
|
12
|
+
"""
|
|
13
|
+
if dt is None:
|
|
14
|
+
return None
|
|
15
|
+
if dt.tzinfo is None:
|
|
16
|
+
if hasattr(tz, "localize"):
|
|
17
|
+
return tz.localize(dt)
|
|
18
|
+
return dt.replace(tzinfo=tz)
|
|
19
|
+
return dt.astimezone(tz)
|
|
@@ -40,6 +40,11 @@ def is_valid_dateoffset(value: str) -> bool:
|
|
|
40
40
|
return False
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def _utc_now() -> datetime.datetime:
|
|
44
|
+
"""Callable default so each row stamps its own creation time, not the import time."""
|
|
45
|
+
return datetime.datetime.now(tz=datetime.timezone.utc)
|
|
46
|
+
|
|
47
|
+
|
|
43
48
|
class StrictDateTimeField(DateTimeField):
|
|
44
49
|
"""DateTimeField that enforces timezone-aware datetime objects in ISO 8601 format."""
|
|
45
50
|
|
|
@@ -122,9 +127,7 @@ class DatasetDimension(DataBaseModel):
|
|
|
122
127
|
# Validity period for this dimension definition, null valid_to means currently valid
|
|
123
128
|
valid_from = StrictDateTimeField()
|
|
124
129
|
valid_to = StrictDateTimeField(null=True)
|
|
125
|
-
created_at = StrictDateTimeField(
|
|
126
|
-
default=datetime.datetime.now(tz=datetime.timezone.utc)
|
|
127
|
-
)
|
|
130
|
+
created_at = StrictDateTimeField(default=_utc_now)
|
|
128
131
|
|
|
129
132
|
class Meta:
|
|
130
133
|
constraints = [
|
|
@@ -152,9 +155,7 @@ class Release(DataBaseModel):
|
|
|
152
155
|
)
|
|
153
156
|
release_date = StrictDateTimeField()
|
|
154
157
|
additional_metadata = JSONField(null=True)
|
|
155
|
-
created_at = StrictDateTimeField(
|
|
156
|
-
default=datetime.datetime.now(tz=datetime.timezone.utc)
|
|
157
|
-
)
|
|
158
|
+
created_at = StrictDateTimeField(default=_utc_now)
|
|
158
159
|
|
|
159
160
|
class Meta:
|
|
160
161
|
constraints = [SQL("UNIQUE(dataset_id, release_date)")]
|
|
@@ -182,9 +183,7 @@ class ReleaseDimension(DataBaseModel):
|
|
|
182
183
|
backref="release_dimensions",
|
|
183
184
|
on_delete="CASCADE",
|
|
184
185
|
)
|
|
185
|
-
created_at = StrictDateTimeField(
|
|
186
|
-
default=datetime.datetime.now(tz=datetime.timezone.utc)
|
|
187
|
-
)
|
|
186
|
+
created_at = StrictDateTimeField(default=_utc_now)
|
|
188
187
|
|
|
189
188
|
class Meta:
|
|
190
189
|
constraints = [SQL("UNIQUE(release_id, dimension_id)")]
|
|
@@ -199,9 +198,7 @@ class ReleaseDimension(DataBaseModel):
|
|
|
199
198
|
class Series(DataBaseModel):
|
|
200
199
|
dataset = ForeignKeyField(Dataset, backref="series", on_delete="CASCADE")
|
|
201
200
|
series_key = JSONField()
|
|
202
|
-
created_at = StrictDateTimeField(
|
|
203
|
-
default=datetime.datetime.now(tz=datetime.timezone.utc)
|
|
204
|
-
)
|
|
201
|
+
created_at = StrictDateTimeField(default=_utc_now)
|
|
205
202
|
|
|
206
203
|
def __repr__(self):
|
|
207
204
|
return (
|
|
@@ -249,9 +246,7 @@ class Observation(DataBaseModel):
|
|
|
249
246
|
|
|
250
247
|
observation_timestamp = StrictDateTimeField()
|
|
251
248
|
value = FloatField(null=True) # null if the observation is missing
|
|
252
|
-
created_at = StrictDateTimeField(
|
|
253
|
-
default=datetime.datetime.now(tz=datetime.timezone.utc)
|
|
254
|
-
)
|
|
249
|
+
created_at = StrictDateTimeField(default=_utc_now)
|
|
255
250
|
|
|
256
251
|
class Meta:
|
|
257
252
|
constraints = [SQL("UNIQUE(release_id, observation_timestamp)")]
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
|
|
5
|
+
import datetime
|
|
5
6
|
import logging
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
@@ -510,7 +511,6 @@ class MTTimeSeriesAnalysis:
|
|
|
510
511
|
darts_ts = self.ts.to_darts_timeseries()
|
|
511
512
|
|
|
512
513
|
if len(darts_ts) >= (min_train_size + 1):
|
|
513
|
-
|
|
514
514
|
for i in range(min_train_size, len(darts_ts)):
|
|
515
515
|
train = darts_ts[:i]
|
|
516
516
|
test = darts_ts[i : i + 1]
|
|
@@ -629,7 +629,10 @@ class MTTimeSeriesAnalysis:
|
|
|
629
629
|
)
|
|
630
630
|
|
|
631
631
|
def vintage_comparison(
|
|
632
|
-
self,
|
|
632
|
+
self,
|
|
633
|
+
vintage_dates: List[str | datetime.datetime | datetime.date],
|
|
634
|
+
mode: str = "growth",
|
|
635
|
+
strategy: str = "all",
|
|
633
636
|
) -> "VintageComparison":
|
|
634
637
|
"""
|
|
635
638
|
Compare vintages across summary measures describing revisions of a
|
|
@@ -677,7 +680,9 @@ class MTTimeSeriesAnalysis:
|
|
|
677
680
|
period-over-period change in the level changes sign between vintages.
|
|
678
681
|
|
|
679
682
|
Args:
|
|
680
|
-
vintage_dates (List[str]):
|
|
683
|
+
vintage_dates (List[str | datetime | date]): The vintages to
|
|
684
|
+
compare, each resolved through ``as_of()`` — a ``YYYY-MM-DD``
|
|
685
|
+
string or date for a calendar day, or a datetime for an exact instant.
|
|
681
686
|
mode (str): The mode of comparison ("growth" or "levels").
|
|
682
687
|
strategy (str): The strategy for comparison ("sequential", "final", or "all").
|
|
683
688
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
2
|
-
from datetime import datetime
|
|
2
|
+
from datetime import date, datetime
|
|
3
3
|
import warnings
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
@@ -147,9 +147,8 @@ class MTTimeSeriesPlotter:
|
|
|
147
147
|
go.Figure: Plotly figure showing observation revisions over time.
|
|
148
148
|
"""
|
|
149
149
|
if isinstance(observation_datetime, str):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
) # Returns UTC timezone
|
|
150
|
+
# Resolves to the source's midnight on that day
|
|
151
|
+
observation_datetime = self.ts._parse_string_date(observation_datetime)
|
|
153
152
|
elif not isinstance(observation_datetime, datetime):
|
|
154
153
|
raise ValueError(
|
|
155
154
|
f"Invalid observation datetime type: {type(observation_datetime)}. Must be a string or a datetime."
|
|
@@ -588,7 +587,7 @@ class MTTimeSeriesPlotter:
|
|
|
588
587
|
|
|
589
588
|
def timeseries_comparison(
|
|
590
589
|
self,
|
|
591
|
-
vintage_dates: List[str | datetime],
|
|
590
|
+
vintage_dates: List[str | datetime | date],
|
|
592
591
|
chart_type: str = "bar",
|
|
593
592
|
mode: str = "default",
|
|
594
593
|
y_axis_zero_indexed: bool = False,
|
|
@@ -597,7 +596,7 @@ class MTTimeSeriesPlotter:
|
|
|
597
596
|
Plots a comparison of time series vintages.
|
|
598
597
|
|
|
599
598
|
Args:
|
|
600
|
-
vintage_dates (List[str | datetime]): List of vintage identifiers
|
|
599
|
+
vintage_dates (List[str | datetime | date]): List of vintage identifiers, resolved via ``as_of``. Ex. '2025-11-01'
|
|
601
600
|
chart_type (str, optional): Type of chart to plot. Either "bar" or "line". Defaults to "bar".
|
|
602
601
|
mode (str, optional): The mode for which the dataframe is provided. Supports "default", "first_difference", and "pct_change". Defaults to "default".
|
|
603
602
|
y_axis_zero_indexed (bool, optional): Sets base of the y-axis to zero.
|
|
@@ -610,14 +609,6 @@ class MTTimeSeriesPlotter:
|
|
|
610
609
|
f"Invalid mode: {mode}. Supported modes are 'default', 'first_difference', and 'pct_change'."
|
|
611
610
|
)
|
|
612
611
|
|
|
613
|
-
for vintage_date in vintage_dates:
|
|
614
|
-
if (not isinstance(vintage_date, str)) and (
|
|
615
|
-
not isinstance(vintage_date, datetime)
|
|
616
|
-
):
|
|
617
|
-
raise TypeError(
|
|
618
|
-
"Vintage dates must be provided as strings or datetime objects."
|
|
619
|
-
)
|
|
620
|
-
|
|
621
612
|
fig = go.Figure()
|
|
622
613
|
all_values = []
|
|
623
614
|
hoverinfo = "x+y+name"
|
|
@@ -626,7 +617,7 @@ class MTTimeSeriesPlotter:
|
|
|
626
617
|
df = self.ts.as_of(vintage_date).to_dataframe(mode=mode)
|
|
627
618
|
vintage_date = (
|
|
628
619
|
vintage_date.strftime("%Y-%m-%d")
|
|
629
|
-
if isinstance(vintage_date,
|
|
620
|
+
if isinstance(vintage_date, date)
|
|
630
621
|
else vintage_date
|
|
631
622
|
)
|
|
632
623
|
if chart_type == "bar":
|