pond-ts 0.66.0 → 0.68.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/AGENTS.md +244 -0
- package/API.md +146 -123
- package/CHANGELOG.md +188 -1
- package/README.md +20 -0
- package/dist/batch/partitioned-time-series.d.ts +37 -19
- package/dist/batch/time-series.d.ts +2 -2
- package/package.json +29 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Using pond from a coding agent
|
|
2
|
+
|
|
3
|
+
You are reading this because a project depends on `pond-ts` or one of the
|
|
4
|
+
`@pond-ts/*` packages, or because you are deciding whether it should. This
|
|
5
|
+
file is the shortest route to correct code. It ships inside every pond
|
|
6
|
+
tarball as `AGENTS.md`, next to `API.md` (every public export, one line
|
|
7
|
+
each, with its source file) and `CHANGELOG.md`.
|
|
8
|
+
|
|
9
|
+
Docs: <https://pond-ts.org> · index for agents: <https://pond-ts.org/llms.txt>
|
|
10
|
+
· source: <https://github.com/pond-ts/pond>.
|
|
11
|
+
|
|
12
|
+
## What pond is, in three lines
|
|
13
|
+
|
|
14
|
+
- A **typed, immutable time series** (`TimeSeries`) whose schema is declared
|
|
15
|
+
once `as const` and narrows every downstream transform — no casts.
|
|
16
|
+
- The **same operator vocabulary on a streaming buffer** (`LiveSeries`):
|
|
17
|
+
push events in, subscribe to incremental `rolling` / `aggregate` views,
|
|
18
|
+
bounded by retention.
|
|
19
|
+
- **Domain packages on top**: React hooks, canvas charts that read a series
|
|
20
|
+
directly, financial studies + trading calendars, fitness analytics, and an
|
|
21
|
+
experimental processing-graph runtime.
|
|
22
|
+
|
|
23
|
+
## Which package
|
|
24
|
+
|
|
25
|
+
| You need to… | Install | Import from |
|
|
26
|
+
| ----------------------------------------------------------------------- | ------------------------------------ | ----------------------------------------------------- |
|
|
27
|
+
| Load timestamped rows; bucket, regrid, roll, fill, join, partition them | `pond-ts` | `'pond-ts'` |
|
|
28
|
+
| Ingest a live feed and keep rolling stats over the last N minutes | `pond-ts` | `'pond-ts'` (`LiveSeries`) |
|
|
29
|
+
| Share a series' type across a wire boundary with zero runtime | `pond-ts` | `'pond-ts/types'` |
|
|
30
|
+
| Own / subscribe to a series inside React | `@pond-ts/react` | `'@pond-ts/react'` |
|
|
31
|
+
| Draw it (line, area, band, bar, scatter, box, candlestick, heat map) | `@pond-ts/charts` (+ react, pond-ts) | `'@pond-ts/charts'` |
|
|
32
|
+
| OHLCV bars, SMA/EMA/RSI/MACD/Bollinger/ATR/VWAP…, market-hours calendar | `@pond-ts/financial` | `'@pond-ts/financial'`, `'@pond-ts/financial/fluent'` |
|
|
33
|
+
| GPS / power / heart-rate activity analytics | `@pond-ts/fit` | `'@pond-ts/fit'` |
|
|
34
|
+
| Computations as JSON plans with caching + provenance (experimental) | `@pond-ts/process` | `'@pond-ts/process'` |
|
|
35
|
+
|
|
36
|
+
All six release together under one version and release often. Install with
|
|
37
|
+
`@latest` rather than a version written from memory (a cold-start agent once
|
|
38
|
+
wrote `^0.3.0` and spent turns on a 2024 API); keep their ranges in step — a
|
|
39
|
+
pre-1.0 caret (`^0.67.0`) does **not** span minors.
|
|
40
|
+
|
|
41
|
+
## The idioms that cover most jobs
|
|
42
|
+
|
|
43
|
+
### 1. Declare the schema, build the series
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { TimeSeries, Sequence } from 'pond-ts';
|
|
47
|
+
|
|
48
|
+
const schema = [
|
|
49
|
+
{ name: 'time', kind: 'time' },
|
|
50
|
+
{ name: 'host', kind: 'string' },
|
|
51
|
+
{ name: 'latencyMs', kind: 'number' },
|
|
52
|
+
] as const; // ← load-bearing. Without it every column widens to string.
|
|
53
|
+
|
|
54
|
+
const s = TimeSeries.fromJSON({
|
|
55
|
+
name: 'latency',
|
|
56
|
+
schema,
|
|
57
|
+
rows, // positional tuples [time, host, latencyMs] or objects { time, host, latencyMs }
|
|
58
|
+
sort: true, // input not already time-ordered? sort on construction (stable)
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Time cells accept ms-since-epoch numbers, `Date`s, or ISO strings **with an
|
|
63
|
+
offset** (`…Z`, `…+01:00`). A wall-clock string with no offset throws unless
|
|
64
|
+
you pass `parse: { timeZone: 'America/New_York' }`.
|
|
65
|
+
|
|
66
|
+
Other doors: `TimeSeries.fromPoints(points)` for wide `{ ts, a, b }` rows,
|
|
67
|
+
`fromColumns` for struct-of-arrays / `Float64Array`, `fromArrow` for an Arrow
|
|
68
|
+
table, `fromEvents`. `toJSON()` round-trips.
|
|
69
|
+
|
|
70
|
+
### 2. Downsample, regrid, slide — three different verbs
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
// Fewer rows out than in: one row per bucket.
|
|
74
|
+
const perMin = s.aggregate(Sequence.every('1m'), {
|
|
75
|
+
latencyMs: 'avg', // reducer by column …
|
|
76
|
+
p95: { from: 'latencyMs', using: 'p95' }, // … or a named output; reducers: sum avg min max count first last median stdev pNN
|
|
77
|
+
host: 'last',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Same information, on a regular grid (hold / interpolate). No reduction.
|
|
81
|
+
const gridded = s.align(Sequence.every('10s'), { method: 'hold' });
|
|
82
|
+
|
|
83
|
+
// One output per input event, looking back over a window.
|
|
84
|
+
const rolled = s.rolling('5m', {
|
|
85
|
+
latencyMs: 'avg',
|
|
86
|
+
sd: { from: 'latencyMs', using: 'stdev' },
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`Sequence.every()` takes fixed durations only (`'10s'`, `'5m'`, `'1h'`,
|
|
91
|
+
`'1d'`). Months, weeks-in-a-zone, calendar days: `Sequence.calendar('month',
|
|
92
|
+
{ timeZone })`. Common shortcuts: `s.baseline('latencyMs', { window: '1h',
|
|
93
|
+
sigma: 2 })` appends avg / sd / upper / lower in one pass;
|
|
94
|
+
`s.outliers(col, { window, sigma })` keeps only the rows outside the band.
|
|
95
|
+
|
|
96
|
+
### 3. Per-entity, then flatten
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const perHost = s
|
|
100
|
+
.partitionBy('host') // every stateful operator below runs per host
|
|
101
|
+
.rolling('5m', { latencyMs: 'avg' })
|
|
102
|
+
.collect(); // one flat TimeSeries, `host` carried through (type and runtime)
|
|
103
|
+
// or .toMap() → Map<host, TimeSeries>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`aggregate` and `rolling` under `partitionBy` carry the partition column
|
|
107
|
+
through in both the runtime **and** the static type (since 0.68), so
|
|
108
|
+
`e.get('host')` works on the collected result without naming it. On 0.67 or
|
|
109
|
+
older, name it in the mapping — `{ host: 'first', … }`.
|
|
110
|
+
|
|
111
|
+
### 4. Clean, fill, join, read out
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const clean = s.dedupe().fill({ latencyMs: 'hold' }); // also 'linear', 'zero', gap caps
|
|
115
|
+
const joined = a.join(b); // on the time key; see API.md for options
|
|
116
|
+
clean.toPoints(); // [{ ts, host, latencyMs }, …] — chart-library friendly
|
|
117
|
+
clean.toRows(); // positional tuples
|
|
118
|
+
clean.column('latencyMs').mean(); // typed column: min/max/sum/mean/stdev/median/percentile
|
|
119
|
+
clean.column('latencyMs').toFloat64Array(); // zero-copy for canvas / WebGL loops
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Everything returns a **new** series. There is no `push` on a `TimeSeries`;
|
|
123
|
+
if you are appending, you want a `LiveSeries`.
|
|
124
|
+
|
|
125
|
+
### 5. Streaming
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { LiveSeries, Sequence } from 'pond-ts';
|
|
129
|
+
|
|
130
|
+
const live = new LiveSeries({
|
|
131
|
+
name: 'latency',
|
|
132
|
+
schema,
|
|
133
|
+
retention: { maxAge: '15m' }, // or { maxEvents: 10_000 }
|
|
134
|
+
ordering: 'reorder', // tolerate late rows …
|
|
135
|
+
graceWindow: '5s', // … up to this late
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const view = live.partitionBy('host').rolling('5m', { latencyMs: 'avg' });
|
|
139
|
+
const stop = view.on('event', (e) => render(e.get('host'), e.get('latencyMs')));
|
|
140
|
+
|
|
141
|
+
live.push([Date.now(), 'api-1', 42]); // validated against the schema
|
|
142
|
+
live.pushMany(batch);
|
|
143
|
+
const snapshot = live.toTimeSeries(); // immutable batch copy for analytics
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`live.aggregate(Sequence.every('1m'), …)` emits `'bucket'` (partial) and
|
|
147
|
+
`'close'` (final) events. Retention bounds memory; `sample({ stride })`
|
|
148
|
+
between `partitionBy` and a long `rolling` bounds it further at firehose
|
|
149
|
+
rates.
|
|
150
|
+
|
|
151
|
+
### React and charts
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { useLiveSeries } from '@pond-ts/react';
|
|
155
|
+
import {
|
|
156
|
+
ChartContainer,
|
|
157
|
+
ChartRow,
|
|
158
|
+
Layers,
|
|
159
|
+
LineChart,
|
|
160
|
+
YAxis,
|
|
161
|
+
} from '@pond-ts/charts';
|
|
162
|
+
|
|
163
|
+
const [live, snap] = useLiveSeries({
|
|
164
|
+
name: 'latency',
|
|
165
|
+
schema,
|
|
166
|
+
retention: { maxAge: '10m' },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
<ChartContainer width={800} cursor="crosshair" panZoom>
|
|
170
|
+
<ChartRow height={240}>
|
|
171
|
+
<YAxis id="ms" />
|
|
172
|
+
<Layers>
|
|
173
|
+
{snap && <LineChart series={snap} column="latencyMs" axis="ms" />}
|
|
174
|
+
</Layers>
|
|
175
|
+
</ChartRow>
|
|
176
|
+
</ChartContainer>;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Charts read a pond series directly — do the maths in pond (`rolling`,
|
|
180
|
+
`aggregate`, `align`) and hand the result to a layer. `useLiveSeries`'s snapshot is `null` before the first
|
|
181
|
+
push, hence the guard. `width` is a pixel
|
|
182
|
+
number or `'auto'` (the parent then needs a definite width, or nothing draws).
|
|
183
|
+
Hooks: `useTimeSeries`, `useLiveSeries`, `useSnapshot`, `useLiveQuery`,
|
|
184
|
+
`useDerived`, `useWindow`, `useCurrent`, `useLatest`.
|
|
185
|
+
|
|
186
|
+
### Financial
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import '@pond-ts/financial/fluent'; // once, anywhere: adds studies to TimeSeries
|
|
190
|
+
import { TradingCalendar } from '@pond-ts/financial';
|
|
191
|
+
|
|
192
|
+
const studied = bars
|
|
193
|
+
.sma({ period: 20 })
|
|
194
|
+
.rsi({ period: 14 })
|
|
195
|
+
.bollinger({ period: 20 });
|
|
196
|
+
// or, function form: sma(bars, { period: 20 })
|
|
197
|
+
const cal = TradingCalendar.fromRules(
|
|
198
|
+
{ timeZone: 'America/New_York', open: '09:30', close: '16:00' },
|
|
199
|
+
{ from: '2026-01-05', to: '2026-02-13' },
|
|
200
|
+
);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Studies read `'close'` by default, take **bar-count** periods, append
|
|
204
|
+
columns, preserve row count (warm-up rows are `undefined`). Sixty-plus of
|
|
205
|
+
them; `import { STUDIES } from '@pond-ts/financial/catalog'` lists them at runtime. Session-aligned bars: `ticks.aggregate(cal.barSequence('5m'), {...})`.
|
|
206
|
+
|
|
207
|
+
## Mistakes agents actually make
|
|
208
|
+
|
|
209
|
+
1. **Dropping `as const` on the schema.** Everything compiles and every
|
|
210
|
+
column is `string`. If `.get('x')` is not `number | undefined`, this is
|
|
211
|
+
why.
|
|
212
|
+
2. **`aggregate` when you meant `rolling`, or vice versa.** `aggregate`
|
|
213
|
+
changes the row count (one per bucket); `rolling` keeps it (one per
|
|
214
|
+
event); `align` puts rows on a grid without reducing.
|
|
215
|
+
3. **`Sequence.every('1M')` for months.** Not fixed-length → use
|
|
216
|
+
`Sequence.calendar('month', { timeZone })`.
|
|
217
|
+
4. **Wall-clock strings without a zone.** `'2025-01-01T09:00'` throws; add
|
|
218
|
+
`parse: { timeZone }` or use offset strings / ms numbers.
|
|
219
|
+
5. **Unsorted rows.** The constructor throws and names the row; pass
|
|
220
|
+
`sort: true` rather than sorting by hand.
|
|
221
|
+
6. **Mutating.** Nothing mutates. Capture the return value.
|
|
222
|
+
7. **Iterating events in a hot loop for a chart.** Use `column(name)` /
|
|
223
|
+
`toFloat64Array()` or hand the series to `@pond-ts/charts` — do not
|
|
224
|
+
rebuild point arrays per frame.
|
|
225
|
+
8. **Mismatched package versions.** All `pond-ts` / `@pond-ts/*` at the same
|
|
226
|
+
version, always.
|
|
227
|
+
9. **Reaching for a chart-library adapter first.** If the project uses React,
|
|
228
|
+
`@pond-ts/charts` consumes the series with no adapter; `toPoints()` is the
|
|
229
|
+
bridge for other libraries.
|
|
230
|
+
|
|
231
|
+
## Where to read next
|
|
232
|
+
|
|
233
|
+
- `API.md` (this folder) — find any export and its source file.
|
|
234
|
+
- <https://pond-ts.org/llms.txt> — every docs page with a one-line
|
|
235
|
+
description; `https://pond-ts.org/llms-<area>.txt` for a single-fetch dump
|
|
236
|
+
of one area (`pond-ts`, `charts`, `financial`, …).
|
|
237
|
+
- <https://pond-ts.org/docs/pond-ts/mental-model> — one picture, and the
|
|
238
|
+
pandas / pondjs translation tables.
|
|
239
|
+
- <https://pond-ts.org/docs/how-to-guides> — end-to-end builds with the
|
|
240
|
+
friction already ironed out (dashboard, messy CSV ingest, histograms,
|
|
241
|
+
large series).
|
|
242
|
+
- Claude Code users: `/plugin marketplace add pond-ts/pond` then
|
|
243
|
+
`/plugin install pond-ts@pond-ts` installs skills for core, charts and
|
|
244
|
+
financial.
|