mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-28 00:29:58 -07:00
global: MASSIVE snapshot
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import { serdeString } from "./serde";
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {HTMLElement}
|
||||
@@ -215,15 +213,17 @@ export function importStyle(href) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {Readonly<string[]>} T
|
||||
* @template T
|
||||
* @param {Object} args
|
||||
* @param {T[number]} args.defaultValue
|
||||
* @param {T} args.defaultValue
|
||||
* @param {string} [args.id]
|
||||
* @param {T | Accessor<T>} args.choices
|
||||
* @param {readonly T[] | Accessor<readonly T[]>} args.choices
|
||||
* @param {string} [args.keyPrefix]
|
||||
* @param {string} args.key
|
||||
* @param {boolean} [args.sorted]
|
||||
* @param {Signals} args.signals
|
||||
* @param {(choice: T) => string} [args.toKey] - Extract string key for storage (defaults to identity for strings)
|
||||
* @param {(choice: T) => string} [args.toLabel] - Extract display label (defaults to identity for strings)
|
||||
*/
|
||||
export function createHorizontalChoiceField({
|
||||
id,
|
||||
@@ -233,9 +233,13 @@ export function createHorizontalChoiceField({
|
||||
key,
|
||||
signals,
|
||||
sorted,
|
||||
toKey = /** @type {(choice: T) => string} */ ((/** @type {any} */ c) => c),
|
||||
toLabel = /** @type {(choice: T) => string} */ ((/** @type {any} */ c) => c),
|
||||
}) {
|
||||
const defaultKey = toKey(defaultValue);
|
||||
|
||||
const choices = signals.createMemo(() => {
|
||||
/** @type {T} */
|
||||
/** @type {readonly T[]} */
|
||||
let c;
|
||||
if (typeof unsortedChoices === "function") {
|
||||
c = unsortedChoices();
|
||||
@@ -244,16 +248,28 @@ export function createHorizontalChoiceField({
|
||||
}
|
||||
|
||||
return sorted
|
||||
? /** @type {T} */ (
|
||||
/** @type {any} */ (c.toSorted((a, b) => a.localeCompare(b)))
|
||||
? /** @type {readonly T[]} */ (
|
||||
/** @type {any} */ (
|
||||
c.toSorted((a, b) => toLabel(a).localeCompare(toLabel(b)))
|
||||
)
|
||||
)
|
||||
: c;
|
||||
});
|
||||
|
||||
/** @type {Signal<T[number]>} */
|
||||
/**
|
||||
* @param {string} storedKey
|
||||
* @returns {T}
|
||||
*/
|
||||
function fromKey(storedKey) {
|
||||
const found = choices().find((c) => toKey(c) === storedKey);
|
||||
return found ?? defaultValue;
|
||||
}
|
||||
|
||||
/** @type {Signal<T>} */
|
||||
const selected = signals.createSignal(defaultValue, {
|
||||
save: {
|
||||
...serdeString,
|
||||
serialize: (v) => toKey(v),
|
||||
deserialize: (s) => fromKey(s),
|
||||
keyPrefix: keyPrefix ?? "",
|
||||
key,
|
||||
saveDefaultValue: true,
|
||||
@@ -268,8 +284,10 @@ export function createHorizontalChoiceField({
|
||||
|
||||
signals.createEffect(choices, (choices) => {
|
||||
const s = selected();
|
||||
if (!choices.includes(s)) {
|
||||
if (choices.includes(defaultValue)) {
|
||||
const sKey = toKey(s);
|
||||
const keys = choices.map(toKey);
|
||||
if (!keys.includes(sKey)) {
|
||||
if (keys.includes(defaultKey)) {
|
||||
selected.set(() => defaultValue);
|
||||
} else if (choices.length) {
|
||||
selected.set(() => choices[0]);
|
||||
@@ -279,17 +297,18 @@ export function createHorizontalChoiceField({
|
||||
div.innerHTML = "";
|
||||
|
||||
choices.forEach((choice) => {
|
||||
const inputValue = choice;
|
||||
const choiceKey = toKey(choice);
|
||||
const choiceLabel = toLabel(choice);
|
||||
const { label } = createLabeledInput({
|
||||
inputId: `${id ?? key}-${choice.toLowerCase()}`,
|
||||
inputId: `${id ?? key}-${choiceKey.toLowerCase()}`,
|
||||
inputName: id ?? key,
|
||||
inputValue,
|
||||
inputChecked: inputValue === selected(),
|
||||
// title: choice,
|
||||
inputValue: choiceKey,
|
||||
inputChecked: choiceKey === sKey,
|
||||
// title: choiceLabel,
|
||||
type: "radio",
|
||||
});
|
||||
|
||||
const text = window.document.createTextNode(choice);
|
||||
const text = window.document.createTextNode(choiceLabel);
|
||||
label.append(text);
|
||||
div.append(label);
|
||||
});
|
||||
@@ -298,7 +317,7 @@ export function createHorizontalChoiceField({
|
||||
field.addEventListener("change", (event) => {
|
||||
// @ts-ignore
|
||||
const value = event.target.value;
|
||||
selected.set(value);
|
||||
selected.set(() => fromKey(value));
|
||||
});
|
||||
|
||||
return { field, selected };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getElementById } from "./dom";
|
||||
import { getElementById } from "./dom.js";
|
||||
|
||||
export const style = getComputedStyle(window.document.documentElement);
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ export const serdeChartableIndex = {
|
||||
},
|
||||
/**
|
||||
* @param {ChartableIndexName} v
|
||||
* @returns {IndexName}
|
||||
* @returns {ChartableIndex}
|
||||
*/
|
||||
deserialize(v) {
|
||||
switch (v) {
|
||||
@@ -223,309 +223,3 @@ export const serdeChartableIndex = {
|
||||
* "years" |
|
||||
* "" } Unit
|
||||
*/
|
||||
|
||||
export const serdeUnit = {
|
||||
/**
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
/** @type {Unit | undefined} */
|
||||
let unit;
|
||||
|
||||
/**
|
||||
* @param {Unit} u
|
||||
*/
|
||||
function setUnit(u) {
|
||||
if (unit)
|
||||
throw Error(
|
||||
`Can't assign "${u}" to unit, "${unit}" is already assigned to "${v}"`,
|
||||
);
|
||||
unit = u;
|
||||
}
|
||||
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v.includes("in_sats") ||
|
||||
v.endsWith("_sats") ||
|
||||
(v.endsWith("supply") &&
|
||||
!(v.endsWith("circulating_supply") || v.endsWith("_own_supply"))) ||
|
||||
v === "sent" ||
|
||||
v === "annualized_volume" ||
|
||||
v.endsWith("supply_half") ||
|
||||
v.endsWith("supply_in_profit") ||
|
||||
v.endsWith("supply_in_loss") ||
|
||||
v.endsWith("stack") ||
|
||||
(v.endsWith("value") && !v.includes("realized")) ||
|
||||
((v.includes("coinbase") ||
|
||||
v.includes("fee") ||
|
||||
v.includes("subsidy") ||
|
||||
v.includes("rewards")) &&
|
||||
!(
|
||||
v.startsWith("is_") ||
|
||||
v.includes("_btc") ||
|
||||
v.includes("_usd") ||
|
||||
v.includes("fee_rate") ||
|
||||
v.endsWith("dominance")
|
||||
)))
|
||||
) {
|
||||
setUnit("sats");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
!v.endsWith("velocity") &&
|
||||
((v.includes("_btc") &&
|
||||
!(v.includes("0k_btc") || v.includes("1k_btc"))) ||
|
||||
v.endsWith("_btc"))
|
||||
) {
|
||||
setUnit("btc");
|
||||
}
|
||||
if ((!unit || localhost) && v === "chain") {
|
||||
setUnit("block");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("blocks_before")) {
|
||||
setUnit("blocks");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v === "emptyaddressdata" || v === "loadedaddressdata")
|
||||
) {
|
||||
setUnit("address data");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v === "price_high" ||
|
||||
v === "price_ohlc" ||
|
||||
v === "price_low" ||
|
||||
v === "price_close" ||
|
||||
v === "price_open" ||
|
||||
v === "price_ath" ||
|
||||
v === "market_cap" ||
|
||||
v.startsWith("price_true_range") ||
|
||||
(v.includes("_usd") && !v.endsWith("velocity")) ||
|
||||
v.includes("cointime_value") ||
|
||||
v.endsWith("_ago") ||
|
||||
v.includes("cost_basis") ||
|
||||
v.endsWith("_price") ||
|
||||
(v.startsWith("price") && (v.endsWith("min") || v.endsWith("max"))) ||
|
||||
(v.endsWith("_cap") && !v.includes("rel_to")) ||
|
||||
v.endsWith("value_created") ||
|
||||
v.endsWith("value_destroyed") ||
|
||||
((v.includes("realized") || v.includes("true_market_mean")) &&
|
||||
!v.includes("unrealized") &&
|
||||
!v.includes("ratio") &&
|
||||
!v.includes("rel_to")) ||
|
||||
(v.includes("unrealized") && !v.includes("rel_to")) ||
|
||||
((v.endsWith("sma") || v.includes("sma_x") || v.endsWith("ema")) &&
|
||||
!v.includes("ratio") &&
|
||||
!v.includes("sopr") &&
|
||||
!v.includes("hash_rate")) ||
|
||||
v === "ath")
|
||||
) {
|
||||
setUnit("usd");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("cents")) {
|
||||
setUnit("cents");
|
||||
}
|
||||
if (
|
||||
((!unit || localhost) &&
|
||||
(v.endsWith("ratio") ||
|
||||
(v.includes("ratio") &&
|
||||
(v.endsWith("sma") || v.endsWith("ema") || v.endsWith("zscore"))) ||
|
||||
v.includes("sopr") ||
|
||||
v.endsWith("_5sd") ||
|
||||
v.endsWith("1sd") ||
|
||||
v.endsWith("2sd") ||
|
||||
v.endsWith("3sd") ||
|
||||
v.endsWith("pct1") ||
|
||||
v.endsWith("pct2") ||
|
||||
v.endsWith("pct5") ||
|
||||
v.endsWith("pct95") ||
|
||||
v.endsWith("pct98") ||
|
||||
v.endsWith("pct99"))) ||
|
||||
v.includes("liveliness") ||
|
||||
v.includes("vaultedness") ||
|
||||
v == "puell_multiple" ||
|
||||
v.endsWith("velocity")
|
||||
) {
|
||||
setUnit("ratio");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v === "price_drawdown" ||
|
||||
v === "difficulty_adjustment" ||
|
||||
v.endsWith("inflation_rate") ||
|
||||
v.endsWith("_oscillator") ||
|
||||
v.endsWith("_dominance") ||
|
||||
v.endsWith("_returns") ||
|
||||
v.endsWith("_rebound") ||
|
||||
v.endsWith("_volatility") ||
|
||||
v.endsWith("_cagr"))
|
||||
) {
|
||||
setUnit("percentage");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v.endsWith("count") ||
|
||||
v.includes("_count_") ||
|
||||
v.startsWith("block_count") ||
|
||||
v.includes("blocks_mined") ||
|
||||
(v.includes("tx_v") && !v.includes("vsize")))
|
||||
) {
|
||||
setUnit("count");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v.startsWith("hash_rate") || v.endsWith("as_hash"))
|
||||
) {
|
||||
setUnit("h/s");
|
||||
}
|
||||
if ((!unit || localhost) && v === "pool") {
|
||||
setUnit("id");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("fee_rate")) {
|
||||
setUnit("sat/vb");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("is_")) {
|
||||
setUnit("bool");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("type")) {
|
||||
setUnit("type");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v === "interval" || v.startsWith("block_interval"))
|
||||
) {
|
||||
setUnit("secs");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("_per_sec")) {
|
||||
setUnit("/sec");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("locktime")) {
|
||||
setUnit("locktime");
|
||||
}
|
||||
|
||||
if ((!unit || localhost) && v.endsWith("version")) {
|
||||
setUnit("version");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v === "txid" ||
|
||||
(v.endsWith("bytes") && !v.endsWith("vbytes")) ||
|
||||
v.endsWith("base_size") ||
|
||||
v.endsWith("total_size") ||
|
||||
v.includes("block_size"))
|
||||
) {
|
||||
setUnit("bytes");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("_sd")) {
|
||||
setUnit("sd");
|
||||
}
|
||||
if ((!unit || localhost) && (v.includes("vsize") || v.includes("vbytes"))) {
|
||||
setUnit("vb");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("weight")) {
|
||||
setUnit("wu");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("index")) {
|
||||
setUnit("index");
|
||||
}
|
||||
if ((!unit || localhost) && (v === "date" || v === "date_fixed")) {
|
||||
setUnit("date");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v === "timestamp" || v === "timestamp_fixed")
|
||||
) {
|
||||
setUnit("timestamp");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("coinblocks")) {
|
||||
setUnit("coinblocks");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("coindays")) {
|
||||
setUnit("coindays");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("satblocks")) {
|
||||
setUnit("satblocks");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("satdays")) {
|
||||
setUnit("satdays");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("height")) {
|
||||
setUnit("height");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("rel_to_market_cap")) {
|
||||
setUnit("%mcap");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("rel_to_own_market_cap")) {
|
||||
setUnit("%cmcap");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("rel_to_own_total_unrealized_pnl")) {
|
||||
setUnit("%cp+l");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("rel_to_realized_cap")) {
|
||||
setUnit("%rcap");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("rel_to_circulating_supply")) {
|
||||
setUnit("%all");
|
||||
}
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v.includes("rel_to_realized_profit") ||
|
||||
v.includes("rel_to_realized_loss"))
|
||||
) {
|
||||
setUnit("%pnl");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("rel_to_own_supply")) {
|
||||
setUnit("%self");
|
||||
}
|
||||
if ((!unit || localhost) && v.endsWith("epoch")) {
|
||||
setUnit("epoch");
|
||||
}
|
||||
if ((!unit || localhost) && v === "difficulty") {
|
||||
setUnit("difficulty");
|
||||
}
|
||||
if ((!unit || localhost) && v === "blockhash") {
|
||||
setUnit("hash");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("hash_price_phs")) {
|
||||
setUnit("usd/(ph/s)/day");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("hash_price_ths")) {
|
||||
setUnit("usd/(th/s)/day");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("hash_value_phs")) {
|
||||
setUnit("sats/(ph/s)/day");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("hash_value_ths")) {
|
||||
setUnit("sats/(th/s)/day");
|
||||
}
|
||||
|
||||
if (
|
||||
(!unit || localhost) &&
|
||||
(v.includes("days_between") ||
|
||||
v.includes("days_since") ||
|
||||
v.startsWith("days_before"))
|
||||
) {
|
||||
setUnit("days");
|
||||
}
|
||||
if ((!unit || localhost) && v.includes("years_between")) {
|
||||
setUnit("years");
|
||||
}
|
||||
if ((!unit || localhost) && v == "len") {
|
||||
setUnit("len");
|
||||
}
|
||||
if ((!unit || localhost) && v == "position") {
|
||||
setUnit("position");
|
||||
}
|
||||
if ((!unit || localhost) && v.startsWith("constant")) {
|
||||
setUnit("constant");
|
||||
}
|
||||
|
||||
if (!unit) {
|
||||
console.log();
|
||||
throw Error(`Unit not set for "${v}"`);
|
||||
}
|
||||
|
||||
return /** @type {Unit} */ (unit);
|
||||
},
|
||||
};
|
||||
|
||||
62
websites/bitview/scripts/utils/units.js
Normal file
62
websites/bitview/scripts/utils/units.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/** Unit definitions for chart series */
|
||||
|
||||
/**
|
||||
* Unit enum with id (for serialization) and name (for display)
|
||||
*/
|
||||
export const Unit = /** @type {const} */ ({
|
||||
// Value units
|
||||
sats: { id: "sats", name: "Satoshis" },
|
||||
btc: { id: "btc", name: "Bitcoin" },
|
||||
usd: { id: "usd", name: "US Dollars" },
|
||||
|
||||
// Ratios & percentages
|
||||
percentage: { id: "percentage", name: "Percentage" },
|
||||
ratio: { id: "ratio", name: "Ratio" },
|
||||
index: { id: "index", name: "Index" },
|
||||
sd: { id: "sd", name: "Std Dev" },
|
||||
|
||||
// Relative percentages
|
||||
pctSupply: { id: "pct-supply", name: "% of Supply" },
|
||||
pctOwn: { id: "pct-own", name: "% of Own Supply" },
|
||||
pctMcap: { id: "pct-mcap", name: "% of Market Cap" },
|
||||
pctRcap: { id: "pct-rcap", name: "% of Realized Cap" },
|
||||
pctOwnMcap: { id: "pct-own-mcap", name: "% of Own Market Cap" },
|
||||
|
||||
// Time
|
||||
days: { id: "days", name: "Days" },
|
||||
years: { id: "years", name: "Years" },
|
||||
secs: { id: "secs", name: "Seconds" },
|
||||
|
||||
// Counts
|
||||
count: { id: "count", name: "Count" },
|
||||
blocks: { id: "blocks", name: "Blocks" },
|
||||
|
||||
// Size
|
||||
bytes: { id: "bytes", name: "Bytes" },
|
||||
vb: { id: "vb", name: "Virtual Bytes" },
|
||||
wu: { id: "wu", name: "Weight Units" },
|
||||
|
||||
// Mining
|
||||
hashRate: { id: "hashrate", name: "Hash Rate" },
|
||||
difficulty: { id: "difficulty", name: "Difficulty" },
|
||||
epoch: { id: "epoch", name: "Epoch" },
|
||||
|
||||
// Fees
|
||||
feeRate: { id: "feerate", name: "Sats/vByte" },
|
||||
|
||||
// Rates
|
||||
perSec: { id: "per-sec", name: "Per Second" },
|
||||
|
||||
// Cointime
|
||||
coinblocks: { id: "coinblocks", name: "Coinblocks" },
|
||||
coindays: { id: "coindays", name: "Coindays" },
|
||||
|
||||
// Hash price/value
|
||||
usdPerThsPerDay: { id: "usd-ths-day", name: "USD/TH/s/Day" },
|
||||
usdPerPhsPerDay: { id: "usd-phs-day", name: "USD/PH/s/Day" },
|
||||
satsPerThsPerDay: { id: "sats-ths-day", name: "Sats/TH/s/Day" },
|
||||
satsPerPhsPerDay: { id: "sats-phs-day", name: "Sats/PH/s/Day" },
|
||||
});
|
||||
|
||||
/** @typedef {keyof typeof Unit} UnitKey */
|
||||
/** @typedef {typeof Unit[UnitKey]} UnitObject */
|
||||
Reference in New Issue
Block a user