SPB Git

spb/anomaly-atlas Public License

Systematic discovery & rigorous validation of statistical anomalies in open HF market data (hfmarketdata.io) — pre-registered, artifact-null-driven, fully reproducible. Live atlas: www.anomaly-atlas.io

Python 61.4% JavaScript 28.7% CSS 8.6% Shell 0.7% Makefile 0.5%

Phase 0.5: Experiment A (data reality check) + hf_client implementation

- hf_client.py: cache-first single API client (throttle, 429/5xx back-off,
  50k-row pagination, committed data-manifest index) + 5 offline unit tests
- expA benchmark: 8 probe families, results with embedded hardware manifest
- research/data_source_profile.md (reviewed): coverage, timestamps ET,
  session structure, daily-vs-1min semantics, adjustment re-basing, limits
- LOG entry; expA hypothesis/analysis finalized

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 4 h ago (Aug 12, 2026) parent e77d5c0

Showing 23 changed files with +2,277 and −62

modified benchmarks/hardware_manifest.py +2 −2
@@ -30,7 +30,7 @@ import json
30 30 import platform
31 31 import subprocess
32 32 import sys
33 from datetime import datetime, timezone
33 +from datetime import UTC, datetime
34 34
35 35
36 36 def _run(cmd: list[str]) -> str:
@@ -105,7 +105,7 @@ def collect_manifest() -> dict:
105 105 "contact": "contact@spboucher.ai",
106 106 "project": "anomaly-atlas",
107 107 "data_source": "hfmarketdata.io",
108 "collected_utc": datetime.now(timezone.utc).isoformat(),
108 + "collected_utc": datetime.now(UTC).isoformat(),
109 109 "chip": {
110 110 "brand": _sysctl("machdep.cpu.brand_string"),
111 111 "arch": platform.machine(),
added data_manifest/index.jsonl +54 −0
@@ -0,0 +1,54 @@
1 +{"author": "Simon-Pierre Boucher", "cache_key": "d9e8ee7a0fffc5ef0e1e5592d30d6e73", "data_source": "hfmarketdata.io", "endpoint": "/v1/status", "fetched_utc": "2026-08-12T05:43:11Z", "first": null, "last": null, "params": {}, "rows": null, "sha256": "f33ac5e698b44872826cc115f8fac03788f333598486bae0220d431e1bfa4e13"}
2 +{"author": "Simon-Pierre Boucher", "cache_key": "2d039a4218a2f367f7af84f342d6fdf3", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:43:16Z", "first": "2000-01-04 09:30:00", "last": "2000-01-04 09:30:00", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "asc", "timeframe": "1min"}, "rows": 1, "sha256": "ad60d273f6e8195bc11223e1cbfdc7b35973b4acc456ff86696a0cb76ff3cb88"}
3 +{"author": "Simon-Pierre Boucher", "cache_key": "6bab124fe37c2d1e3013626d517f0523", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:43:21Z", "first": "2026-08-07 19:59:00", "last": "2026-08-07 19:59:00", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "desc", "timeframe": "1min"}, "rows": 1, "sha256": "d0f02237197c0375be859c19c702481a999cc9bbb059fbcb7ce324caf3f28125"}
4 +{"author": "Simon-Pierre Boucher", "cache_key": "d92fb70fe8b2eb7337101cb6bbd98438", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:43:21Z", "first": "2000-01-04", "last": "2000-01-04", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "asc", "timeframe": "1day"}, "rows": 1, "sha256": "21f3277a04bfe3ebdd014255497773c9dc764c921fe382bd3aac20b765778aff"}
5 +{"author": "Simon-Pierre Boucher", "cache_key": "88bbf2e2e69e98f8c59e749810e42a69", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:43:21Z", "first": "2026-08-07", "last": "2026-08-07", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "desc", "timeframe": "1day"}, "rows": 1, "sha256": "63adbe3e9ca00193e90d5c201ced3bdc8e83c2f0a7c132fb3c396a171a0de00b"}
6 +{"author": "Simon-Pierre Boucher", "cache_key": "b9e4313e31740ac56cdf5205eb97f96c", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:43:27Z", "first": "2000-01-03 09:31:00", "last": "2000-01-03 09:31:00", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "asc", "timeframe": "1min"}, "rows": 1, "sha256": "d6851d8cabf02fd16768df2c9b600cd83f57e489354c745c3f8e379ee00c6602"}
7 +{"author": "Simon-Pierre Boucher", "cache_key": "ffecfdf7e67f4f3cbc2d3ea2e5ade7a4", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:43:33Z", "first": "2026-08-07 19:59:00", "last": "2026-08-07 19:59:00", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "desc", "timeframe": "1min"}, "rows": 1, "sha256": "eb65098064c447bd903abbce9961b0780d426f5798ae771817bf6512b97c639e"}
8 +{"author": "Simon-Pierre Boucher", "cache_key": "79061cbe0cdc62d07c6e5f41650864c0", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:43:33Z", "first": "2000-01-03", "last": "2000-01-03", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "asc", "timeframe": "1day"}, "rows": 1, "sha256": "b37ebad0ac8540749165ba8166e0fe83adf93e44d1abfab11682b5c0600f8b02"}
9 +{"author": "Simon-Pierre Boucher", "cache_key": "72399df0660c837efcd638bca52242ad", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:43:33Z", "first": "2026-08-07", "last": "2026-08-07", "params": {"adjustment": "adj_splitdiv", "limit": 1, "order": "desc", "timeframe": "1day"}, "rows": 1, "sha256": "ca5540f4c6614ba79520ec33781461d8d55d63e1a5b0d9541540434551e1193b"}
10 +{"author": "Simon-Pierre Boucher", "cache_key": "3095e67d28ff2507394bf432223569cc", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/futures/ES", "fetched_utc": "2026-08-12T05:43:42Z", "first": "2008-01-02 06:00:00", "last": "2008-01-02 06:00:00", "params": {"adjustment": "contin_adj_ratio", "limit": 1, "order": "asc", "timeframe": "1min"}, "rows": 1, "sha256": "2dc4f88315bb11adf2e0e05fdfbe292ebd4df7fddc262c94a901ee101a0f348d"}
11 +{"author": "Simon-Pierre Boucher", "cache_key": "67c14a1918f8ead9df51bad4cdddc6fc", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/futures/ES", "fetched_utc": "2026-08-12T05:43:50Z", "first": "2026-08-06 23:59:00", "last": "2026-08-06 23:59:00", "params": {"adjustment": "contin_adj_ratio", "limit": 1, "order": "desc", "timeframe": "1min"}, "rows": 1, "sha256": "1bc821a4afc98e2951585a53f4857247a06fcbddf2ebc157e63c2e6d6612c695"}
12 +{"author": "Simon-Pierre Boucher", "cache_key": "8b3051bb6359cff735e3399d24df0905", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/futures/ES", "fetched_utc": "2026-08-12T05:43:50Z", "first": "2008-01-02", "last": "2008-01-02", "params": {"adjustment": "contin_adj_ratio", "limit": 1, "order": "asc", "timeframe": "1day"}, "rows": 1, "sha256": "3a7bbead6d70a97a84b9414c34254c1c56537074f9579d40fdc9b4be53d092c3"}
13 +{"author": "Simon-Pierre Boucher", "cache_key": "d88d227c0a429094688bff27d7c8279a", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/futures/ES", "fetched_utc": "2026-08-12T05:43:50Z", "first": "2026-08-06", "last": "2026-08-06", "params": {"adjustment": "contin_adj_ratio", "limit": 1, "order": "desc", "timeframe": "1day"}, "rows": 1, "sha256": "eb92b360a0f8040c4eeda7d7c3f0b79d74d4900932aed00f329b0ca8ede60ac4"}
14 +{"author": "Simon-Pierre Boucher", "cache_key": "dfc74c681a49d449f1838c5099096ed0", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/index/SPX", "fetched_utc": "2026-08-12T05:43:53Z", "first": "2008-01-02 09:30:00", "last": "2008-01-02 09:30:00", "params": {"limit": 1, "order": "asc", "timeframe": "1min"}, "rows": 1, "sha256": "b961b4630ff08d507bd1cae247405b440c661086cb5377d346a610cfb0a078dd"}
15 +{"author": "Simon-Pierre Boucher", "cache_key": "c3cd0b9e944d6c6061a523046a11b2c1", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/index/SPX", "fetched_utc": "2026-08-12T05:43:55Z", "first": "2026-08-07 16:50:00", "last": "2026-08-07 16:50:00", "params": {"limit": 1, "order": "desc", "timeframe": "1min"}, "rows": 1, "sha256": "6657ae1edb6b82de4b0aa274dc2ef1f1318df0fb6cbf8c579088818663901146"}
16 +{"author": "Simon-Pierre Boucher", "cache_key": "df0cc5499bf58fa49bedd90d3356194a", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/index/SPX", "fetched_utc": "2026-08-12T05:43:55Z", "first": "2000-11-27", "last": "2000-11-27", "params": {"limit": 1, "order": "asc", "timeframe": "1day"}, "rows": 1, "sha256": "d9a41a7d5ad7f802ca87d59540cc3e9b9f34b33d8d10a75282b9e8825b350e77"}
17 +{"author": "Simon-Pierre Boucher", "cache_key": "26848931ce2dae26131c145202f7dd8b", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/index/SPX", "fetched_utc": "2026-08-12T05:43:55Z", "first": "2026-08-07", "last": "2026-08-07", "params": {"limit": 1, "order": "desc", "timeframe": "1day"}, "rows": 1, "sha256": "00f56b0ef7c8a1460940a9f21b80fe1371a406607ad8d0d30807f0608766789c"}
18 +{"author": "Simon-Pierre Boucher", "cache_key": "afba11594e629e777a007d43a6620410", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/fx/EURUSD", "fetched_utc": "2026-08-12T05:44:03Z", "first": "2010-01-03 17:00:00", "last": "2010-01-03 17:00:00", "params": {"limit": 1, "order": "asc", "timeframe": "1min"}, "rows": 1, "sha256": "49710ed7e27e808edca1fd5fa180eccfc7e24b57bdc10c117dadd2314957b53b"}
19 +{"author": "Simon-Pierre Boucher", "cache_key": "61f2e21ac7552455d76d9b110d5c1b31", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/fx/EURUSD", "fetched_utc": "2026-08-12T05:44:10Z", "first": "2026-08-07 16:59:00", "last": "2026-08-07 16:59:00", "params": {"limit": 1, "order": "desc", "timeframe": "1min"}, "rows": 1, "sha256": "51a6ab967626259c471846232f1dcb8fe73fa05b1cc16df5576bf89f9738c700"}
20 +{"author": "Simon-Pierre Boucher", "cache_key": "88ca4ca59f563561e10305c290b7c03b", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/fx/EURUSD", "fetched_utc": "2026-08-12T05:44:10Z", "first": "2010-01-04", "last": "2010-01-04", "params": {"limit": 1, "order": "asc", "timeframe": "1day"}, "rows": 1, "sha256": "de42eb6802d5b777a2787d5af03f1265db02855a61d01ccb47908bdb132bba89"}
21 +{"author": "Simon-Pierre Boucher", "cache_key": "ef2eda5330e0dd6eb3b4088b3a380d64", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/fx/EURUSD", "fetched_utc": "2026-08-12T05:44:11Z", "first": "2026-08-07", "last": "2026-08-07", "params": {"limit": 1, "order": "desc", "timeframe": "1day"}, "rows": 1, "sha256": "4a2052df07e786ec3b1bf208c5b4b94e350e3ba5f06b0c3143f20fd86a770122"}
22 +{"author": "Simon-Pierre Boucher", "cache_key": "d787ad079539b3adbf9f9bf1fd55a819", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/crypto/BTC", "fetched_utc": "2026-08-12T05:44:19Z", "first": "2013-04-01 00:30:00", "last": "2013-04-01 00:30:00", "params": {"limit": 1, "order": "asc", "timeframe": "1min"}, "rows": 1, "sha256": "b95f05424ecec0bf8f0e7efa448a5ca4592a3834002279e77a736488104edcd8"}
23 +{"author": "Simon-Pierre Boucher", "cache_key": "4e9eb0ef2b8cd4212374a229005f8706", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/crypto/BTC", "fetched_utc": "2026-08-12T05:44:27Z", "first": "2026-08-09 23:59:00", "last": "2026-08-09 23:59:00", "params": {"limit": 1, "order": "desc", "timeframe": "1min"}, "rows": 1, "sha256": "77b17bc16f50e53933e510b26fbcd4c38d6d025cdb58617f213ef79aaae10fdc"}
24 +{"author": "Simon-Pierre Boucher", "cache_key": "8cd5388045f89e93889a3382c114d25b", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/crypto/BTC", "fetched_utc": "2026-08-12T05:44:27Z", "first": "2013-04-01", "last": "2013-04-01", "params": {"limit": 1, "order": "asc", "timeframe": "1day"}, "rows": 1, "sha256": "03b9a31cc02b00efbee13b89e2ff7d4155f0040ea23ff6dde17668520611b1d6"}
25 +{"author": "Simon-Pierre Boucher", "cache_key": "2dbbc407d9075c897037b0bf60bcc654", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/crypto/BTC", "fetched_utc": "2026-08-12T05:44:28Z", "first": "2026-08-09", "last": "2026-08-09", "params": {"limit": 1, "order": "desc", "timeframe": "1day"}, "rows": 1, "sha256": "9a53fae43dbaa42cd1b7ced0c4c72207b034d71264f9989e55b5cc0ab999e2cb"}
26 +{"author": "Simon-Pierre Boucher", "cache_key": "852d5cc40dc3b505f41b5b572ac5ac3b", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:44:28Z", "first": "2026-08-06 04:00:00", "last": "2026-08-06 19:59:00", "params": {"adjustment": "adj_splitdiv", "end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 904, "sha256": "389f925b7420bf6e032d9ca8851c3ec3000875e8bd6a23eaddd25f73b3cadf7f"}
27 +{"author": "Simon-Pierre Boucher", "cache_key": "b9e2ebd52ba9ce62e0fe77543cf40f83", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:28Z", "first": "2026-08-06 04:00:00", "last": "2026-08-06 19:59:00", "params": {"adjustment": "adj_splitdiv", "end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 879, "sha256": "bec8d31a096138ac33ffd0b9ee83ef7bf130bc44b0d29fb188d1db163d5a755c"}
28 +{"author": "Simon-Pierre Boucher", "cache_key": "2659cb4ce96acdbc4ac53d9d0a46de17", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/futures/ES", "fetched_utc": "2026-08-12T05:44:28Z", "first": "2026-08-06 00:00:00", "last": "2026-08-06 23:59:00", "params": {"adjustment": "contin_adj_ratio", "end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 1380, "sha256": "a7bfec684a53fec94f20c1d78819b79a33f3c8a54471922d7692bc6a5225b343"}
29 +{"author": "Simon-Pierre Boucher", "cache_key": "259e3538bf14a76958ede83515992039", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/index/SPX", "fetched_utc": "2026-08-12T05:44:28Z", "first": "2026-08-06 09:30:00", "last": "2026-08-06 16:20:00", "params": {"end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 411, "sha256": "090e3b520b655b586436b9ae59d879ed6bc698d159b5067ce16b3af03e03c369"}
30 +{"author": "Simon-Pierre Boucher", "cache_key": "3a6d3acca6bfc113a77a83d4cd91404c", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/fx/EURUSD", "fetched_utc": "2026-08-12T05:44:28Z", "first": "2026-08-06 00:00:00", "last": "2026-08-07 00:00:00", "params": {"end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 1439, "sha256": "3d157e8ff409dc312bd2c8a640960b17919e1f2e07a797be1d5bb7dab0080258"}
31 +{"author": "Simon-Pierre Boucher", "cache_key": "23d64e7084f192935c91a312fd9467e6", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/crypto/BTC", "fetched_utc": "2026-08-12T05:44:29Z", "first": "2026-08-06 00:00:00", "last": "2026-08-07 00:00:00", "params": {"end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 1441, "sha256": "edc23f14f5df9ca465d9fc4e7a9c088c33777b7336d112504ba01910a25047d5"}
32 +{"author": "Simon-Pierre Boucher", "cache_key": "24aa39d914859b2bbe7e6ab25b2b98f3", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AIZN", "fetched_utc": "2026-08-12T05:44:29Z", "first": "2026-08-06 09:30:00", "last": "2026-08-06 16:00:00", "params": {"adjustment": "adj_splitdiv", "end": "2026-08-07", "limit": 50000, "order": "asc", "start": "2026-08-06", "timeframe": "1min"}, "rows": 38, "sha256": "0b3633cab5c4115223a5fa086bfdce59f014da5422577c80919cb5f91d210f3b"}
33 +{"author": "Simon-Pierre Boucher", "cache_key": "5e2dbb1a084575274acf716e4c77ee3d", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:44:29Z", "first": "2026-08-06", "last": "2026-08-07", "params": {"adjustment": "adj_splitdiv", "end": "2026-08-07", "start": "2026-08-06", "timeframe": "1day"}, "rows": 2, "sha256": "c43eacb07d216202b5e63ab022866974e82a035256181c722731bd687eefaeda"}
34 +{"author": "Simon-Pierre Boucher", "cache_key": "6bf4b6e0a245818fb2d7ad7436fb6d7e", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:44:29Z", "first": "2020-08-28", "last": "2020-09-01", "params": {"adjustment": "UNADJUSTED", "end": "2020-09-01", "start": "2020-08-28", "timeframe": "1day"}, "rows": 3, "sha256": "030a7834a1c8f76205c9117fbc47b62f87bca1bb9f9edd80986002957ce4b82b"}
35 +{"author": "Simon-Pierre Boucher", "cache_key": "1556af00047ceb687d44d9c4a5b20d50", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:44:29Z", "first": "2020-08-28", "last": "2020-09-01", "params": {"adjustment": "adj_split", "end": "2020-09-01", "start": "2020-08-28", "timeframe": "1day"}, "rows": 3, "sha256": "cb80b2723a406f21cd4f34415138e59d9f1bc0535d45cd3299e33c65407ae93a"}
36 +{"author": "Simon-Pierre Boucher", "cache_key": "f23ce9411d60aad59cbafde0fb4c37f7", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/AAPL", "fetched_utc": "2026-08-12T05:44:29Z", "first": "2020-08-28", "last": "2020-09-01", "params": {"adjustment": "adj_splitdiv", "end": "2020-09-01", "start": "2020-08-28", "timeframe": "1day"}, "rows": 3, "sha256": "0c2702b1dff10e96396bba353691b99031275ff82a6ed9211afadbf26f6de57f"}
37 +{"author": "Simon-Pierre Boucher", "cache_key": "ccc560ecc95620597adb14f18fc3be9d", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:31Z", "first": "2020-01-02 04:00:00", "last": "2020-03-26 10:29:00", "params": {"adjustment": "adj_splitdiv", "end": "2021-01-01", "limit": 1000000, "order": "asc", "start": "2020-01-01", "timeframe": "1min"}, "rows": 50000, "sha256": "43b61457535e561721e85fd8e96f2183a1908e343578d7a1f4602f548b97f4c5"}
38 +{"author": "Simon-Pierre Boucher", "cache_key": "18e39597f93d7b6ff9a48580ae7874bd", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:33Z", "first": "2020-01-02 04:00:00", "last": "2020-03-26 10:29:00", "params": {"adjustment": "adj_splitdiv", "end": "2021-01-01", "limit": 50000, "order": "asc", "start": "2020-01-01", "timeframe": "1min"}, "rows": 50000, "sha256": "43b61457535e561721e85fd8e96f2183a1908e343578d7a1f4602f548b97f4c5"}
39 +{"author": "Simon-Pierre Boucher", "cache_key": "e968ab722d22eb39ef81513f7c30a80d", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:35Z", "first": "2020-03-26 10:29:00", "last": "2020-06-12 18:32:00", "params": {"adjustment": "adj_splitdiv", "end": "2021-01-01", "limit": 50000, "order": "asc", "start": "2020-03-26 10:29:00", "timeframe": "1min"}, "rows": 50000, "sha256": "f7e7245f5cf626be74262b5c8618173b9f9273e48bd963847efe49ef49147e07"}
40 +{"author": "Simon-Pierre Boucher", "cache_key": "16439df7af0f596734c85a331cdc7958", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:36Z", "first": "2020-06-12 18:32:00", "last": "2020-09-08 06:52:00", "params": {"adjustment": "adj_splitdiv", "end": "2021-01-01", "limit": 50000, "order": "asc", "start": "2020-06-12 18:32:00", "timeframe": "1min"}, "rows": 50000, "sha256": "e2afa657281ecc8246ec5ad734a166d3f3a335c27b1f3e808efea52cf355407c"}
41 +{"author": "Simon-Pierre Boucher", "cache_key": "20bc915120d50d320644da2bffd47967", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:38Z", "first": "2020-09-08 06:52:00", "last": "2020-11-30 09:50:00", "params": {"adjustment": "adj_splitdiv", "end": "2021-01-01", "limit": 50000, "order": "asc", "start": "2020-09-08 06:52:00", "timeframe": "1min"}, "rows": 50000, "sha256": "356f291b5d09f25cc3b58798d7d393cb2da9d6aee8f8570c04195b23b49ce164"}
42 +{"author": "Simon-Pierre Boucher", "cache_key": "1c2976e202374cd8aace8c4f4c91d3e5", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/etf/SPY", "fetched_utc": "2026-08-12T05:44:38Z", "first": "2020-11-30 09:50:00", "last": "2020-12-31 19:59:00", "params": {"adjustment": "adj_splitdiv", "end": "2021-01-01", "limit": 50000, "order": "asc", "start": "2020-11-30 09:50:00", "timeframe": "1min"}, "rows": 18010, "sha256": "b215670844ee2e0d719050ddb58173d638cf1a3042de1ec25ede5d7d88ded962"}
43 +{"author": "Simon-Pierre Boucher", "cache_key": "3091094d6c8b293650f8827f6c166905", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:38Z", "first": "2025-01-02", "last": "2025-01-03", "params": {"adjustment": "adj_splitdiv", "end": "2025-01-05", "start": "2025-01-01", "timeframe": "1day"}, "rows": 2, "sha256": "6e8865bcd45eec460088dca5aa8bc93925e77d8083c3c464adce2371c6bc58c1"}
44 +{"author": "Simon-Pierre Boucher", "cache_key": "0111c60ee1420b5c5489c4085494766e", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:38Z", "first": "2025-02-03", "last": "2025-02-05", "params": {"adjustment": "adj_splitdiv", "end": "2025-02-05", "start": "2025-02-01", "timeframe": "1day"}, "rows": 3, "sha256": "aa424d68b65bb62fd6483d0008d087e1ed5ccc0ff69b5d29bc88a72befa6f552"}
45 +{"author": "Simon-Pierre Boucher", "cache_key": "6dc3654c9152391c02584c884d04c8ed", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-03-03", "last": "2025-03-05", "params": {"adjustment": "adj_splitdiv", "end": "2025-03-05", "start": "2025-03-01", "timeframe": "1day"}, "rows": 3, "sha256": "2a9fbd63acb3d4c704d95e2ef511315792e9a65450f618b9b0a33be04b46a84c"}
46 +{"author": "Simon-Pierre Boucher", "cache_key": "54e03e5507d0fb79f9edde78d43f32b1", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-04-01", "last": "2025-04-04", "params": {"adjustment": "adj_splitdiv", "end": "2025-04-05", "start": "2025-04-01", "timeframe": "1day"}, "rows": 4, "sha256": "0e1674937b23af9d126d9b59a8c27c14d94b75bba2eb1a38faefb340191db18b"}
47 +{"author": "Simon-Pierre Boucher", "cache_key": "6ebd191f1dd454e09e38c2dd74cec05c", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-05-01", "last": "2025-05-05", "params": {"adjustment": "adj_splitdiv", "end": "2025-05-05", "start": "2025-05-01", "timeframe": "1day"}, "rows": 3, "sha256": "8b1e5c8f7fa824223b8b0b5262b3c40b6037e01edcf596c6cf54f03090996317"}
48 +{"author": "Simon-Pierre Boucher", "cache_key": "5e74b76333d3b37966e5ab22837beced", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-06-02", "last": "2025-06-05", "params": {"adjustment": "adj_splitdiv", "end": "2025-06-05", "start": "2025-06-01", "timeframe": "1day"}, "rows": 4, "sha256": "812ca7078eb819f0df29fd7763252ebca0e77c5c651343e4993db8edb0207fde"}
49 +{"author": "Simon-Pierre Boucher", "cache_key": "c4cf16876122a71fa6a122877146306f", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-07-01", "last": "2025-07-03", "params": {"adjustment": "adj_splitdiv", "end": "2025-07-05", "start": "2025-07-01", "timeframe": "1day"}, "rows": 3, "sha256": "dad9a1475a025e175e7cc10ea451a20b023d706c60ef1628dbe88c4216fe9ff3"}
50 +{"author": "Simon-Pierre Boucher", "cache_key": "8d2b3f3d31e98477f0ec889ec059a9e6", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-08-01", "last": "2025-08-05", "params": {"adjustment": "adj_splitdiv", "end": "2025-08-05", "start": "2025-08-01", "timeframe": "1day"}, "rows": 3, "sha256": "a31f4f06d1a72e4378de6316110f9f107732e3da361ce99b3365961aebc337a2"}
51 +{"author": "Simon-Pierre Boucher", "cache_key": "fc516e63ce22815272d6ee359891e3cc", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:39Z", "first": "2025-09-02", "last": "2025-09-05", "params": {"adjustment": "adj_splitdiv", "end": "2025-09-05", "start": "2025-09-01", "timeframe": "1day"}, "rows": 4, "sha256": "2271dbbe5119a77f6afcd88f67e430833cb021ed03671b962d49adf630c0bf46"}
52 +{"author": "Simon-Pierre Boucher", "cache_key": "1f72e7b9a118cc59ee59baafdfd5ad65", "data_source": "hfmarketdata.io", "endpoint": "/v1/bars/stock/MSFT", "fetched_utc": "2026-08-12T05:44:40Z", "first": "2025-10-01", "last": "2025-10-03", "params": {"adjustment": "adj_splitdiv", "end": "2025-10-05", "start": "2025-10-01", "timeframe": "1day"}, "rows": 3, "sha256": "54892bb0c9a6bda2b3e56338602d65e3f281d6ba1807028ac60a66352ea04edf"}
53 +{"author": "Simon-Pierre Boucher", "cache_key": "98078fa812ec7189fd243149762722aa", "data_source": "hfmarketdata.io", "endpoint": "/v1/options/expirations/SPY", "fetched_utc": "2026-08-12T05:44:40Z", "first": null, "last": null, "params": {"trade_date": "2026-06-15"}, "rows": null, "sha256": "84ddfee82f215136f022f1b375895b964894f516d732dfb8f85baa3ab5b83fbc"}
54 +{"author": "Simon-Pierre Boucher", "cache_key": "b2ce81ddb1aa764e164205ca08aec02d", "data_source": "hfmarketdata.io", "endpoint": "/v1/options/chain/SPY", "fetched_utc": "2026-08-12T05:44:40Z", "first": "2026-06-15", "last": "2026-06-15", "params": {"limit": 2, "trade_date": "2026-06-15"}, "rows": 2, "sha256": "cc63767f8abc43b71f5618e19bfd92b725e9b377d7aa9d828e05bd133d426d62"}
modified experiments/candidate_01/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/candidate_01/benchmark.py
4 # Purpose : Benchmark runner: Candidate 01 (selected in Phase 4 from research/candidate_ranking.md)
4 +# Purpose : Benchmark runner: Candidate 01 (selected in Phase 4 from research/candidate_rankin…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/candidate_02/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/candidate_02/benchmark.py
4 # Purpose : Benchmark runner: Candidate 02 (selected in Phase 4 from research/candidate_ranking.md)
4 +# Purpose : Benchmark runner: Candidate 02 (selected in Phase 4 from research/candidate_rankin…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/candidate_03/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/candidate_03/benchmark.py
4 # Purpose : Benchmark runner: Candidate 03 (selected in Phase 4 from research/candidate_ranking.md)
4 +# Purpose : Benchmark runner: Candidate 03 (selected in Phase 4 from research/candidate_rankin…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expA_data_reality/README.md +1 −1
@@ -12,4 +12,4 @@ status: draft
12 12
13 13 Data reality check: what hfmarketdata.io actually returns (granularity, depth, timestamps, corporate actions, limits)
14 14
15 Status: scaffolded 2026-08-12, not yet run.
15 +Status: **completed 2026-08-12** — see analysis.md and results/expA_data_reality/. Output: research/data_source_profile.md.
modified experiments/micro/expA_data_reality/analysis.md +45 −2
@@ -5,9 +5,52 @@ author: Simon-Pierre Boucher
5 5 contact: contact@spboucher.ai
6 6 data_source: hfmarketdata.io
7 7 created: 2026-08-12
8 status: draft
8 +modified: 2026-08-12
9 +status: reviewed
9 10 ---
10 11
11 12 # Analysis — expA_data_reality
12 13
13 *To be written after results exist. Must include the seven-field block and the evidence standard of CLAUDE.md §10 (never report an in-sample number as a finding).*
14 +Run: `results/expA_data_reality/20260812T054515Z/results.json`
15 +(hardware manifest embedded; 54 network requests on first pass, 100 % cache
16 +hits on re-run — the reproducibility anchor works).
17 +
18 +## What was established
19 +
20 +1. **Granularity is real.** 1-minute bars are served for every advertised
21 + class; 1min is the floor. Options are daily chain snapshots (17 columns,
22 + with bid/ask/IV/Greeks/OI) over 67 quarters, 2010_q1 → 2026_q3_partial.
23 +2. **Depth.** stock/etf 1min from Jan 2000 (26+ years); futures & index from
24 + Jan 2008; fx from 2010; crypto from Apr 2013. Archive lags a few days
25 + (equities ended previous Friday on run date) — not a live feed.
26 +3. **Timestamps** are US/Eastern wall-clock, bar-start labeled, no TZ marker.
27 + Sessions: equities 04:00–19:59 ET, fx Sun 17:00 → Fri 16:59, futures ≈24 h,
28 + crypto 24/7, SPX prints until 16:20. Cross-asset closes are therefore
29 + non-synchronous **by construction**.
30 +4. **Bars exist only where trades occurred** — no zero-volume placeholders.
31 + Liquid RTH is complete (390/390); an illiquid name printed 38 bars/day.
32 +5. **Daily ≠ aggregated 1min.** Daily bars are RTH-only with the official
33 + auction close (312.41 vs 312.49) and include auction/consolidated volume
34 + missing from 1min bars (46.1 M vs 34.7 M extended / 25.7 M RTH).
35 +6. **Adjustments verified** on the AAPL 4:1 split (exact), and `adj_splitdiv`
36 + re-bases the whole history to the build date — adjusted series are not
37 + point-in-time stable.
38 +7. **Limits.** Hard 50 000-row JSON cap (pagination verified: 218 006 rows of
39 + SPY 1min/2020, 0 dupes, monotonic); no rate limiting observed; 0.08–0.22 s
40 + small-request latency.
41 +
42 +## Interpretation (confidence framing)
43 +
44 +No anomaly is claimed; Level n/a. The experiment succeeded by its own
45 +criterion: every downstream-critical property was pinned down. Items 3–6 are
46 +direct seeds for the artifact taxonomy — each is a mechanism that can
47 +manufacture a fake anomaly (spurious lead-lag from non-synchronous sessions;
48 +spurious autocorrelation from stale prints; phantom overnight returns from
49 +auction-vs-last-bar close mismatch; look-ahead from rolling adjustment).
50 +
51 +## Verdict
52 +
53 +**Complete.** Proceed to expB_artifact_baselines (measure the artifact nulls);
54 +in parallel, Phase 1 literature work can start. Everything downstream must
55 +join bars explicitly on time with declared staleness rules — never implicit
56 +LOCF — and must state which close (auction vs last-bar) it uses.
modified experiments/micro/expA_data_reality/benchmark.py +238 −9
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expA_data_reality/benchmark.py
4 # Purpose : Benchmark runner: Data reality check: what hfmarketdata.io actually returns (granularity, depth, timestamps, corporate actions, limits)
4 +# Purpose : Benchmark runner: empirical data reality check of hfmarketdata.io
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -10,25 +10,254 @@
10 10 # Platform : macOS / Apple Silicon (arm64)
11 11 # License : All rights reserved (research code)
12 12 # =============================================================================
13 +"""Experiment A — establish empirically what hfmarketdata.io actually returns.
13 14
14 """Benchmark entry point for expA_data_reality.
15 +Probes (all through the single hf_client, so every response is cached and
16 +manifest-indexed):
17 + A1 dataset inventory (/v1/status): assets x timeframes x adjustments
18 + A2 history bounds per asset class (earliest/latest 1min and 1day)
19 + A3 intraday session structure and missing-minute patterns (liquid vs sparse)
20 + A4 daily-bar vs 1min-aggregate semantics (official close vs last bar)
21 + A5 corporate-action adjustment semantics around the AAPL 2020 4:1 split
22 + A6 response row cap + pagination correctness on a full year of 1min bars
23 + A7 request latency profile (small cached-miss requests)
24 + A8 options coverage (quarters, expirations, chain columns)
15 25
16 Must embed the hardware manifest in all result output
17 (see benchmarks/hardware_manifest.py) and write results to
18 results/expA_data_reality/<timestamp>/. Uses hfmarketdata.io data ONLY, exclusively
19 through src/anomaly_atlas/data/hf_client.py.
26 +Writes results/expA_data_reality/<UTC timestamp>/results.json embedding the
27 +hardware manifest and the client's instrumentation stats.
20 28 """
21 29
30 +from __future__ import annotations
31 +
32 +import json
22 33 import sys
34 +import time
35 +from collections import Counter
36 +from datetime import UTC, datetime
23 37 from pathlib import Path
24 38
25 sys.path.insert(0, str(Path(__file__).resolve().parents[3] / "benchmarks"))
39 +REPO_ROOT = Path(__file__).resolve().parents[3]
40 +sys.path.insert(0, str(REPO_ROOT / "benchmarks"))
41 +sys.path.insert(0, str(REPO_ROOT / "src"))
42 +
26 43 from hardware_manifest import collect_manifest # noqa: E402
27 44
45 +from anomaly_atlas.data.hf_client import HFMarketDataClient # noqa: E402
46 +
47 +REPRESENTATIVE = {
48 + "stock": ("AAPL", "adj_splitdiv"),
49 + "etf": ("SPY", "adj_splitdiv"),
50 + "futures": ("ES", "contin_adj_ratio"),
51 + "index": ("SPX", None),
52 + "fx": ("EURUSD", None),
53 + "crypto": ("BTC", None),
54 +}
55 +PROBE_DAY = "2026-08-06" # a regular Thursday inside every dataset's coverage
56 +
57 +
58 +def bounds(
59 + client: HFMarketDataClient, asset: str, ticker: str, adjustment: str | None, timeframe: str
60 +) -> dict:
61 + def one(order: str) -> str | None:
62 + rows = client.get(
63 + f"/v1/bars/{asset}/{ticker}",
64 + {
65 + "timeframe": timeframe,
66 + "adjustment": adjustment,
67 + "order": order,
68 + "limit": 1,
69 + },
70 + ).get("data", [])
71 + return rows[0]["datetime"] if rows else None
72 +
73 + return {"earliest": one("asc"), "latest": one("desc")}
74 +
75 +
76 +def session_structure(
77 + client: HFMarketDataClient, asset: str, ticker: str, adjustment: str | None
78 +) -> dict:
79 + rows = client.get(
80 + f"/v1/bars/{asset}/{ticker}",
81 + {
82 + "timeframe": "1min",
83 + "adjustment": adjustment,
84 + "start": PROBE_DAY,
85 + "end": "2026-08-07",
86 + "order": "asc",
87 + "limit": 50_000,
88 + },
89 + ).get("data", [])
90 + rows = [r for r in rows if r["datetime"][:10] == PROBE_DAY]
91 + if not rows:
92 + return {"bars": 0}
93 + per_hour = Counter(r["datetime"][11:13] for r in rows)
94 + rth = [r for r in rows if "09:30" <= r["datetime"][11:16] < "16:00"]
95 + return {
96 + "bars": len(rows),
97 + "first": rows[0]["datetime"],
98 + "last": rows[-1]["datetime"],
99 + "bars_per_hour": dict(sorted(per_hour.items())),
100 + "zero_volume_bars": sum(1 for r in rows if r.get("volume") == 0),
101 + "has_volume_field": "volume" in rows[0],
102 + "rth_bars_of_390": len(rth),
103 + }
104 +
28 105
29 106 def main() -> None:
30 manifest = collect_manifest()
31 raise NotImplementedError("experiment not yet implemented")
107 + run_utc = datetime.now(UTC)
108 + client = HFMarketDataClient()
109 + results: dict = {
110 + "experiment": "expA_data_reality",
111 + "run_utc": run_utc.isoformat(),
112 + "author": "Simon-Pierre Boucher",
113 + "contact": "contact@spboucher.ai",
114 + "data_source": "hfmarketdata.io",
115 + }
116 +
117 + # A1 — inventory
118 + status = client.status()
119 + results["A1_inventory"] = status.get("datasets", {})
120 + results["A1_options_quarters"] = (
121 + status.get("datasets", {}).get("options", {}).get("quarters", [])
122 + )
123 +
124 + # A2 — history bounds per class, 1min and 1day
125 + results["A2_bounds"] = {
126 + asset: {tf: bounds(client, asset, tk, adj, tf) for tf in ("1min", "1day")}
127 + for asset, (tk, adj) in REPRESENTATIVE.items()
128 + }
129 +
130 + # A3 — session structure, liquid + sparse
131 + results["A3_session"] = {
132 + f"{asset}:{tk}": session_structure(client, asset, tk, adj)
133 + for asset, (tk, adj) in REPRESENTATIVE.items()
134 + }
135 + results["A3_session"]["stock:AIZN(sparse)"] = session_structure(
136 + client, "stock", "AIZN", "adj_splitdiv"
137 + )
138 +
139 + # A4 — daily bar vs 1min RTH aggregate (close semantics)
140 + day = client.get(
141 + "/v1/bars/stock/AAPL",
142 + {
143 + "timeframe": "1day",
144 + "adjustment": "adj_splitdiv",
145 + "start": PROBE_DAY,
146 + "end": "2026-08-07",
147 + },
148 + )["data"][0]
149 + intraday = client.get(
150 + "/v1/bars/stock/AAPL",
151 + {
152 + "timeframe": "1min",
153 + "adjustment": "adj_splitdiv",
154 + "start": PROBE_DAY,
155 + "end": "2026-08-07",
156 + "order": "asc",
157 + "limit": 50_000,
158 + },
159 + )["data"]
160 + rth = [r for r in intraday if "09:30" <= r["datetime"][11:16] < "16:00"]
161 + results["A4_daily_vs_intraday"] = {
162 + "daily_bar": day,
163 + "rth_1min_aggregate": {
164 + "open": rth[0]["open"],
165 + "high": max(r["high"] for r in rth),
166 + "low": min(r["low"] for r in rth),
167 + "close": rth[-1]["close"],
168 + "volume": sum(r["volume"] for r in rth),
169 + "bars": len(rth),
170 + },
171 + "extended_1min_volume": sum(r["volume"] for r in intraday),
172 + }
173 +
174 + # A5 — adjustment semantics around AAPL 2020-08-31 4:1 split
175 + results["A5_adjustments"] = {}
176 + for adj in ("UNADJUSTED", "adj_split", "adj_splitdiv"):
177 + rows = client.get(
178 + "/v1/bars/stock/AAPL",
179 + {
180 + "timeframe": "1day",
181 + "adjustment": adj,
182 + "start": "2020-08-28",
183 + "end": "2020-09-01",
184 + },
185 + )["data"]
186 + results["A5_adjustments"][adj] = [
187 + {"date": r["datetime"][:10], "close": r["close"]} for r in rows
188 + ]
189 +
190 + # A6 — row cap + pagination on a full year of SPY 1min
191 + capped = client.get(
192 + "/v1/bars/etf/SPY",
193 + {
194 + "timeframe": "1min",
195 + "adjustment": "adj_splitdiv",
196 + "start": "2020-01-01",
197 + "end": "2021-01-01",
198 + "order": "asc",
199 + "limit": 1_000_000,
200 + },
201 + )
202 + year = client.get_bars("etf", "SPY", "1min", "adj_splitdiv", "2020-01-01", "2021-01-01")
203 + dts = [r["datetime"] for r in year]
204 + results["A6_row_cap_and_pagination"] = {
205 + "requested_rows": 1_000_000,
206 + "returned_rows_single_request": capped.get("count"),
207 + "paginated_total_rows": len(year),
208 + "paginated_first": dts[0],
209 + "paginated_last": dts[-1],
210 + "duplicates_after_pagination": len(dts) - len(set(dts)),
211 + "monotonic_ascending": all(a < b for a, b in zip(dts, dts[1:], strict=False)),
212 + }
213 +
214 + # A7 — latency profile (cache misses: distinct small requests)
215 + latencies = []
216 + for month in range(1, 11):
217 + t0 = time.perf_counter()
218 + client.get(
219 + "/v1/bars/stock/MSFT",
220 + {
221 + "timeframe": "1day",
222 + "adjustment": "adj_splitdiv",
223 + "start": f"2025-{month:02d}-01",
224 + "end": f"2025-{month:02d}-05",
225 + },
226 + )
227 + latencies.append(round(time.perf_counter() - t0, 4))
228 + results["A7_latency_s"] = {
229 + "samples": latencies,
230 + "note": "sequential small requests, cold cache; no rate-limit headers observed",
231 + }
232 +
233 + # A8 — options coverage
234 + expiries = client.options_expirations("SPY", trade_date="2026-06-15")
235 + chain = client.options_chain("SPY", "2026-06-15", limit=2)
236 + results["A8_options"] = {
237 + "n_quarters": len(results["A1_options_quarters"]),
238 + "first_quarter": results["A1_options_quarters"][:1],
239 + "last_quarter": results["A1_options_quarters"][-1:],
240 + "spy_expirations_on_2026-06-15": len(expiries),
241 + "chain_columns": sorted(chain[0].keys()) if chain else None,
242 + "granularity": "daily (one row per contract per trade_date)",
243 + }
244 +
245 + # instrumentation + hardware
246 + results["client_stats"] = {
247 + "network_requests": client.stats.network_requests,
248 + "cache_hits": client.stats.cache_hits,
249 + "rows_fetched": client.stats.rows_fetched,
250 + "seconds_waiting": round(client.stats.seconds_waiting, 2),
251 + "errors_retried": client.stats.errors_retried,
252 + }
253 + results["manifest"] = collect_manifest()
254 +
255 + out_dir = REPO_ROOT / "results" / "expA_data_reality" / run_utc.strftime("%Y%m%dT%H%M%SZ")
256 + out_dir.mkdir(parents=True)
257 + out = out_dir / "results.json"
258 + out.write_text(json.dumps(results, indent=2) + "\n")
259 + print(f"wrote {out.relative_to(REPO_ROOT)}")
260 + print(json.dumps(results["client_stats"], indent=2))
32 261
33 262
34 263 if __name__ == "__main__":
modified experiments/micro/expA_data_reality/hypothesis.md +26 −12
@@ -5,34 +5,48 @@ author: Simon-Pierre Boucher
5 5 contact: contact@spboucher.ai
6 6 data_source: hfmarketdata.io
7 7 created: 2026-08-12
8 status: draft
8 +modified: 2026-08-12
9 +status: final
9 10 ---
10 11
11 12 # Hypothesis — expA_data_reality
12 13
13 14 ```text
14 15 Hypothesis
15 <what we believe and why — pre-specified BEFORE looking at results>
16 + hfmarketdata.io advertises 1min→1day bars across stock/etf/futures/index/
17 + fx/crypto plus daily options chains. We believe the advertised granularity
18 + is real, but that session semantics, timestamp conventions, adjustment
19 + behavior, missing-data patterns and response limits are NOT documented
20 + precisely enough to design honest experiments — they must be measured.
16 21
17 22 Falsification criterion
18 <the concrete measurable outcome that would prove this wrong>
23 + Not a statistical hypothesis: this experiment FAILS if any downstream-
24 + critical property (granularity, timestamp basis, adjustment arithmetic,
25 + row caps, completeness) cannot be pinned down empirically, or if the
26 + advertised 1-minute granularity turns out to be resampled/absent.
19 27
20 28 Artifact null(s)
21 <the fake-signal baseline(s) this must beat: bounce / staleness /
22 non-synchronous timestamps / permuted calendar / random walk>
29 + None (no anomaly is claimed). This experiment EXISTS to seed the artifact
30 + taxonomy that later experiments must beat.
23 31
24 32 Method
25 <exact procedure, universe, split (train/validation/holdout), seeds,
26 number of hypotheses tested, correction applied>
33 + Eight probe families (A1–A8) through the single cached client:
34 + inventory; history bounds per class (representative tickers AAPL, SPY, ES,
35 + SPX, EURUSD, BTC); intraday session structure incl. a sparse ticker (AIZN);
36 + daily-vs-1min aggregate comparison; AAPL 2020 4:1 split across all three
37 + adjustment series; row-cap + pagination check on one year of SPY 1min;
38 + latency profile; options coverage. All responses cached; manifest indexed.
27 39
28 40 Result
29 <filled after the run: effect size, bootstrap CIs, corrected p-values,
30 OOS status, cost-adjusted effect, credits used>
41 + See analysis.md and results/expA_data_reality/20260812T054515Z/results.json.
42 + 54 network requests, 274 571 rows, 0 retries; re-run = 55 cache hits,
43 + 0 network requests.
31 44
32 45 Interpretation
33 <what the numbers mean, WITH confidence level (0-3); alternative
34 explanations considered — artifact first>
46 + Confidence n/a (no anomaly claim). Dataset is fit for Q1–Q3 research with
47 + five load-bearing caveats (see data_source_profile.md §9).
35 48
36 49 Next experiment
37 <the most informative follow-up given this result>
50 + expB_artifact_baselines: build the measured null distributions for
51 + bid-ask bounce, stale prices, and non-synchronous lead-lag on THIS data.
38 52 ```
modified experiments/micro/expB_artifact_baselines/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expB_artifact_baselines/benchmark.py
4 # Purpose : Benchmark runner: Artifact baselines: null distributions of bounce, staleness, non-synchronous lead-lag
4 +# Purpose : Benchmark runner: Artifact baselines: null distributions of bounce, staleness, non…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expC_reversion_scan/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expC_reversion_scan/benchmark.py
4 # Purpose : Benchmark runner: Mean-reversion scan: variance ratios, half-life, AR tests net of the bounce null
4 +# Purpose : Benchmark runner: Mean-reversion scan: variance ratios, half-life, AR tests net of…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expD_leadlag_scan/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expD_leadlag_scan/benchmark.py
4 # Purpose : Benchmark runner: Lead-lag scan: lagged cross-correlations and Granger, separating timestamp artifacts
4 +# Purpose : Benchmark runner: Lead-lag scan: lagged cross-correlations and Granger, separating…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expE_calendar_scan/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expE_calendar_scan/benchmark.py
4 # Purpose : Benchmark runner: Calendar scan: intraday, day-of-week, turn-of-month with pre-counted hypothesis budget
4 +# Purpose : Benchmark runner: Calendar scan: intraday, day-of-week, turn-of-month with pre-cou…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expF_multiple_testing/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expF_multiple_testing/benchmark.py
4 # Purpose : Benchmark runner: Multiple-testing survival: White RC, SPA, FDR, deflated Sharpe on C-E output
4 +# Purpose : Benchmark runner: Multiple-testing survival: White RC, SPA, FDR, deflated Sharpe o…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expG_cost_frontier/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expG_cost_frontier/benchmark.py
4 # Purpose : Benchmark runner: Cost frontier: transaction-cost sweep, where each surviving net effect crosses zero
4 +# Purpose : Benchmark runner: Cost frontier: transaction-cost sweep, where each surviving net …
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified experiments/micro/expH_oos_stability/benchmark.py +2 −2
@@ -1,7 +1,7 @@
1 1 # =============================================================================
2 2 # Project : anomaly-atlas
3 3 # File : experiments/micro/expH_oos_stability/benchmark.py
4 # Purpose : Benchmark runner: Out-of-sample stability: once-touched holdout and sub-period evaluation of survivors
4 +# Purpose : Benchmark runner: Out-of-sample stability: once-touched holdout and sub-period eva…
5 5 # Author : Simon-Pierre Boucher
6 6 # Contact : contact@spboucher.ai
7 7 # Data src : hfmarketdata.io (sole data source)
@@ -27,7 +27,7 @@ from hardware_manifest import collect_manifest # noqa: E402
27 27
28 28
29 29 def main() -> None:
30 manifest = collect_manifest()
30 + collect_manifest() # embedded in results once implemented
31 31 raise NotImplementedError("experiment not yet implemented")
32 32
33 33
modified research/LOG.md +26 −0
@@ -28,3 +28,29 @@ MacLustr node M3U96b (port 8150) behind ngrok at www.anomaly-atlas.io.
28 28 establish empirically what hfmarketdata.io actually provides before any
29 29 methodology is designed. No statistical work before the detectors pass the
30 30 synthetic-series tests (§8.1).
31 +
32 +## 2026-08-12 01:50 ET — Phase 0.5 complete: Experiment A (data reality check)
33 +
34 +**Question.** What does hfmarketdata.io actually provide (granularity,
35 +depth, timestamps, adjustments, completeness, limits)?
36 +
37 +**Experiment.** expA_data_reality — 8 probe families through the newly
38 +implemented single client (`hf_client.py`: cache-first, throttled, back-off,
39 +data-manifest index; 5 offline unit tests pass). 54 network requests,
40 +274 571 rows; re-run served 100 % from cache (0 network requests).
41 +
42 +**Result.** 1min granularity is real; stock/etf from 2000, futures/index 2008,
43 +fx 2010, crypto 2013; options daily chains 2010→2026 (67 quarters). Timestamps
44 +US/Eastern bar-start. Archive lags ~2–5 days. Hard 50k-row cap (client
45 +paginates, verified 218 006 rows, 0 dupes). Split arithmetic exact;
46 +adj_splitdiv re-bases history to build date. No zero-volume bars — missing
47 +minutes are real gaps. Daily bars = RTH + auction close + consolidated volume
48 +NOT present in 1min bars.
49 +
50 +**Interpretation.** Dataset is fit for Q1–Q3. Four built-in artifact
51 +mechanisms identified (non-synchronous sessions, stale prints, auction-close
52 +mismatch, rolling adjustment anchor) — these seed the artifact taxonomy and
53 +the expB nulls.
54 +
55 +**Decision.** Full findings in research/data_source_profile.md (status:
56 +reviewed). Next: expB_artifact_baselines + Phase 1 literature sweep.
modified research/data_source_profile.md +134 −5
@@ -5,13 +5,142 @@ author: Simon-Pierre Boucher
5 5 contact: contact@spboucher.ai
6 6 data_source: hfmarketdata.io
7 7 created: 2026-08-12
8 status: draft
8 +modified: 2026-08-12
9 +status: reviewed
9 10 ---
10 11
11 12 # Data source profile — hfmarketdata.io
12 13
13 *Phase 0.5 output (Experiment A). Not yet written.*
14 +**Phase 0.5 output (Experiment A).** Everything below was established
15 +*empirically* against the live API on 2026-08-12, through the single client
16 +(`src/anomaly_atlas/data/hf_client.py`); every probe is cached and recorded in
17 +`data_manifest/index.jsonl`. Raw evidence:
18 +`results/expA_data_reality/20260812T054515Z/results.json`.
14 19
15 To establish empirically: symbols per asset class, history depth, true finest
16 granularity, timestamp semantics (exchange vs vendor), corporate-action
17 handling, missing-data patterns, rate/credit limits.
20 +## 1. API shape
21 +
22 +OpenAPI-documented FastAPI service, open (no auth), JSON/CSV:
23 +
24 +| Endpoint | Purpose |
25 +|---|---|
26 +| `GET /v1/status` | full dataset inventory (assets × timeframes × adjustments × ticker counts) |
27 +| `GET /v1/{asset}/tickers` | ticker lists (`search`, `limit`) |
28 +| `GET /v1/bars/{asset}/{ticker}` | OHLCV bars (`timeframe`, `adjustment`, `start`, `end`, `order`, `limit`, `format`) |
29 +| `GET /v1/bars/{asset}` | same, multi-ticker (`tickers=A,B,C`) |
30 +| `GET /v1/snapshot/{asset}` | last bar ≤ instant `at` for each ticker (cross-section) |
31 +| `GET /v1/options/{quarters,tickers,chain,expirations,history}` | daily options chains |
32 +
33 +Bar row schema: `{ticker, datetime, open, high, low, close, volume}`
34 +**index bars have no `volume` field**. Errors: 404 unknown ticker/asset,
35 +400 invalid timeframe. `end` is an **exclusive instant** (a bare date means
36 +midnight — `start=D&end=D` returns nothing; use `end = D+1 day`).
37 +
38 +## 2. Coverage and history depth (verified per class)
39 +
40 +| Asset | Tickers (1min) | Earliest 1min | Latest seen | Adjustments |
41 +|---|---|---|---|---|
42 +| stock | 7 670 | 2000-01-04 09:30 (AAPL) | 2026-08-07 19:59 | UNADJUSTED, adj_split, adj_splitdiv |
43 +| etf | 5 161 | 2000-01-03 09:31 (SPY) | 2026-08-07 19:59 | UNADJUSTED, adj_split, adj_splitdiv |
44 +| futures (continuous) | 131 | 2008-01-02 06:00 (ES) | 2026-08-06 23:59 | contin_UNadj, contin_adj_absolute, contin_adj_ratio |
45 +| futures_contracts | ~14.5k archive + 2.5k live | — | — | archive / update |
46 +| index | 123 | 2008-01-02 09:30 (SPX; 1day back to 2000-11) | 2026-08-07 16:50 | none |
47 +| fx | 79 | 2010-01-03 17:00 (EURUSD) | 2026-08-07 16:59 | none |
48 +| crypto | 74 | 2013-04-01 (BTC) | 2026-08-09 23:59 | none |
49 +| options | ~6 000 underlyings/quarter | 2010_q1 | 2026_q3_partial (67 quarters) | n/a |
50 +
51 +Timeframes: **1min, 5min, 30min, 1hour, 1day** — 1-minute is genuinely served
52 +(verified), it is the finest granularity. Note: UNADJUSTED intraday exists only
53 +at 1min and 1day for stock/etf; 5min/30min/1hour exist only adjusted.
54 +
55 +**Data lag: this is a periodically refreshed archive, not a live feed.**
56 +On Wednesday 2026-08-12, equities/fx/index ended 2026-08-07 (previous Friday),
57 +futures 2026-08-06, crypto 2026-08-09. Design experiments accordingly — no
58 +same-day data.
59 +
60 +## 3. Timestamp semantics (critical for lead-lag work)
61 +
62 +* Timestamps are **US/Eastern wall-clock strings with no timezone marker**
63 + (`YYYY-MM-DD HH:MM:SS`). Evidence: FX week runs Sunday 17:00 → Friday 16:59
64 + (the classic ET convention); equity sessions run 04:00–19:59.
65 +* Bars are **bar-start labeled** (first RTH bar 09:30, last extended bar 19:59).
66 +* Session windows on the probe day (Thu 2026-08-06):
67 + stock/etf 04:00–19:59 (pre + RTH + post), futures ≈ 24 h, fx 24 h (ET week),
68 + crypto 24/7, **index SPX 09:30–16:20** (settlement prints after the close).
69 +* These are vendor-consolidated last-trade bars at minute resolution. Assume
70 + vendor time ≈ exchange time, but **cross-asset closes are non-synchronous by
71 + construction** (16:00 auction vs 16:20 index prints vs 24 h sessions) — a
72 + built-in source of spurious lead-lag (see artifact taxonomy).
73 +
74 +## 4. Missing data: bars exist only where trades occurred
75 +
76 +* **No zero-volume placeholder bars anywhere** (0 found on probe day).
77 +* Liquid names are near-complete in RTH (AAPL & SPY: 390/390 RTH minutes) but
78 + sparse pre/post (AAPL: 904 of 960 extended minutes).
79 +* Illiquid names are sparse even in RTH: AIZN printed **38 bars in the whole
80 + day**. A last-observation-carried-forward join makes such series look
81 + autocorrelated and cross-predictable — this is the **stale-price artifact**,
82 + to be neutralized explicitly (Experiment B).
83 +
84 +## 5. Daily-bar semantics (do NOT mix carelessly with 1min)
85 +
86 +For AAPL on 2026-08-06 (`adj_splitdiv`):
87 +
88 +* Daily bar = **RTH-only OHLC with the official auction close**: daily close
89 + 312.41 vs 312.49 for the last 1min RTH bar — the closing auction is not in
90 + the 1min series.
91 +* Daily **volume includes consolidated/auction volume absent from 1min bars**:
92 + daily 46.14 M vs 34.67 M (sum of all extended-hours 1min) vs 25.69 M (RTH
93 + 1min only). Any volume-based signal must pick one convention and stick to it.
94 +
95 +## 6. Corporate actions / adjustments (verified on AAPL 4:1, 2020-08-31)
96 +
97 +| Series | 2020-08-28 close | 2020-08-31 close |
98 +|---|---|---|
99 +| UNADJUSTED | 499.23 | 129.04 |
100 +| adj_split | 124.8075 (= 499.23/4) | 129.04 |
101 +| adj_splitdiv | 121.06 | 125.1654 |
102 +
103 +* Split arithmetic is exact.
104 +* **`adj_splitdiv` re-bases the entire history to the dataset build date**
105 + even the 2020-08-31 close differs from its traded price. Adjusted series are
106 + therefore *not point-in-time stable*: they change whenever a new dividend
107 + occurs. For any experiment sensitive to this, use UNADJUSTED + explicit
108 + adjustment, or freeze the cache (which our client does by design).
109 +
110 +## 7. Options
111 +
112 +Daily granularity: one row per contract per `trade_date`, 17 columns —
113 +`bid/ask`, `last_price`, `bid_iv/ask_iv`, `delta/gamma/vega/theta/rho`,
114 +`open_interest`, `volume`, `strike`, `expiry`, `call_put`. 67 quarters
115 +(2010_q1 → 2026_q3_partial). SPY had 35 listed expirations on 2026-06-15.
116 +Old quarters may carry null Greeks (documented server-side NaN handling).
117 +This is the only bid/ask information anywhere in the source — options spreads
118 +may inform equity cost models (Experiment G), with care.
119 +
120 +## 8. Limits, performance, credits
121 +
122 +* **Hard cap: 50 000 rows per JSON response** (requested 1 000 000, got
123 + 50 000). The client paginates on the last datetime; verified on a full year
124 + of SPY 1min: 218 006 rows, 0 duplicates, strictly ascending.
125 +* No rate-limit headers, no 429 observed at gentle sequential rates
126 + (~5–7 req/s). Latency: 0.08–0.22 s small requests, ~1.7 s per 50 000-row
127 + page. There is no credit system — but the client throttles anyway
128 + (min 0.15 s between requests) and caches everything; the cache is the
129 + reproducibility anchor.
130 +* `format=csv` exists (not exercised in Experiment A; JSON + local parquet
131 + cache is our path).
132 +
133 +## 9. Consequences for the research design
134 +
135 +1. **Universe**: ample for Q1–Q3 — thousands of stock/etf tickers × 26 years
136 + × 1min. Holdout years are affordable.
137 +2. **Artifact taxonomy seeds** (→ `artifact_taxonomy.md`): stale/missing
138 + minutes; non-synchronous session ends across assets; auction close absent
139 + from 1min bars; daily-vs-intraday volume mismatch; rolling adjustment
140 + anchor; continuous-futures splicing method (3 variants exposed — good:
141 + the choice is testable).
142 +3. **No bid/ask on bars**: spreads must be *estimated* (Roll model et al.) —
143 + the bounce null (Experiment B) is mandatory, not optional.
144 +4. **Minute data is the floor**: intraday lead-lag finer than 1 minute is
145 + untestable here; hypotheses must respect that (Epps effect at 1min scale).
146 +5. Data ends ~the previous Friday: "recent regime" claims are bounded.
added results/expA_data_reality/20260812T054515Z/results.json +720 −0
@@ -0,0 +1,720 @@
1 +{
2 + "experiment": "expA_data_reality",
3 + "run_utc": "2026-08-12T05:45:15.765371+00:00",
4 + "author": "Simon-Pierre Boucher",
5 + "contact": "contact@spboucher.ai",
6 + "data_source": "hfmarketdata.io",
7 + "A1_inventory": {
8 + "crypto": {
9 + "1day": {
10 + "none": 74
11 + },
12 + "1hour": {
13 + "none": 74
14 + },
15 + "1min": {
16 + "none": 74
17 + },
18 + "30min": {
19 + "none": 74
20 + },
21 + "5min": {
22 + "none": 74
23 + }
24 + },
25 + "etf": {
26 + "1day": {
27 + "UNADJUSTED": 5161,
28 + "adj_split": 5161,
29 + "adj_splitdiv": 5161
30 + },
31 + "1hour": {
32 + "adj_split": 5161,
33 + "adj_splitdiv": 5161
34 + },
35 + "1min": {
36 + "UNADJUSTED": 5161,
37 + "adj_split": 5161,
38 + "adj_splitdiv": 5161
39 + },
40 + "30min": {
41 + "adj_split": 5161,
42 + "adj_splitdiv": 5161
43 + },
44 + "5min": {
45 + "adj_split": 5161,
46 + "adj_splitdiv": 5161
47 + }
48 + },
49 + "futures": {
50 + "1day": {
51 + "contin_UNadj": 132,
52 + "contin_adj_absolute": 132,
53 + "contin_adj_ratio": 132
54 + },
55 + "1hour": {
56 + "contin_UNadj": 131,
57 + "contin_adj_absolute": 131,
58 + "contin_adj_ratio": 131
59 + },
60 + "1min": {
61 + "contin_UNadj": 131,
62 + "contin_adj_absolute": 131,
63 + "contin_adj_ratio": 131
64 + },
65 + "30min": {
66 + "contin_UNadj": 131,
67 + "contin_adj_absolute": 131,
68 + "contin_adj_ratio": 131
69 + },
70 + "5min": {
71 + "contin_UNadj": 131,
72 + "contin_adj_absolute": 131,
73 + "contin_adj_ratio": 131
74 + }
75 + },
76 + "futures_contracts": {
77 + "1day": {
78 + "archive": 15279,
79 + "update": 3233
80 + },
81 + "1hour": {
82 + "archive": 14497,
83 + "update": 2513
84 + },
85 + "1min": {
86 + "archive": 14577,
87 + "update": 2513
88 + },
89 + "30min": {
90 + "archive": 14497,
91 + "update": 2513
92 + },
93 + "5min": {
94 + "archive": 14497,
95 + "update": 2513
96 + }
97 + },
98 + "fx": {
99 + "1day": {
100 + "none": 79
101 + },
102 + "1hour": {
103 + "none": 79
104 + },
105 + "1min": {
106 + "none": 79
107 + },
108 + "30min": {
109 + "none": 79
110 + },
111 + "5min": {
112 + "none": 79
113 + }
114 + },
115 + "index": {
116 + "1day": {
117 + "none": 125
118 + },
119 + "1hour": {
120 + "none": 122
121 + },
122 + "1min": {
123 + "none": 123
124 + },
125 + "30min": {
126 + "none": 122
127 + },
128 + "5min": {
129 + "none": 122
130 + }
131 + },
132 + "options": {
133 + "files_latest_quarter": 6007,
134 + "quarters": [
135 + "2010_q1",
136 + "2010_q2",
137 + "2010_q3",
138 + "2010_q4",
139 + "2011_q1",
140 + "2011_q2",
141 + "2011_q3",
142 + "2011_q4",
143 + "2012_q1",
144 + "2012_q2",
145 + "2012_q3",
146 + "2012_q4",
147 + "2013_q1",
148 + "2013_q2",
149 + "2013_q3",
150 + "2013_q4",
151 + "2014_q1",
152 + "2014_q2",
153 + "2014_q3",
154 + "2014_q4",
155 + "2015_q1",
156 + "2015_q2",
157 + "2015_q3",
158 + "2015_q4",
159 + "2016_q1",
160 + "2016_q2",
161 + "2016_q3",
162 + "2016_q4",
163 + "2017_q1",
164 + "2017_q2",
165 + "2017_q3",
166 + "2017_q4",
167 + "2018_q1",
168 + "2018_q2",
169 + "2018_q3",
170 + "2018_q4",
171 + "2019_q1",
172 + "2019_q2",
173 + "2019_q3",
174 + "2019_q4",
175 + "2020_q1",
176 + "2020_q2",
177 + "2020_q3",
178 + "2020_q4",
179 + "2021_q1",
180 + "2021_q2",
181 + "2021_q3",
182 + "2021_q4",
183 + "2022_q1",
184 + "2022_q2",
185 + "2022_q3",
186 + "2022_q4",
187 + "2023_q1",
188 + "2023_q2",
189 + "2023_q3",
190 + "2023_q4",
191 + "2024_q1",
192 + "2024_q2",
193 + "2024_q3",
194 + "2024_q4",
195 + "2025_q1",
196 + "2025_q2",
197 + "2025_q3",
198 + "2025_q4",
199 + "2026_q1",
200 + "2026_q2",
201 + "2026_q3_partial"
202 + ]
203 + },
204 + "stock": {
205 + "1day": {
206 + "UNADJUSTED": 7665,
207 + "adj_split": 7664,
208 + "adj_splitdiv": 7664
209 + },
210 + "1hour": {
211 + "adj_split": 7669,
212 + "adj_splitdiv": 7669
213 + },
214 + "1min": {
215 + "UNADJUSTED": 7670,
216 + "adj_split": 7670,
217 + "adj_splitdiv": 7670
218 + },
219 + "30min": {
220 + "adj_split": 7669,
221 + "adj_splitdiv": 7669
222 + },
223 + "5min": {
224 + "adj_split": 7669,
225 + "adj_splitdiv": 7669
226 + }
227 + }
228 + },
229 + "A1_options_quarters": [
230 + "2010_q1",
231 + "2010_q2",
232 + "2010_q3",
233 + "2010_q4",
234 + "2011_q1",
235 + "2011_q2",
236 + "2011_q3",
237 + "2011_q4",
238 + "2012_q1",
239 + "2012_q2",
240 + "2012_q3",
241 + "2012_q4",
242 + "2013_q1",
243 + "2013_q2",
244 + "2013_q3",
245 + "2013_q4",
246 + "2014_q1",
247 + "2014_q2",
248 + "2014_q3",
249 + "2014_q4",
250 + "2015_q1",
251 + "2015_q2",
252 + "2015_q3",
253 + "2015_q4",
254 + "2016_q1",
255 + "2016_q2",
256 + "2016_q3",
257 + "2016_q4",
258 + "2017_q1",
259 + "2017_q2",
260 + "2017_q3",
261 + "2017_q4",
262 + "2018_q1",
263 + "2018_q2",
264 + "2018_q3",
265 + "2018_q4",
266 + "2019_q1",
267 + "2019_q2",
268 + "2019_q3",
269 + "2019_q4",
270 + "2020_q1",
271 + "2020_q2",
272 + "2020_q3",
273 + "2020_q4",
274 + "2021_q1",
275 + "2021_q2",
276 + "2021_q3",
277 + "2021_q4",
278 + "2022_q1",
279 + "2022_q2",
280 + "2022_q3",
281 + "2022_q4",
282 + "2023_q1",
283 + "2023_q2",
284 + "2023_q3",
285 + "2023_q4",
286 + "2024_q1",
287 + "2024_q2",
288 + "2024_q3",
289 + "2024_q4",
290 + "2025_q1",
291 + "2025_q2",
292 + "2025_q3",
293 + "2025_q4",
294 + "2026_q1",
295 + "2026_q2",
296 + "2026_q3_partial"
297 + ],
298 + "A2_bounds": {
299 + "stock": {
300 + "1min": {
301 + "earliest": "2000-01-04 09:30:00",
302 + "latest": "2026-08-07 19:59:00"
303 + },
304 + "1day": {
305 + "earliest": "2000-01-04",
306 + "latest": "2026-08-07"
307 + }
308 + },
309 + "etf": {
310 + "1min": {
311 + "earliest": "2000-01-03 09:31:00",
312 + "latest": "2026-08-07 19:59:00"
313 + },
314 + "1day": {
315 + "earliest": "2000-01-03",
316 + "latest": "2026-08-07"
317 + }
318 + },
319 + "futures": {
320 + "1min": {
321 + "earliest": "2008-01-02 06:00:00",
322 + "latest": "2026-08-06 23:59:00"
323 + },
324 + "1day": {
325 + "earliest": "2008-01-02",
326 + "latest": "2026-08-06"
327 + }
328 + },
329 + "index": {
330 + "1min": {
331 + "earliest": "2008-01-02 09:30:00",
332 + "latest": "2026-08-07 16:50:00"
333 + },
334 + "1day": {
335 + "earliest": "2000-11-27",
336 + "latest": "2026-08-07"
337 + }
338 + },
339 + "fx": {
340 + "1min": {
341 + "earliest": "2010-01-03 17:00:00",
342 + "latest": "2026-08-07 16:59:00"
343 + },
344 + "1day": {
345 + "earliest": "2010-01-04",
346 + "latest": "2026-08-07"
347 + }
348 + },
349 + "crypto": {
350 + "1min": {
351 + "earliest": "2013-04-01 00:30:00",
352 + "latest": "2026-08-09 23:59:00"
353 + },
354 + "1day": {
355 + "earliest": "2013-04-01",
356 + "latest": "2026-08-09"
357 + }
358 + }
359 + },
360 + "A3_session": {
361 + "stock:AAPL": {
362 + "bars": 904,
363 + "first": "2026-08-06 04:00:00",
364 + "last": "2026-08-06 19:59:00",
365 + "bars_per_hour": {
366 + "04": 59,
367 + "05": 55,
368 + "06": 49,
369 + "07": 60,
370 + "08": 60,
371 + "09": 60,
372 + "10": 60,
373 + "11": 60,
374 + "12": 60,
375 + "13": 60,
376 + "14": 60,
377 + "15": 60,
378 + "16": 57,
379 + "17": 50,
380 + "18": 47,
381 + "19": 47
382 + },
383 + "zero_volume_bars": 0,
384 + "has_volume_field": true,
385 + "rth_bars_of_390": 390
386 + },
387 + "etf:SPY": {
388 + "bars": 879,
389 + "first": "2026-08-06 04:00:00",
390 + "last": "2026-08-06 19:59:00",
391 + "bars_per_hour": {
392 + "04": 52,
393 + "05": 41,
394 + "06": 45,
395 + "07": 59,
396 + "08": 60,
397 + "09": 60,
398 + "10": 60,
399 + "11": 60,
400 + "12": 60,
401 + "13": 60,
402 + "14": 60,
403 + "15": 60,
404 + "16": 60,
405 + "17": 48,
406 + "18": 43,
407 + "19": 51
408 + },
409 + "zero_volume_bars": 0,
410 + "has_volume_field": true,
411 + "rth_bars_of_390": 390
412 + },
413 + "futures:ES": {
414 + "bars": 1380,
415 + "first": "2026-08-06 00:00:00",
416 + "last": "2026-08-06 23:59:00",
417 + "bars_per_hour": {
418 + "00": 60,
419 + "01": 60,
420 + "02": 60,
421 + "03": 60,
422 + "04": 60,
423 + "05": 60,
424 + "06": 60,
425 + "07": 60,
426 + "08": 60,
427 + "09": 60,
428 + "10": 60,
429 + "11": 60,
430 + "12": 60,
431 + "13": 60,
432 + "14": 60,
433 + "15": 60,
434 + "16": 60,
435 + "18": 60,
436 + "19": 60,
437 + "20": 60,
438 + "21": 60,
439 + "22": 60,
440 + "23": 60
441 + },
442 + "zero_volume_bars": 0,
443 + "has_volume_field": true,
444 + "rth_bars_of_390": 390
445 + },
446 + "index:SPX": {
447 + "bars": 411,
448 + "first": "2026-08-06 09:30:00",
449 + "last": "2026-08-06 16:20:00",
450 + "bars_per_hour": {
451 + "09": 30,
452 + "10": 60,
453 + "11": 60,
454 + "12": 60,
455 + "13": 60,
456 + "14": 60,
457 + "15": 60,
458 + "16": 21
459 + },
460 + "zero_volume_bars": 0,
461 + "has_volume_field": false,
462 + "rth_bars_of_390": 390
463 + },
464 + "fx:EURUSD": {
465 + "bars": 1438,
466 + "first": "2026-08-06 00:00:00",
467 + "last": "2026-08-06 23:59:00",
468 + "bars_per_hour": {
469 + "00": 60,
470 + "01": 60,
471 + "02": 60,
472 + "03": 60,
473 + "04": 60,
474 + "05": 60,
475 + "06": 60,
476 + "07": 60,
477 + "08": 60,
478 + "09": 60,
479 + "10": 60,
480 + "11": 60,
481 + "12": 60,
482 + "13": 60,
483 + "14": 60,
484 + "15": 60,
485 + "16": 60,
486 + "17": 60,
487 + "18": 60,
488 + "19": 58,
489 + "20": 60,
490 + "21": 60,
491 + "22": 60,
492 + "23": 60
493 + },
494 + "zero_volume_bars": 0,
495 + "has_volume_field": true,
496 + "rth_bars_of_390": 390
497 + },
498 + "crypto:BTC": {
499 + "bars": 1440,
500 + "first": "2026-08-06 00:00:00",
501 + "last": "2026-08-06 23:59:00",
502 + "bars_per_hour": {
503 + "00": 60,
504 + "01": 60,
505 + "02": 60,
506 + "03": 60,
507 + "04": 60,
508 + "05": 60,
509 + "06": 60,
510 + "07": 60,
511 + "08": 60,
512 + "09": 60,
513 + "10": 60,
514 + "11": 60,
515 + "12": 60,
516 + "13": 60,
517 + "14": 60,
518 + "15": 60,
519 + "16": 60,
520 + "17": 60,
521 + "18": 60,
522 + "19": 60,
523 + "20": 60,
524 + "21": 60,
525 + "22": 60,
526 + "23": 60
527 + },
528 + "zero_volume_bars": 77,
529 + "has_volume_field": true,
530 + "rth_bars_of_390": 390
531 + },
532 + "stock:AIZN(sparse)": {
533 + "bars": 38,
534 + "first": "2026-08-06 09:30:00",
535 + "last": "2026-08-06 16:00:00",
536 + "bars_per_hour": {
537 + "09": 1,
538 + "10": 1,
539 + "11": 1,
540 + "12": 4,
541 + "13": 6,
542 + "14": 9,
543 + "15": 15,
544 + "16": 1
545 + },
546 + "zero_volume_bars": 0,
547 + "has_volume_field": true,
548 + "rth_bars_of_390": 37
549 + }
550 + },
551 + "A4_daily_vs_intraday": {
552 + "daily_bar": {
553 + "close": 312.41,
554 + "datetime": "2026-08-06",
555 + "high": 316.2894,
556 + "low": 309.23,
557 + "open": 314.34,
558 + "ticker": "AAPL",
559 + "volume": 46139901.0
560 + },
561 + "rth_1min_aggregate": {
562 + "open": 314.34,
563 + "high": 316.2894,
564 + "low": 309.23,
565 + "close": 312.49,
566 + "volume": 25693543.0,
567 + "bars": 390
568 + },
569 + "extended_1min_volume": 34671540.0
570 + },
571 + "A5_adjustments": {
572 + "UNADJUSTED": [
573 + {
574 + "date": "2020-08-28",
575 + "close": 499.23
576 + },
577 + {
578 + "date": "2020-08-31",
579 + "close": 129.04
580 + },
581 + {
582 + "date": "2020-09-01",
583 + "close": 134.18
584 + }
585 + ],
586 + "adj_split": [
587 + {
588 + "date": "2020-08-28",
589 + "close": 124.8075
590 + },
591 + {
592 + "date": "2020-08-31",
593 + "close": 129.04
594 + },
595 + {
596 + "date": "2020-09-01",
597 + "close": 134.18
598 + }
599 + ],
600 + "adj_splitdiv": [
601 + {
602 + "date": "2020-08-28",
603 + "close": 121.06
604 + },
605 + {
606 + "date": "2020-08-31",
607 + "close": 125.1654
608 + },
609 + {
610 + "date": "2020-09-01",
611 + "close": 130.1511
612 + }
613 + ]
614 + },
615 + "A6_row_cap_and_pagination": {
616 + "requested_rows": 1000000,
617 + "returned_rows_single_request": 50000,
618 + "paginated_total_rows": 218006,
619 + "paginated_first": "2020-01-02 04:00:00",
620 + "paginated_last": "2020-12-31 19:59:00",
621 + "duplicates_after_pagination": 0,
622 + "monotonic_ascending": true
623 + },
624 + "A7_latency_s": {
625 + "samples": [
626 + 0.0004,
627 + 0.0002,
628 + 0.0001,
629 + 0.0001,
630 + 0.0001,
631 + 0.0001,
632 + 0.0003,
633 + 0.0006,
634 + 0.003,
635 + 0.0009
636 + ],
637 + "note": "sequential small requests, cold cache; no rate-limit headers observed"
638 + },
639 + "A8_options": {
640 + "n_quarters": 67,
641 + "first_quarter": [
642 + "2010_q1"
643 + ],
644 + "last_quarter": [
645 + "2026_q3_partial"
646 + ],
647 + "spy_expirations_on_2026-06-15": 35,
648 + "chain_columns": [
649 + "ask",
650 + "ask_iv",
651 + "bid",
652 + "bid_iv",
653 + "call_put",
654 + "delta",
655 + "expiry",
656 + "gamma",
657 + "last_price",
658 + "open_interest",
659 + "rho",
660 + "strike",
661 + "theta",
662 + "ticker",
663 + "trade_date",
664 + "vega",
665 + "volume"
666 + ],
667 + "granularity": "daily (one row per contract per trade_date)"
668 + },
669 + "client_stats": {
670 + "network_requests": 0,
671 + "cache_hits": 55,
672 + "rows_fetched": 0,
673 + "seconds_waiting": 0.0,
674 + "errors_retried": 0
675 + },
676 + "manifest": {
677 + "author": "Simon-Pierre Boucher",
678 + "contact": "contact@spboucher.ai",
679 + "project": "anomaly-atlas",
680 + "data_source": "hfmarketdata.io",
681 + "collected_utc": "2026-08-12T05:45:16.106357+00:00",
682 + "chip": {
683 + "brand": "Apple M5 Max",
684 + "arch": "arm64",
685 + "cores_total": 18,
686 + "cores_performance": 6,
687 + "cores_efficiency": 12,
688 + "gpu_cores": 40
689 + },
690 + "memory": {
691 + "unified_bytes": 51539607552,
692 + "unified_gb": 48.0,
693 + "pagesize": 16384
694 + },
695 + "ssd": {
696 + "model": "APPLE SSD AP2048Z",
697 + "size": "2 TB",
698 + "smart_status": "Verified"
699 + },
700 + "os": {
701 + "product": "macOS",
702 + "version": "27.0",
703 + "build": "26A5388g",
704 + "kernel": "27.0.0"
705 + },
706 + "software": {
707 + "python": "3.14.4",
708 + "numpy": "2.5.2",
709 + "pandas": "3.0.5",
710 + "polars": "1.43.2",
711 + "duckdb": "1.5.5",
712 + "statsmodels": "0.14.6",
713 + "arch": "8.0.0"
714 + },
715 + "git": {
716 + "commit": "e77d5c041c3b4554c92f32704b1c0edea71ffac0",
717 + "dirty_tree": true
718 + }
719 + }
720 +}
modified tools/check_headers.py +34 −6
@@ -39,15 +39,39 @@ from pathlib import Path
39 39
40 40 REPO_ROOT = Path(__file__).resolve().parent.parent
41 41
42 REQUIRED_FIELDS = ("Project", "File", "Purpose", "Author", "Contact",
43 "Data src", "Created", "Modified", "Platform", "License")
42 +REQUIRED_FIELDS = (
43 + "Project",
44 + "File",
45 + "Purpose",
46 + "Author",
47 + "Contact",
48 + "Data src",
49 + "Created",
50 + "Modified",
51 + "Platform",
52 + "License",
53 +)
44 54 AUTHOR = "Simon-Pierre Boucher"
45 55 CONTACT = "contact@spboucher.ai"
46 56 DATA_SOURCE = "hfmarketdata.io"
47 57
48 58 HASH_EXTS = {".py", ".sh", ".zsh", ".bash", ".yaml", ".yml", ".toml", ".cff", ".sql"}
49 SLASH_EXTS = {".c", ".cc", ".cpp", ".h", ".hpp", ".metal", ".swift", ".m", ".mm",
50 ".js", ".mjs", ".ts", ".tsx", ".css"}
59 +SLASH_EXTS = {
60 + ".c",
61 + ".cc",
62 + ".cpp",
63 + ".h",
64 + ".hpp",
65 + ".metal",
66 + ".swift",
67 + ".m",
68 + ".mm",
69 + ".js",
70 + ".mjs",
71 + ".ts",
72 + ".tsx",
73 + ".css",
74 +}
51 75 HASH_NAMES = {"Makefile", "CMakeLists.txt", ".gitignore"}
52 76
53 77 EXEMPT_NAMES = {"LICENSE", "CLAUDE.md", "MEMORY.md", "package-lock.json"}
@@ -95,8 +119,12 @@ def check_markdown_front_matter(lines: list[str]) -> list[str]:
95 119 except StopIteration:
96 120 return ["unterminated YAML front matter"]
97 121 block = "\n".join(lines[1:end])
98 for key in ("project: anomaly-atlas", f"author: {AUTHOR}",
99 f"contact: {CONTACT}", f"data_source: {DATA_SOURCE}"):
122 + for key in (
123 + "project: anomaly-atlas",
124 + f"author: {AUTHOR}",
125 + f"contact: {CONTACT}",
126 + f"data_source: {DATA_SOURCE}",
127 + ):
100 128 if key not in block:
101 129 errors.append(f"front matter missing '{key}'")
102 130 return errors
modified tools/new_experiment.py +8 −3
@@ -106,7 +106,7 @@ from hardware_manifest import collect_manifest # noqa: E402
106 106
107 107
108 108 def main() -> None:
109 manifest = collect_manifest()
109 + collect_manifest() # embedded in results once implemented
110 110 raise NotImplementedError("experiment not yet implemented")
111 111
112 112
@@ -127,7 +127,8 @@ def scaffold(exp_dir: Path, purpose: str) -> None:
127 127 return MD_HEADER.format(doc=doc, today=TODAY)
128 128
129 129 (exp_dir / "README.md").write_text(
130 md(f"{name}/README") + f"\n# {name}\n\n{purpose}\n\nStatus: scaffolded {TODAY}, not yet run.\n"
130 + md(f"{name}/README")
131 + + f"\n# {name}\n\n{purpose}\n\nStatus: scaffolded {TODAY}, not yet run.\n"
131 132 )
132 133 (exp_dir / "hypothesis.md").write_text(
133 134 md(f"{name}/hypothesis") + HYPOTHESIS_BODY.format(name=name)
@@ -139,7 +140,11 @@ def scaffold(exp_dir: Path, purpose: str) -> None:
139 140 )
140 141 depth = len(rel.parts) # parents[] index up to repo root
141 142 (exp_dir / "benchmark.py").write_text(
142 PY_HEADER.format(rel=rel / "benchmark.py", purpose=f"Benchmark runner: {purpose}", today=TODAY)
143 + PY_HEADER.format(
144 + rel=rel / "benchmark.py",
145 + purpose=f"Benchmark runner: {purpose}"[:82],
146 + today=TODAY,
147 + )
143 148 + BENCHMARK_BODY.format(name=name, depth=depth)
144 149 )
145 150 print(f"scaffolded {rel} ({purpose})")
modified tools/new_finding.py +8 −2
@@ -39,8 +39,14 @@ from pathlib import Path
39 39 REPO_ROOT = Path(__file__).resolve().parent.parent
40 40
41 41 PROVENANCE_KEYS = (
42 "commit", "config", "data_manifest_hash", "hardware_manifest",
43 "generated_utc", "n_hypotheses_tested", "correction_method", "oos_status",
42 + "commit",
43 + "config",
44 + "data_manifest_hash",
45 + "hardware_manifest",
46 + "generated_utc",
47 + "n_hypotheses_tested",
48 + "correction_method",
49 + "oos_status",
44 50 )
45 51 FINDING_ATTRIBUTION = {
46 52 "author": "Simon-Pierre Boucher",
added web/package-lock.json +961 −0
@@ -0,0 +1,961 @@
1 +{
2 + "name": "anomaly-atlas-web",
3 + "version": "1.0.0",
4 + "lockfileVersion": 3,
5 + "requires": true,
6 + "packages": {
7 + "": {
8 + "name": "anomaly-atlas-web",
9 + "version": "1.0.0",
10 + "license": "UNLICENSED",
11 + "dependencies": {
12 + "express": "^4.21.0",
13 + "gray-matter": "^4.0.3",
14 + "highlight.js": "^11.10.0",
15 + "marked": "^14.1.0"
16 + }
17 + },
18 + "node_modules/accepts": {
19 + "version": "1.3.8",
20 + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
21 + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
22 + "license": "MIT",
23 + "dependencies": {
24 + "mime-types": "~2.1.34",
25 + "negotiator": "0.6.3"
26 + },
27 + "engines": {
28 + "node": ">= 0.6"
29 + }
30 + },
31 + "node_modules/argparse": {
32 + "version": "1.0.10",
33 + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
34 + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
35 + "license": "MIT",
36 + "dependencies": {
37 + "sprintf-js": "~1.0.2"
38 + }
39 + },
40 + "node_modules/array-flatten": {
41 + "version": "1.1.1",
42 + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
43 + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
44 + "license": "MIT"
45 + },
46 + "node_modules/body-parser": {
47 + "version": "1.20.6",
48 + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
49 + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==",
50 + "license": "MIT",
51 + "dependencies": {
52 + "bytes": "~3.1.2",
53 + "content-type": "~1.0.5",
54 + "debug": "2.6.9",
55 + "depd": "2.0.0",
56 + "destroy": "~1.2.0",
57 + "http-errors": "~2.0.1",
58 + "iconv-lite": "~0.4.24",
59 + "on-finished": "~2.4.1",
60 + "qs": "~6.15.1",
61 + "raw-body": "~2.5.3",
62 + "type-is": "~1.6.18",
63 + "unpipe": "~1.0.0"
64 + },
65 + "engines": {
66 + "node": ">= 0.8",
67 + "npm": "1.2.8000 || >= 1.4.16"
68 + }
69 + },
70 + "node_modules/bytes": {
71 + "version": "3.1.2",
72 + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
73 + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
74 + "license": "MIT",
75 + "engines": {
76 + "node": ">= 0.8"
77 + }
78 + },
79 + "node_modules/call-bind-apply-helpers": {
80 + "version": "1.0.2",
81 + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
82 + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
83 + "license": "MIT",
84 + "dependencies": {
85 + "es-errors": "^1.3.0",
86 + "function-bind": "^1.1.2"
87 + },
88 + "engines": {
89 + "node": ">= 0.4"
90 + }
91 + },
92 + "node_modules/call-bound": {
93 + "version": "1.0.4",
94 + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
95 + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
96 + "license": "MIT",
97 + "dependencies": {
98 + "call-bind-apply-helpers": "^1.0.2",
99 + "get-intrinsic": "^1.3.0"
100 + },
101 + "engines": {
102 + "node": ">= 0.4"
103 + },
104 + "funding": {
105 + "url": "https://github.com/sponsors/ljharb"
106 + }
107 + },
108 + "node_modules/content-disposition": {
109 + "version": "0.5.4",
110 + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
111 + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
112 + "license": "MIT",
113 + "dependencies": {
114 + "safe-buffer": "5.2.1"
115 + },
116 + "engines": {
117 + "node": ">= 0.6"
118 + }
119 + },
120 + "node_modules/content-type": {
121 + "version": "1.0.5",
122 + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
123 + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
124 + "license": "MIT",
125 + "engines": {
126 + "node": ">= 0.6"
127 + }
128 + },
129 + "node_modules/cookie": {
130 + "version": "0.7.2",
131 + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
132 + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
133 + "license": "MIT",
134 + "engines": {
135 + "node": ">= 0.6"
136 + }
137 + },
138 + "node_modules/cookie-signature": {
139 + "version": "1.0.7",
140 + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
141 + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
142 + "license": "MIT"
143 + },
144 + "node_modules/debug": {
145 + "version": "2.6.9",
146 + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
147 + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
148 + "license": "MIT",
149 + "dependencies": {
150 + "ms": "2.0.0"
151 + }
152 + },
153 + "node_modules/depd": {
154 + "version": "2.0.0",
155 + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
156 + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
157 + "license": "MIT",
158 + "engines": {
159 + "node": ">= 0.8"
160 + }
161 + },
162 + "node_modules/destroy": {
163 + "version": "1.2.0",
164 + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
165 + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
166 + "license": "MIT",
167 + "engines": {
168 + "node": ">= 0.8",
169 + "npm": "1.2.8000 || >= 1.4.16"
170 + }
171 + },
172 + "node_modules/dunder-proto": {
173 + "version": "1.0.1",
174 + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
175 + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
176 + "license": "MIT",
177 + "dependencies": {
178 + "call-bind-apply-helpers": "^1.0.1",
179 + "es-errors": "^1.3.0",
180 + "gopd": "^1.2.0"
181 + },
182 + "engines": {
183 + "node": ">= 0.4"
184 + }
185 + },
186 + "node_modules/ee-first": {
187 + "version": "1.1.1",
188 + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
189 + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
190 + "license": "MIT"
191 + },
192 + "node_modules/encodeurl": {
193 + "version": "2.0.0",
194 + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
195 + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
196 + "license": "MIT",
197 + "engines": {
198 + "node": ">= 0.8"
199 + }
200 + },
201 + "node_modules/es-define-property": {
202 + "version": "1.0.1",
203 + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
204 + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
205 + "license": "MIT",
206 + "engines": {
207 + "node": ">= 0.4"
208 + }
209 + },
210 + "node_modules/es-errors": {
211 + "version": "1.3.0",
212 + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
213 + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
214 + "license": "MIT",
215 + "engines": {
216 + "node": ">= 0.4"
217 + }
218 + },
219 + "node_modules/es-object-atoms": {
220 + "version": "1.1.2",
221 + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
222 + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
223 + "license": "MIT",
224 + "dependencies": {
225 + "es-errors": "^1.3.0"
226 + },
227 + "engines": {
228 + "node": ">= 0.4"
229 + }
230 + },
231 + "node_modules/escape-html": {
232 + "version": "1.0.3",
233 + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
234 + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
235 + "license": "MIT"
236 + },
237 + "node_modules/esprima": {
238 + "version": "4.0.1",
239 + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
240 + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
241 + "license": "BSD-2-Clause",
242 + "bin": {
243 + "esparse": "bin/esparse.js",
244 + "esvalidate": "bin/esvalidate.js"
245 + },
246 + "engines": {
247 + "node": ">=4"
248 + }
249 + },
250 + "node_modules/etag": {
251 + "version": "1.8.1",
252 + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
253 + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
254 + "license": "MIT",
255 + "engines": {
256 + "node": ">= 0.6"
257 + }
258 + },
259 + "node_modules/express": {
260 + "version": "4.22.2",
261 + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
262 + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
263 + "license": "MIT",
264 + "dependencies": {
265 + "accepts": "~1.3.8",
266 + "array-flatten": "1.1.1",
267 + "body-parser": "~1.20.5",
268 + "content-disposition": "~0.5.4",
269 + "content-type": "~1.0.4",
270 + "cookie": "~0.7.1",
271 + "cookie-signature": "~1.0.6",
272 + "debug": "2.6.9",
273 + "depd": "2.0.0",
274 + "encodeurl": "~2.0.0",
275 + "escape-html": "~1.0.3",
276 + "etag": "~1.8.1",
277 + "finalhandler": "~1.3.1",
278 + "fresh": "~0.5.2",
279 + "http-errors": "~2.0.0",
280 + "merge-descriptors": "1.0.3",
281 + "methods": "~1.1.2",
282 + "on-finished": "~2.4.1",
283 + "parseurl": "~1.3.3",
284 + "path-to-regexp": "~0.1.12",
285 + "proxy-addr": "~2.0.7",
286 + "qs": "~6.15.1",
287 + "range-parser": "~1.2.1",
288 + "safe-buffer": "5.2.1",
289 + "send": "~0.19.0",
290 + "serve-static": "~1.16.2",
291 + "setprototypeof": "1.2.0",
292 + "statuses": "~2.0.1",
293 + "type-is": "~1.6.18",
294 + "utils-merge": "1.0.1",
295 + "vary": "~1.1.2"
296 + },
297 + "engines": {
298 + "node": ">= 0.10.0"
299 + },
300 + "funding": {
301 + "type": "opencollective",
302 + "url": "https://opencollective.com/express"
303 + }
304 + },
305 + "node_modules/extend-shallow": {
306 + "version": "2.0.1",
307 + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
308 + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
309 + "license": "MIT",
310 + "dependencies": {
311 + "is-extendable": "^0.1.0"
312 + },
313 + "engines": {
314 + "node": ">=0.10.0"
315 + }
316 + },
317 + "node_modules/finalhandler": {
318 + "version": "1.3.2",
319 + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
320 + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
321 + "license": "MIT",
322 + "dependencies": {
323 + "debug": "2.6.9",
324 + "encodeurl": "~2.0.0",
325 + "escape-html": "~1.0.3",
326 + "on-finished": "~2.4.1",
327 + "parseurl": "~1.3.3",
328 + "statuses": "~2.0.2",
329 + "unpipe": "~1.0.0"
330 + },
331 + "engines": {
332 + "node": ">= 0.8"
333 + }
334 + },
335 + "node_modules/forwarded": {
336 + "version": "0.2.0",
337 + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
338 + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
339 + "license": "MIT",
340 + "engines": {
341 + "node": ">= 0.6"
342 + }
343 + },
344 + "node_modules/fresh": {
345 + "version": "0.5.2",
346 + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
347 + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
348 + "license": "MIT",
349 + "engines": {
350 + "node": ">= 0.6"
351 + }
352 + },
353 + "node_modules/function-bind": {
354 + "version": "1.1.2",
355 + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
356 + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
357 + "license": "MIT",
358 + "funding": {
359 + "url": "https://github.com/sponsors/ljharb"
360 + }
361 + },
362 + "node_modules/get-intrinsic": {
363 + "version": "1.3.0",
364 + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
365 + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
366 + "license": "MIT",
367 + "dependencies": {
368 + "call-bind-apply-helpers": "^1.0.2",
369 + "es-define-property": "^1.0.1",
370 + "es-errors": "^1.3.0",
371 + "es-object-atoms": "^1.1.1",
372 + "function-bind": "^1.1.2",
373 + "get-proto": "^1.0.1",
374 + "gopd": "^1.2.0",
375 + "has-symbols": "^1.1.0",
376 + "hasown": "^2.0.2",
377 + "math-intrinsics": "^1.1.0"
378 + },
379 + "engines": {
380 + "node": ">= 0.4"
381 + },
382 + "funding": {
383 + "url": "https://github.com/sponsors/ljharb"
384 + }
385 + },
386 + "node_modules/get-proto": {
387 + "version": "1.0.1",
388 + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
389 + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
390 + "license": "MIT",
391 + "dependencies": {
392 + "dunder-proto": "^1.0.1",
393 + "es-object-atoms": "^1.0.0"
394 + },
395 + "engines": {
396 + "node": ">= 0.4"
397 + }
398 + },
399 + "node_modules/gopd": {
400 + "version": "1.2.0",
401 + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
402 + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
403 + "license": "MIT",
404 + "engines": {
405 + "node": ">= 0.4"
406 + },
407 + "funding": {
408 + "url": "https://github.com/sponsors/ljharb"
409 + }
410 + },
411 + "node_modules/gray-matter": {
412 + "version": "4.0.3",
413 + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
414 + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
415 + "license": "MIT",
416 + "dependencies": {
417 + "js-yaml": "^3.13.1",
418 + "kind-of": "^6.0.2",
419 + "section-matter": "^1.0.0",
420 + "strip-bom-string": "^1.0.0"
421 + },
422 + "engines": {
423 + "node": ">=6.0"
424 + }
425 + },
426 + "node_modules/has-symbols": {
427 + "version": "1.1.0",
428 + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
429 + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
430 + "license": "MIT",
431 + "engines": {
432 + "node": ">= 0.4"
433 + },
434 + "funding": {
435 + "url": "https://github.com/sponsors/ljharb"
436 + }
437 + },
438 + "node_modules/hasown": {
439 + "version": "2.0.4",
440 + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
441 + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
442 + "license": "MIT",
443 + "dependencies": {
444 + "function-bind": "^1.1.2"
445 + },
446 + "engines": {
447 + "node": ">= 0.4"
448 + }
449 + },
450 + "node_modules/highlight.js": {
451 + "version": "11.12.0",
452 + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz",
453 + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==",
454 + "license": "BSD-3-Clause",
455 + "engines": {
456 + "node": ">=12.0.0"
457 + }
458 + },
459 + "node_modules/http-errors": {
460 + "version": "2.0.1",
461 + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
462 + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
463 + "license": "MIT",
464 + "dependencies": {
465 + "depd": "~2.0.0",
466 + "inherits": "~2.0.4",
467 + "setprototypeof": "~1.2.0",
468 + "statuses": "~2.0.2",
469 + "toidentifier": "~1.0.1"
470 + },
471 + "engines": {
472 + "node": ">= 0.8"
473 + },
474 + "funding": {
475 + "type": "opencollective",
476 + "url": "https://opencollective.com/express"
477 + }
478 + },
479 + "node_modules/iconv-lite": {
480 + "version": "0.4.24",
481 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
482 + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
483 + "license": "MIT",
484 + "dependencies": {
485 + "safer-buffer": ">= 2.1.2 < 3"
486 + },
487 + "engines": {
488 + "node": ">=0.10.0"
489 + }
490 + },
491 + "node_modules/inherits": {
492 + "version": "2.0.4",
493 + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
494 + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
495 + "license": "ISC"
496 + },
497 + "node_modules/ipaddr.js": {
498 + "version": "1.9.1",
499 + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
500 + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
501 + "license": "MIT",
502 + "engines": {
503 + "node": ">= 0.10"
504 + }
505 + },
506 + "node_modules/is-extendable": {
507 + "version": "0.1.1",
508 + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
509 + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
510 + "license": "MIT",
511 + "engines": {
512 + "node": ">=0.10.0"
513 + }
514 + },
515 + "node_modules/js-yaml": {
516 + "version": "3.15.1",
517 + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
518 + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
519 + "license": "MIT",
520 + "dependencies": {
521 + "argparse": "^1.0.7",
522 + "esprima": "^4.0.0"
523 + },
524 + "bin": {
525 + "js-yaml": "bin/js-yaml.js"
526 + }
527 + },
528 + "node_modules/kind-of": {
529 + "version": "6.0.3",
530 + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
531 + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
532 + "license": "MIT",
533 + "engines": {
534 + "node": ">=0.10.0"
535 + }
536 + },
537 + "node_modules/marked": {
538 + "version": "14.1.4",
539 + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.4.tgz",
540 + "integrity": "sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==",
541 + "license": "MIT",
542 + "bin": {
543 + "marked": "bin/marked.js"
544 + },
545 + "engines": {
546 + "node": ">= 18"
547 + }
548 + },
549 + "node_modules/math-intrinsics": {
550 + "version": "1.1.0",
551 + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
552 + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
553 + "license": "MIT",
554 + "engines": {
555 + "node": ">= 0.4"
556 + }
557 + },
558 + "node_modules/media-typer": {
559 + "version": "0.3.0",
560 + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
561 + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
562 + "license": "MIT",
563 + "engines": {
564 + "node": ">= 0.6"
565 + }
566 + },
567 + "node_modules/merge-descriptors": {
568 + "version": "1.0.3",
569 + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
570 + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
571 + "license": "MIT",
572 + "funding": {
573 + "url": "https://github.com/sponsors/sindresorhus"
574 + }
575 + },
576 + "node_modules/methods": {
577 + "version": "1.1.2",
578 + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
579 + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
580 + "license": "MIT",
581 + "engines": {
582 + "node": ">= 0.6"
583 + }
584 + },
585 + "node_modules/mime": {
586 + "version": "1.6.0",
587 + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
588 + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
589 + "license": "MIT",
590 + "bin": {
591 + "mime": "cli.js"
592 + },
593 + "engines": {
594 + "node": ">=4"
595 + }
596 + },
597 + "node_modules/mime-db": {
598 + "version": "1.52.0",
599 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
600 + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
601 + "license": "MIT",
602 + "engines": {
603 + "node": ">= 0.6"
604 + }
605 + },
606 + "node_modules/mime-types": {
607 + "version": "2.1.35",
608 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
609 + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
610 + "license": "MIT",
611 + "dependencies": {
612 + "mime-db": "1.52.0"
613 + },
614 + "engines": {
615 + "node": ">= 0.6"
616 + }
617 + },
618 + "node_modules/ms": {
619 + "version": "2.0.0",
620 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
621 + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
622 + "license": "MIT"
623 + },
624 + "node_modules/negotiator": {
625 + "version": "0.6.3",
626 + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
627 + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
628 + "license": "MIT",
629 + "engines": {
630 + "node": ">= 0.6"
631 + }
632 + },
633 + "node_modules/object-inspect": {
634 + "version": "1.13.4",
635 + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
636 + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
637 + "license": "MIT",
638 + "engines": {
639 + "node": ">= 0.4"
640 + },
641 + "funding": {
642 + "url": "https://github.com/sponsors/ljharb"
643 + }
644 + },
645 + "node_modules/on-finished": {
646 + "version": "2.4.1",
647 + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
648 + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
649 + "license": "MIT",
650 + "dependencies": {
651 + "ee-first": "1.1.1"
652 + },
653 + "engines": {
654 + "node": ">= 0.8"
655 + }
656 + },
657 + "node_modules/parseurl": {
658 + "version": "1.3.3",
659 + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
660 + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
661 + "license": "MIT",
662 + "engines": {
663 + "node": ">= 0.8"
664 + }
665 + },
666 + "node_modules/path-to-regexp": {
667 + "version": "0.1.13",
668 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
669 + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
670 + "license": "MIT"
671 + },
672 + "node_modules/proxy-addr": {
673 + "version": "2.0.7",
674 + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
675 + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
676 + "license": "MIT",
677 + "dependencies": {
678 + "forwarded": "0.2.0",
679 + "ipaddr.js": "1.9.1"
680 + },
681 + "engines": {
682 + "node": ">= 0.10"
683 + }
684 + },
685 + "node_modules/qs": {
686 + "version": "6.15.3",
687 + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
688 + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
689 + "license": "BSD-3-Clause",
690 + "dependencies": {
691 + "es-define-property": "^1.0.1",
692 + "side-channel": "^1.1.1"
693 + },
694 + "engines": {
695 + "node": ">=0.6"
696 + },
697 + "funding": {
698 + "url": "https://github.com/sponsors/ljharb"
699 + }
700 + },
701 + "node_modules/range-parser": {
702 + "version": "1.2.1",
703 + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
704 + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
705 + "license": "MIT",
706 + "engines": {
707 + "node": ">= 0.6"
708 + }
709 + },
710 + "node_modules/raw-body": {
711 + "version": "2.5.3",
712 + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
713 + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
714 + "license": "MIT",
715 + "dependencies": {
716 + "bytes": "~3.1.2",
717 + "http-errors": "~2.0.1",
718 + "iconv-lite": "~0.4.24",
719 + "unpipe": "~1.0.0"
720 + },
721 + "engines": {
722 + "node": ">= 0.8"
723 + }
724 + },
725 + "node_modules/safe-buffer": {
726 + "version": "5.2.1",
727 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
728 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
729 + "funding": [
730 + {
731 + "type": "github",
732 + "url": "https://github.com/sponsors/feross"
733 + },
734 + {
735 + "type": "patreon",
736 + "url": "https://www.patreon.com/feross"
737 + },
738 + {
739 + "type": "consulting",
740 + "url": "https://feross.org/support"
741 + }
742 + ],
743 + "license": "MIT"
744 + },
745 + "node_modules/safer-buffer": {
746 + "version": "2.1.2",
747 + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
748 + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
749 + "license": "MIT"
750 + },
751 + "node_modules/section-matter": {
752 + "version": "1.0.0",
753 + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
754 + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
755 + "license": "MIT",
756 + "dependencies": {
757 + "extend-shallow": "^2.0.1",
758 + "kind-of": "^6.0.0"
759 + },
760 + "engines": {
761 + "node": ">=4"
762 + }
763 + },
764 + "node_modules/send": {
765 + "version": "0.19.2",
766 + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
767 + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
768 + "license": "MIT",
769 + "dependencies": {
770 + "debug": "2.6.9",
771 + "depd": "2.0.0",
772 + "destroy": "1.2.0",
773 + "encodeurl": "~2.0.0",
774 + "escape-html": "~1.0.3",
775 + "etag": "~1.8.1",
776 + "fresh": "~0.5.2",
777 + "http-errors": "~2.0.1",
778 + "mime": "1.6.0",
779 + "ms": "2.1.3",
780 + "on-finished": "~2.4.1",
781 + "range-parser": "~1.2.1",
782 + "statuses": "~2.0.2"
783 + },
784 + "engines": {
785 + "node": ">= 0.8.0"
786 + }
787 + },
788 + "node_modules/send/node_modules/ms": {
789 + "version": "2.1.3",
790 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
791 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
792 + "license": "MIT"
793 + },
794 + "node_modules/serve-static": {
795 + "version": "1.16.3",
796 + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
797 + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
798 + "license": "MIT",
799 + "dependencies": {
800 + "encodeurl": "~2.0.0",
801 + "escape-html": "~1.0.3",
802 + "parseurl": "~1.3.3",
803 + "send": "~0.19.1"
804 + },
805 + "engines": {
806 + "node": ">= 0.8.0"
807 + }
808 + },
809 + "node_modules/setprototypeof": {
810 + "version": "1.2.0",
811 + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
812 + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
813 + "license": "ISC"
814 + },
815 + "node_modules/side-channel": {
816 + "version": "1.1.1",
817 + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
818 + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
819 + "license": "MIT",
820 + "dependencies": {
821 + "es-errors": "^1.3.0",
822 + "object-inspect": "^1.13.4",
823 + "side-channel-list": "^1.0.1",
824 + "side-channel-map": "^1.0.1",
825 + "side-channel-weakmap": "^1.0.2"
826 + },
827 + "engines": {
828 + "node": ">= 0.4"
829 + },
830 + "funding": {
831 + "url": "https://github.com/sponsors/ljharb"
832 + }
833 + },
834 + "node_modules/side-channel-list": {
835 + "version": "1.0.1",
836 + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
837 + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
838 + "license": "MIT",
839 + "dependencies": {
840 + "es-errors": "^1.3.0",
841 + "object-inspect": "^1.13.4"
842 + },
843 + "engines": {
844 + "node": ">= 0.4"
845 + },
846 + "funding": {
847 + "url": "https://github.com/sponsors/ljharb"
848 + }
849 + },
850 + "node_modules/side-channel-map": {
851 + "version": "1.0.1",
852 + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
853 + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
854 + "license": "MIT",
855 + "dependencies": {
856 + "call-bound": "^1.0.2",
857 + "es-errors": "^1.3.0",
858 + "get-intrinsic": "^1.2.5",
859 + "object-inspect": "^1.13.3"
860 + },
861 + "engines": {
862 + "node": ">= 0.4"
863 + },
864 + "funding": {
865 + "url": "https://github.com/sponsors/ljharb"
866 + }
867 + },
868 + "node_modules/side-channel-weakmap": {
869 + "version": "1.0.2",
870 + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
871 + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
872 + "license": "MIT",
873 + "dependencies": {
874 + "call-bound": "^1.0.2",
875 + "es-errors": "^1.3.0",
876 + "get-intrinsic": "^1.2.5",
877 + "object-inspect": "^1.13.3",
878 + "side-channel-map": "^1.0.1"
879 + },
880 + "engines": {
881 + "node": ">= 0.4"
882 + },
883 + "funding": {
884 + "url": "https://github.com/sponsors/ljharb"
885 + }
886 + },
887 + "node_modules/sprintf-js": {
888 + "version": "1.0.3",
889 + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
890 + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
891 + "license": "BSD-3-Clause"
892 + },
893 + "node_modules/statuses": {
894 + "version": "2.0.2",
895 + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
896 + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
897 + "license": "MIT",
898 + "engines": {
899 + "node": ">= 0.8"
900 + }
901 + },
902 + "node_modules/strip-bom-string": {
903 + "version": "1.0.0",
904 + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
905 + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
906 + "license": "MIT",
907 + "engines": {
908 + "node": ">=0.10.0"
909 + }
910 + },
911 + "node_modules/toidentifier": {
912 + "version": "1.0.1",
913 + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
914 + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
915 + "license": "MIT",
916 + "engines": {
917 + "node": ">=0.6"
918 + }
919 + },
920 + "node_modules/type-is": {
921 + "version": "1.6.18",
922 + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
923 + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
924 + "license": "MIT",
925 + "dependencies": {
926 + "media-typer": "0.3.0",
927 + "mime-types": "~2.1.24"
928 + },
929 + "engines": {
930 + "node": ">= 0.6"
931 + }
932 + },
933 + "node_modules/unpipe": {
934 + "version": "1.0.0",
935 + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
936 + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
937 + "license": "MIT",
938 + "engines": {
939 + "node": ">= 0.8"
940 + }
941 + },
942 + "node_modules/utils-merge": {
943 + "version": "1.0.1",
944 + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
945 + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
946 + "license": "MIT",
947 + "engines": {
948 + "node": ">= 0.4.0"
949 + }
950 + },
951 + "node_modules/vary": {
952 + "version": "1.1.2",
953 + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
954 + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
955 + "license": "MIT",
956 + "engines": {
957 + "node": ">= 0.8"
958 + }
959 + }
960 + }
961 +}
962