jgt-pricedb
The persistence layer of the next-generation Price Service: bars live in a database with an incremental, anchor-based refresh — CSV files become an export, not the source of truth.
Lineage: implementation of the PDSP specifications reverse-engineered from
the Caishen .NET stack — caishen/rispecs/PDSP/ (specs 70–77) — translated
into the jgt ecosystem's shapes.
Structural Tension
- Current reality: price data as flat CSVs (
$JGTPY_DATA/pds/<INSTR>_<TF>.csv); every refresh rewrites whole files and downstream recomputes whole series. - Desired state: a database-backed store where refresh finds the anchor (the single forming bar per series) and writes only forward; downstream learns which bar changed and computes incrementally.
Decisions
Production ownership (2026-09-04)
The projection contract is in production: jgtpdb derive-cds
(jgtpricedb-util 0.2.0) materializes indicator-complete CDS from a PDS
export, and the jgt-data-server updater builds its rolling CDS through the same
materialize_cds seam. The Williams engine (jgtpy) is a dependency of that
job alone; no chart service imports it.
PDS (today: written by the updater's broker fetch, jgtfxcon)
-> jgtpdb derive-cds / materialize_cds -> indicator-complete CDS -> chart
The CDS layer is database-native (2026-09-09)
Phase 5's first table exists: cds_rows, keyed (series_id, ts_utc) against
bars, indicators in JSONB. jgtpdb derive-cds-db computes them from stored
prices through the same Williams engine the file projection uses, and
SqlPriceStore.read_cds joins the two in one indexed read. The data server's
services/price_store reads chart history from there when
JGT_PRICE_STORE_URL is set, which collapses the rolling/deep/CDS/PDS merge
the files required — one series holds every depth.
Two things the DB path had to learn that the file path got for free:
- The engine is fed the export quantum, not the raw arithmetic. A mid is
(bid+ask)/2and carries binary dust:1.1672699999999998where the quotes say1.16727. The CSV job never saw it because its input had already been rendered throughCsvFormat. Feeding the dust through made a Williams fractal fire on one bar of 941 on live EUR-USD H1. With the quantum applied, the two paths are identical across 62 indicator columns and 941 bars. - A non-finite indicator is null, not a number.
mfiis range over volume and divides by zero on a bar the broker published with none. The CSV spells thatinf; JSONB has no infinity, and arithmetic on one propagates it.
What is not yet true: the store is not the production PDS writer. The
updater's fetch still writes current/pds and the store ingests from it
(updater/src/store_worker.py); jgtpricedb export becomes the source at the
cutover. One writer at a time. TTF and MLF remain future layers on the same
seam.
- Periods are cut on the broker's trading session, not on UTC midnight
(0.1.1). The feed publishes H4, D1, W1 and M1 bars on a 17:00
America/New_York boundary — 21:00Z in summer, 22:00Z in winter — so a UTC grid
renamed 100% of them, silently.
m1..H1stay on UTC, where they measurably already were. The session is stated per market, resolved insidebar_key, and recorded on the series;tests/test_session_grid.pyis the measurement, run against the real holdings. Seerispecs/01-price-store.spec.md, The Session Grid. - Portable schema (SQLite for local-first dev, PostgreSQL for deployment);
the anchor invariant is enforced by a partial unique index
(
UNIQUE(series_id) WHERE is_forming), not by application discipline. - No standalone importer. Backfill is the refresh engine's bootstrap path
(PDSP spec 72, Algorithm C) fed by a source adapter.
CsvSourcereads the existingfull/CSVs through the exact same upsert path asBrokerSourcelive updates. The system can always rebuild itself from its own sources. - CSV compatibility export (PDSP spec 75 pattern, 0.1.2): consumers
(
jgtpy,jgtml,jgt-data-server) keep reading the same file paths while the database becomes authoritative underneath. The format is measured rather than assumed — quotes keep the double the feed published, the derived columns are quantized at display precision +1 and +2 (with builtinround, which is not the one pandas reaches for), and the line terminator belongs to the file being replaced. A holdings file read in and written back out is byte-for-byte itself: 89 of 89 live files, 13 instruments, all seven timeframes, 95 208 bars. Seerispecs/03-csv-export.spec.md. - Redis pub/sub (already in the jgt-data-server stack) carries bar-completed events in Phase 4 — the role Rebus/MSMQ played in Caishen.
pyproject.tomlwith a static version — deliberately avoiding thesetup.pycircular-import pattern that blocks other jgt packages from installing in containers.
Phases
| Phase | Creates | PDSP spec |
|---|---|---|
| 1 | Schema + store core + bootstrap-backfill through source adapters | 70, 71, 72 |
| 2 | Jobs (jgtpdb) replacing refresh_data.sh — shipped as jgtpricedb-util |
72, 74 |
| 3 | CSV export bridge — DB authoritative, downstream untouched (0.1.2) | 75 |
| 4 | Redis events → incremental IDS/CDS recompute | 73, 77 |
| 5 | Serving from DB; indicator tables (CDS layer 0.2.0); strategy layer (SpiderDb heir) | 76 |
Layout
rispecs/— jgt-native specifications (the buildable truth; start here)src/jgtpricedb/— the librarydocker-compose.yml— optional Postgres for deployment; SQLite needs nothing../jgt-pricedb-util/— the sibling package (jgtpricedb-util): the jobs an operator runs against a store — bootstrap, refresh, a bounded forming-bar loop, an OANDA fetch on this session grid, and the freshness and relabel probes. This repository is the store; that one is the steward.
Release files for jgtpricedb 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jgtpricedb-0.2.1.tar.gz | 133.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jgtpricedb-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 186.9 kB
Release files / jgtpricedb-0.2.1.tar.gz
| Download URL | jgtpricedb-0.2.1.tar.gz |
|---|---|
| Size | 133.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
37e654378b5c5b4ec12f3ea78a747e157329936882587c1916a4930fc75376e0
|
|
BLAKE2b-256 checksum How to use checksums |
07823b9f3bf556ba1dffeee0bec9c85208c05936d0d7512ff99abe8349355d61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.4
|
Release files / jgtpricedb-0.2.1-py3-none-any.whl
| Download URL | jgtpricedb-0.2.1-py3-none-any.whl |
|---|---|
| Size | 53.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
62ea0934d023dbf2fd133d12beb903ef52d25c18c477796af8caa16daaf9c3f2
|
|
BLAKE2b-256 checksum How to use checksums |
48d55eab4386b48305b217765cd7cfcbc98efce5c4fc827a7e2e2e873a95b36f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.4
|