diff --git a/.gitignore b/.gitignore
index 76cffb2ea..d11c88af2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@ _*
/filter_*
/heatmaps*
/oracle*
+/playground
# Logs
*.log*
diff --git a/crates/brk_cli/README.md b/crates/brk_cli/README.md
index a9fd9f453..6d5d289f6 100644
--- a/crates/brk_cli/README.md
+++ b/crates/brk_cli/README.md
@@ -37,6 +37,8 @@ brk
Indexes the blockchain, computes datasets, starts the server on `localhost:3110`, and waits for new blocks.
+**Note:** When more than 10,000 blocks behind, indexing completes before the server starts to free up memory from fragmentation that occurs during large syncs. The web interface at `localhost:3110` won't be available until sync finishes.
+
## Options
```bash
diff --git a/crates/brk_cli/src/main.rs b/crates/brk_cli/src/main.rs
index e2b0b46dd..f21d13684 100644
--- a/crates/brk_cli/src/main.rs
+++ b/crates/brk_cli/src/main.rs
@@ -57,7 +57,7 @@ pub fn run() -> anyhow::Result<()> {
let chain_height = client.get_last_height()?;
let indexed_height = indexer.vecs.starting_height();
let blocks_behind = chain_height.saturating_sub(*indexed_height);
- if blocks_behind > 1000 {
+ if blocks_behind > 10_000 {
info!("Indexing {blocks_behind} blocks before starting server...");
sleep(Duration::from_secs(3));
indexer.index(&blocks, &client, &exit)?;
diff --git a/website/index.html b/website/index.html
index 7397d9ba5..43e3e5554 100644
--- a/website/index.html
+++ b/website/index.html
@@ -1533,6 +1533,11 @@
"(prefers-color-scheme: dark)",
);
+ let storedTheme;
+ try { storedTheme = localStorage.getItem("theme"); } catch (_) {}
+ const isDark = storedTheme ? storedTheme === "dark" : preferredColorSchemeMatchMedia.matches;
+ document.documentElement.style.colorScheme = isDark ? "dark" : "light";
+
const themeColor = window.document.createElement("meta");
themeColor.name = "theme-color";
window.document.getElementsByTagName("head")[0].appendChild(themeColor);
@@ -1545,7 +1550,7 @@
themeColor.content = theme;
}
- updateThemeColor(preferredColorSchemeMatchMedia.matches);
+ updateThemeColor(isDark);
preferredColorSchemeMatchMedia.addEventListener(
"change",
({ matches }) => {
@@ -1832,6 +1837,7 @@
+
diff --git a/website/scripts/chart/index.js b/website/scripts/chart/index.js
index 6e20fca4d..dc8a86626 100644
--- a/website/scripts/chart/index.js
+++ b/website/scripts/chart/index.js
@@ -33,16 +33,18 @@ import { resources } from "../resources.js";
/**
* @template T
* @typedef {Object} Series
+ * @property {string} key
* @property {string} id
* @property {number} paneIndex
* @property {Signal} active
+ * @property {(value: boolean) => void} setActive
* @property {() => void} show
* @property {() => void} hide
* @property {(order: number) => void} setOrder
* @property {() => void} highlight
* @property {() => void} tame
* @property {() => boolean} hasData
- * @property {Signal} url
+ * @property {string | null} url
* @property {() => readonly T[]} getData
* @property {(data: T) => void} update
* @property {VoidFunction} remove
@@ -102,14 +104,18 @@ export function createChart({
/** @type {Map>} */
const sharedActiveSignals = new Map();
- const legendTop = createLegend(signals);
+ // Registry for linked series (same key = linked across panes)
+ /** @type {Map>} */
+ const seriesByKey = new Map();
+
+ const legendTop = createLegend();
div.append(legendTop.element);
const chartDiv = window.document.createElement("div");
chartDiv.classList.add("lightweight-chart");
div.append(chartDiv);
- const legendBottom = createLegend(signals);
+ const legendBottom = createLegend();
div.append(legendBottom.element);
const ichart = lcCreateChart(
@@ -175,15 +181,18 @@ export function createChart({
/** @type {Set} */
const onZoomChange = new Set();
- ichart.timeScale().subscribeVisibleLogicalRangeChange(
- throttle((range) => {
- if (!range) return;
- const count = range.to - range.from;
- if (count === visibleBarsCount) return;
- visibleBarsCount = count;
- onZoomChange.forEach((cb) => cb(count));
- }, 100),
- );
+ /** @param {{ from: number, to: number } | null} range */
+ function updateVisibleBarsCount(range) {
+ if (!range) return;
+ const count = range.to - range.from;
+ if (count === visibleBarsCount) return;
+ visibleBarsCount = count;
+ onZoomChange.forEach((cb) => cb(count));
+ }
+
+ ichart
+ .timeScale()
+ .subscribeVisibleLogicalRangeChange(throttle(updateVisibleBarsCount, 100));
signals.createEffect(
() => ({
@@ -391,27 +400,24 @@ export function createChart({
onRemove,
}) {
return signals.createRoot((dispose) => {
- const id = `${stringToId(name)}-${paneIndex}`;
- const urlId = stringToId(name);
+ const key = stringToId(name);
+ const id = `${key}-${paneIndex}`;
// Reuse existing signal if same name (links legends across panes)
- let active = sharedActiveSignals.get(urlId);
+ let active = sharedActiveSignals.get(key);
if (!active) {
active = signals.createPersistedSignal({
defaultValue: defaultActive ?? true,
storageKey: id,
- urlKey: urlId,
+ urlKey: key,
...serdeBool,
});
- sharedActiveSignals.set(urlId, active);
+ sharedActiveSignals.set(key, active);
}
setOrder(-order);
- // Bridge signal to series methods
- signals.createEffect(active, (isActive) => {
- isActive ? show() : hide();
- });
+ active() ? show() : hide();
let hasData = false;
let lastTime = -Infinity;
@@ -422,26 +428,47 @@ export function createChart({
/** @type {AnySeries} */
const series = {
active,
+ setActive(value) {
+ active.set(value);
+ seriesByKey.get(key)?.forEach((s) => {
+ value ? s.show() : s.hide();
+ });
+ document.querySelectorAll(`[data-series="${key}"]`).forEach((el) => {
+ if (el instanceof HTMLInputElement && el.type === "checkbox") {
+ el.checked = value;
+ }
+ });
+ },
setOrder,
show,
hide,
highlight,
tame,
hasData: () => hasData,
+ key,
id,
paneIndex,
- url: signals.createSignal(/** @type {string | null} */ (null)),
+ url: null,
getData,
update,
remove() {
dispose();
onRemove();
+ seriesByKey.get(key)?.delete(series);
if (_valuesResource) {
activeResources.delete(_valuesResource);
}
},
};
+ // Register series for cross-pane linking
+ let keySet = seriesByKey.get(key);
+ if (!keySet) {
+ keySet = new Set();
+ seriesByKey.set(key, keySet);
+ }
+ keySet.add(series);
+
if (metric) {
signals.createScopedEffect(index, (index) => {
// Get timestamp metric from tree based on index type
@@ -462,11 +489,15 @@ export function createChart({
const valuesResource = resources.useMetricEndpoint(valuesNode);
_valuesResource = valuesResource;
- series.url.set(() => {
- const base = brk.baseUrl.endsWith("/")
- ? brk.baseUrl.slice(0, -1)
- : brk.baseUrl;
- return `${base}${valuesResource.path}`;
+ series.url = `${
+ brk.baseUrl.endsWith("/") ? brk.baseUrl.slice(0, -1) : brk.baseUrl
+ }${valuesResource.path}`;
+
+ (paneIndex ? legendBottom : legendTop).addOrReplace({
+ series,
+ name,
+ colors,
+ order,
});
// Create memo outside active check (cheap, just checks data existence)
@@ -608,22 +639,24 @@ export function createChart({
},
);
});
- } else if (data) {
- signals.createEffect(data, (data) => {
- setData(data);
- hasData = true;
- if (fitContent) {
- ichart.timeScale().fitContent();
- }
+ } else {
+ (paneIndex ? legendBottom : legendTop).addOrReplace({
+ series,
+ name,
+ colors,
+ order,
});
- }
- (paneIndex ? legendBottom : legendTop).addOrReplace({
- series,
- name,
- colors,
- order,
- });
+ if (data) {
+ signals.createEffect(data, (data) => {
+ setData(data);
+ hasData = true;
+ if (fitContent) {
+ ichart.timeScale().fitContent();
+ }
+ });
+ }
+ }
addPriceScaleSelectorIfNeeded({
paneIndex,
@@ -693,7 +726,6 @@ export function createChart({
wickUpColor: upColor(),
wickDownColor: downColor(),
borderVisible: false,
- visible: defaultActive !== false,
...options,
},
paneIndex,
@@ -707,14 +739,13 @@ export function createChart({
{
color: colors.default(),
lineWidth,
- visible: false,
priceLineVisible: true,
},
paneIndex,
)
);
- let active = true;
+ let active = defaultActive !== false;
let highlighted = true;
let showLine = visibleBarsCount > 500;
@@ -733,11 +764,11 @@ export function createChart({
color: colors.default.highlight(highlighted),
});
}
+ update();
/** @type {ZoomChangeCallback} */
function handleZoom(count) {
const newShowLine = count > 500;
- if (newShowLine === showLine) return;
showLine = newShowLine;
update();
}
@@ -827,8 +858,6 @@ export function createChart({
ichart.addSeries(
/** @type {SeriesDefinition<'Histogram'>} */ (HistogramSeries),
{
- color: positiveColor(),
- visible: defaultActive !== false,
priceLineVisible: false,
...options,
},
@@ -836,7 +865,7 @@ export function createChart({
)
);
- let active = true;
+ let active = defaultActive !== false;
let highlighted = true;
function update() {
@@ -846,6 +875,7 @@ export function createChart({
color: positiveColor.highlight(highlighted),
});
}
+ update();
const series = addSeries({
colors: isDualColor ? [positiveColor, negativeColor] : [positiveColor],
@@ -931,16 +961,14 @@ export function createChart({
/** @type {SeriesDefinition<'Line'>} */ (LineSeries),
{
lineWidth,
- visible: defaultActive !== false,
priceLineVisible: false,
- color: color(),
...options,
},
paneIndex,
)
);
- let active = true;
+ let active = defaultActive !== false;
let highlighted = true;
function update() {
@@ -950,6 +978,7 @@ export function createChart({
color: color.highlight(highlighted),
});
}
+ update();
const series = addSeries({
colors: [color],
@@ -1020,9 +1049,7 @@ export function createChart({
ichart.addSeries(
/** @type {SeriesDefinition<'Line'>} */ (LineSeries),
{
- visible: defaultActive !== false,
priceLineVisible: false,
- color: color(),
lineVisible: false,
pointMarkersVisible: true,
pointMarkersRadius: 1,
@@ -1032,7 +1059,7 @@ export function createChart({
)
);
- let active = true;
+ let active = defaultActive !== false;
let highlighted = true;
let radius =
visibleBarsCount > 1000 ? 1 : visibleBarsCount > 200 ? 1.5 : 2;
@@ -1044,6 +1071,7 @@ export function createChart({
color: color.highlight(highlighted),
});
}
+ update();
/** @type {ZoomChangeCallback} */
function handleZoom(count) {
@@ -1128,13 +1156,10 @@ export function createChart({
/** @type {SeriesDefinition<'Baseline'>} */ (BaselineSeries),
{
lineWidth,
- visible: defaultActive !== false,
baseValue: {
price: options?.baseValue?.price ?? 0,
},
...options,
- topLineColor: topColor(),
- bottomLineColor: bottomColor(),
priceLineVisible: false,
bottomFillColor1: "transparent",
bottomFillColor2: "transparent",
@@ -1146,7 +1171,7 @@ export function createChart({
)
);
- let active = true;
+ let active = defaultActive !== false;
let highlighted = true;
function update() {
@@ -1157,6 +1182,7 @@ export function createChart({
bottomLineColor: bottomColor.highlight(highlighted),
});
}
+ update();
const series = addSeries({
colors: [topColor, bottomColor],
diff --git a/website/scripts/chart/legend.js b/website/scripts/chart/legend.js
index 786a69aa4..b8203fa9e 100644
--- a/website/scripts/chart/legend.js
+++ b/website/scripts/chart/legend.js
@@ -1,10 +1,7 @@
import { createLabeledInput, createSpanName } from "../utils/dom.js";
import { stringToId } from "../utils/format.js";
-/**
- * @param {Signals} signals
- */
-export function createLegend(signals) {
+export function createLegend() {
const element = window.document.createElement("legend");
/** @type {AnySeries | null} */
@@ -60,15 +57,11 @@ export function createLegend(signals) {
title: "Click to toggle",
inputChecked: series.active(),
onClick: () => {
- series.active.set(input.checked);
+ series.setActive(input.checked);
},
type: "checkbox",
});
-
- // Sync checkbox with signal (for shared signals across panes)
- signals.createEffect(series.active, (active) => {
- input.checked = active;
- });
+ input.dataset.series = series.key;
const spanMain = window.document.createElement("span");
spanMain.classList.add("main");
@@ -94,17 +87,14 @@ export function createLegend(signals) {
});
seriesColorSpans.set(series, colorSpans);
- const anchor = window.document.createElement("a");
-
- signals.createEffect(series.url, (url) => {
- if (url) {
- anchor.href = url;
- anchor.target = "_blank";
- anchor.rel = "noopener noreferrer";
- anchor.title = "Click to view data";
- div.append(anchor);
- }
- });
+ if (series.url) {
+ const anchor = window.document.createElement("a");
+ anchor.href = series.url;
+ anchor.target = "_blank";
+ anchor.rel = "noopener noreferrer";
+ anchor.title = "Click to view data";
+ div.append(anchor);
+ }
},
/**
* @param {number} start
diff --git a/website/scripts/entry.js b/website/scripts/entry.js
index f5b0cd172..2badf2fc4 100644
--- a/website/scripts/entry.js
+++ b/website/scripts/entry.js
@@ -18,7 +18,6 @@
*
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupBasic, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint } from "./options/partial.js"
*
- * @import { line as LineSeriesFn, dots as DotsSeriesFn, candlestick as CandlestickSeriesFn, baseline as BaselineSeriesFn, histogram as HistogramSeriesFn } from "./options/series.js"
*
* @import { UnitObject as Unit } from "./utils/units.js"
*
diff --git a/website/scripts/main.js b/website/scripts/main.js
index a01129ed4..626958f3d 100644
--- a/website/scripts/main.js
+++ b/website/scripts/main.js
@@ -14,6 +14,7 @@ import { init as initSimulation } from "./panes/_simulation.js";
import { next } from "./utils/timing.js";
import { replaceHistory } from "./utils/url.js";
import { removeStored, writeToStorage } from "./utils/storage.js";
+import { dark } from "./utils/theme.js";
import {
asideElement,
asideLabelElement,
@@ -121,18 +122,6 @@ signals.createRoot(() => {
console.log(`VERSION = ${brk.VERSION}`);
- function initDark() {
- const preferredColorSchemeMatchMedia = window.matchMedia(
- "(prefers-color-scheme: dark)",
- );
- const dark = signals.createSignal(preferredColorSchemeMatchMedia.matches);
- preferredColorSchemeMatchMedia.addEventListener("change", ({ matches }) => {
- dark.set(matches);
- });
- return dark;
- }
- const dark = initDark();
-
const qrcode = signals.createSignal(/** @type {string | null} */ (null));
signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
@@ -552,6 +541,7 @@ signals.createRoot(() => {
qrcode.set(window.location.href);
});
+
shareDiv.addEventListener("click", () => {
qrcode.set(null);
});
diff --git a/website/scripts/options/chain.js b/website/scripts/options/chain.js
index 67a60b9ec..c9e1df214 100644
--- a/website/scripts/options/chain.js
+++ b/website/scripts/options/chain.js
@@ -1,6 +1,7 @@
/** Chain section builder - typed tree-based patterns */
import { Unit } from "../utils/units.js";
+import { line, baseline, dots } from "./series.js";
import { satsBtcUsd } from "./shared.js";
/**
@@ -12,9 +13,6 @@ export function createChainSection(ctx) {
const {
colors,
brk,
- line,
- baseline,
- dots,
createPriceLine,
fromSizePattern,
fromFullnessPattern,
@@ -240,7 +238,7 @@ export function createChainSection(ctx) {
name: "Volume",
title: "Transaction Volume",
bottom: [
- ...satsBtcUsd(ctx, transactions.volume.sentSum, "Sent"),
+ ...satsBtcUsd( transactions.volume.sentSum, "Sent"),
line({
metric: transactions.volume.annualizedVolume.sats,
name: "annualized",
diff --git a/website/scripts/options/cohorts/address.js b/website/scripts/options/cohorts/address.js
index e79f18c8d..eba3b37ab 100644
--- a/website/scripts/options/cohorts/address.js
+++ b/website/scripts/options/cohorts/address.js
@@ -5,6 +5,7 @@
*/
import { Unit } from "../../utils/units.js";
+import { line, baseline } from "../series.js";
import {
createSingleSupplySeries,
createGroupedSupplyTotalSeries,
@@ -54,12 +55,12 @@ export function createAddressCohortFolder(ctx, args) {
{
name: "in profit",
title: `Supply In Profit ${title}`,
- bottom: createGroupedSupplyInProfitSeries(ctx, list),
+ bottom: createGroupedSupplyInProfitSeries(list),
},
{
name: "in loss",
title: `Supply In Loss ${title}`,
- bottom: createGroupedSupplyInLossSeries(ctx, list),
+ bottom: createGroupedSupplyInLossSeries(list),
},
],
},
@@ -68,7 +69,7 @@ export function createAddressCohortFolder(ctx, args) {
{
name: "utxo count",
title: `UTXO Count ${title}`,
- bottom: createUtxoCountSeries(ctx, list, useGroupName),
+ bottom: createUtxoCountSeries(list, useGroupName),
},
// Address count (ADDRESS COHORTS ONLY - fully type safe!)
@@ -87,7 +88,7 @@ export function createAddressCohortFolder(ctx, args) {
{
name: "Price",
title: `Realized Price ${title}`,
- top: createRealizedPriceSeries(ctx, list),
+ top: createRealizedPriceSeries(list),
},
{
name: "Ratio",
@@ -96,7 +97,6 @@ export function createAddressCohortFolder(ctx, args) {
},
]
: createRealizedPriceOptions(
- ctx,
/** @type {AddressCohortObject} */ (args),
title,
)),
@@ -119,24 +119,22 @@ export function createAddressCohortFolder(ctx, args) {
...createUnrealizedSection(ctx, list, useGroupName, title),
// Cost basis section (no percentiles for address cohorts)
- ...createCostBasisSection(ctx, list, useGroupName, title),
+ ...createCostBasisSection(list, useGroupName, title),
// Activity section
- ...createActivitySection(ctx, list, useGroupName, title),
+ ...createActivitySection(list, useGroupName, title),
],
};
}
/**
* Create realized price options for single cohort
- * @param {PartialContext} ctx
* @param {AddressCohortObject} args
* @param {string} title
* @returns {PartialOptionsTree}
*/
-function createRealizedPriceOptions(ctx, args, title) {
- const { line } = ctx;
- const { tree, color } = args;
+function createRealizedPriceOptions(args, title) {
+ const { tree, color } = args;
return [
{
@@ -163,7 +161,7 @@ function createRealizedPriceOptions(ctx, args, title) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function createRealizedCapWithExtras(ctx, list, args, useGroupName) {
- const { line, baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
const isSingle = !("list" in args);
return list.flatMap(({ color, name, tree }) => [
@@ -196,7 +194,7 @@ function createRealizedCapWithExtras(ctx, list, args, useGroupName) {
* @returns {PartialOptionsTree}
*/
function createRealizedPnlSection(ctx, args, title) {
- const { colors, line } = ctx;
+ const { colors } = ctx;
const { realized } = args.tree;
return [
@@ -251,7 +249,7 @@ function createRealizedPnlSection(ctx, args, title) {
* @returns {PartialOptionsTree}
*/
function createUnrealizedSection(ctx, list, useGroupName, title) {
- const { colors, line, baseline } = ctx;
+ const { colors } = ctx;
return [
{
@@ -301,15 +299,12 @@ function createUnrealizedSection(ctx, list, useGroupName, title) {
/**
* Create cost basis section (no percentiles for address cohorts)
- * @param {PartialContext} ctx
* @param {readonly AddressCohortObject[]} list
* @param {boolean} useGroupName
* @param {string} title
* @returns {PartialOptionsTree}
*/
-function createCostBasisSection(ctx, list, useGroupName, title) {
- const { line } = ctx;
-
+function createCostBasisSection(list, useGroupName, title) {
return [
{
name: "Cost Basis",
@@ -345,15 +340,12 @@ function createCostBasisSection(ctx, list, useGroupName, title) {
/**
* Create activity section
- * @param {PartialContext} ctx
* @param {readonly AddressCohortObject[]} list
* @param {boolean} useGroupName
* @param {string} title
* @returns {PartialOptionsTree}
*/
-function createActivitySection(ctx, list, useGroupName, title) {
- const { line } = ctx;
-
+function createActivitySection(list, useGroupName, title) {
return [
{
name: "Activity",
diff --git a/website/scripts/options/cohorts/shared.js b/website/scripts/options/cohorts/shared.js
index f8da67d69..2b92d0303 100644
--- a/website/scripts/options/cohorts/shared.js
+++ b/website/scripts/options/cohorts/shared.js
@@ -1,6 +1,7 @@
/** Shared cohort chart section builders */
import { Unit } from "../../utils/units.js";
+import { line } from "../series.js";
import { satsBtcUsd } from "../shared.js";
/**
@@ -10,11 +11,11 @@ import { satsBtcUsd } from "../shared.js";
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createSingleSupplySeries(ctx, cohort) {
- const { colors, line, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const { tree } = cohort;
return [
- ...satsBtcUsd(ctx, tree.supply.total, "Supply", colors.default),
+ ...satsBtcUsd( tree.supply.total, "Supply", colors.default),
...("supplyRelToCirculatingSupply" in tree.relative
? [
line({
@@ -25,9 +26,9 @@ export function createSingleSupplySeries(ctx, cohort) {
}),
]
: []),
- ...satsBtcUsd(ctx, tree.unrealized.supplyInProfit, "In Profit", colors.green),
- ...satsBtcUsd(ctx, tree.unrealized.supplyInLoss, "In Loss", colors.red),
- ...satsBtcUsd(ctx, tree.supply.halved, "half", colors.gray).map((s) => ({
+ ...satsBtcUsd( tree.unrealized.supplyInProfit, "In Profit", colors.green),
+ ...satsBtcUsd( tree.unrealized.supplyInLoss, "In Loss", colors.red),
+ ...satsBtcUsd( tree.supply.halved, "half", colors.gray).map((s) => ({
...s,
options: { lineStyle: 4 },
})),
@@ -76,11 +77,11 @@ export function createSingleSupplySeries(ctx, cohort) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSupplyTotalSeries(ctx, list) {
- const { line, brk } = ctx;
+ const { brk } = ctx;
const constant100 = brk.metrics.constants.constant100;
return list.flatMap(({ color, name, tree }) => [
- ...satsBtcUsd(ctx, tree.supply.total, name, color),
+ ...satsBtcUsd( tree.supply.total, name, color),
line({
metric:
"supplyRelToCirculatingSupply" in tree.relative
@@ -95,15 +96,13 @@ export function createGroupedSupplyTotalSeries(ctx, list) {
/**
* Create supply in profit series for grouped cohorts
- * @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createGroupedSupplyInProfitSeries(ctx, list) {
- const { line } = ctx;
-
+export function createGroupedSupplyInProfitSeries(list) {
+
return list.flatMap(({ color, name, tree }) => [
- ...satsBtcUsd(ctx, tree.unrealized.supplyInProfit, name, color),
+ ...satsBtcUsd( tree.unrealized.supplyInProfit, name, color),
...("supplyInProfitRelToCirculatingSupply" in tree.relative
? [
line({
@@ -119,15 +118,13 @@ export function createGroupedSupplyInProfitSeries(ctx, list) {
/**
* Create supply in loss series for grouped cohorts
- * @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createGroupedSupplyInLossSeries(ctx, list) {
- const { line } = ctx;
-
+export function createGroupedSupplyInLossSeries(list) {
+
return list.flatMap(({ color, name, tree }) => [
- ...satsBtcUsd(ctx, tree.unrealized.supplyInLoss, name, color),
+ ...satsBtcUsd( tree.unrealized.supplyInLoss, name, color),
...("supplyInLossRelToCirculatingSupply" in tree.relative
? [
line({
@@ -143,14 +140,11 @@ export function createGroupedSupplyInLossSeries(ctx, list) {
/**
* Create UTXO count series
- * @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createUtxoCountSeries(ctx, list, useGroupName) {
- const { line } = ctx;
-
+export function createUtxoCountSeries(list, useGroupName) {
return list.flatMap(({ color, name, tree }) => [
line({
metric: tree.outputs.utxoCount,
@@ -169,7 +163,7 @@ export function createUtxoCountSeries(ctx, list, useGroupName) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createAddressCountSeries(ctx, list, useGroupName) {
- const { line, colors } = ctx;
+ const { colors } = ctx;
return list.flatMap(({ color, name, tree }) => [
line({
@@ -183,13 +177,10 @@ export function createAddressCountSeries(ctx, list, useGroupName) {
/**
* Create realized price series for grouped cohorts
- * @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createRealizedPriceSeries(ctx, list) {
- const { line } = ctx;
-
+export function createRealizedPriceSeries(list) {
return list.map(({ color, name, tree }) =>
line({ metric: tree.realized.realizedPrice, name, color, unit: Unit.usd }),
);
@@ -202,7 +193,7 @@ export function createRealizedPriceSeries(ctx, list) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createRealizedPriceRatioSeries(ctx, list) {
- const { line, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return [
...list.map(({ color, name, tree }) =>
@@ -219,14 +210,11 @@ export function createRealizedPriceRatioSeries(ctx, list) {
/**
* Create realized capitalization series
- * @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createRealizedCapSeries(ctx, list, useGroupName) {
- const { line } = ctx;
-
+export function createRealizedCapSeries(list, useGroupName) {
return list.flatMap(({ color, name, tree }) => [
line({
metric: tree.realized.realizedCap,
@@ -239,14 +227,11 @@ export function createRealizedCapSeries(ctx, list, useGroupName) {
/**
* Create cost basis min/max series (available on all cohorts)
- * @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createCostBasisMinMaxSeries(ctx, list, useGroupName) {
- const { line } = ctx;
-
+export function createCostBasisMinMaxSeries(list, useGroupName) {
return list.flatMap(({ color, name, tree }) => [
line({
metric: tree.costBasis.min,
@@ -265,14 +250,11 @@ export function createCostBasisMinMaxSeries(ctx, list, useGroupName) {
/**
* Create cost basis percentile series (only for cohorts with CostBasisPattern2)
- * @param {PartialContext} ctx
* @param {readonly CohortWithCostBasisPercentiles[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-export function createCostBasisPercentilesSeries(ctx, list, useGroupName) {
- const { line } = ctx;
-
+export function createCostBasisPercentilesSeries(list, useGroupName) {
return list.flatMap(({ color, name, tree }) => {
const percentiles = tree.costBasis.percentiles;
return [
diff --git a/website/scripts/options/cohorts/utxo.js b/website/scripts/options/cohorts/utxo.js
index 36e5c8491..393101c74 100644
--- a/website/scripts/options/cohorts/utxo.js
+++ b/website/scripts/options/cohorts/utxo.js
@@ -34,6 +34,7 @@ import {
createCostBasisPercentilesSeries,
} from "./shared.js";
import { Unit } from "../../utils/units.js";
+import { line, baseline } from "../series.js";
// ============================================================================
// Folder Builders (4 variants based on pattern capabilities)
@@ -51,10 +52,10 @@ export function createCohortFolderAll(ctx, args) {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
- createSingleUtxoCountChart(ctx, args, title),
+ createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithAdjusted(ctx, args, title),
createSingleUnrealizedSectionAll(ctx, args, title),
- createSingleCostBasisSectionWithPercentiles(ctx, args, title),
+ createSingleCostBasisSectionWithPercentiles(args, title),
...createSingleActivitySectionWithAdjusted(ctx, args, title),
],
};
@@ -74,11 +75,11 @@ export function createCohortFolderFull(ctx, args) {
name: args.name || "all",
tree: [
createGroupedSupplySection(ctx, list, title),
- createGroupedUtxoCountChart(ctx, list, title),
+ createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionWithAdjusted(ctx, list, title),
createGroupedUnrealizedSectionFull(ctx, list, title),
- createGroupedCostBasisSectionWithPercentiles(ctx, list, title),
- ...createGroupedActivitySectionWithAdjusted(ctx, list, title),
+ createGroupedCostBasisSectionWithPercentiles(list, title),
+ ...createGroupedActivitySectionWithAdjusted(list, title),
],
};
}
@@ -87,10 +88,10 @@ export function createCohortFolderFull(ctx, args) {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
- createSingleUtxoCountChart(ctx, args, title),
+ createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithAdjusted(ctx, args, title),
createSingleUnrealizedSectionFull(ctx, args, title),
- createSingleCostBasisSectionWithPercentiles(ctx, args, title),
+ createSingleCostBasisSectionWithPercentiles(args, title),
...createSingleActivitySectionWithAdjusted(ctx, args, title),
],
};
@@ -110,11 +111,11 @@ export function createCohortFolderWithAdjusted(ctx, args) {
name: args.name || "all",
tree: [
createGroupedSupplySection(ctx, list, title),
- createGroupedUtxoCountChart(ctx, list, title),
+ createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionWithAdjusted(ctx, list, title),
createGroupedUnrealizedSectionWithMarketCap(ctx, list, title),
- createGroupedCostBasisSection(ctx, list, title),
- ...createGroupedActivitySectionWithAdjusted(ctx, list, title),
+ createGroupedCostBasisSection(list, title),
+ ...createGroupedActivitySectionWithAdjusted(list, title),
],
};
}
@@ -123,10 +124,10 @@ export function createCohortFolderWithAdjusted(ctx, args) {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
- createSingleUtxoCountChart(ctx, args, title),
+ createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithAdjusted(ctx, args, title),
createSingleUnrealizedSectionWithMarketCap(ctx, args, title),
- createSingleCostBasisSection(ctx, args, title),
+ createSingleCostBasisSection(args, title),
...createSingleActivitySectionWithAdjusted(ctx, args, title),
],
};
@@ -146,11 +147,11 @@ export function createCohortFolderWithPercentiles(ctx, args) {
name: args.name || "all",
tree: [
createGroupedSupplySection(ctx, list, title),
- createGroupedUtxoCountChart(ctx, list, title),
+ createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionBasic(ctx, list, title),
createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title),
- createGroupedCostBasisSectionWithPercentiles(ctx, list, title),
- ...createGroupedActivitySectionBasic(ctx, list, title),
+ createGroupedCostBasisSectionWithPercentiles(list, title),
+ ...createGroupedActivitySectionBasic(list, title),
],
};
}
@@ -159,10 +160,10 @@ export function createCohortFolderWithPercentiles(ctx, args) {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
- createSingleUtxoCountChart(ctx, args, title),
+ createSingleUtxoCountChart(args, title),
createSingleRealizedSectionBasic(ctx, args, title),
createSingleUnrealizedSectionWithOwnCaps(ctx, args, title),
- createSingleCostBasisSectionWithPercentiles(ctx, args, title),
+ createSingleCostBasisSectionWithPercentiles(args, title),
...createSingleActivitySectionBasic(ctx, args, title),
],
};
@@ -182,11 +183,11 @@ export function createCohortFolderBasic(ctx, args) {
name: args.name || "all",
tree: [
createGroupedSupplySection(ctx, list, title),
- createGroupedUtxoCountChart(ctx, list, title),
+ createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionBasic(ctx, list, title),
createGroupedUnrealizedSectionBase(ctx, list, title),
- createGroupedCostBasisSection(ctx, list, title),
- ...createGroupedActivitySectionBasic(ctx, list, title),
+ createGroupedCostBasisSection(list, title),
+ ...createGroupedActivitySectionBasic(list, title),
],
};
}
@@ -195,10 +196,10 @@ export function createCohortFolderBasic(ctx, args) {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
- createSingleUtxoCountChart(ctx, args, title),
+ createSingleUtxoCountChart(args, title),
createSingleRealizedSectionBasic(ctx, args, title),
createSingleUnrealizedSectionBase(ctx, args, title),
- createSingleCostBasisSection(ctx, args, title),
+ createSingleCostBasisSection(args, title),
...createSingleActivitySectionBasic(ctx, args, title),
],
};
@@ -238,12 +239,12 @@ function createGroupedSupplySection(ctx, list, title) {
{
name: "in profit",
title: `Supply In Profit ${title}`,
- bottom: createGroupedSupplyInProfitSeries(ctx, list),
+ bottom: createGroupedSupplyInProfitSeries(list),
},
{
name: "in loss",
title: `Supply In Loss ${title}`,
- bottom: createGroupedSupplyInLossSeries(ctx, list),
+ bottom: createGroupedSupplyInLossSeries(list),
},
],
};
@@ -251,31 +252,29 @@ function createGroupedSupplySection(ctx, list, title) {
/**
* Create UTXO count chart for single cohort
- * @param {PartialContext} ctx
* @param {UtxoCohortObject} cohort
* @param {string} title
* @returns {PartialChartOption}
*/
-function createSingleUtxoCountChart(ctx, cohort, title) {
+function createSingleUtxoCountChart(cohort, title) {
return {
name: "utxo count",
title: `UTXO Count ${title}`,
- bottom: createUtxoCountSeries(ctx, [cohort], false),
+ bottom: createUtxoCountSeries( [cohort], false),
};
}
/**
* Create UTXO count chart for grouped cohorts
- * @param {PartialContext} ctx
* @param {readonly UtxoCohortObject[]} list
* @param {string} title
* @returns {PartialChartOption}
*/
-function createGroupedUtxoCountChart(ctx, list, title) {
+function createGroupedUtxoCountChart(list, title) {
return {
name: "utxo count",
title: `UTXO Count ${title}`,
- bottom: createUtxoCountSeries(ctx, list, true),
+ bottom: createUtxoCountSeries( list, true),
};
}
@@ -290,7 +289,7 @@ function createSingleRealizedSectionWithAdjusted(ctx, cohort, title) {
return {
name: "Realized",
tree: [
- createSingleRealizedPriceChart(ctx, cohort, title),
+ createSingleRealizedPriceChart(cohort, title),
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
@@ -316,7 +315,7 @@ function createGroupedRealizedSectionWithAdjusted(ctx, list, title) {
{
name: "Price",
title: `Realized Price ${title}`,
- top: createRealizedPriceSeries(ctx, list),
+ top: createRealizedPriceSeries(list),
},
{
name: "Ratio",
@@ -326,7 +325,7 @@ function createGroupedRealizedSectionWithAdjusted(ctx, list, title) {
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
- bottom: createGroupedRealizedCapSeries(ctx, list),
+ bottom: createGroupedRealizedCapSeries(list),
},
...createGroupedRealizedPnlSections(ctx, list, title),
createGroupedSoprSectionWithAdjusted(ctx, list, title),
@@ -345,7 +344,7 @@ function createSingleRealizedSectionBasic(ctx, cohort, title) {
return {
name: "Realized",
tree: [
- createSingleRealizedPriceChart(ctx, cohort, title),
+ createSingleRealizedPriceChart(cohort, title),
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
@@ -371,7 +370,7 @@ function createGroupedRealizedSectionBasic(ctx, list, title) {
{
name: "Price",
title: `Realized Price ${title}`,
- top: createRealizedPriceSeries(ctx, list),
+ top: createRealizedPriceSeries(list),
},
{
name: "Ratio",
@@ -381,7 +380,7 @@ function createGroupedRealizedSectionBasic(ctx, list, title) {
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
- bottom: createGroupedRealizedCapSeries(ctx, list),
+ bottom: createGroupedRealizedCapSeries(list),
},
...createGroupedRealizedPnlSections(ctx, list, title),
createGroupedSoprSectionBasic(ctx, list, title),
@@ -391,14 +390,12 @@ function createGroupedRealizedSectionBasic(ctx, list, title) {
/**
* Create realized price chart for single cohort
- * @param {PartialContext} ctx
* @param {UtxoCohortObject} cohort
* @param {string} title
* @returns {PartialChartOption}
*/
-function createSingleRealizedPriceChart(ctx, cohort, title) {
- const { line } = ctx;
- const { tree, color } = cohort;
+function createSingleRealizedPriceChart(cohort, title) {
+ const { tree, color } = cohort;
return {
name: "price",
@@ -421,7 +418,7 @@ function createSingleRealizedPriceChart(ctx, cohort, title) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function createSingleRealizedCapSeries(ctx, cohort) {
- const { colors, line, baseline, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const { color, tree } = cohort;
return [
@@ -459,13 +456,10 @@ function createSingleRealizedCapSeries(ctx, cohort) {
/**
* Create realized cap series for grouped cohorts
- * @param {PartialContext} ctx
* @param {readonly UtxoCohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
-function createGroupedRealizedCapSeries(ctx, list) {
- const { line } = ctx;
-
+function createGroupedRealizedCapSeries(list) {
return list.map(({ color, name, tree }) =>
line({
metric: tree.realized.realizedCap,
@@ -486,8 +480,6 @@ function createGroupedRealizedCapSeries(ctx, list) {
function createSingleRealizedPnlSection(ctx, cohort, title) {
const {
colors,
- line,
- baseline,
createPriceLine,
fromBlockCountWithUnit,
fromBitcoinPatternWithUnit,
@@ -598,7 +590,7 @@ function createSingleRealizedPnlSection(ctx, cohort, title) {
* @returns {PartialOptionsTree}
*/
function createGroupedRealizedPnlSections(ctx, list, title) {
- const { line, baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return [
{
@@ -795,7 +787,7 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
* @returns {PartialChartOption}
*/
function createSingleBaseSoprChart(ctx, cohort, title) {
- const { colors, baseline, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const { tree } = cohort;
return {
@@ -837,7 +829,7 @@ function createSingleBaseSoprChart(ctx, cohort, title) {
* @returns {PartialChartOption}
*/
function createSingleAdjustedSoprChart(ctx, cohort, title) {
- const { colors, baseline, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const { tree } = cohort;
return {
@@ -880,7 +872,7 @@ function createSingleAdjustedSoprChart(ctx, cohort, title) {
* @returns {PartialChartOption}
*/
function createGroupedBaseSoprChart(ctx, list, title) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return {
name: "Normal",
@@ -924,7 +916,7 @@ function createGroupedBaseSoprChart(ctx, list, title) {
* @returns {PartialChartOption}
*/
function createGroupedAdjustedSoprChart(ctx, list, title) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return {
name: "Adjusted",
@@ -1035,7 +1027,7 @@ function createGroupedSoprSectionBasic(ctx, list, title) {
* @param {RelativeWithMarketCap} rel
*/
function createUnrealizedPnlRelToMarketCapMetrics(ctx, rel) {
- const { colors, line } = ctx;
+ const { colors } = ctx;
return [
line({
metric: rel.unrealizedProfitRelToMarketCap,
@@ -1064,7 +1056,7 @@ function createUnrealizedPnlRelToMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnMarketCap} rel
*/
function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
- const { colors, line, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
return [
line({
metric: rel.unrealizedProfitRelToOwnMarketCap,
@@ -1095,7 +1087,7 @@ function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnPnl} rel
*/
function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
- const { colors, line, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
return [
line({
metric: rel.unrealizedProfitRelToOwnTotalUnrealizedPnl,
@@ -1122,12 +1114,10 @@ function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
}
/**
- * @param {PartialContext} ctx
* @param {RelativeWithMarketCap} rel
*/
-function createNetUnrealizedPnlRelToMarketCapMetrics(ctx, rel) {
- const { baseline } = ctx;
- return [
+function createNetUnrealizedPnlRelToMarketCapMetrics(rel) {
+ return [
baseline({
metric: rel.netUnrealizedPnlRelToMarketCap,
name: "Net",
@@ -1141,7 +1131,7 @@ function createNetUnrealizedPnlRelToMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnMarketCap} rel
*/
function createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return [
baseline({
metric: rel.netUnrealizedPnlRelToOwnMarketCap,
@@ -1157,7 +1147,7 @@ function createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnPnl} rel
*/
function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return [
baseline({
metric: rel.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
@@ -1174,7 +1164,7 @@ function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
* @param {{ unrealized: { totalUnrealizedPnl: AnyMetricPattern, unrealizedProfit: AnyMetricPattern, unrealizedLoss: AnyMetricPattern, negUnrealizedLoss: AnyMetricPattern } }} tree
*/
function createUnrealizedPnlBaseMetrics(ctx, tree) {
- const { colors, line } = ctx;
+ const { colors } = ctx;
return [
line({
metric: tree.unrealized.totalUnrealizedPnl,
@@ -1206,12 +1196,10 @@ function createUnrealizedPnlBaseMetrics(ctx, tree) {
/**
* Base net unrealized metric (always present)
- * @param {PartialContext} ctx
* @param {{ unrealized: { netUnrealizedPnl: AnyMetricPattern } }} tree
*/
-function createNetUnrealizedPnlBaseMetric(ctx, tree) {
- const { baseline } = ctx;
- return baseline({
+function createNetUnrealizedPnlBaseMetric(tree) {
+ return baseline({
metric: tree.unrealized.netUnrealizedPnl,
name: "Net",
unit: Unit.ratio,
@@ -1249,7 +1237,7 @@ function createSingleUnrealizedSectionAll(ctx, cohort, title) {
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
bottom: [
- createNetUnrealizedPnlBaseMetric(ctx, tree),
+ createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd }),
],
@@ -1287,8 +1275,8 @@ function createSingleUnrealizedSectionFull(ctx, cohort, title) {
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
bottom: [
- createNetUnrealizedPnlBaseMetric(ctx, tree),
- ...createNetUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
+ createNetUnrealizedPnlBaseMetric(tree),
+ ...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd }),
@@ -1326,8 +1314,8 @@ function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) {
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
bottom: [
- createNetUnrealizedPnlBaseMetric(ctx, tree),
- ...createNetUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
+ createNetUnrealizedPnlBaseMetric(tree),
+ ...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctMcap }),
],
@@ -1364,7 +1352,7 @@ function createSingleUnrealizedSectionWithOwnCaps(ctx, cohort, title) {
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
bottom: [
- createNetUnrealizedPnlBaseMetric(ctx, tree),
+ createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd }),
@@ -1400,7 +1388,7 @@ function createSingleUnrealizedSectionBase(ctx, cohort, title) {
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
bottom: [
- createNetUnrealizedPnlBaseMetric(ctx, tree),
+ createNetUnrealizedPnlBaseMetric(tree),
createPriceLine({ unit: Unit.usd }),
],
},
@@ -1410,13 +1398,11 @@ function createSingleUnrealizedSectionBase(ctx, cohort, title) {
/**
* Grouped unrealized base charts (profit, loss, total pnl)
- * @param {PartialContext} ctx
* @param {readonly { color: Color, name: string, tree: { unrealized: PatternAll["unrealized"] } }[]} list
* @param {string} title
*/
-function createGroupedUnrealizedBaseCharts(ctx, list, title) {
- const { line } = ctx;
- return [
+function createGroupedUnrealizedBaseCharts(list, title) {
+ return [
{
name: "profit",
title: `Unrealized Profit ${title}`,
@@ -1464,11 +1450,11 @@ function createGroupedUnrealizedBaseCharts(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionFull(ctx, list, title) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
- ...createGroupedUnrealizedBaseCharts(ctx, list, title),
+ ...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
@@ -1517,11 +1503,11 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
- ...createGroupedUnrealizedBaseCharts(ctx, list, title),
+ ...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
@@ -1556,11 +1542,11 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
- ...createGroupedUnrealizedBaseCharts(ctx, list, title),
+ ...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
@@ -1602,11 +1588,11 @@ function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionBase(ctx, list, title) {
- const { baseline, createPriceLine } = ctx;
+ const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
- ...createGroupedUnrealizedBaseCharts(ctx, list, title),
+ ...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
@@ -1628,14 +1614,12 @@ function createGroupedUnrealizedSectionBase(ctx, list, title) {
/**
* Create cost basis section for single cohort WITH percentiles
- * @param {PartialContext} ctx
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
* @param {string} title
* @returns {PartialOptionsGroup}
*/
-function createSingleCostBasisSectionWithPercentiles(ctx, cohort, title) {
- const { line } = ctx;
- const { color, tree } = cohort;
+function createSingleCostBasisSectionWithPercentiles(cohort, title) {
+ const { color, tree } = cohort;
return {
name: "Cost Basis",
@@ -1668,7 +1652,7 @@ function createSingleCostBasisSectionWithPercentiles(ctx, cohort, title) {
{
name: "percentiles",
title: `Cost Basis Percentiles ${title}`,
- top: createCostBasisPercentilesSeries(ctx, [cohort], false),
+ top: createCostBasisPercentilesSeries( [cohort], false),
},
],
};
@@ -1676,14 +1660,11 @@ function createSingleCostBasisSectionWithPercentiles(ctx, cohort, title) {
/**
* Create cost basis section for grouped cohorts WITH percentiles
- * @param {PartialContext} ctx
* @param {readonly (CohortFull | CohortWithPercentiles)[]} list
* @param {string} title
* @returns {PartialOptionsGroup}
*/
-function createGroupedCostBasisSectionWithPercentiles(ctx, list, title) {
- const { line } = ctx;
-
+function createGroupedCostBasisSectionWithPercentiles(list, title) {
return {
name: "Cost Basis",
tree: [
@@ -1719,14 +1700,12 @@ function createGroupedCostBasisSectionWithPercentiles(ctx, list, title) {
/**
* Create cost basis section for single cohort (no percentiles)
- * @param {PartialContext} ctx
* @param {CohortWithAdjusted | CohortBasic} cohort
* @param {string} title
* @returns {PartialOptionsGroup}
*/
-function createSingleCostBasisSection(ctx, cohort, title) {
- const { line } = ctx;
- const { color, tree } = cohort;
+function createSingleCostBasisSection(cohort, title) {
+ const { color, tree } = cohort;
return {
name: "Cost Basis",
@@ -1762,14 +1741,11 @@ function createSingleCostBasisSection(ctx, cohort, title) {
/**
* Create cost basis section for grouped cohorts (no percentiles)
- * @param {PartialContext} ctx
* @param {readonly (CohortWithAdjusted | CohortBasic)[]} list
* @param {string} title
* @returns {PartialOptionsGroup}
*/
-function createGroupedCostBasisSection(ctx, list, title) {
- const { line } = ctx;
-
+function createGroupedCostBasisSection(list, title) {
return {
name: "Cost Basis",
tree: [
@@ -1811,7 +1787,7 @@ function createGroupedCostBasisSection(ctx, list, title) {
* @returns {PartialOptionsTree}
*/
function createSingleActivitySectionWithAdjusted(ctx, cohort, title) {
- const { colors, line } = ctx;
+ const { colors } = ctx;
const { tree, color } = cohort;
return [
@@ -1925,7 +1901,7 @@ function createSingleActivitySectionWithAdjusted(ctx, cohort, title) {
* @returns {PartialOptionsTree}
*/
function createSingleActivitySectionBasic(ctx, cohort, title) {
- const { colors, line } = ctx;
+ const { colors } = ctx;
const { tree, color } = cohort;
return [
@@ -2021,14 +1997,11 @@ function createSingleActivitySectionBasic(ctx, cohort, title) {
/**
* Create activity section for grouped cohorts with adjusted values (for cohorts with RealizedPattern3/4)
- * @param {PartialContext} ctx
* @param {readonly (CohortFull | CohortWithAdjusted)[]} list
* @param {string} title
* @returns {PartialOptionsTree}
*/
-function createGroupedActivitySectionWithAdjusted(ctx, list, title) {
- const { line } = ctx;
-
+function createGroupedActivitySectionWithAdjusted(list, title) {
return [
{
name: "Sell Side Risk",
@@ -2151,14 +2124,11 @@ function createGroupedActivitySectionWithAdjusted(ctx, list, title) {
/**
* Create activity section for grouped cohorts without adjusted values (for cohorts with RealizedPattern/2)
- * @param {PartialContext} ctx
* @param {readonly (CohortWithPercentiles | CohortBasic)[]} list
* @param {string} title
* @returns {PartialOptionsTree}
*/
-function createGroupedActivitySectionBasic(ctx, list, title) {
- const { line } = ctx;
-
+function createGroupedActivitySectionBasic(list, title) {
return [
{
name: "Sell Side Risk",
diff --git a/website/scripts/options/cointime.js b/website/scripts/options/cointime.js
index 0b84dca70..675b39f52 100644
--- a/website/scripts/options/cointime.js
+++ b/website/scripts/options/cointime.js
@@ -1,6 +1,7 @@
/** Cointime section builder - typed tree-based patterns */
import { Unit } from "../utils/units.js";
+import { line, baseline } from "./series.js";
import {
satsBtcUsd,
priceLines,
@@ -25,7 +26,7 @@ function createCointimePriceWithRatioOptions(
ctx,
{ title, legend, price, ratio, color },
) {
- const { line, colors, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const pctUsdMap = percentileUsdMap(colors, ratio);
const pctMap = percentileMap(colors, ratio);
@@ -54,42 +55,53 @@ function createCointimePriceWithRatioOptions(
),
],
bottom: [
- line({ metric: ratio.ratio, name: "Ratio", color, unit: Unit.ratio }),
+ baseline({
+ metric: ratio.ratio,
+ name: "Ratio",
+ color,
+ unit: Unit.ratio,
+ }),
line({
metric: ratio.ratio1wSma,
name: "1w SMA",
color: colors.lime,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio1mSma,
name: "1m SMA",
color: colors.teal,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio1ySd.sma,
name: "1y SMA",
color: colors.sky,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio2ySd.sma,
name: "2y SMA",
color: colors.indigo,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio4ySd.sma,
name: "4y SMA",
color: colors.purple,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratioSd.sma,
name: "All SMA",
color: colors.rose,
unit: Unit.ratio,
+ defaultActive: false,
}),
...pctMap.map(({ name: pctName, prop, color: pctColor }) =>
line({
@@ -174,13 +186,14 @@ function createCointimePriceWithRatioOptions(
...sdPats.map(({ nameAddon, titleAddon, sd }) => ({
name: nameAddon,
title: `${title} ${titleAddon} Z-Score`,
- top: sdBands(colors, sd).map(({ name: bandName, prop, color: bandColor }) =>
- line({
- metric: prop,
- name: bandName,
- color: bandColor,
- unit: Unit.usd,
- }),
+ top: sdBands(colors, sd).map(
+ ({ name: bandName, prop, color: bandColor }) =>
+ line({
+ metric: prop,
+ name: bandName,
+ color: bandColor,
+ unit: Unit.usd,
+ }),
),
bottom: [
line({ metric: sd.zscore, name: "Z-Score", color, unit: Unit.sd }),
@@ -198,7 +211,7 @@ function createCointimePriceWithRatioOptions(
* @returns {PartialOptionsGroup}
*/
export function createCointimeSection(ctx) {
- const { colors, brk, line } = ctx;
+ const { colors, brk } = ctx;
const { cointime, distribution, supply } = brk.metrics;
const { pricing, cap, activity, supply: cointimeSupply, adjusted } = cointime;
const { all } = distribution.utxoCohorts;
@@ -348,9 +361,17 @@ export function createCointimeSection(ctx) {
name: "Supply",
title: "Cointime Supply",
bottom: [
- ...satsBtcUsd(ctx, all.supply.total, "All", colors.orange),
- ...satsBtcUsd(ctx, cointimeSupply.vaultedSupply, "Vaulted", colors.lime),
- ...satsBtcUsd(ctx, cointimeSupply.activeSupply, "Active", colors.rose),
+ ...satsBtcUsd( all.supply.total, "All", colors.orange),
+ ...satsBtcUsd(
+ cointimeSupply.vaultedSupply,
+ "Vaulted",
+ colors.lime,
+ ),
+ ...satsBtcUsd(
+ cointimeSupply.activeSupply,
+ "Active",
+ colors.rose,
+ ),
],
},
diff --git a/website/scripts/options/context.js b/website/scripts/options/context.js
index 011bfbf5a..d98b47048 100644
--- a/website/scripts/options/context.js
+++ b/website/scripts/options/context.js
@@ -1,9 +1,4 @@
import {
- line,
- dots,
- candlestick,
- baseline,
- histogram,
fromBlockCount,
fromBitcoin,
fromBlockSize,
@@ -37,12 +32,6 @@ export function createContext({ colors, brk }) {
colors,
brk,
- // Series helpers
- line,
- dots,
- candlestick,
- baseline,
- histogram,
fromBlockCount: (pattern, title, color) =>
fromBlockCount(colors, pattern, title, color),
fromBitcoin: (pattern, title, color) =>
diff --git a/website/scripts/options/market/averages.js b/website/scripts/options/market/averages.js
index f6029114f..d1b9da692 100644
--- a/website/scripts/options/market/averages.js
+++ b/website/scripts/options/market/averages.js
@@ -1,6 +1,7 @@
/** Moving averages section */
import { Unit } from "../../utils/units.js";
+import { line, baseline } from "../series.js";
import {
priceLines,
percentileUsdMap,
@@ -55,7 +56,7 @@ export function createPriceWithRatioOptions(
ctx,
{ title, legend, ratio, color },
) {
- const { line, colors, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const priceMetric = ratio.price;
const pctUsdMap = percentileUsdMap(colors, ratio);
@@ -85,42 +86,54 @@ export function createPriceWithRatioOptions(
),
],
bottom: [
- line({ metric: ratio.ratio, name: "Ratio", color, unit: Unit.ratio }),
+ baseline({
+ metric: ratio.ratio,
+ name: "Ratio",
+ base: 1,
+ color,
+ unit: Unit.ratio,
+ }),
line({
metric: ratio.ratio1wSma,
name: "1w SMA",
color: colors.lime,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio1mSma,
name: "1m SMA",
color: colors.teal,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio1ySd.sma,
name: "1y SMA",
color: colors.sky,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio2ySd.sma,
name: "2y SMA",
color: colors.indigo,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratio4ySd.sma,
name: "4y SMA",
color: colors.purple,
unit: Unit.ratio,
+ defaultActive: false,
}),
line({
metric: ratio.ratioSd.sma,
name: "All SMA",
color: colors.rose,
unit: Unit.ratio,
+ defaultActive: false,
}),
...pctMap.map(({ name: pctName, prop, color: pctColor }) =>
line({
@@ -140,13 +153,14 @@ export function createPriceWithRatioOptions(
tree: sdPats.map(({ nameAddon, titleAddon, sd }) => ({
name: nameAddon,
title: `${title} ${titleAddon} Z-Score`,
- top: sdBands(colors, sd).map(({ name: bandName, prop, color: bandColor }) =>
- line({
- metric: prop,
- name: bandName,
- color: bandColor,
- unit: Unit.usd,
- }),
+ top: sdBands(colors, sd).map(
+ ({ name: bandName, prop, color: bandColor }) =>
+ line({
+ metric: prop,
+ name: bandName,
+ color: bandColor,
+ unit: Unit.usd,
+ }),
),
bottom: [
line({ metric: sd.zscore, name: "Z-Score", color, unit: Unit.sd }),
@@ -163,8 +177,7 @@ export function createPriceWithRatioOptions(
* @param {ReturnType} averages
*/
export function createAveragesSection(ctx, averages) {
- const { line } = ctx;
-
+
return {
name: "Averages",
tree: [
diff --git a/website/scripts/options/market/index.js b/website/scripts/options/market/index.js
index fc3440552..921fccc6a 100644
--- a/website/scripts/options/market/index.js
+++ b/website/scripts/options/market/index.js
@@ -2,6 +2,7 @@
import { localhost } from "../../utils/env.js";
import { Unit } from "../../utils/units.js";
+import { line } from "../series.js";
import { buildAverages, createAveragesSection } from "./averages.js";
import { createPerformanceSection } from "./performance.js";
import { createIndicatorsSection } from "./indicators/index.js";
@@ -13,7 +14,7 @@ import { createInvestingSection } from "./investing.js";
* @returns {PartialOptionsGroup}
*/
export function createMarketSection(ctx) {
- const { colors, brk, line } = ctx;
+ const { colors, brk } = ctx;
const { market, supply, price } = brk.metrics;
const {
movingAverage,
diff --git a/website/scripts/options/market/indicators/bands.js b/website/scripts/options/market/indicators/bands.js
index 3a52bec57..21eeac599 100644
--- a/website/scripts/options/market/indicators/bands.js
+++ b/website/scripts/options/market/indicators/bands.js
@@ -1,6 +1,7 @@
/** Bands indicators (MinMax, Mayer Multiple) */
import { Unit } from "../../../utils/units.js";
+import { line } from "../../series.js";
/**
* Create Bands section
@@ -10,7 +11,7 @@ import { Unit } from "../../../utils/units.js";
* @param {Market["movingAverage"]} args.movingAverage
*/
export function createBandsSection(ctx, { range, movingAverage }) {
- const { line, colors } = ctx;
+ const { colors } = ctx;
return {
name: "Bands",
diff --git a/website/scripts/options/market/indicators/momentum.js b/website/scripts/options/market/indicators/momentum.js
index 87ae6c215..f032fc0df 100644
--- a/website/scripts/options/market/indicators/momentum.js
+++ b/website/scripts/options/market/indicators/momentum.js
@@ -1,6 +1,7 @@
/** Momentum indicators (RSI, StochRSI, Stochastic, MACD) */
import { Unit } from "../../../utils/units.js";
+import { line, histogram } from "../../series.js";
/**
* Create Momentum section
@@ -8,7 +9,7 @@ import { Unit } from "../../../utils/units.js";
* @param {Market["indicators"]} indicators
*/
export function createMomentumSection(ctx, indicators) {
- const { line, histogram, colors, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
return {
name: "Momentum",
diff --git a/website/scripts/options/market/indicators/onchain.js b/website/scripts/options/market/indicators/onchain.js
index b72d00319..52c4c1a5a 100644
--- a/website/scripts/options/market/indicators/onchain.js
+++ b/website/scripts/options/market/indicators/onchain.js
@@ -1,6 +1,7 @@
/** On-chain indicators (Pi Cycle, Puell, NVT, Gini) */
import { Unit } from "../../../utils/units.js";
+import { line } from "../../series.js";
/**
* Create On-chain section
@@ -10,7 +11,7 @@ import { Unit } from "../../../utils/units.js";
* @param {Market["movingAverage"]} args.movingAverage
*/
export function createOnchainSection(ctx, { indicators, movingAverage }) {
- const { line, colors, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
return {
name: "On-chain",
diff --git a/website/scripts/options/market/indicators/volatility.js b/website/scripts/options/market/indicators/volatility.js
index a0d44c522..78ad8a5b7 100644
--- a/website/scripts/options/market/indicators/volatility.js
+++ b/website/scripts/options/market/indicators/volatility.js
@@ -1,6 +1,7 @@
/** Volatility indicators (Index, True Range, Choppiness, Sharpe, Sortino) */
import { Unit } from "../../../utils/units.js";
+import { line } from "../../series.js";
/**
* Create Volatility section
@@ -10,7 +11,7 @@ import { Unit } from "../../../utils/units.js";
* @param {Market["range"]} args.range
*/
export function createVolatilitySection(ctx, { volatility, range }) {
- const { line, colors, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
return {
name: "Volatility",
diff --git a/website/scripts/options/market/investing.js b/website/scripts/options/market/investing.js
index e790ef894..61d671e53 100644
--- a/website/scripts/options/market/investing.js
+++ b/website/scripts/options/market/investing.js
@@ -1,6 +1,7 @@
/** Investing section (DCA) */
import { Unit } from "../../utils/units.js";
+import { line, baseline } from "../series.js";
import { satsBtcUsd } from "../shared.js";
import { periodIdToName } from "./utils.js";
@@ -41,7 +42,7 @@ export function buildDcaClasses(colors, dca) {
* @param {Market["returns"]} args.returns
*/
export function createInvestingSection(ctx, { dca, lookback, returns }) {
- const { line, baseline, colors, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
const dcaClasses = buildDcaClasses(colors, dca);
return {
@@ -114,8 +115,8 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
name: "Stack",
title: `${name} DCA vs Lump Sum Stack ($100/day)`,
bottom: [
- ...satsBtcUsd(ctx, dcaStack, "DCA", colors.green),
- ...satsBtcUsd(ctx, lumpSumStack, "Lump sum", colors.orange),
+ ...satsBtcUsd( dcaStack, "DCA", colors.green),
+ ...satsBtcUsd( lumpSumStack, "Lump sum", colors.orange),
],
},
],
@@ -164,7 +165,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
title: "DCA Stack by Year ($100/day)",
bottom: dcaClasses.flatMap(
({ year, color, defaultActive, stack }) =>
- satsBtcUsd(ctx, stack, `${year}`, color, { defaultActive }),
+ satsBtcUsd( stack, `${year}`, color, { defaultActive }),
),
},
],
@@ -200,7 +201,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
{
name: "Stack",
title: `DCA Class ${year} Stack ($100/day)`,
- bottom: satsBtcUsd(ctx, stack, "Stack", color),
+ bottom: satsBtcUsd( stack, "Stack", color),
},
],
})),
diff --git a/website/scripts/options/market/performance.js b/website/scripts/options/market/performance.js
index d6e84ff59..20c045920 100644
--- a/website/scripts/options/market/performance.js
+++ b/website/scripts/options/market/performance.js
@@ -1,6 +1,7 @@
/** Performance section */
import { Unit } from "../../utils/units.js";
+import { baseline } from "../series.js";
import { periodIdToName } from "./utils.js";
/**
@@ -9,7 +10,7 @@ import { periodIdToName } from "./utils.js";
* @param {Market["returns"]} returns
*/
export function createPerformanceSection(ctx, returns) {
- const { colors, baseline, createPriceLine } = ctx;
+ const { colors, createPriceLine } = ctx;
return {
name: "Performance",
diff --git a/website/scripts/options/series.js b/website/scripts/options/series.js
index f7212adf8..54cc8a4c0 100644
--- a/website/scripts/options/series.js
+++ b/website/scripts/options/series.js
@@ -85,6 +85,8 @@ export function candlestick({
* @param {Unit} args.unit
* @param {Color | [Color, Color]} [args.color]
* @param {boolean} [args.defaultActive]
+ * @param {boolean} [args.defaultActive]
+ * @param {number | undefined} [args.base]
* @param {BaselineSeriesPartialOptions} [args.options]
* @returns {FetchedBaselineSeriesBlueprint}
*/
@@ -94,6 +96,7 @@ export function baseline({
color,
defaultActive,
unit,
+ base,
options,
}) {
const isTuple = Array.isArray(color);
@@ -105,7 +108,12 @@ export function baseline({
colors: isTuple ? color : undefined,
unit,
defaultActive,
- options,
+ options: {
+ baseValue: {
+ price: base,
+ },
+ ...options,
+ },
};
}
diff --git a/website/scripts/options/shared.js b/website/scripts/options/shared.js
index 9bd5da545..36a195937 100644
--- a/website/scripts/options/shared.js
+++ b/website/scripts/options/shared.js
@@ -1,22 +1,22 @@
/** Shared helpers for options */
import { Unit } from "../utils/units.js";
+import { line } from "./series.js";
/**
* Create sats/btc/usd line series from a pattern with .sats/.bitcoin/.dollars
- * @param {PartialContext} ctx
* @param {{ sats: AnyMetricPattern, bitcoin: AnyMetricPattern, dollars: AnyMetricPattern }} pattern
* @param {string} name
* @param {Color} [color]
* @param {{ defaultActive?: boolean }} [options]
* @returns {FetchedLineSeriesBlueprint[]}
*/
-export function satsBtcUsd(ctx, pattern, name, color, options) {
+export function satsBtcUsd(pattern, name, color, options) {
const { defaultActive } = options || {};
return [
- ctx.line({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }),
- ctx.line({ metric: pattern.bitcoin, name, color, unit: Unit.btc, defaultActive }),
- ctx.line({ metric: pattern.dollars, name, color, unit: Unit.usd, defaultActive }),
+ line({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }),
+ line({ metric: pattern.bitcoin, name, color, unit: Unit.btc, defaultActive }),
+ line({ metric: pattern.dollars, name, color, unit: Unit.usd, defaultActive }),
];
}
diff --git a/website/scripts/options/types.js b/website/scripts/options/types.js
index d7eb0a058..641e1b26f 100644
--- a/website/scripts/options/types.js
+++ b/website/scripts/options/types.js
@@ -241,11 +241,6 @@
* @typedef {Object} PartialContext
* @property {Colors} colors
* @property {BrkClient} brk
- * @property {LineSeriesFn} line
- * @property {DotsSeriesFn} dots
- * @property {CandlestickSeriesFn} candlestick
- * @property {BaselineSeriesFn} baseline
- * @property {HistogramSeriesFn} histogram
* @property {(pattern: BlockCountPattern, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromBlockCount
* @property {(pattern: FullnessPattern, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromBitcoin
* @property {(pattern: AnyStatsPattern, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromBlockSize
diff --git a/website/scripts/utils/theme.js b/website/scripts/utils/theme.js
new file mode 100644
index 000000000..ea28a60bb
--- /dev/null
+++ b/website/scripts/utils/theme.js
@@ -0,0 +1,36 @@
+import signals from "../signals.js";
+import { readStored, removeStored, writeToStorage } from "./storage.js";
+
+const preferredColorSchemeMatchMedia = window.matchMedia(
+ "(prefers-color-scheme: dark)",
+);
+const stored = readStored("theme");
+const initial = stored ? stored === "dark" : preferredColorSchemeMatchMedia.matches;
+
+export const dark = signals.createSignal(initial);
+
+/** @param {boolean} isDark */
+function apply(isDark) {
+ document.documentElement.style.colorScheme = isDark ? "dark" : "light";
+}
+apply(initial);
+
+preferredColorSchemeMatchMedia.addEventListener("change", ({ matches }) => {
+ if (!readStored("theme")) {
+ dark.set(matches);
+ apply(matches);
+ }
+});
+
+function invert() {
+ const newValue = !dark();
+ dark.set(newValue);
+ apply(newValue);
+ if (newValue === preferredColorSchemeMatchMedia.matches) {
+ removeStored("theme");
+ } else {
+ writeToStorage("theme", newValue ? "dark" : "light");
+ }
+}
+
+document.getElementById("invert-button")?.addEventListener("click", invert);