@pond-ts/react 0.44.1 → 0.46.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.
Files changed (2) hide show
  1. package/CHANGELOG.md +143 -1
  2. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -8,7 +8,9 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`,
8
8
  tag, so this file covers them all. Pre-1.0: minor bumps may include new features
9
9
  and type-level changes; patch bumps are strictly additive.
10
10
 
11
- [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.44.1...HEAD
11
+ [Unreleased]: https://github.com/pjm17971/pond-ts/compare/v0.46.0...HEAD
12
+ [0.46.0]: https://github.com/pjm17971/pond-ts/compare/v0.45.0...v0.46.0
13
+ [0.45.0]: https://github.com/pjm17971/pond-ts/compare/v0.44.1...v0.45.0
12
14
  [0.44.1]: https://github.com/pjm17971/pond-ts/compare/v0.44.0...v0.44.1
13
15
  [0.44.0]: https://github.com/pjm17971/pond-ts/compare/v0.43.0...v0.44.0
14
16
  [0.43.0]: https://github.com/pjm17971/pond-ts/compare/v0.42.0...v0.43.0
@@ -43,6 +45,146 @@ and type-level changes; patch bumps are strictly additive.
43
45
 
44
46
  ## [Unreleased]
45
47
 
48
+ ## [0.46.0] — 2026-07-14
49
+
50
+ ### Changed
51
+
52
+ - **charts:** the boundary (second-row) axis label's **context** now pins to
53
+ the plot's left edge instead of riding the first tick: it shows the period
54
+ the *domain start* is in, and a crossing label sliding toward the edge
55
+ pushes it off (the sticky-header behavior). On a live sliding window the
56
+ old first-tick anchoring made `Jan 01` hop tick-to-tick as ticks scrolled
57
+ out; pinned, it stays put until the period actually changes. Crossing
58
+ labels (day/year turns) still ride their ticks — including a first tick
59
+ whose period differs from the domain start's. `TradingTimeScale` gains
60
+ `boundaryContext(count)`; `tickBoundaries` now labels crossings only.
61
+
62
+ ### Fixed
63
+
64
+ - **charts:** a live (sliding-window) time axis no longer flickers between two
65
+ tick grains: the clock-rung choice now derives from the window's live span
66
+ (constant while sliding) instead of the enumerated anchor count, which
67
+ oscillates ±1 with the window's phase and flipped the grain for single
68
+ frames whenever it sat exactly at the width-derived cap.
69
+
70
+ ### Added
71
+
72
+ - **charts:** **dual x-axes** — two tick layouts on one shared scale. A second
73
+ `<XAxis>` stacks by declaration order (above/below the plot, either side,
74
+ same side twice); the new **`transform`** prop (`{ to, from }`, exported
75
+ `AxisTransform`) relabels an axis into a derived unit: strike ↔ moneyness on
76
+ a top axis, or a nonlinear BS-delta strip under a std-moneyness chart. Ticks
77
+ are nice derived-unit values chosen by a pixel-aware multi-resolution fill
78
+ (1-2-5 steps, coarsest first, admitted where they keep room), so a span the
79
+ transform compresses gets coarse ticks and a stretched span picks up finer
80
+ ones — and a label-honesty filter drops any tick whose formatted label would
81
+ lie about its position. Gridlines stay on the container's primary ticks; the
82
+ cursor pill on a transformed axis reads in the derived unit. Stories under
83
+ `Charts/Axes/DualX`. Each `<XAxis>` **and `<YAxis>`** also takes a
84
+ per-instance **`color`** (labels, tick marks, rule, title) — the lever that
85
+ distinguishes stacked x strips (a blue delta strip under a grey primary) and
86
+ colours a y axis to match its series (the dual-axis convention).
87
+
88
+ ### Fixed
89
+
90
+ - **charts:** annotation label chips now clip to the plot: a marker whose pole
91
+ pans off-plot no longer leaves its chip floating in the axis gutter, and a
92
+ partially visible region's chip clamps to the plot's left edge (culled only
93
+ when the region is entirely out of view). The lines/fills were already
94
+ SVG-clipped — only the DOM chips escaped.
95
+
96
+ ## [0.45.0] — 2026-07-14
97
+
98
+ ### Added
99
+
100
+ - **charts:** the time axis now walks a **logical tick ladder** — clock/calendar
101
+ units (1s…30s, 1m…30m, 1H…12H, day / week / month / quarter / year), picking
102
+ the finest grain that fits the width-derived cap — and renders **two-tier labels**:
103
+ a first row at the tick grain (`14:00`, `Feb 02`, `Feb`, `2026`) plus a
104
+ **boundary row** carrying the coarser context the first row omits (the date
105
+ under clock ticks, the year under day/week/month ticks — never a unit the
106
+ first row already shows), shown once under
107
+ the first tick of each new period. Hour anchors are generated in **live**
108
+ trading time, so none lands in a collapsed gap, an early close, or a lunch
109
+ break. `TradingTimeScale` gains `tickBoundaries(count)`; a cramped leading
110
+ partial-period anchor (the `"Jun 23Jul 07"` pile-up) is dropped. Systematic
111
+ story matrix under `Charts/TimeAxisTicks`.
112
+ - **financial:** the rest of the first study batch — `rollingStdev`,
113
+ `rollingMin`, `rollingMax`, `rollingPercentile` (linear interpolation),
114
+ `zScore` (rolling), `envelope` (MA ± percent, `maType` sma/ema), and
115
+ `percentChange` (n-bar rate of change). Same shape as the others — `column` /
116
+ `output` (or `prefix`) on every one, bar-count periods, length-preserving
117
+ warm-up — and each has a fluent method (`bars.zScore({ period: 20 })`) and a
118
+ **pandas oracle** case (values cross-validated bar-for-bar). This completes the
119
+ #449 first batch.
120
+ - **financial:** opt-in **fluent studies** via `import '@pond-ts/financial/fluent'`
121
+ — mounts `sma` / `ema` / `bollinger` as chainable `TimeSeries` methods so
122
+ composition reads like the core operators beside it:
123
+ `bars.sma({ period: 20 }).ema({ period: 12 }).bollinger({ period: 20 })`.
124
+ Opt-in by import (the default entry leaves `TimeSeries` untouched, so a
125
+ non-financial series never sees `.sma()`); the methods are exactly the
126
+ standalone functions bound to `this`, fully typed (each appends its column to
127
+ the schema). Same prototype-augmentation pattern core uses for column methods.
128
+ - **financial:** `@pond-ts/financial` gains its first **studies** — `sma`, `ema`,
129
+ and `bollinger` (Bollinger Bands®) — pure functions that append a column (or a
130
+ `${prefix}Middle/Upper/Lower` family) to a bar series. Every study takes a
131
+ `column` source (default `'close'`) and an `output` name, so a study runs over
132
+ any numeric column including another study's output; periods are **bar counts**
133
+ (on core's count window, gap-correct); warm-up rows are `undefined`
134
+ (length-preserving). Plus the `OhlcvColumns` contract + `DEFAULT_OHLCV`. (Core
135
+ `AppendColumn` is now exported so study return types name their appended
136
+ column.)
137
+ - **core:** `smooth(col, 'ema', …)` gains the financial **`span`** rate
138
+ convention (`α = 2/(span+1)`, e.g. `{ span: 12 }` for a 12-period EMA) as an
139
+ alternative to `alpha` (exactly one required), and a length-preserving
140
+ **`minSamples`** warm-up that emits `undefined` for the first `N` present
141
+ values while **keeping the row count** — mirroring `rolling`'s `minSamples`,
142
+ the one warm-up convention studies use so a smoothed line aligns on its
143
+ source's time axis. (The existing `warmup` option is unchanged — it still
144
+ *drops* the head rows; `minSamples` is the length-preserving counterpart.)
145
+ - **core:** `TimeSeries.rolling` accepts a **count-based** window —
146
+ `rolling({ count: N }, mapping, opts?)` reduces the last / next / centered
147
+ `N` _rows_ (bars) by position instead of a time span. Unlike a duration
148
+ window, an N-bar window stays correct across session gaps (weekends,
149
+ overnight) where `N` bars do not equal `N × barSize` of time — the load-
150
+ bearing primitive for financial N-bar studies (SMA-20, Bollinger-20, rolling
151
+ stdev). Honours `alignment` and `minSamples` exactly like a duration window
152
+ (`minSamples: N` gives the conventional first-`N-1`-rows-`undefined` warmup);
153
+ per-row output only (not supported with a sequence). Same amortized-O(1)-per-
154
+ row sweep as the duration path.
155
+ - **charts:** `<LineChart sessionBreaks>` breaks the line at each trading-axis
156
+ discontinuity (a session / day / lunch close→open) instead of connecting the
157
+ prior close straight to the next open across the collapsed gap. For intraday
158
+ data on a `discontinuities` / `calendar` axis this gives the terminal look —
159
+ the line ends at the close and re-starts at the open, so one session's price
160
+ doesn't visually flow into the next. It's a **scale** break (driven by the
161
+ axis's collapsed gaps), orthogonal to `gaps` (a **data** break, a NaN run) —
162
+ set both independently. Default `false`; a no-op on a continuous axis or a
163
+ provider without `boundaries`. New `Charts/TradingTimeAxis/SessionBreaks`
164
+ story (connected vs broken) + `sessionRuns` / `gappingTicks` helpers.
165
+
166
+ ### Changed
167
+
168
+ - **charts:** a **plain continuous time axis** now runs the same ladder as a
169
+ trading-calendar axis (via an internal gap-free identity provider) instead of
170
+ d3's multi-scale default — so a year of daily data ticks cleanly on month
171
+ starts (`Jul Aug … Jun` + the year underneath) rather than mixed
172
+ `"Jun 23" / "Sep" / "Dec"` labels, and its tick count is now width-derived
173
+ like the trading axis's. Month/quarter-grain anchor labels are now bare
174
+ months (`Feb`, was `Feb 02`) with the year on the boundary row;
175
+ `coarsenCalendar`'s finest granularity is renamed `'session'` → `'day'`.
176
+
177
+ ### Fixed
178
+
179
+ - **core:** `TimeSeries.withColumn` now appends an **optional** column
180
+ (`required: false`) rather than a required one. It already accepted
181
+ `(number | undefined)[]`, but marking the column required meant a later
182
+ strict-intake rebuild (`smooth`, any row reconstruction) threw on the gaps —
183
+ so a column with a warm-up (a rolling study fed into an EMA, e.g.
184
+ `ema(sma(bars, …), …)`) crashed. The appended column's type is now
185
+ `number | undefined`, matching `smooth`'s optional append. (`OptionalNumberColumn`
186
+ is exported for the widened return type.)
187
+
46
188
  ## [0.44.1] — 2026-07-13
47
189
 
48
190
  A `@pond-ts/charts` patch: fixes trading-time axis tick density. `pond-ts`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pond-ts/react",
3
- "version": "0.44.1",
3
+ "version": "0.46.0",
4
4
  "description": "React hooks for pond-ts live time series",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "test:runtime": "vitest run"
34
34
  },
35
35
  "peerDependencies": {
36
- "pond-ts": "^0.44.0",
36
+ "pond-ts": "^0.46.0",
37
37
  "react": "^18.0.0 || ^19.0.0"
38
38
  },
39
39
  "devDependencies": {