@pond-ts/react 0.63.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +27 -14
- package/CHANGELOG.md +284 -1
- package/package.json +2 -2
package/API.md
CHANGED
|
@@ -164,7 +164,7 @@ Deliberately small — the ordering-based slice of the algebra, no calendar ops
|
|
|
164
164
|
| Operation schemas | `RollingSchema`, `RollingAlignment`, `AlignSchema`, `DiffSchema`, `SmoothSchema`, `SmoothMethod`, `FillStrategy`, `FillMapping` | `packages/core/src/schema/index.ts` |
|
|
165
165
|
| Column/data kinds | `Column`, `KeyColumn`, `ColumnKind`, `ScalarKind`, `ScalarValue`, `ColumnValue`, `ArrayValue`, `ValidityBitmap` | `packages/core/src/columnar/` |
|
|
166
166
|
| JSON wire format | `JsonRowFormat`, `JsonRowForSchema`, `JsonObjectRowForSchema`, `JsonValueForKind`, `JsonTimestampInput`, `JsonTimeRangeInput`, `JsonIntervalInput` | `packages/core/src/schema/index.ts` |
|
|
167
|
-
| Temporal utility | `TemporalLike`, `DurationInput`, `CalendarUnit`, `TimeZoneOptions`, `KeyLike`, `BatchSampleStrategy`
|
|
167
|
+
| Temporal utility | `TemporalLike`, `DurationInput`, `CalendarUnit`, `TimeZoneOptions`, `KeyLike`, `BatchSampleStrategy`, `SequenceSample`, `SequenceCoverage` | `packages/core/src/core/`, `packages/core/src/sequence/` |
|
|
168
168
|
|
|
169
169
|
The `pond-ts/types` subpath re-exports the schema-as-contract types with zero
|
|
170
170
|
runtime (`packages/core/src/schema/public.ts`).
|
|
@@ -432,19 +432,32 @@ Series shapes (same file): `ChartSeries`, `BandSeries`, `BoxSeries`,
|
|
|
432
432
|
|
|
433
433
|
All are pure `(series, options) → TimeSeries` appending output columns;
|
|
434
434
|
`column` defaults to `'close'`; periods are bar counts; warm-up is
|
|
435
|
-
length-preserving (`undefined` head rows).
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
|
441
|
-
|
|
|
442
|
-
| `
|
|
443
|
-
| `
|
|
444
|
-
| `
|
|
445
|
-
| `
|
|
446
|
-
| `
|
|
447
|
-
| `
|
|
435
|
+
length-preserving (`undefined` head rows). A **multi-input** study (`atr`)
|
|
436
|
+
names each input instead of taking one `column`, each defaulting to its
|
|
437
|
+
`DEFAULT_OHLCV` name — the same "never hard-code a column" rule applied
|
|
438
|
+
per input.
|
|
439
|
+
|
|
440
|
+
| Study | Output column(s) | Options gist | Source |
|
|
441
|
+
| --------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
442
|
+
| `sma` | `sma` | `{ period, column?, output? }` | `packages/financial/src/studies/moving-average.ts` |
|
|
443
|
+
| `ema` | `ema` | `{ period, column?, output? }` (α = 2/(period+1)) | `packages/financial/src/studies/moving-average.ts` |
|
|
444
|
+
| `bollinger` | `bbMiddle`, `bbUpper`, `bbLower` | `{ period, stdDev?, column?, prefix? }` | `packages/financial/src/studies/bollinger.ts` |
|
|
445
|
+
| `envelope` | `envMiddle`, `envUpper`, `envLower` | `{ period, percent?, maType?, column?, prefix? }` | `packages/financial/src/studies/envelope.ts` |
|
|
446
|
+
| `rollingStdev` | `stdev` | `{ period, column?, output? }` (population, ddof=0) | `packages/financial/src/studies/rolling-stat.ts` |
|
|
447
|
+
| `rollingMin` / `rollingMax` | `min` / `max` | `{ period, column?, output? }` (one edge; `donchian` gives the channel) | `packages/financial/src/studies/rolling-stat.ts` |
|
|
448
|
+
| `rollingPercentile` | `p{q}` (e.g. `p90`) | `{ period, q, column?, output? }` | `packages/financial/src/studies/rolling-stat.ts` |
|
|
449
|
+
| `zScore` | `zscore` | `{ period, column?, output? }` | `packages/financial/src/studies/z-score.ts` |
|
|
450
|
+
| `percentChange` | `pctChange` | `{ periods?, column?, output? }` (= ROC; TA-Lib-verified) | `packages/financial/src/studies/percent-change.ts` |
|
|
451
|
+
| `rsi` | `rsi` | `{ period?, column?, output? }` (Wilder, default 14) | `packages/financial/src/studies/rsi.ts` |
|
|
452
|
+
| `macd` | `macdLine`, `macdSignal`, `macdHist` | `{ fastPeriod?, slowPeriod?, signalPeriod?, column?, prefix? }` (12/26/9) | `packages/financial/src/studies/macd.ts` |
|
|
453
|
+
| `atr` | `atr` | `{ period?, high?, low?, close?, output? }` (Wilder, default 14) | `packages/financial/src/studies/atr.ts` |
|
|
454
|
+
| `momentum` | `momentum` | `{ period?, column?, output? }` (`v − v[−period]`, default 10) | `packages/financial/src/studies/momentum.ts` |
|
|
455
|
+
| `historicalVolatility` | `hv` | `{ period?, annualize?, column?, output? }` (σ of log returns, ×√252) | `packages/financial/src/studies/volatility.ts` |
|
|
456
|
+
| `stochastic` | `stochK`, `stochD` | `{ kPeriod?, slowing?, dPeriod?, high?, low?, close?, prefix? }` (14/3/3; `slowing: 1` = fast) | `packages/financial/src/studies/stochastic.ts` |
|
|
457
|
+
| `williamsR` | `williamsR` | `{ period?, high?, low?, close?, output? }` (default 14, bounded −100..0) | `packages/financial/src/studies/williams-r.ts` |
|
|
458
|
+
| `donchian` | `dcUpper`, `dcLower`, `dcMiddle` | `{ period?, high?, low?, prefix? }` (default 20) | `packages/financial/src/studies/donchian.ts` |
|
|
459
|
+
| `obv` | `obv` | `{ close?, volume?, output? }` (no period; TA-Lib seed `volume[0]`) | `packages/financial/src/studies/obv.ts` |
|
|
460
|
+
| `vwap` | `vwap` | `{ period, high?, low?, close?, volume?, output? }` (rolling, typical px) | `packages/financial/src/studies/vwap.ts` |
|
|
448
461
|
|
|
449
462
|
Adding a study? Follow `packages/financial/src/studies/README.md` (uniform
|
|
450
463
|
shape + pandas oracle case + fluent method are all REQUIRED).
|
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,9 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`,
|
|
|
8
8
|
under a single `v*` tag, so this file covers them all. Pre-1.0: minor bumps may
|
|
9
9
|
include new features and type-level changes; patch bumps are strictly additive.
|
|
10
10
|
|
|
11
|
-
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.
|
|
11
|
+
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.65.0...HEAD
|
|
12
|
+
[0.65.0]: https://github.com/pond-ts/pond/compare/v0.64.0...v0.65.0
|
|
13
|
+
[0.64.0]: https://github.com/pond-ts/pond/compare/v0.63.0...v0.64.0
|
|
12
14
|
[0.63.0]: https://github.com/pond-ts/pond/compare/v0.62.0...v0.63.0
|
|
13
15
|
[0.62.0]: https://github.com/pond-ts/pond/compare/v0.61.0...v0.62.0
|
|
14
16
|
[0.61.0]: https://github.com/pond-ts/pond/compare/v0.60.0...v0.61.0
|
|
@@ -66,6 +68,287 @@ include new features and type-level changes; patch bumps are strictly additive.
|
|
|
66
68
|
|
|
67
69
|
## [Unreleased]
|
|
68
70
|
|
|
71
|
+
## [0.65.0] — 2026-09-06
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- `@pond-ts/financial`: **`stochastic(...)`**, **`williamsR(...)`** and
|
|
76
|
+
**`donchian(...)`** — the three range-position studies, built on one new
|
|
77
|
+
kernel (`highestLowestValues`: the trailing highest high / lowest low in a
|
|
78
|
+
single `rolling` scan) plus fluent `.stochastic()` / `.williamsR()` /
|
|
79
|
+
`.donchian()`. All name their bar inputs per column (`high` / `low` /
|
|
80
|
+
`close`, defaulting to `DEFAULT_OHLCV`), the `atr` shape.
|
|
81
|
+
|
|
82
|
+
**`stochastic`** — `{ kPeriod = 14, slowing = 3, dPeriod = 3, prefix =
|
|
83
|
+
'stoch' }` appending `stochK` / `stochD`: the slow stochastic, with
|
|
84
|
+
`slowing: 1` giving the fast one (TA-Lib's `STOCHF`) from the same
|
|
85
|
+
function. Values match TA-Lib's `STOCH`/`STOCHF` to `5.7e-14` on every bar
|
|
86
|
+
TA-Lib emits, `%D` with an identical warm-up. Two deliberate deltas,
|
|
87
|
+
both documented on the study: **`%K` starts when it can** (bar 15 at the
|
|
88
|
+
defaults) where TA-Lib masks it back to `%D`'s first bar (17), discarding
|
|
89
|
+
`dPeriod − 1` real values — the `macd` precedent; and **a flat window
|
|
90
|
+
(`HH === LL`) is `undefined`** where TA-Lib reports `0`, which is also its
|
|
91
|
+
value for "close at the very bottom of a real range" — the `rsi` precedent.
|
|
92
|
+
The smoothing uses a new raw-array kernel, `rollingMeanValues`, that waits
|
|
93
|
+
for `slowing` _values_ rather than rows: a scratch-column SMA would have
|
|
94
|
+
put a "3-bar" `%K` on bar 13 with one value in it.
|
|
95
|
+
|
|
96
|
+
**`williamsR`** — `{ period = 14, output = 'williamsR' }`, bounded
|
|
97
|
+
`−100..0`. Matches TA-Lib's `WILLR` bar-for-bar to `1.4e-14` with an
|
|
98
|
+
identical warm-up; the only delta is the same flat-window `undefined`.
|
|
99
|
+
It is fast `%K − 100`, computed on the same kernel, and a test pins that
|
|
100
|
+
identity against `stochastic({ slowing: 1 })`.
|
|
101
|
+
|
|
102
|
+
**`donchian`** — `{ period = 20, prefix = 'dc' }` appending `dcUpper` /
|
|
103
|
+
`dcLower` / `dcMiddle`. `upper` is exactly `rollingMax(high)` and `lower`
|
|
104
|
+
exactly `rollingMin(low)` (pinned), in one scan plus the midpoint; pandas
|
|
105
|
+
oracle only (TA-Lib has no Donchian). Scales linearly with price, like
|
|
106
|
+
`atr`; a missing `high`/`low` is skipped (core's reducer policy), not
|
|
107
|
+
propagated.
|
|
108
|
+
|
|
109
|
+
- `@pond-ts/financial`: **`obv(...)`** and **`vwap(...)`** — the first two
|
|
110
|
+
**volume** studies, plus fluent `.obv()` / `.vwap()`. Both name their inputs
|
|
111
|
+
per column (`close` / `volume`, and `high` / `low` for VWAP), each
|
|
112
|
+
defaulting to its `DEFAULT_OHLCV` name, the rule `atr` established.
|
|
113
|
+
|
|
114
|
+
**`obv`** is Granville's On-Balance Volume — the running total of each bar's
|
|
115
|
+
volume signed by its close change — and is the first study with **no
|
|
116
|
+
`period`**: it is read for its shape, not its level, so there is nothing to
|
|
117
|
+
size a window over. It is **TA-Lib's OBV exactly**, including the seed
|
|
118
|
+
(`OBV[0] = volume[0]`) and an unchanged close adding nothing; the oracle
|
|
119
|
+
asserts equality, with an identical (empty) warm-up mask. On **interior
|
|
120
|
+
gaps** it deliberately departs from TA-Lib: a running sum has no local
|
|
121
|
+
answer for a missing term, so a missing close or volume leaves the level
|
|
122
|
+
`undefined` from that bar on (the same asymmetry the Wilder recursion has —
|
|
123
|
+
a leading gap shifts the seed, an interior one propagates). TA-Lib has no
|
|
124
|
+
gap semantics; measured on a seven-bar series it is 400 out of level at
|
|
125
|
+
the gap bar and 100 out from then on, presented as a value.
|
|
126
|
+
|
|
127
|
+
**`vwap`** is a **rolling** Volume-Weighted Average Price:
|
|
128
|
+
`Σ typicalPrice · volume / Σ volume` over `period` bars, typical price
|
|
129
|
+
`(high + low + close) / 3`. There is no TA-Lib VWAP and no single
|
|
130
|
+
definition, so this is a documented design choice: the rolling form fits
|
|
131
|
+
the package's count-window shape, and the intraday desk's _anchored_ VWAP
|
|
132
|
+
is not a special case of it (a count window emits only once it spans
|
|
133
|
+
`period` rows) — that one needs a session reset and is deferred to the
|
|
134
|
+
session-anchored phase. `period` is **required** (no conventional length to
|
|
135
|
+
default to). A window with no volume is `undefined`; a bar missing any
|
|
136
|
+
input is dropped from **both** sums so the ratio is never biased. Verified
|
|
137
|
+
against a pandas replication on a fixture whose volume spikes make a
|
|
138
|
+
dropped weighting visible (≥ 1.24 price units from the plain mean).
|
|
139
|
+
|
|
140
|
+
The oracle fixture gains a `volumes` input, and four kernels land under
|
|
141
|
+
them for the studies that follow: `cumulativeValues` (A/D line, PVT),
|
|
142
|
+
`signedVolumeValues`, `typicalPriceValues` (CCI, MFI, Keltner) and
|
|
143
|
+
`rollingWeightedMeanValues` (VWMA).
|
|
144
|
+
|
|
145
|
+
- `@pond-ts/financial`: **`atr(...)`** — Wilder's Average True Range, the third
|
|
146
|
+
[PND-STUDY] named indicator, plus a fluent `.atr()`. `{ period = 14,
|
|
147
|
+
high = 'high', low = 'low', close = 'close', output = 'atr' }`. Verified
|
|
148
|
+
against **TA-Lib** bar-for-bar in the oracle — exact agreement at both
|
|
149
|
+
periods tested, with identical warm-up.
|
|
150
|
+
|
|
151
|
+
**The first multi-input study.** Where every study so far took a single
|
|
152
|
+
`column`, ATR names each of its three inputs, each defaulting to its
|
|
153
|
+
`DEFAULT_OHLCV` name — the same "never hard-code a column" rule applied per
|
|
154
|
+
input rather than a new mechanism. Because the inputs are columns of one
|
|
155
|
+
series they are aligned by construction, so the mismatched-length error
|
|
156
|
+
array-based libraries must check for at every call is unrepresentable here.
|
|
157
|
+
|
|
158
|
+
Warm-up is `period` rows, not `period − 1`: true range needs a previous
|
|
159
|
+
close, so bar 0 has none.
|
|
160
|
+
|
|
161
|
+
- `@pond-ts/financial`: **`macd(...)`** — Moving Average Convergence/Divergence,
|
|
162
|
+
the second [PND-STUDY] named indicator. `{ fastPeriod = 12, slowPeriod = 26,
|
|
163
|
+
signalPeriod = 9, column = 'close', prefix = 'macd' }`, appending
|
|
164
|
+
`macdLine` / `macdSignal` / `macdHist`, plus a fluent `.macd()`.
|
|
165
|
+
|
|
166
|
+
Two deliberate deltas from TA-Lib, both documented on the study:
|
|
167
|
+
**each column warms up when it can** (the line at bar 25, signal and
|
|
168
|
+
histogram at 33) where TA-Lib masks all three back to 33, keeping eight real
|
|
169
|
+
values it discards; and the EMAs are **pond's own** (`α = 2/(span+1)` seeded
|
|
170
|
+
on the first sample — what `ema()` already ships) rather than TA-Lib's
|
|
171
|
+
SMA-seeded ones. The second is a _seed_ difference, so it decays: measured
|
|
172
|
+
at 3.8% of the line's magnitude at the first shared bar, 0.089% by bar 79.
|
|
173
|
+
Seeding TA-Lib's way would make `macd()` disagree with
|
|
174
|
+
`ema(fast) − ema(slow)` inside this package, which is the worse surprise —
|
|
175
|
+
and is pinned by a test.
|
|
176
|
+
|
|
177
|
+
- `@pond-ts/financial`: **`rsi(...)`** — Wilder's Relative Strength Index, the
|
|
178
|
+
first of the [PND-STUDY] named indicators. `{ period = 14, column = 'close',
|
|
179
|
+
output = 'rsi' }`, length-preserving warm-up (`period` rows, not `period − 1`
|
|
180
|
+
— RSI averages _differences_, so it needs one extra bar), and a fluent
|
|
181
|
+
`.rsi()`. Verified against **TA-Lib** bar-for-bar in the oracle fixture,
|
|
182
|
+
agreeing to `1.4e-14`.
|
|
183
|
+
|
|
184
|
+
Worth knowing which RSI this is: the gain/loss averages are seeded on the
|
|
185
|
+
arithmetic mean of the first `period` differences, then carried by Wilder's
|
|
186
|
+
recursion — TA-Lib's definition and Wilder's original. Implementations that
|
|
187
|
+
instead smooth with a plain first-sample-seeded EMA of the same `α = 1/period`
|
|
188
|
+
produce a **materially different** series: on this package's own oracle input
|
|
189
|
+
the two sit up to **7.03 RSI points** apart, and are still `0.15` apart 65
|
|
190
|
+
bars later. That is enough to move a reading across the conventional 70/30
|
|
191
|
+
thresholds, so it is a definition choice rather than a rounding detail.
|
|
192
|
+
|
|
193
|
+
- `@pond-ts/financial`: **`momentum(...)`** — the absolute `period`-bar
|
|
194
|
+
difference, `value − value[i − period]`. `{ period = 10, column = 'close',
|
|
195
|
+
output = 'momentum' }`, warm-up `period` rows, plus a fluent `.momentum()`.
|
|
196
|
+
Verified against **TA-Lib's `MOM`** bar-for-bar in the oracle: exact
|
|
197
|
+
agreement with identical warm-up masks. The additive companion to
|
|
198
|
+
`percentChange`'s ratio; it is in the price's units and scales with them
|
|
199
|
+
(pinned by a property test).
|
|
200
|
+
|
|
201
|
+
- `@pond-ts/financial`: **`historicalVolatility(...)`** — the standard
|
|
202
|
+
deviation of log returns over `period` bars, annualised.
|
|
203
|
+
`{ period = 20, annualize = 252, column = 'close', output = 'hv' }`, plus a
|
|
204
|
+
fluent `.historicalVolatility()`. Warm-up is `period` rows, not
|
|
205
|
+
`period − 1` — `period` returns need `period + 1` prices, the same
|
|
206
|
+
off-by-one RSI and ATR have.
|
|
207
|
+
|
|
208
|
+
TA-Lib has no HV to arbitrate the conventions, so they are pinned on the
|
|
209
|
+
study and in the pandas oracle (`np.log(s).diff().rolling(n).std(ddof=0)
|
|
210
|
+
· √annualize`): **population σ** (`ddof = 0`, the package convention shared
|
|
211
|
+
with Bollinger / `rollingStdev` / `zScore`; a sample σ is
|
|
212
|
+
`√(n/(n−1))` larger — 2.6% at 20); **log returns**, not simple; annualised
|
|
213
|
+
by `√annualize` with `252` the default for daily bars and an **option**
|
|
214
|
+
rather than a hidden constant (`1` for per-bar σ, `252·390` for one-minute
|
|
215
|
+
bars); and a **decimal** (`0.18` = 18%), the form volatility is consumed
|
|
216
|
+
in. Scale-invariant, pinned by a property test. A non-positive price has
|
|
217
|
+
no log return: both returns touching it are missing (explicitly, since
|
|
218
|
+
`ln(−4/−5)` is a finite non-answer). A leading gap shifts the start so the
|
|
219
|
+
first window still covers `period` real returns, matching pandas'
|
|
220
|
+
`min_periods` and the Wilder-family studies — the σ is computed by calling
|
|
221
|
+
the range-exact kernel on the returns array directly, because a scratch
|
|
222
|
+
column through `rollingValues` counts rows and would emit one bar early
|
|
223
|
+
over one return too few.
|
|
224
|
+
|
|
225
|
+
- `@pond-ts/financial`: **`percentChange` is ROC** — TA-Lib's `ROC` is the same
|
|
226
|
+
`(price / prevPrice − 1) × 100`, and the oracle now cross-checks the study
|
|
227
|
+
against it: exact agreement (`0.0`) with identical warm-up masks at both
|
|
228
|
+
periods. Documented on the study and in API.md; there is deliberately no
|
|
229
|
+
separate `roc` study.
|
|
230
|
+
|
|
231
|
+
## [0.64.0] — 2026-08-28
|
|
232
|
+
|
|
233
|
+
### Added
|
|
234
|
+
|
|
235
|
+
- `pond-ts`: **`Sequence.bounded(range, { coverage })`** — chooses whether a
|
|
236
|
+
range selects buckets by their sample point (`'sample'`, the default and the
|
|
237
|
+
existing behaviour) or by their extent (`'overlap'`, every bucket the range
|
|
238
|
+
touches, including the one containing `range.begin()`). `aggregate` realizes
|
|
239
|
+
its grid with `'overlap'`; alignment keeps `'sample'`. As a side benefit
|
|
240
|
+
`bounded({ start: t, end: t }, { coverage: 'overlap' })` is a flooring
|
|
241
|
+
primitive — it returns exactly the bucket containing `t` — so consumers no
|
|
242
|
+
longer have to re-derive pond's calendar anchoring by hand to floor a
|
|
243
|
+
timestamp. New exported type `SequenceCoverage`.
|
|
244
|
+
|
|
245
|
+
- `@pond-ts/charts`: **`<YAxis zeroAnchored>`** — pins a y-axis's zoom to the
|
|
246
|
+
value-`0` gridline instead of the pointer, and drops drag-to-pan entirely.
|
|
247
|
+
For a bar chart, whose baseline must never move: a bar rests on `0` (at the
|
|
248
|
+
plot floor for all-positive data, or somewhere mid-plot for data that
|
|
249
|
+
straddles positive and negative), and an ordinary pointer-pivoted gesture
|
|
250
|
+
would slide that baseline around the plot as you scroll. With
|
|
251
|
+
`zeroAnchored`, every wheel notch scales the axis around wherever `0`
|
|
252
|
+
currently renders, so the baseline holds however far in or out you zoom.
|
|
253
|
+
One qualification: the pivot is clamped into the axis's pixel range, so once
|
|
254
|
+
`0` has been scrolled off the plot the zoom pivots about the nearer edge and
|
|
255
|
+
the baseline does creep — benign for bars, where that clamp coincides with
|
|
256
|
+
the declared floor, but not an unconditional guarantee. Still gated by the
|
|
257
|
+
container's own `axisPanZoom` opt-in — this changes what the gesture does,
|
|
258
|
+
not whether it's on.
|
|
259
|
+
|
|
260
|
+
### Changed
|
|
261
|
+
|
|
262
|
+
- `pond-ts`: **`aggregate` now emits the bucket containing the first event**,
|
|
263
|
+
where before it started at the first grid boundary _at or after_ it — so
|
|
264
|
+
every event between the two aggregated into nothing, silently. 60 daily bars
|
|
265
|
+
rolled to a calendar month came back holding 38 of them: no error, no
|
|
266
|
+
warning, no `undefined`, just a well-formed series with a third of the input
|
|
267
|
+
gone. It contradicted `aggregate`'s own documented membership rule ("point
|
|
268
|
+
events contribute to the bucket containing their timestamp") and was
|
|
269
|
+
asymmetric — the trailing partial bucket _was_ emitted. **This changes
|
|
270
|
+
results for any caller whose first event did not land exactly on a grid
|
|
271
|
+
boundary**: expect an extra leading bucket, and sums/counts that now account
|
|
272
|
+
for the previously-dropped events. Callers who worked around it by passing a
|
|
273
|
+
pre-floored explicit `range` are unaffected — the floor is now what the
|
|
274
|
+
default does. **Note what `range` does and does not bound:** it selects the
|
|
275
|
+
grid, not the event scan, so the leading bucket is filled from every event it
|
|
276
|
+
contains — including events _before_ `range.begin()` when the series has
|
|
277
|
+
them. On a window narrower than the series (a chart viewport, say) the edge
|
|
278
|
+
bucket is therefore a complete bucket rather than a clipped one, which is
|
|
279
|
+
what keeps bar heights stable under a pan; if you need the window to bound
|
|
280
|
+
the events too, narrow the series rather than the grid. `align(...)` and
|
|
281
|
+
`materialize(...)` are deliberately unchanged: their grids are _sampled_,
|
|
282
|
+
and the sample point becomes the output key. A pre-realized
|
|
283
|
+
`BoundedSequence` argument is an explicit bucket list, used as given and
|
|
284
|
+
unaffected by any of this. Reported by Tidal (#672).
|
|
285
|
+
|
|
286
|
+
- `@pond-ts/charts`: **y gutter drag now pans instead of zooming** — matching
|
|
287
|
+
the x strip's own gesture (drag pans, wheel zooms) instead of the
|
|
288
|
+
drag-to-zoom behavior it shipped with in `axisPanZoom="y"`/`"xy"`. Wheel
|
|
289
|
+
still zooms the grabbed axis about the pointer, and double-click still
|
|
290
|
+
releases it back to its fit; only the drag's own effect changed.
|
|
291
|
+
|
|
292
|
+
### Fixed
|
|
293
|
+
|
|
294
|
+
- `@pond-ts/charts`: **a trading-time date band clamped onto a labelled tick
|
|
295
|
+
keeps its segment boundary.** The F-charts-21 collision fix dropped the whole
|
|
296
|
+
`{ start, label, shaded }` entry, but `<XAxis>` spans each band
|
|
297
|
+
`[its start, the next band's start)` and takes its zebra parity from
|
|
298
|
+
`shaded` — so removing an entry deleted the _period_, not a duplicate label,
|
|
299
|
+
and the preceding band ran on to the next surviving boundary under its own
|
|
300
|
+
name and shading. (Aug 1 2026 is a Saturday, so the August band clamps onto
|
|
301
|
+
Mon Aug 3, already a day tick — August vanished and July painted across it.)
|
|
302
|
+
`bands()` entries now carry **`showLabel`**, and only the text is suppressed.
|
|
303
|
+
Found by Codex reviewing the v0.64.0 range.
|
|
304
|
+
- `@pond-ts/charts`: **a controlled y-gutter wheel zoom no longer pivots about
|
|
305
|
+
the wrong point under a plot-level y zoom.** The `zeroAnchored` pivot was
|
|
306
|
+
corrected above, but the ordinary (non-anchored) controlled wheel still fed
|
|
307
|
+
the pointer's _screen_ pixel into the pre-transform scale's pixel space, so
|
|
308
|
+
the value under the pointer slid out from under it — ~0.14 units per notch at
|
|
309
|
+
`k = 1.198`. Third and last face of one root cause: a screen-space pixel used
|
|
310
|
+
where `base`'s pixel space is meant.
|
|
311
|
+
- `@pond-ts/charts`: **a controlled y-gutter pan no longer overshoots the
|
|
312
|
+
cursor under a plot-level y zoom.** With `onBoundsChange` set, the drag
|
|
313
|
+
translated the axis's pre-transform scale by a _screen_ pixel delta. Those
|
|
314
|
+
two pixel spaces differ by the container's `yTransform`, so with the plot's
|
|
315
|
+
own y pan/zoom engaged (`panZoom="panZoomY"` / `"panZoomXY"`) the axis
|
|
316
|
+
panned `k`× too far and the grabbed value outran the pointer. Both
|
|
317
|
+
transforms are applied in pixel space, so the base↔screen relation is
|
|
318
|
+
exactly affine and the correction is a division, on `log` and `symlog` as
|
|
319
|
+
well. (The division is exact on all three; end-to-end tracking on `symlog`
|
|
320
|
+
still drifts, because its knee is re-derived from the panned domain — a
|
|
321
|
+
pre-existing property the sibling zoom test already documents.) Invisible at
|
|
322
|
+
`k === 1`.
|
|
323
|
+
- `@pond-ts/charts`: **`<YAxis zeroAnchored>` no longer lets the baseline drift
|
|
324
|
+
in the controlled path.** With `onBoundsChange` set, the zoom read `0`'s
|
|
325
|
+
pixel off the axis's _live_ scale but inverted it through `baseYScales`.
|
|
326
|
+
Those two pixel spaces differ by the container's own `yTransform`, so with
|
|
327
|
+
the plot's y pan/zoom active (`panZoom="panZoomY"` / `"panZoomXY"`) the
|
|
328
|
+
baseline crept a few pixels per wheel notch — monotonically, in exactly the
|
|
329
|
+
gesture whose purpose is to hold it still. It was invisible without a
|
|
330
|
+
plot-level y zoom, where the two spaces coincide. Found by a post-merge
|
|
331
|
+
adversarial review of #676.
|
|
332
|
+
- `@pond-ts/charts`: **a `BarChart`'s baseline no longer relocates under a y
|
|
333
|
+
gutter pan/zoom.** `resolveBarBaseline` decided whether bars rest on zero or
|
|
334
|
+
on the axis floor by reading the axis's _live, transformed_ domain — the
|
|
335
|
+
same read a pan/zoom keeps moving — so once a gesture scrolled zero out of
|
|
336
|
+
view, every bar's rendered top silently snapped to the moving floor instead
|
|
337
|
+
of its actual value. It now reads the axis's declared (pre-transform)
|
|
338
|
+
domain for that decision, so a pan/zoom can move the viewport without ever
|
|
339
|
+
relocating a bar's own baseline.
|
|
340
|
+
- `@pond-ts/charts`: **[F-charts-21] a trading-time axis's stacked date-band
|
|
341
|
+
row no longer draws a second, colliding label over a collapsed-gap seam.**
|
|
342
|
+
`scale.bands()` picked its anchors from the raw calendar (a month or day
|
|
343
|
+
start) without checking whether that instant was live, so a period
|
|
344
|
+
beginning on a collapsed weekend or holiday clamped its label onto the same
|
|
345
|
+
seam a tick (or another band) already occupied — rendering as two
|
|
346
|
+
overlapping labels (`"Aug"` over `"Aug 3"`, or worse at tight zoom, fully
|
|
347
|
+
interleaved). Bands whose raw start needed clamping now dedupe against a
|
|
348
|
+
run of other clamped starts landing on the same live instant (picking the
|
|
349
|
+
genuinely-live member as the representative when one exists) and against
|
|
350
|
+
an already-labelled tick, so a seam carries exactly one label.
|
|
351
|
+
|
|
69
352
|
## [0.63.0] — 2026-08-18
|
|
70
353
|
|
|
71
354
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"description": "React hooks for pond-ts live time series",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"test:runtime": "vitest run"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"pond-ts": "^0.
|
|
37
|
+
"pond-ts": "^0.65.0",
|
|
38
38
|
"react": "^18.0.0 || ^19.0.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|