diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 7225f2f18..68c02a83b 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -8195,7 +8195,7 @@ pub struct BrkClient { impl BrkClient { /// Client version. - pub const VERSION: &'static str = "v0.2.2"; + pub const VERSION: &'static str = "v0.2.3"; /// Create a new client with the given base URL. pub fn new(base_url: impl Into) -> Self { diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1ada2c6f5..de7a8cbff 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,86 @@ All notable changes to the Bitcoin Research Kit (BRK) project will be documented > *This changelog was generated by Claude Code* +## [v0.2.3](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.2.3) - 2026-03-26 + +### New Features + +#### `brk_computer` +- Added new `investing` module with complete Dollar-Cost Averaging (DCA) and lump-sum investment analysis — computes DCA stack (cumulative sats purchased at $100/day), cost basis, returns, and CAGR by rolling period (1w, 1m, 3m, 6m, 1y through 10y); lump-sum stack and returns by rolling period; and DCA stack, cost basis, and returns by year class (2015 through 2026). Extracted from `market::dca` into a standalone top-level module with parallel import and compute ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/investing/mod.rs)) +- Added `RealizedEnvelope` indicator that computes the tightest percentile bounds across 10 pricing models (realized price, investor price, STH/LTH variants, cointime vaultedness/activity/true market mean/cointime price), producing 8 envelope price bands (0.5th through 99.5th percentile), a zone index (-4 to +4) based on spot price position relative to envelope bands, and a composite score (-40 to +40) summing band crossings across all models ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/indicators/realized_envelope.rs)) +- Expanded `RatioPerBlockPercentiles` from 6 to 8 percentile bands, adding 0.5th and 99.5th percentiles alongside the existing 1st/2nd/5th/95th/98th/99th — `ExpandingPercentiles` Fenwick tree computation also widened from 6-quantile to 8-quantile output ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs)) +- Consolidated timestamp data into a new `indexes::timestamp::Timestamps` struct — moved `timestamp_monotonic`, `date`, and per-resolution timestamp lookups from `blocks::time::Vecs` into the indexes module, making timestamp data accessible without depending on the blocks module ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/indexes/timestamp.rs)) + +#### `brk_indexer` +- Added `take_all_pending_ingests()` to batch all KV store puts/deletes into closures that can be executed on a background thread, enabling the indexer to hand off fjall store commits to `run_bg()` instead of blocking the main indexing loop ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_indexer/src/stores.rs)) +- Extracted `stamped_write()` from `flush()` so vec stamping can happen on the main thread while compaction and store ingests run in the background ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_indexer/src/vecs/mod.rs)) + +#### `brk_store` +- Added `take_pending_ingest()` method that drains buffered puts/deletes and returns a `Send` closure for background ingestion into the fjall keyspace ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_store/src/lib.rs)) + +#### `website` +- Added range preset buttons (1w, 1m, 3m, 6m, 1y, 4y, 8y, YTD, all) to the chart that automatically select the appropriate sub-day resolution index (30min through 1w) and scroll the visible range to that time window ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/chart/index.js)) +- Added value-dependent coloring support (`colorFn`) for histogram and baseline chart series, enabling per-bar colors based on data values ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/chart/index.js)) +- Added "Realized Envelope" chart to market indicators showing 8-level percentile price bands as dashed lines, a color-coded zone index histogram, and a score baseline ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/options/market.js)) +- Added `percentileBands()` and `priceBands()` shared helpers for displaying 8-level percentile bands with dedicated colors per level, reusable across ratio and price chart types ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/options/shared.js)) +- Added subtle grid lines to charts using a new `offBorder` color variable ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/chart/index.js)) + +### Internal Changes + +#### `brk_computer` +- Switched all module `compute()` methods to use `db.run_bg()` for background compaction instead of synchronous `db.compact()`, and added `db.sync_bg_tasks()` at compute entry to await prior background work — applies to blocks, cointime, distribution, indicators, inputs, investing, market, mining, outputs, pools, positions, prices, scripts, supply, and transactions ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/blocks/compute.rs)) +- Removed `market::dca` sub-module — DCA types (`ByDcaClass`, `ByDcaPeriod`, `ByDcaCagr`) and computation logic moved to the new top-level `investing` module; `market::returns` now imports `ByDcaPeriod` from `investing` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/market/mod.rs)) +- Removed `blocks::time` sub-module — `timestamp_monotonic` compute logic and per-resolution timestamp imports relocated to `indexes::timestamp::Timestamps` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/blocks/mod.rs)) +- Changed `lookback::compute()` to take `&indexes::Vecs` instead of `&time::Vecs`, sourcing monotonic timestamps from the indexes module ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/blocks/lookback.rs)) +- Changed `market::compute()` parameter order: now takes `(prices, indexes, blocks)` instead of `(indexes, prices, blocks)` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_computer/src/market/compute.rs)) + +#### `brk_indexer` +- Moved indexer store ingest and fjall persist to background via `run_bg()`, running compaction, store commit, and fjall `SyncData` persist concurrently with the next indexing batch ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_indexer/src/lib.rs)) + +#### `brk_client` +- Added `Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern` replacing `Pct1Pct2Pct5Pct95Pct98Pct99Pattern` to include the new 0.5th and 99.5th percentile bands ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_client/src/lib.rs)) +- Added `SeriesTree_Investing` with full period/class sub-trees for DCA stack, cost basis, returns, CAGR, and lump-sum series ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_client/src/lib.rs)) +- Added `SeriesTree_Indicators_RealizedEnvelope` with percentile price bands, index, and score series ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_client/src/lib.rs)) +- Added `SeriesTree_Indexes_Timestamp` with monotonic and per-resolution timestamp series ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_client/src/lib.rs)) +- Removed `date` and `timestamp_monotonic` from `SeriesTree_Blocks_Time`, simplified to only expose `timestamp` as a per-resolution pattern ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_client/src/lib.rs)) + +#### `brk_query` +- Updated all mining query endpoints (`day1_iter`, `difficulty`, `epochs`, `hashrate`) and series timestamp resolution to read from `indexes.timestamp` instead of `blocks.time.timestamp` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/crates/brk_query/src/impl/series.rs)) + +#### `website` +- Refactored investing chart options to use the new `investing` tree path instead of `market.dca` ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/options/investing.js)) +- Added dedicated percentile color palette (`_0_5` through `_99_5`) replacing the previous 6-color scheme with 8 distinct colors ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/utils/colors.js)) +- Added `createRatioChart()` and `groupedWindowsCumulative()` shared chart builders for reuse across ratio-based chart options ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/website/scripts/options/shared.js)) + +#### `docker` +- Switched health check from `curl http://localhost:3110/health` to `pgrep -x brk`, replacing `curl` package dependency with `procps` and reducing start period from 60s to 10s ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/docker/docker-compose.yml)) + +#### `workspace` +- Bumped Rust toolchain from 1.94.0 to 1.94.1 ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/rust-toolchain.toml)) +- Bumped `quickmatch-js` from 0.4.0 to 0.4.1 ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.3/modules/quickmatch-js/0.4.1/src/index.js)) + +[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.2.2...v0.2.3) + +## [v0.2.2](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.2.2) - 2026-03-23 + +### New Features + +#### `website` +- Created a comprehensive LLM-readable API reference (`llms-full.txt`) documenting every endpoint with curl examples, response shapes, parameter descriptions, and series categories — enabling LLM agents to discover and use the BRK API via the [llms.txt standard](https://llmstxt.org/) ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.2/website/llms-full.txt)) +- Rewrote `llms.txt` from a minimal overview into a structured quick-start guide with live links to key endpoints (search, series data, blocks, transactions, addresses, fees, live price) and links to the full reference, OpenAPI specs, and interactive docs ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.2/website/llms.txt)) +- Added `llms-full.txt` to `robots.txt` as a sitemap entry so crawlers and LLM agents can discover it ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.2/website/robots.txt)) + +#### `brk_server` +- Added Open Graph and Twitter Card meta tags to the Scalar API documentation page, providing proper title, description, and image for social media link previews ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.2/crates/brk_server/src/api/scalar.html)) +- Renamed the API docs page title from "BRK API" to "BRK API — Bitcoin Research Kit" with a richer meta description emphasizing the free, no-auth nature of the API ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.2/crates/brk_server/src/api/scalar.html)) + +### Internal Changes + +#### `workspace` +- Re-enabled `cargo build --workspace --release` in the release script (was previously commented out) ([source](https://github.com/bitcoinresearchkit/brk/blob/v0.2.2/scripts/quick-release.sh)) + +[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.2.1...v0.2.2) + ## [v0.2.1](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.2.1) - 2026-03-23 ### Breaking Changes diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 614aab80a..aee5f7715 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -6381,7 +6381,7 @@ function createTransferPattern(client, acc) { * @extends BrkClientBase */ class BrkClient extends BrkClientBase { - VERSION = "v0.2.2"; + VERSION = "v0.2.3"; INDEXES = /** @type {const} */ ([ "minute10", diff --git a/modules/brk-client/package.json b/modules/brk-client/package.json index 095914af0..d4b23136e 100644 --- a/modules/brk-client/package.json +++ b/modules/brk-client/package.json @@ -40,5 +40,5 @@ "url": "git+https://github.com/bitcoinresearchkit/brk.git" }, "type": "module", - "version": "0.2.2" + "version": "0.2.3" } diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 2f3d632e0..b6358aaa0 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -5679,7 +5679,7 @@ class SeriesTree: class BrkClient(BrkClientBase): """Main BRK client with series tree and API methods.""" - VERSION = "v0.2.2" + VERSION = "v0.2.3" INDEXES = [ "minute10", diff --git a/packages/brk_client/pyproject.toml b/packages/brk_client/pyproject.toml index ff4e51c5c..bf08b1ed4 100644 --- a/packages/brk_client/pyproject.toml +++ b/packages/brk_client/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "brk-client" -version = "0.2.2" +version = "0.2.3" description = "Bitcoin on-chain analytics client — thousands of metrics, block explorer, and address index" readme = "README.md" requires-python = ">=3.9" diff --git a/website/.gitignore b/website/.gitignore index ab66c8faa..3390df3d5 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -1,5 +1,4 @@ */**/*.md !scripts/**/_*.js *_old.js -_*.js -dump-* +*dump* diff --git a/website/scripts/types.js b/website/scripts/_types.js similarity index 99% rename from website/scripts/types.js rename to website/scripts/_types.js index d226815b2..e18ca10eb 100644 --- a/website/scripts/types.js +++ b/website/scripts/_types.js @@ -248,5 +248,4 @@ * * Generic tree node type for walking * @typedef {AnySeriesPattern | Record} TreeNode - * */ diff --git a/website/scripts/chart/index.js b/website/scripts/chart/index.js index 9ed4241f1..b81cdb7e3 100644 --- a/website/scripts/chart/index.js +++ b/website/scripts/chart/index.js @@ -4,7 +4,6 @@ import { HistogramSeries, LineSeries, BaselineSeries, - // } from "../modules/lightweight-charts/5.1.0/dist/lightweight-charts.standalone.development.mjs"; } from "../modules/lightweight-charts/5.1.0/dist/lightweight-charts.standalone.production.mjs"; import { createLegend, createSeriesLegend } from "./legend.js"; import { capture } from "./capture.js"; @@ -83,17 +82,18 @@ function getRangePresets() { const m = now.getUTCMonth(); const d = now.getUTCDate(); /** @param {number} months @param {number} [days] */ - const ago = (months, days = 0) => Math.floor(Date.UTC(y, m - months, d - days) / 1000); + const ago = (months, days = 0) => + Math.floor(Date.UTC(y, m - months, d - days) / 1000); /** @type {RangePreset[]} */ const presets = [ { label: "1w", index: /** @type {IndexLabel} */ ("30mn"), from: ago(0, 7) }, - { label: "1m", index: /** @type {IndexLabel} */ ("1h"), from: ago(1) }, - { label: "3m", index: /** @type {IndexLabel} */ ("4h"), from: ago(3) }, - { label: "6m", index: /** @type {IndexLabel} */ ("12h"), from: ago(6) }, - { label: "1y", index: /** @type {IndexLabel} */ ("1d"), from: ago(12) }, - { label: "4y", index: /** @type {IndexLabel} */ ("3d"), from: ago(48) }, - { label: "8y", index: /** @type {IndexLabel} */ ("1w"), from: ago(96) }, + { label: "1m", index: /** @type {IndexLabel} */ ("1h"), from: ago(1) }, + { label: "3m", index: /** @type {IndexLabel} */ ("4h"), from: ago(3) }, + { label: "6m", index: /** @type {IndexLabel} */ ("12h"), from: ago(6) }, + { label: "1y", index: /** @type {IndexLabel} */ ("1d"), from: ago(12) }, + { label: "4y", index: /** @type {IndexLabel} */ ("3d"), from: ago(48) }, + { label: "8y", index: /** @type {IndexLabel} */ ("1w"), from: ago(96) }, ]; const ytdFrom = Math.floor(Date.UTC(y, 0, 1) / 1000); @@ -105,7 +105,11 @@ function getRangePresets() { from: ytdFrom, }); - presets.push({ label: "all", index: /** @type {IndexLabel} */ ("1w"), from: -Infinity }); + presets.push({ + label: "all", + index: /** @type {IndexLabel} */ ("1w"), + from: -Infinity, + }); return presets; } @@ -445,7 +449,11 @@ export function createChart({ parent, brk, fitContent }) { if (this.isAllHidden(paneIndex)) { const collapsedHeight = paneIndex === 0 ? 32 : 64; const chartHeight = ichart.chartElement().clientHeight; - pane.setStretchFactor(chartHeight > 0 ? collapsedHeight / (chartHeight - collapsedHeight) : 0); + pane.setStretchFactor( + chartHeight > 0 + ? collapsedHeight / (chartHeight - collapsedHeight) + : 0, + ); } else { pane.setStretchFactor(1); } diff --git a/website/styles/chart.css b/website/styles/chart.css index de65ea014..8de2d5bc2 100644 --- a/website/styles/chart.css +++ b/website/styles/chart.css @@ -62,6 +62,10 @@ padding: 0 var(--main-padding); padding-top: 0.375rem; + @media (pointer: coarse) { + pointer-events: auto; + } + > * { pointer-events: auto; } @@ -255,11 +259,11 @@ button { color: var(--off-color); - padding: 0.375rem; - margin: -0.375rem 0.25rem; + padding: 0.375rem 0.5rem; + margin: -0.375rem 0; &:first-of-type { - margin-left: -0.25rem; + margin-left: -0.5rem; } } } diff --git a/website/styles/elements.css b/website/styles/elements.css index 6fc3ba1ad..2c480498c 100644 --- a/website/styles/elements.css +++ b/website/styles/elements.css @@ -55,11 +55,7 @@ aside { flex: 1; @media (max-width: 767px) { - padding-bottom: calc(var(--main-padding) + 1.5rem); - - html[data-display="standalone"] & { - padding-bottom: calc(var(--main-padding) + 2rem); - } + padding-bottom: calc(var(--main-padding) + 0.5rem); } @media (min-width: 768px) { @@ -95,16 +91,23 @@ button { } h1 { - font-size: var(--font-size-xl); + font-size: 2rem; line-height: var(--line-height-xl); - font-weight: 300; } h3 { - font-size: var(--font-size-lg); + font-size: 1.5rem; line-height: var(--line-height-lg); } +h1, +h2, +h3 { + font-family: instrument; + letter-spacing: 0.05rem; + font-weight: 400; +} + html { background-color: var(--background-color); color: var(--color); @@ -115,6 +118,8 @@ html { input { width: 100%; + padding: 0; + letter-spacing: inherit; &::placeholder { color: var(--off-color); @@ -194,6 +199,7 @@ select { flex-shrink: 0; width: 100%; text-transform: uppercase; + color: var(--color); } :is(input, select):focus-visible { diff --git a/website/styles/main.css b/website/styles/main.css index da1eec4ce..5e97a9989 100644 --- a/website/styles/main.css +++ b/website/styles/main.css @@ -29,7 +29,7 @@ main { background-image: linear-gradient( to bottom, transparent, - var(--background-color) 90%, + var(--background-color) 85%, var(--background-color) ); } @@ -96,12 +96,6 @@ main { ); } - @media (max-width: 767px) { - html[data-display="standalone"] & { - margin-bottom: calc(var(--main-padding) + 0.5rem); - } - } - > fieldset { display: flex; gap: 1.25rem; diff --git a/website/styles/panes/chart.css b/website/styles/panes/chart.css index 6aadc2805..65bc7000b 100644 --- a/website/styles/panes/chart.css +++ b/website/styles/panes/chart.css @@ -52,7 +52,7 @@ h1 { font-size: 2rem; - letter-spacing: 0.075rem; + letter-spacing: 0.05rem; text-wrap: nowrap; } } diff --git a/website/styles/reset.css b/website/styles/reset.css index cdba26461..742d679a1 100644 --- a/website/styles/reset.css +++ b/website/styles/reset.css @@ -49,184 +49,3 @@ h5, h6 { text-wrap: balance; } - -/**, -::after, -::before, -::backdrop, -::file-selector-button { - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0 solid; -} - -html, -:host { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - tab-size: 4; - font-family: - "Lilex", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, - "Liberation Mono", "Courier New", monospace; - -webkit-tap-highlight-color: transparent; -} - -body { - line-height: inherit; -} - -hr { - height: 0; - color: inherit; - border-top-width: 1px; -} - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -a { - color: inherit; -} - -b, -strong { - font-weight: bolder; -} - -code, -kbd, -samp, -pre { - font-family: - var(--default-mono-font-family), ui-monospace, SFMono-Regular, Menlo, - Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-feature-settings: var(--default-mono-font-feature-settings, normal); - font-variation-settings: var(--default-mono-font-variation-settings, normal); - font-size: 1em; -} - -small { - font-size: 80%; -} - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; -} - -button, -input, -optgroup, -select, -textarea, -::file-selector-button { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - background: transparent; - text-transform: inherit; -} - -button, -input:where([type="button"], [type="reset"], [type="submit"]), -::file-selector-button { - appearance: button; -} - -:-moz-focusring { - outline: auto; -} - -:-moz-ui-invalid { - box-shadow: none; -} - -progress { - vertical-align: baseline; -} - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -summary { - display: list-item; -} - -ol, -ul, -menu { - list-style: none; -} - -textarea { - resize: vertical; -} - -::placeholder { - opacity: 1; - color: color-mix(in srgb, currentColor 50%, transparent); -} - -:disabled { - cursor: default; -} - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - vertical-align: middle; -} - -img, -video { - max-width: 100%; - height: auto; -} - -[hidden] { - display: none !important; -}*/ diff --git a/website/styles/variables.css b/website/styles/variables.css index 7638c7695..ce08b0ffc 100644 --- a/website/styles/variables.css +++ b/website/styles/variables.css @@ -45,6 +45,9 @@ --line-height-xl: calc(1.75 / 1.25); --main-padding: 2rem; + /*@media (max-width: 767px) { + --main-padding: 1.5rem; + }*/ --negative-main-padding: calc(-1 * var(--main-padding)); --font-weight-base: 400; --default-main-width: 25rem;