mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-16 17:59:45 -07:00
bitview: reorg part 9
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../../packages/lightweight-charts/5.0.8/dist/typings' */
|
||||
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../../modules/lightweight-charts/5.0.8/dist/typings' */
|
||||
|
||||
import {
|
||||
createChart,
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
HistogramSeries,
|
||||
LineSeries,
|
||||
BaselineSeries,
|
||||
// } from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
|
||||
} from "../../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
|
||||
// } from "../modules/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
|
||||
} from "../../modules/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
|
||||
|
||||
import {
|
||||
createHorizontalChoiceField,
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
import { createOklchToRGBA } from "./oklch";
|
||||
import { throttle } from "../timing";
|
||||
import { serdeBool } from "../serde";
|
||||
import { stringToId } from "../format";
|
||||
import { style } from "../elements";
|
||||
|
||||
/**
|
||||
* @typedef {Object} Valued
|
||||
@@ -50,7 +52,7 @@ import { serdeBool } from "../serde";
|
||||
* @typedef {_BaselineData<number>} BaselineData
|
||||
* @typedef {_HistogramData<number>} HistogramData
|
||||
*
|
||||
* @typedef {function({ iseries: ISeries; unit: Unit; index: Index }): void} SetDataCallback
|
||||
* @typedef {function({ iseries: ISeries; unit: Unit; index: IndexName }): void} SetDataCallback
|
||||
*/
|
||||
|
||||
const oklchToRGBA = createOklchToRGBA();
|
||||
@@ -63,10 +65,8 @@ const lineWidth = /** @type {any} */ (1.5);
|
||||
* @param {HTMLElement} args.parent
|
||||
* @param {Signals} args.signals
|
||||
* @param {Colors} args.colors
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {Accessor<Index>} args.index
|
||||
* @param {Resources} args.resources
|
||||
* @param {Accessor<IndexName>} args.index
|
||||
* @param {((unknownTimeScaleCallback: VoidFunction) => void)} [args.timeScaleSetCallback]
|
||||
* @param {true} [args.fitContent]
|
||||
* @param {{unit: Unit; blueprints: AnySeriesBlueprint[]}[]} [args.config]
|
||||
@@ -75,11 +75,9 @@ function createChartElement({
|
||||
parent,
|
||||
signals,
|
||||
colors,
|
||||
utils,
|
||||
elements,
|
||||
id: chartId,
|
||||
index,
|
||||
vecsResources,
|
||||
resources,
|
||||
timeScaleSetCallback,
|
||||
fitContent,
|
||||
config,
|
||||
@@ -88,20 +86,14 @@ function createChartElement({
|
||||
div.classList.add("chart");
|
||||
parent.append(div);
|
||||
|
||||
const legendTop = createLegend({
|
||||
utils,
|
||||
signals,
|
||||
});
|
||||
const legendTop = createLegend(signals);
|
||||
div.append(legendTop.element);
|
||||
|
||||
const chartDiv = window.document.createElement("div");
|
||||
chartDiv.classList.add("lightweight-chart");
|
||||
div.append(chartDiv);
|
||||
|
||||
const legendBottom = createLegend({
|
||||
utils,
|
||||
signals,
|
||||
});
|
||||
const legendBottom = createLegend(signals);
|
||||
div.append(legendBottom.element);
|
||||
|
||||
/** @type {IChartApi} */
|
||||
@@ -110,7 +102,7 @@ function createChartElement({
|
||||
/** @satisfies {DeepPartial<ChartOptions>} */ ({
|
||||
autoSize: true,
|
||||
layout: {
|
||||
fontFamily: elements.style.fontFamily,
|
||||
fontFamily: style.fontFamily,
|
||||
background: { color: "transparent" },
|
||||
attributionLogo: false,
|
||||
colorSpace: "display-p3",
|
||||
@@ -185,24 +177,24 @@ function createChartElement({
|
||||
|
||||
signals.createEffect(index, (index) => {
|
||||
const minBarSpacing =
|
||||
index === /** @satisfies {MonthIndex} */ (7)
|
||||
index === "monthindex"
|
||||
? 1
|
||||
: index === /** @satisfies {QuarterIndex} */ (19)
|
||||
: index === "quarterindex"
|
||||
? 2
|
||||
: index === /** @satisfies {SemesterIndex} */ (20)
|
||||
: index === "semesterindex"
|
||||
? 3
|
||||
: index === /** @satisfies {YearIndex} */ (24)
|
||||
: index === "yearindex"
|
||||
? 6
|
||||
: index === /** @satisfies {DecadeIndex} */ (1)
|
||||
: index === "decadeindex"
|
||||
? 60
|
||||
: 0.5;
|
||||
|
||||
ichart.applyOptions({
|
||||
timeScale: {
|
||||
timeVisible:
|
||||
index === /** @satisfies {Height} */ (5) ||
|
||||
index === /** @satisfies {DifficultyEpoch} */ (2) ||
|
||||
index === /** @satisfies {HalvingEpoch} */ (4),
|
||||
index === "height" ||
|
||||
index === "difficultyepoch" ||
|
||||
index === "halvingepoch",
|
||||
...(!fitContent
|
||||
? {
|
||||
minBarSpacing,
|
||||
@@ -212,7 +204,7 @@ function createChartElement({
|
||||
});
|
||||
});
|
||||
|
||||
const activeResources = /** @type {Set<VecResource>} */ (new Set());
|
||||
const activeResources = /** @type {Set<MetricResource>} */ (new Set());
|
||||
ichart.subscribeCrosshairMove(
|
||||
throttle(() => {
|
||||
activeResources.forEach((v) => {
|
||||
@@ -292,7 +284,7 @@ function createChartElement({
|
||||
createChild(pane) {
|
||||
const { field, selected } = createHorizontalChoiceField({
|
||||
choices: /** @type {const} */ (["lin", "log"]),
|
||||
id: utils.stringToId(`${id} ${paneIndex} ${unit}`),
|
||||
id: stringToId(`${id} ${paneIndex} ${unit}`),
|
||||
defaultValue:
|
||||
unit === "usd" && seriesType !== "Baseline" ? "log" : "lin",
|
||||
key: `${id}-price-scale-${paneIndex}`,
|
||||
@@ -340,7 +332,7 @@ function createChartElement({
|
||||
data,
|
||||
}) {
|
||||
return signals.createRoot((dispose) => {
|
||||
const id = `${utils.stringToId(name)}-${paneIndex}`;
|
||||
const id = `${stringToId(name)}-${paneIndex}`;
|
||||
|
||||
const active = signals.createSignal(defaultActive ?? true, {
|
||||
save: {
|
||||
@@ -361,7 +353,7 @@ function createChartElement({
|
||||
|
||||
iseries.setSeriesOrder(order);
|
||||
|
||||
/** @type {VecResource | undefined} */
|
||||
/** @type {MetricResource | undefined} */
|
||||
let _valuesResource;
|
||||
|
||||
/** @type {Series} */
|
||||
@@ -383,15 +375,13 @@ function createChartElement({
|
||||
|
||||
if (metric) {
|
||||
signals.createEffect(index, (index) => {
|
||||
const timeResource = vecsResources.getOrCreate(
|
||||
const timeResource = resources.metrics.getOrCreate(
|
||||
index === "height" ? "timestamp_fixed" : "timestamp",
|
||||
index,
|
||||
index === /** @satisfies {Height} */ (5)
|
||||
? "timestamp_fixed"
|
||||
: "timestamp",
|
||||
);
|
||||
timeResource.fetch();
|
||||
|
||||
const valuesResource = vecsResources.getOrCreate(index, metric);
|
||||
const valuesResource = resources.metrics.getOrCreate(metric, index);
|
||||
_valuesResource = valuesResource;
|
||||
|
||||
series.url.set(() => valuesResource.url);
|
||||
@@ -401,11 +391,11 @@ function createChartElement({
|
||||
valuesResource.fetch();
|
||||
activeResources.add(valuesResource);
|
||||
|
||||
const fetchedKey = vecsResources.defaultFetchedKey;
|
||||
const fetchedKey = resources.metrics.genKey();
|
||||
signals.createEffect(
|
||||
() => ({
|
||||
_indexes: timeResource.fetched().get(fetchedKey)?.vec(),
|
||||
values: valuesResource.fetched().get(fetchedKey)?.vec(),
|
||||
_indexes: timeResource.fetched().get(fetchedKey)?.data(),
|
||||
values: valuesResource.fetched().get(fetchedKey)?.data(),
|
||||
}),
|
||||
({ _indexes, values }) => {
|
||||
if (!_indexes?.length || !values?.length) return;
|
||||
@@ -477,10 +467,10 @@ function createChartElement({
|
||||
|
||||
timeScaleSetCallback?.(() => {
|
||||
if (
|
||||
index === /** @satisfies {QuarterIndex} */ (19) ||
|
||||
index === /** @satisfies {SemesterIndex} */ (20) ||
|
||||
index === /** @satisfies {YearIndex} */ (24) ||
|
||||
index === /** @satisfies {DecadeIndex} */ (1)
|
||||
index === "quarterindex" ||
|
||||
index === "semesterindex" ||
|
||||
index === "yearindex" ||
|
||||
index === "decadeindex"
|
||||
) {
|
||||
ichart.timeScale().setVisibleLogicalRange({
|
||||
from: -1,
|
||||
@@ -836,11 +826,9 @@ function createChartElement({
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Signals} signals
|
||||
*/
|
||||
function createLegend({ signals, utils }) {
|
||||
function createLegend(signals) {
|
||||
const element = window.document.createElement("legend");
|
||||
|
||||
const hovered = signals.createSignal(/** @type {Series | null} */ (null));
|
||||
@@ -874,8 +862,8 @@ function createLegend({ signals, utils }) {
|
||||
legends[order] = div;
|
||||
|
||||
const { input, label } = createLabeledInput({
|
||||
inputId: utils.stringToId(`legend-${series.id}`),
|
||||
inputName: utils.stringToId(`selected-${series.id}`),
|
||||
inputId: stringToId(`legend-${series.id}`),
|
||||
inputName: stringToId(`selected-${series.id}`),
|
||||
inputValue: "value",
|
||||
title: "Click to toggle",
|
||||
inputChecked: series.active(),
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import { getElementById } from "./dom";
|
||||
|
||||
export default {
|
||||
head: window.document.getElementsByTagName("head")[0],
|
||||
body: window.document.body,
|
||||
main: getElementById("main"),
|
||||
aside: getElementById("aside"),
|
||||
asideLabel: getElementById("aside-selector-label"),
|
||||
navLabel: getElementById(`nav-selector-label`),
|
||||
searchLabel: getElementById(`search-selector-label`),
|
||||
search: getElementById("search"),
|
||||
nav: getElementById("nav"),
|
||||
searchInput: /** @type {HTMLInputElement} */ (getElementById("search-input")),
|
||||
searchResults: getElementById("search-results"),
|
||||
selectors: getElementById("frame-selectors"),
|
||||
style: getComputedStyle(window.document.documentElement),
|
||||
charts: getElementById("charts"),
|
||||
table: getElementById("table"),
|
||||
explorer: getElementById("explorer"),
|
||||
simulation: getElementById("simulation"),
|
||||
};
|
||||
export const style = getComputedStyle(window.document.documentElement);
|
||||
|
||||
export const headElement = window.document.getElementsByTagName("head")[0];
|
||||
export const bodyElement = window.document.body;
|
||||
|
||||
export const mainElement = getElementById("main");
|
||||
export const asideElement = getElementById("aside");
|
||||
export const searchElement = getElementById("search");
|
||||
export const navElement = getElementById("nav");
|
||||
export const chartElement = getElementById("chart");
|
||||
export const tableElement = getElementById("table");
|
||||
export const explorerElement = getElementById("explorer");
|
||||
export const simulationElement = getElementById("simulation");
|
||||
|
||||
export const asideLabelElement = getElementById("aside-selector-label");
|
||||
export const navLabelElement = getElementById(`nav-selector-label`);
|
||||
export const searchLabelElement = getElementById(`search-selector-label`);
|
||||
export const searchInput = /** @type {HTMLInputElement} */ (
|
||||
getElementById("search-input")
|
||||
);
|
||||
export const searchResultsElement = getElementById("search-results");
|
||||
export const frameSelectorsElement = getElementById("frame-selectors");
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
const localhost = window.location.hostname === "localhost";
|
||||
const standalone =
|
||||
export const localhost = window.location.hostname === "localhost";
|
||||
export const standalone =
|
||||
"standalone" in window.navigator && !!window.navigator.standalone;
|
||||
const userAgent = navigator.userAgent.toLowerCase();
|
||||
const isChrome = userAgent.includes("chrome");
|
||||
const safari = userAgent.includes("safari");
|
||||
const safariOnly = safari && !isChrome;
|
||||
const macOS = userAgent.includes("mac os");
|
||||
const iphone = userAgent.includes("iphone");
|
||||
const ipad = userAgent.includes("ipad");
|
||||
const ios = iphone || ipad;
|
||||
|
||||
export default {
|
||||
standalone,
|
||||
userAgent,
|
||||
isChrome,
|
||||
safari,
|
||||
safariOnly,
|
||||
macOS,
|
||||
iphone,
|
||||
ipad,
|
||||
ios,
|
||||
localhost,
|
||||
};
|
||||
export const userAgent = navigator.userAgent.toLowerCase();
|
||||
export const isChrome = userAgent.includes("chrome");
|
||||
export const safari = userAgent.includes("safari");
|
||||
export const safariOnly = safari && !isChrome;
|
||||
export const macOS = userAgent.includes("mac os");
|
||||
export const iphone = userAgent.includes("iphone");
|
||||
export const ipad = userAgent.includes("ipad");
|
||||
export const ios = iphone || ipad;
|
||||
export const canShare = "canShare" in navigator;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @param {number} [digits]
|
||||
* @param {Intl.NumberFormatOptions} [options]
|
||||
*/
|
||||
function numberToUSFormat(value, digits, options) {
|
||||
export function numberToUSNumber(value, digits, options) {
|
||||
return value.toLocaleString("en-us", {
|
||||
...options,
|
||||
minimumFractionDigits: digits,
|
||||
@@ -11,23 +11,18 @@ function numberToUSFormat(value, digits, options) {
|
||||
});
|
||||
}
|
||||
|
||||
export const locale = {
|
||||
numberToUSFormat,
|
||||
};
|
||||
export const numberToDollars = new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
|
||||
export const formatters = {
|
||||
dollars: new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}),
|
||||
percentage: new Intl.NumberFormat("en-US", {
|
||||
style: "percent",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}),
|
||||
};
|
||||
export const numberToPercentage = new Intl.NumberFormat("en-US", {
|
||||
style: "percent",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
@@ -7,26 +7,16 @@ import {
|
||||
import { serdeUnit } from "../serde";
|
||||
import { pushHistory, resetParams } from "../url";
|
||||
import { readStored, writeToStorage } from "../storage";
|
||||
import { stringToId } from "../format";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Colors} args.colors
|
||||
* @param {Signals} args.signals
|
||||
* @param {Env} args.env
|
||||
* @param {Utilities} args.utils
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Pools} args.pools
|
||||
* @param {BRK} args.brk
|
||||
* @param {Signal<string | null>} args.qrcode
|
||||
*/
|
||||
export function initOptions({
|
||||
colors,
|
||||
signals,
|
||||
env,
|
||||
utils,
|
||||
qrcode,
|
||||
metricToIndexes,
|
||||
pools,
|
||||
}) {
|
||||
export function initOptions({ colors, signals, brk, qrcode }) {
|
||||
const LS_SELECTED_KEY = `selected_path`;
|
||||
|
||||
const urlPath_ = window.document.location.pathname
|
||||
@@ -42,10 +32,8 @@ export function initOptions({
|
||||
const selected = signals.createSignal(/** @type {any} */ (undefined));
|
||||
|
||||
const partialOptions = createPartialOptions({
|
||||
env,
|
||||
colors,
|
||||
metricToIndexes,
|
||||
pools,
|
||||
brk,
|
||||
});
|
||||
|
||||
/** @type {Option[]} */
|
||||
@@ -58,7 +46,8 @@ export function initOptions({
|
||||
*/
|
||||
function arrayToRecord(arr = []) {
|
||||
return (arr || []).reduce((record, blueprint) => {
|
||||
if (env.localhost && !(blueprint.metric in metricToIndexes)) {
|
||||
if (!brk.hasMetric(blueprint.metric)) {
|
||||
// if (localhost && !brk.hasMetric(blueprint.metric)) {
|
||||
throw Error(`${blueprint.metric} not recognized`);
|
||||
}
|
||||
const unit = blueprint.unit ?? serdeUnit.deserialize(blueprint.metric);
|
||||
@@ -186,7 +175,7 @@ export function initOptions({
|
||||
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
|
||||
);
|
||||
|
||||
const serName = utils.stringToId(anyPartial.name);
|
||||
const serName = stringToId(anyPartial.name);
|
||||
const path = [...parentPath, serName];
|
||||
const childOptionsCount = recursiveProcessPartialTree(
|
||||
anyPartial.tree,
|
||||
@@ -250,7 +239,7 @@ export function initOptions({
|
||||
const option = /** @type {Option} */ (anyPartial);
|
||||
|
||||
const name = option.name;
|
||||
const path = [...parentPath, utils.stringToId(option.name)];
|
||||
const path = [...parentPath, stringToId(option.name)];
|
||||
|
||||
if ("kind" in anyPartial && anyPartial.kind === "explorer") {
|
||||
Object.assign(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @ts_check
|
||||
|
||||
/**
|
||||
* @typedef {Object} BaseSeriesBlueprint
|
||||
* @property {string} title
|
||||
@@ -119,70 +117,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { localhost } from "../env";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Env} args.env
|
||||
* @param {Colors} args.colors
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Pools} args.pools
|
||||
* @param {BRK} args.brk
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
/**
|
||||
* @template {string} S
|
||||
* @typedef {Extract<Metric, `${S}${string}`>} StartsWith
|
||||
*/
|
||||
/**
|
||||
* @template {string} S
|
||||
* @typedef {Extract<Metric, `${string}${S}`>} EndsWith
|
||||
*/
|
||||
/**
|
||||
* @template {string} K
|
||||
* @template {string} S
|
||||
* @typedef {K extends `${S}${infer Rest}` ? Rest : never} WithoutPrefix
|
||||
*/
|
||||
/**
|
||||
* @template {string} K
|
||||
* @template {string} S
|
||||
* @typedef {K extends `${infer Rest}${S}` ? Rest : never} WithoutSuffix
|
||||
*/
|
||||
/**
|
||||
* @template {string} K
|
||||
* @template {string} S
|
||||
* @typedef {K extends `${infer _Prefix}${S}${infer _Suffix}` ? never : K} ExcludeSubstring
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {"cumulative_"} CumulativePrefix
|
||||
* @typedef {"_30d_delta"} _30DChageSubString
|
||||
* @typedef {StartsWith<CumulativePrefix>} CumulativeMetric
|
||||
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeMetric, CumulativePrefix>, _30DChageSubString>} CumulativeMetricBase
|
||||
* @typedef {"_avg"} AverageSuffix
|
||||
* @typedef {EndsWith<AverageSuffix>} MetricAverage
|
||||
* @typedef {WithoutSuffix<MetricAverage, AverageSuffix>} MetricAverageBase
|
||||
* @typedef {"_median"} MedianSuffix
|
||||
* @typedef {EndsWith<MedianSuffix>} MetricMedian
|
||||
* @typedef {WithoutSuffix<MetricMedian, MedianSuffix>} MetricMedianBase
|
||||
* @typedef {"_pct90"} _pct90Suffix
|
||||
* @typedef {EndsWith<_pct90Suffix>} MetricPct90
|
||||
* @typedef {WithoutSuffix<MetricPct90, _pct90Suffix>} MetricPct90Base
|
||||
* @typedef {"_pct75"} _pct75Suffix
|
||||
* @typedef {EndsWith<_pct75Suffix>} MetricPct75
|
||||
* @typedef {WithoutSuffix<MetricPct75, _pct75Suffix>} MetricPct75Base
|
||||
* @typedef {"_pct25"} _pct25Suffix
|
||||
* @typedef {EndsWith<_pct25Suffix>} MetricPct25
|
||||
* @typedef {WithoutSuffix<MetricPct25, _pct25Suffix>} MetricPct25Base
|
||||
* @typedef {"_pct10"} _pct10Suffix
|
||||
* @typedef {EndsWith<_pct10Suffix>} MetricPct10
|
||||
* @typedef {WithoutSuffix<MetricPct10, _pct10Suffix>} MetricPct10Base
|
||||
* @typedef {"_max"} MaxSuffix
|
||||
* @typedef {EndsWith<MaxSuffix>} MetricMax
|
||||
* @typedef {WithoutSuffix<MetricMax, MaxSuffix>} MetricMaxBase
|
||||
* @typedef {"_min"} MinSuffix
|
||||
* @typedef {EndsWith<MinSuffix>} MetricMin
|
||||
* @typedef {WithoutSuffix<MetricMin, MinSuffix>} MetricMinBase
|
||||
*/
|
||||
|
||||
export function createPartialOptions({ colors, brk }) {
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {boolean} compoundAdjective
|
||||
@@ -681,7 +624,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MetricAverageBase} metric
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
function createAverageSeries(metric) {
|
||||
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
||||
@@ -692,7 +635,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {CumulativeMetricBase} args.metric
|
||||
* @param {Metric} args.metric
|
||||
* @param {Color} [args.sumColor]
|
||||
* @param {Color} [args.cumulativeColor]
|
||||
* @param {string} [args.common]
|
||||
@@ -719,14 +662,14 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {CumulativeMetricBase} args.metric
|
||||
* @param {Metric} args.metric
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
*/
|
||||
function createSumSeries({ metric, title = "", color }) {
|
||||
const metric_sum = `${metric}_sum`;
|
||||
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
|
||||
metric: metric_sum in metricToIndexes ? metric_sum : metric,
|
||||
metric: brk.hasMetric(metric_sum) ? metric_sum : metric,
|
||||
title: `Sum ${title}`,
|
||||
color: color ?? colors.red,
|
||||
});
|
||||
@@ -734,7 +677,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {CumulativeMetricBase} args.metric
|
||||
* @param {Metric} args.metric
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
*/
|
||||
@@ -748,7 +691,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
function createMinMaxPercentilesSeries(metric) {
|
||||
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
|
||||
@@ -798,7 +741,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
function createSumCumulativeMinMaxPercentilesSeries(metric) {
|
||||
return [
|
||||
@@ -808,7 +751,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
|
||||
* @param {Metric} metric
|
||||
*/
|
||||
function createAverageSumCumulativeMinMaxPercentilesSeries(metric) {
|
||||
return [
|
||||
@@ -819,7 +762,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Metric & MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.metric
|
||||
* @param {Metric} args.metric
|
||||
* @param {string} args.name
|
||||
*/
|
||||
function createBaseAverageSumCumulativeMinMaxPercentilesSeries({
|
||||
@@ -835,12 +778,6 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {"_ratio_zscore"} RatioZScoreCapSuffix
|
||||
* @typedef {EndsWith<RatioZScoreCapSuffix>} MetricRatioZScoreCap
|
||||
* @typedef {WithoutSuffix<MetricRatioZScoreCap, RatioZScoreCapSuffix>} MetricRatioZScoreCapBase
|
||||
*/
|
||||
|
||||
const percentiles = [
|
||||
{
|
||||
name: "pct1",
|
||||
@@ -906,7 +843,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
* @param {string} args.name
|
||||
* @param {string} args.legend
|
||||
* @param {string} args.title
|
||||
* @param {MetricRatioZScoreCapBase} args.metric
|
||||
* @param {Metric} args.metric
|
||||
* @param {Color} [args.color]
|
||||
*/
|
||||
function createPriceWithRatioOptions({ name, title, legend, metric, color }) {
|
||||
@@ -931,7 +868,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name: legend,
|
||||
color,
|
||||
}),
|
||||
...(`${metric}_ratio_p1sd_usd` in metricToIndexes
|
||||
...(brk.hasMetric(`${metric}_ratio_p1sd_usd`)
|
||||
? percentiles.map(({ name, color }) =>
|
||||
createBaseSeries({
|
||||
metric: `${metric}_ratio_${name}_usd`,
|
||||
@@ -954,7 +891,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
baseValue: { price: 1 },
|
||||
},
|
||||
}),
|
||||
...(`${metric}_ratio_p1sd` in metricToIndexes
|
||||
...(brk.hasMetric(`${metric}_ratio_p1sd`)
|
||||
? percentiles.map(({ name, color }) =>
|
||||
createBaseSeries({
|
||||
metric: `${metric}_ratio_${name}`,
|
||||
@@ -967,7 +904,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}),
|
||||
)
|
||||
: []),
|
||||
...(`${metric}_ratio_sma` in metricToIndexes
|
||||
...(brk.hasMetric(`${metric}_ratio_sma`)
|
||||
? ratioAverages.map(({ name, metric: metricAddon, color }) =>
|
||||
createBaseSeries({
|
||||
metric: `${metric}_ratio_${metricAddon}`,
|
||||
@@ -986,7 +923,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}),
|
||||
],
|
||||
},
|
||||
...(`${metric}_ratio_zscore` in metricToIndexes
|
||||
...(brk.hasMetric(`${metric}_ratio_zscore`)
|
||||
? [
|
||||
{
|
||||
name: "ZScores",
|
||||
@@ -1178,18 +1115,12 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {"_supply_in_profit"} SupplyInProfitSuffix
|
||||
* @typedef {EndsWith<SupplyInProfitSuffix>} MetricSupplyInProfit
|
||||
* @typedef {WithoutSuffix<MetricSupplyInProfit, SupplyInProfitSuffix>} CohortId
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} UTXOGroupObject
|
||||
* @property {string} args.name
|
||||
* @property {string} args.title
|
||||
* @property {Color} args.color
|
||||
* @property {"" | CohortId} args.id
|
||||
* @property {string} args.id
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1200,13 +1131,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {"" | CohortId} T
|
||||
* @param {T} id
|
||||
* @param {string} id
|
||||
*/
|
||||
const fixId = (id) =>
|
||||
id !== ""
|
||||
? /** @type {Exclude<"" | `${T}_`, "_">} */ (`${id}_`)
|
||||
: /** @type {const} */ ("");
|
||||
function fixId(id) {
|
||||
return id !== "" ? `${id}_` : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UTXOGroupObject | UTXOGroupsObject} args
|
||||
@@ -1465,11 +1394,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
]);
|
||||
}),
|
||||
},
|
||||
...(list.filter(({ id }) => `${fixId(id)}addr_count` in metricToIndexes)
|
||||
...(list.filter(({ id }) => brk.hasMetric(`${fixId(id)}addr_count`))
|
||||
.length > ("list" in args ? 1 : 0)
|
||||
? !("list" in args) ||
|
||||
list.filter(
|
||||
({ id }) => `${fixId(id)}empty_addr_count` in metricToIndexes,
|
||||
list.filter(({ id }) =>
|
||||
brk.hasMetric(`${fixId(id)}empty_addr_count`),
|
||||
).length <= 1
|
||||
? [
|
||||
{
|
||||
@@ -1478,7 +1407,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
bottom: list.flatMap(({ name, color, id: _id }) => {
|
||||
const id = fixId(_id);
|
||||
return [
|
||||
...(`${id}addr_count` in metricToIndexes
|
||||
...(brk.hasMetric(`${id}addr_count`)
|
||||
? /** @type {const} */ ([
|
||||
createBaseSeries({
|
||||
metric: `${id}addr_count`,
|
||||
@@ -1487,7 +1416,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}),
|
||||
])
|
||||
: []),
|
||||
...(`${id}empty_addr_count` in metricToIndexes
|
||||
...(brk.hasMetric(`${id}empty_addr_count`)
|
||||
? /** @type {const} */ ([
|
||||
createBaseSeries({
|
||||
metric: `${id}empty_addr_count`,
|
||||
@@ -1509,9 +1438,8 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name: "loaded",
|
||||
title: `Loaded Address Count ${title}`,
|
||||
bottom: list
|
||||
.filter(
|
||||
({ id }) =>
|
||||
`${fixId(id)}addr_count` in metricToIndexes,
|
||||
.filter(({ id }) =>
|
||||
brk.hasMetric(`${fixId(id)}addr_count`),
|
||||
)
|
||||
.flatMap(({ name, color, id: _id }) => {
|
||||
const id = fixId(_id);
|
||||
@@ -1524,19 +1452,16 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
];
|
||||
}),
|
||||
},
|
||||
...(list.filter(
|
||||
({ id }) =>
|
||||
`${fixId(id)}empty_addr_count` in metricToIndexes,
|
||||
...(list.filter(({ id }) =>
|
||||
brk.hasMetric(`${fixId(id)}empty_addr_count`),
|
||||
).length
|
||||
? [
|
||||
{
|
||||
name: "empty",
|
||||
title: `Empty Address Count ${title}`,
|
||||
bottom: list
|
||||
.filter(
|
||||
({ id }) =>
|
||||
`${fixId(id)}empty_addr_count` in
|
||||
metricToIndexes,
|
||||
.filter(({ id }) =>
|
||||
brk.hasMetric(`${fixId(id)}empty_addr_count`),
|
||||
)
|
||||
.flatMap(({ name, color, id: _id }) => {
|
||||
const id = fixId(_id);
|
||||
@@ -1622,7 +1547,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
]
|
||||
: []),
|
||||
...(!("list" in args) &&
|
||||
`${id}realized_cap_rel_to_own_market_cap` in metricToIndexes
|
||||
brk.hasMetric(`${id}realized_cap_rel_to_own_market_cap`)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -1658,8 +1583,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...(`${fixId(args.id)}realized_profit_to_loss_ratio` in
|
||||
metricToIndexes
|
||||
...(brk.hasMetric(
|
||||
`${fixId(args.id)}realized_profit_to_loss_ratio`,
|
||||
)
|
||||
? [
|
||||
createBaseSeries({
|
||||
metric: `${fixId(
|
||||
@@ -1797,7 +1723,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(asoprKey in metricToIndexes
|
||||
...(brk.hasMetric(asoprKey)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -1825,7 +1751,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(asoprKey in metricToIndexes
|
||||
...(brk.hasMetric(asoprKey)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -1853,7 +1779,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
...(asoprKey in metricToIndexes
|
||||
...(brk.hasMetric(asoprKey)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -1940,8 +1866,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
...(`${id}realized_profit_to_loss_ratio` in
|
||||
metricToIndexes
|
||||
...(brk.hasMetric(
|
||||
`${id}realized_profit_to_loss_ratio`,
|
||||
)
|
||||
? [
|
||||
createBaseSeries({
|
||||
metric: `${id}realized_profit_to_loss_ratio`,
|
||||
@@ -2102,7 +2029,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name,
|
||||
metric: `${fixId(id)}adjusted_sopr`,
|
||||
}))
|
||||
.filter(({ metric }) => metric in metricToIndexes);
|
||||
.filter(({ metric }) => brk.hasMetric(metric));
|
||||
|
||||
return reducedList.length
|
||||
? [
|
||||
@@ -2180,7 +2107,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name: "normal",
|
||||
color: colors.emerald,
|
||||
}),
|
||||
...(adjKey in metricToIndexes
|
||||
...(brk.hasMetric(adjKey)
|
||||
? [
|
||||
createBaseSeries({
|
||||
metric: adjKey,
|
||||
@@ -2204,7 +2131,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name: "normal",
|
||||
color: colors.red,
|
||||
}),
|
||||
...(adjKey in metricToIndexes
|
||||
...(brk.hasMetric(adjKey)
|
||||
? [
|
||||
createBaseSeries({
|
||||
metric: adjKey,
|
||||
@@ -2239,9 +2166,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name,
|
||||
metric: `${fixId(id)}adjusted_value_created`,
|
||||
}))
|
||||
.filter(
|
||||
({ metric }) => metric in metricToIndexes,
|
||||
);
|
||||
.filter(({ metric }) => brk.hasMetric(metric));
|
||||
return reducedList.length
|
||||
? [
|
||||
{
|
||||
@@ -2282,9 +2207,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name,
|
||||
metric: `${fixId(id)}adjusted_value_destroyed`,
|
||||
}))
|
||||
.filter(
|
||||
({ metric }) => metric in metricToIndexes,
|
||||
);
|
||||
.filter(({ metric }) => brk.hasMetric(metric));
|
||||
return reducedList.length
|
||||
? [
|
||||
{
|
||||
@@ -2361,10 +2284,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
}),
|
||||
...(`${fixId(
|
||||
args.id,
|
||||
)}unrealized_profit_rel_to_own_market_cap` in
|
||||
metricToIndexes
|
||||
...(brk.hasMetric(
|
||||
`${fixId(
|
||||
args.id,
|
||||
)}unrealized_profit_rel_to_own_market_cap`,
|
||||
)
|
||||
? [
|
||||
createBaseSeries({
|
||||
metric: `${fixId(
|
||||
@@ -2397,10 +2321,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(`${fixId(
|
||||
args.id,
|
||||
)}unrealized_profit_rel_to_own_total_unrealized_pnl` in
|
||||
metricToIndexes
|
||||
...(brk.hasMetric(
|
||||
`${fixId(
|
||||
args.id,
|
||||
)}unrealized_profit_rel_to_own_total_unrealized_pnl`,
|
||||
)
|
||||
? [
|
||||
createBaseSeries({
|
||||
metric: `${fixId(
|
||||
@@ -2505,8 +2430,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
title: useGroupName ? name : "Net",
|
||||
color: useGroupName ? color : undefined,
|
||||
}),
|
||||
...(`${fixId(id)}net_unrealized_pnl_rel_to_own_market_cap` in
|
||||
metricToIndexes
|
||||
...(brk.hasMetric(
|
||||
`${fixId(id)}net_unrealized_pnl_rel_to_own_market_cap`,
|
||||
)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -2521,10 +2447,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(`${fixId(
|
||||
id,
|
||||
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl` in
|
||||
metricToIndexes
|
||||
...(brk.hasMetric(
|
||||
`${fixId(
|
||||
id,
|
||||
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl`,
|
||||
)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
type: "Baseline",
|
||||
@@ -2660,7 +2587,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
}
|
||||
|
||||
return [
|
||||
...(env.localhost
|
||||
...(localhost
|
||||
? /** @type {const} */ ([
|
||||
{
|
||||
name: "Explorer",
|
||||
@@ -2792,7 +2719,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
createPriceLine({
|
||||
unit: "percentage",
|
||||
}),
|
||||
...(cagr in metricToIndexes
|
||||
...(brk.hasMetric(cagr)
|
||||
? [
|
||||
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
|
||||
metric: cagr,
|
||||
@@ -3652,133 +3579,135 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
|
||||
},
|
||||
{
|
||||
name: "Pools",
|
||||
tree: Object.entries(pools).map(([_id, name]) => {
|
||||
const id = /** @type {Pool} */ (_id);
|
||||
return {
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Dominance",
|
||||
title: `Mining Dominance of ${name}`,
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
metric: `${id}_1d_dominance`,
|
||||
name: "1d",
|
||||
color: colors.rose,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1w_dominance`,
|
||||
name: "1w",
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1m_dominance`,
|
||||
name: "1m",
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1y_dominance`,
|
||||
name: "1y",
|
||||
color: colors.lime,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_dominance`,
|
||||
name: "all time",
|
||||
color: colors.teal,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Blocks mined",
|
||||
title: `Blocks mined by ${name}`,
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
metric: `${id}_blocks_mined`,
|
||||
name: "Sum",
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_blocks_mined_cumulative`,
|
||||
name: "Cumulative",
|
||||
color: colors.blue,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1w_blocks_mined`,
|
||||
name: "1w Sum",
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1m_blocks_mined`,
|
||||
name: "1m Sum",
|
||||
color: colors.pink,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1y_blocks_mined`,
|
||||
name: "1y Sum",
|
||||
color: colors.purple,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rewards",
|
||||
title: `Rewards collected by ${name}`,
|
||||
bottom: [
|
||||
{
|
||||
metricAddon: "coinbase",
|
||||
cumulativeColor: colors.red,
|
||||
sumColor: colors.orange,
|
||||
},
|
||||
{
|
||||
metricAddon: "subsidy",
|
||||
cumulativeColor: colors.emerald,
|
||||
sumColor: colors.lime,
|
||||
},
|
||||
{
|
||||
metricAddon: "fee",
|
||||
cumulativeColor: colors.indigo,
|
||||
sumColor: colors.cyan,
|
||||
},
|
||||
].flatMap(
|
||||
({ metricAddon, sumColor, cumulativeColor }) => [
|
||||
...createSumCumulativeSeries({
|
||||
metric: `${id}_${metricAddon}`,
|
||||
common: metricAddon,
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
tree: Object.entries(brk.POOL_ID_TO_POOL_NAME).map(
|
||||
([_id, name]) => {
|
||||
const id = /** @type {PoolId} */ (_id);
|
||||
return {
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Dominance",
|
||||
title: `Mining Dominance of ${name}`,
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
metric: `${id}_1d_dominance`,
|
||||
name: "1d",
|
||||
color: colors.rose,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...createSumCumulativeSeries({
|
||||
metric: `${id}_${metricAddon}_btc`,
|
||||
common: metricAddon,
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
createBaseSeries({
|
||||
metric: `${id}_1w_dominance`,
|
||||
name: "1w",
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...createSumCumulativeSeries({
|
||||
metric: `${id}_${metricAddon}_usd`,
|
||||
common: metricAddon,
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
createBaseSeries({
|
||||
metric: `${id}_1m_dominance`,
|
||||
name: "1m",
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1y_dominance`,
|
||||
name: "1y",
|
||||
color: colors.lime,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_dominance`,
|
||||
name: "all time",
|
||||
color: colors.teal,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Days since block",
|
||||
title: `Days since ${name} mined a block`,
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
metric: `${id}_days_since_block`,
|
||||
name: "Since block",
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Blocks mined",
|
||||
title: `Blocks mined by ${name}`,
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
metric: `${id}_blocks_mined`,
|
||||
name: "Sum",
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_blocks_mined_cumulative`,
|
||||
name: "Cumulative",
|
||||
color: colors.blue,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1w_blocks_mined`,
|
||||
name: "1w Sum",
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1m_blocks_mined`,
|
||||
name: "1m Sum",
|
||||
color: colors.pink,
|
||||
defaultActive: false,
|
||||
}),
|
||||
createBaseSeries({
|
||||
metric: `${id}_1y_blocks_mined`,
|
||||
name: "1y Sum",
|
||||
color: colors.purple,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rewards",
|
||||
title: `Rewards collected by ${name}`,
|
||||
bottom: [
|
||||
{
|
||||
metricAddon: "coinbase",
|
||||
cumulativeColor: colors.red,
|
||||
sumColor: colors.orange,
|
||||
},
|
||||
{
|
||||
metricAddon: "subsidy",
|
||||
cumulativeColor: colors.emerald,
|
||||
sumColor: colors.lime,
|
||||
},
|
||||
{
|
||||
metricAddon: "fee",
|
||||
cumulativeColor: colors.indigo,
|
||||
sumColor: colors.cyan,
|
||||
},
|
||||
].flatMap(
|
||||
({ metricAddon, sumColor, cumulativeColor }) => [
|
||||
...createSumCumulativeSeries({
|
||||
metric: `${id}_${metricAddon}`,
|
||||
common: metricAddon,
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
}),
|
||||
...createSumCumulativeSeries({
|
||||
metric: `${id}_${metricAddon}_btc`,
|
||||
common: metricAddon,
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
}),
|
||||
...createSumCumulativeSeries({
|
||||
metric: `${id}_${metricAddon}_usd`,
|
||||
common: metricAddon,
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
}),
|
||||
],
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Days since block",
|
||||
title: `Days since ${name} mined a block`,
|
||||
bottom: [
|
||||
createBaseSeries({
|
||||
metric: `${id}_days_since_block`,
|
||||
name: "Since block",
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -113,59 +113,57 @@ export const serdeBool = {
|
||||
|
||||
export const serdeChartableIndex = {
|
||||
/**
|
||||
* @param {number} v
|
||||
* @returns {SerializedChartableIndex | null}
|
||||
* @param {IndexName} v
|
||||
* @returns {ChartableIndexName | null}
|
||||
*/
|
||||
serialize(v) {
|
||||
switch (v) {
|
||||
case /** @satisfies {DateIndex} */ (0):
|
||||
case "dateindex":
|
||||
return "date";
|
||||
case /** @satisfies {DecadeIndex} */ (1):
|
||||
case "decadeindex":
|
||||
return "decade";
|
||||
case /** @satisfies {DifficultyEpoch} */ (2):
|
||||
case "difficultyepoch":
|
||||
return "epoch";
|
||||
// case /** @satisfies {HalvingEpoch} */ (4):
|
||||
// return "halving";
|
||||
case /** @satisfies {Height} */ (5):
|
||||
case "height":
|
||||
return "timestamp";
|
||||
case /** @satisfies {MonthIndex} */ (7):
|
||||
case "monthindex":
|
||||
return "month";
|
||||
case /** @satisfies {QuarterIndex} */ (19):
|
||||
case "quarterindex":
|
||||
return "quarter";
|
||||
case /** @satisfies {SemesterIndex} */ (20):
|
||||
case "semesterindex":
|
||||
return "semester";
|
||||
case /** @satisfies {WeekIndex} */ (23):
|
||||
case "weekindex":
|
||||
return "week";
|
||||
case /** @satisfies {YearIndex} */ (24):
|
||||
case "yearindex":
|
||||
return "year";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @param {SerializedChartableIndex} v
|
||||
* @returns {Index}
|
||||
* @param {ChartableIndexName} v
|
||||
* @returns {IndexName}
|
||||
*/
|
||||
deserialize(v) {
|
||||
switch (v) {
|
||||
case "timestamp":
|
||||
return /** @satisfies {Height} */ (5);
|
||||
return "height";
|
||||
case "date":
|
||||
return /** @satisfies {DateIndex} */ (0);
|
||||
return "dateindex";
|
||||
case "week":
|
||||
return /** @satisfies {WeekIndex} */ (23);
|
||||
return "weekindex";
|
||||
case "epoch":
|
||||
return /** @satisfies {DifficultyEpoch} */ (2);
|
||||
return "difficultyepoch";
|
||||
case "month":
|
||||
return /** @satisfies {MonthIndex} */ (7);
|
||||
return "monthindex";
|
||||
case "quarter":
|
||||
return /** @satisfies {QuarterIndex} */ (19);
|
||||
return "quarterindex";
|
||||
case "semester":
|
||||
return /** @satisfies {SemesterIndex} */ (20);
|
||||
return "semesterindex";
|
||||
case "year":
|
||||
return /** @satisfies {YearIndex} */ (24);
|
||||
return "yearindex";
|
||||
case "decade":
|
||||
return /** @satisfies {DecadeIndex} */ (1);
|
||||
return "decadeindex";
|
||||
default:
|
||||
throw Error("todo");
|
||||
}
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
/**
|
||||
* @import * as _ from "./modules/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts"
|
||||
*
|
||||
* @import { Signal, Signals, Accessor } from "./modules/brk-signals/index";
|
||||
*
|
||||
* @import { BRK } from "./modules/brk-client/index.js"
|
||||
* @import { Metric, MetricToIndexes } from "./modules/brk-client/metrics"
|
||||
* @import { IndexName } from "./modules/brk-client/generated/metrics"
|
||||
* @import { PoolId, PoolIdToPoolName } from "./modules/brk-client/generated/pools"
|
||||
*
|
||||
* @import { Resources, MetricResource } from './modules/brk-resources/index.js'
|
||||
*
|
||||
* @import { Valued, SingleValueData, CandlestickData, Series, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions, CreateChartElement, Chart } from "./core/chart/index"
|
||||
*
|
||||
* @import * as _ from "./packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts"
|
||||
*
|
||||
* @import { SerializedChartableIndex } from "./panes/chart";
|
||||
*
|
||||
* @import { Signal, Signals, Accessor } from "./packages/solidjs-signals/wrapper";
|
||||
*
|
||||
* @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, WeekIndex, SemesterIndex, YearIndex, MetricToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex, EmptyAddressIndex, LoadedAddressIndex } from "./bridge/vecs"
|
||||
*
|
||||
* @import { Pools, Pool } from "./bridge/pools"
|
||||
*
|
||||
* @import { Color, ColorName, Colors } from "./core/colors"
|
||||
*
|
||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree } from "./core/options/partial"
|
||||
*
|
||||
* @import { WebSockets } from "./core/ws"
|
||||
*
|
||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree } from "./core/options/partial"
|
||||
*
|
||||
* @import { Unit } from "./core/serde"
|
||||
*
|
||||
* @import { ChartableIndexName } from "./panes/chart/index.js";
|
||||
*/
|
||||
|
||||
// import uFuzzy = require("./modules/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts");
|
||||
|
||||
/**
|
||||
* @typedef {typeof import("./lazy")["default"]} Packages
|
||||
* @typedef {typeof import("./core/utils")} Utilities
|
||||
* @typedef {typeof import("./core/env")["default"]} Env
|
||||
* @typedef {typeof import("./core/elements")["default"]} Elements
|
||||
* @typedef {typeof import("./lazy")["default"]} Modules
|
||||
* @typedef {[number, number, number, number]} OHLCTuple
|
||||
*/
|
||||
|
||||
// DO NOT CHANGE, Exact format is expected in `brk_bundler`
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
const imports = {
|
||||
async signals() {
|
||||
return import("./packages/solidjs-signals/index.js").then((d) => d.default);
|
||||
return import("./modules/brk-signals/index.js").then((d) => d.default);
|
||||
},
|
||||
async leanQr() {
|
||||
return import("./modules/lean-qr/2.6.0/index.mjs").then((d) => d);
|
||||
},
|
||||
async ufuzzy() {
|
||||
return import("./modules/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.mjs").then(
|
||||
({ default: d }) => d,
|
||||
);
|
||||
},
|
||||
async brkClient() {
|
||||
return import("./modules/brk-client/index.js").then((d) => d);
|
||||
},
|
||||
async brkResources() {
|
||||
return import("./modules/brk-resources/index.js").then((d) => d);
|
||||
},
|
||||
|
||||
async chart() {
|
||||
return window.document.fonts.ready.then(() =>
|
||||
import("./core/chart/index.js").then((d) => d.default),
|
||||
);
|
||||
},
|
||||
async leanQr() {
|
||||
return import("./packages/lean-qr/2.5.0/index.mjs").then((d) => d);
|
||||
},
|
||||
async ufuzzy() {
|
||||
return import("./packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.mjs").then(
|
||||
({ default: d }) => d,
|
||||
);
|
||||
},
|
||||
async modernScreenshot() {
|
||||
return import("./packages/modern-screenshot/index.js").then((d) => d);
|
||||
},
|
||||
async brk() {
|
||||
return import("./packages/brk/index.js").then((d) => d);
|
||||
},
|
||||
|
||||
async options() {
|
||||
return import("./core/options/full.js").then((d) => d);
|
||||
},
|
||||
|
||||
+524
-537
File diff suppressed because it is too large
Load Diff
+40
-53
@@ -2,9 +2,11 @@ import {
|
||||
createShadow,
|
||||
createHorizontalChoiceField,
|
||||
createHeader,
|
||||
} from "../core/dom";
|
||||
import { serdeChartableIndex, serdeOptNumber } from "../core/serde";
|
||||
import { throttle } from "../core/timing";
|
||||
} from "../../core/dom";
|
||||
import { chartElement } from "../../core/elements";
|
||||
import { ios, canShare } from "../../core/env";
|
||||
import { serdeChartableIndex, serdeOptNumber } from "../../core/serde";
|
||||
import { throttle } from "../../core/timing";
|
||||
|
||||
const keyPrefix = "chart";
|
||||
const ONE_BTC_IN_SATS = 100_000_000;
|
||||
@@ -13,7 +15,7 @@ const LINE = "line";
|
||||
const CANDLE = "candle";
|
||||
|
||||
/**
|
||||
* @typedef {"timestamp" | "date" | "week" | "epoch" | "month" | "quarter" | "semester" | "year" | "decade" } SerializedChartableIndex
|
||||
* @typedef {"timestamp" | "date" | "week" | "epoch" | "month" | "quarter" | "semester" | "year" | "decade" } ChartableIndexName
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -22,38 +24,29 @@ const CANDLE = "candle";
|
||||
* @param {CreateChartElement} args.createChartElement
|
||||
* @param {Accessor<ChartOption>} args.option
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
* @param {Env} args.env
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Packages} args.packages
|
||||
* @param {Resources} args.resources
|
||||
* @param {BRK} args.brk
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
elements,
|
||||
createChartElement,
|
||||
option,
|
||||
signals,
|
||||
utils,
|
||||
env,
|
||||
webSockets,
|
||||
vecsResources,
|
||||
metricToIndexes,
|
||||
packages,
|
||||
resources,
|
||||
brk,
|
||||
}) {
|
||||
elements.charts.append(createShadow("left"));
|
||||
elements.charts.append(createShadow("right"));
|
||||
chartElement.append(createShadow("left"));
|
||||
chartElement.append(createShadow("right"));
|
||||
|
||||
const { headerElement, headingElement } = createHeader();
|
||||
elements.charts.append(headerElement);
|
||||
chartElement.append(headerElement);
|
||||
|
||||
const { index, fieldset } = createIndexSelector({
|
||||
option,
|
||||
metricToIndexes,
|
||||
brk,
|
||||
signals,
|
||||
utils,
|
||||
});
|
||||
|
||||
const TIMERANGE_LS_KEY = signals.createMemo(
|
||||
@@ -80,13 +73,11 @@ export function init({
|
||||
});
|
||||
|
||||
const chart = createChartElement({
|
||||
parent: elements.charts,
|
||||
parent: chartElement,
|
||||
signals,
|
||||
colors,
|
||||
id: "charts",
|
||||
utils,
|
||||
vecsResources,
|
||||
elements,
|
||||
resources,
|
||||
index,
|
||||
timeScaleSetCallback: (unknownTimeScaleCallback) => {
|
||||
// TODO: Although it mostly works in practice, need to make it more robust, there is no guarantee that this runs in order and wait for `from` and `to` to update when `index` and thus `TIMERANGE_LS_KEY` is updated
|
||||
@@ -105,12 +96,11 @@ export function init({
|
||||
},
|
||||
});
|
||||
|
||||
if (!(env.ios && !("canShare" in navigator))) {
|
||||
if (!(ios && !canShare)) {
|
||||
const chartBottomRightCanvas = Array.from(
|
||||
chart.inner.chartElement().getElementsByTagName("tr"),
|
||||
).at(-1)?.lastChild?.firstChild?.firstChild;
|
||||
if (chartBottomRightCanvas) {
|
||||
const charts = elements.charts;
|
||||
const domain = window.document.createElement("p");
|
||||
domain.innerText = `${window.location.host}`;
|
||||
domain.id = "domain";
|
||||
@@ -121,20 +111,20 @@ export function init({
|
||||
screenshotButton.title = "Screenshot";
|
||||
chartBottomRightCanvas.replaceWith(screenshotButton);
|
||||
screenshotButton.addEventListener("click", () => {
|
||||
packages.modernScreenshot().then(async ({ screenshot }) => {
|
||||
charts.dataset.screenshot = "true";
|
||||
charts.append(domain);
|
||||
import("./screenshot").then(async ({ screenshot }) => {
|
||||
chartElement.dataset.screenshot = "true";
|
||||
chartElement.append(domain);
|
||||
seriesTypeField.hidden = true;
|
||||
try {
|
||||
await screenshot({
|
||||
element: charts,
|
||||
element: chartElement,
|
||||
name: option().path.join("-"),
|
||||
title: option().title,
|
||||
});
|
||||
} catch {}
|
||||
charts.removeChild(domain);
|
||||
chartElement.removeChild(domain);
|
||||
seriesTypeField.hidden = false;
|
||||
charts.dataset.screenshot = "false";
|
||||
chartElement.dataset.screenshot = "false";
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -148,7 +138,7 @@ export function init({
|
||||
}, 250),
|
||||
);
|
||||
|
||||
elements.charts.append(fieldset);
|
||||
chartElement.append(fieldset);
|
||||
|
||||
const { field: seriesTypeField, selected: topSeriesType_ } =
|
||||
createHorizontalChoiceField({
|
||||
@@ -205,7 +195,7 @@ export function init({
|
||||
* @param {Object} params
|
||||
* @param {ISeries} params.iseries
|
||||
* @param {Unit} params.unit
|
||||
* @param {Index} params.index
|
||||
* @param {IndexName} params.index
|
||||
*/
|
||||
function printLatest({ iseries, unit, index }) {
|
||||
const _latest = webSockets.kraken1dCandle.latest();
|
||||
@@ -234,9 +224,9 @@ export function init({
|
||||
const date = new Date(latest.time * 1000);
|
||||
|
||||
switch (index) {
|
||||
case /** @satisfies {Height} */ (5):
|
||||
case /** @satisfies {DifficultyEpoch} */ (2):
|
||||
case /** @satisfies {HalvingEpoch} */ (4): {
|
||||
case "height":
|
||||
case "difficultyepoch":
|
||||
case "halvingepoch": {
|
||||
if ("close" in last) {
|
||||
last.low = Math.min(last.low, latest.close);
|
||||
last.high = Math.max(last.high, latest.close);
|
||||
@@ -244,24 +234,24 @@ export function init({
|
||||
iseries.update(last);
|
||||
break;
|
||||
}
|
||||
case /** @satisfies {DateIndex} */ (0): {
|
||||
case "dateindex": {
|
||||
iseries.update(last);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (index === /** @satisfies {WeekIndex} */ (23)) {
|
||||
if (index === "weekindex") {
|
||||
date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7));
|
||||
} else if (index === /** @satisfies {MonthIndex} */ (7)) {
|
||||
} else if (index === "monthindex") {
|
||||
date.setUTCDate(1);
|
||||
} else if (index === /** @satisfies {QuarterIndex} */ (19)) {
|
||||
} else if (index === "quarterindex") {
|
||||
const month = date.getUTCMonth();
|
||||
date.setUTCMonth(month - (month % 3), 1);
|
||||
} else if (index === /** @satisfies {SemesterIndex} */ (20)) {
|
||||
} else if (index === "semesterindex") {
|
||||
const month = date.getUTCMonth();
|
||||
date.setUTCMonth(month - (month % 6), 1);
|
||||
} else if (index === /** @satisfies {YearIndex} */ (24)) {
|
||||
} else if (index === "yearindex") {
|
||||
date.setUTCMonth(0, 1);
|
||||
} else if (index === /** @satisfies {DecadeIndex} */ (1)) {
|
||||
} else if (index === "decadeindex") {
|
||||
date.setUTCFullYear(
|
||||
Math.floor(date.getUTCFullYear() / 10) * 10,
|
||||
0,
|
||||
@@ -446,9 +436,7 @@ export function init({
|
||||
blueprints[unit]?.forEach((blueprint, order) => {
|
||||
order += orderStart;
|
||||
|
||||
const indexes = /** @type {readonly number[]} */ (
|
||||
metricToIndexes[blueprint.metric]
|
||||
);
|
||||
const indexes = brk.getIndexesFromMetric(blueprint.metric);
|
||||
|
||||
if (indexes.includes(index)) {
|
||||
switch (blueprint.type) {
|
||||
@@ -519,12 +507,11 @@ export function init({
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Accessor<ChartOption>} args.option
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {BRK} args.brk
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
*/
|
||||
function createIndexSelector({ option, metricToIndexes, signals, utils }) {
|
||||
const choices_ = /** @satisfies {SerializedChartableIndex[]} */ ([
|
||||
function createIndexSelector({ option, brk, signals }) {
|
||||
const choices_ = /** @satisfies {ChartableIndexName[]} */ ([
|
||||
"timestamp",
|
||||
"date",
|
||||
"week",
|
||||
@@ -548,7 +535,7 @@ function createIndexSelector({ option, metricToIndexes, signals, utils }) {
|
||||
[Object.values(o.top), Object.values(o.bottom)]
|
||||
.flat(2)
|
||||
.filter((blueprint) => !blueprint.metric.startsWith("constant_"))
|
||||
.map((blueprint) => metricToIndexes[blueprint.metric])
|
||||
.map((blueprint) => brk.getIndexesFromMetric(blueprint.metric))
|
||||
.flat(),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ios } from "../../core/env";
|
||||
import { domToBlob } from "../../modules/modern-screenshot/4.6.6/dist/index.mjs";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Element} args.element
|
||||
* @param {string} args.name
|
||||
* @param {string} args.title
|
||||
*/
|
||||
export async function screenshot({ element, name, title }) {
|
||||
const blob = await domToBlob(element, {
|
||||
scale: 2,
|
||||
});
|
||||
|
||||
if (ios) {
|
||||
const file = new File(
|
||||
[blob],
|
||||
`bitview-${name}-${new Date().toJSON().split(".")[0]}.png`,
|
||||
{
|
||||
type: "image/png",
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
await navigator.share({
|
||||
files: [file],
|
||||
title: `${title} on ${window.document.location.hostname}`,
|
||||
});
|
||||
return;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
window.open(url, "_blank");
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { randomFromArray } from "../core/array";
|
||||
import { explorerElement } from "../core/elements";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
@@ -6,26 +7,22 @@ import { randomFromArray } from "../core/array";
|
||||
* @param {CreateChartElement} args.createChartElement
|
||||
* @param {Accessor<ChartOption>} args.option
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Resources} args.resources
|
||||
* @param {BRK} args.brk
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
elements,
|
||||
createChartElement,
|
||||
option,
|
||||
signals,
|
||||
utils,
|
||||
webSockets,
|
||||
vecsResources,
|
||||
metricToIndexes,
|
||||
resources,
|
||||
brk,
|
||||
}) {
|
||||
const chain = window.document.createElement("div");
|
||||
chain.id = "chain";
|
||||
elements.explorer.append(chain);
|
||||
explorerElement.append(chain);
|
||||
|
||||
// vecsResources.getOrCreate(/** @satisfies {Height}*/ (5), "height");
|
||||
//
|
||||
|
||||
@@ -9,6 +9,12 @@ import {
|
||||
createHeader,
|
||||
createSelect,
|
||||
} from "../core/dom";
|
||||
import { simulationElement } from "../core/elements";
|
||||
import {
|
||||
numberToDollars,
|
||||
numberToPercentage,
|
||||
numberToUSNumber,
|
||||
} from "../core/format";
|
||||
import { serdeDate, serdeOptDate, serdeOptNumber } from "../core/serde";
|
||||
|
||||
/**
|
||||
@@ -16,18 +22,9 @@ import { serdeDate, serdeOptDate, serdeOptNumber } from "../core/serde";
|
||||
* @param {Colors} args.colors
|
||||
* @param {CreateChartElement} args.createChartElement
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {Resources} args.resources
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
elements,
|
||||
createChartElement,
|
||||
signals,
|
||||
utils,
|
||||
vecsResources,
|
||||
}) {
|
||||
export function init({ colors, createChartElement, signals, resources }) {
|
||||
/**
|
||||
* @typedef {Object} Frequency
|
||||
* @property {string} name
|
||||
@@ -39,8 +36,6 @@ export function init({
|
||||
* @property {Frequency[]} list
|
||||
*/
|
||||
|
||||
const simulationElement = elements.simulation;
|
||||
|
||||
const domExtended = {
|
||||
/**
|
||||
* @param {Object} args
|
||||
@@ -686,7 +681,8 @@ export function init({
|
||||
},
|
||||
);
|
||||
|
||||
const index = () => /** @type {DateIndex} */ (0);
|
||||
/** @type {() => IndexName} */
|
||||
const index = () => "dateindex";
|
||||
|
||||
createChartElement({
|
||||
index,
|
||||
@@ -695,9 +691,7 @@ export function init({
|
||||
colors,
|
||||
id: `result`,
|
||||
fitContent: true,
|
||||
vecsResources,
|
||||
utils,
|
||||
elements,
|
||||
resources,
|
||||
config: [
|
||||
{
|
||||
unit: "usd",
|
||||
@@ -740,9 +734,7 @@ export function init({
|
||||
colors,
|
||||
id: `bitcoin`,
|
||||
fitContent: true,
|
||||
vecsResources,
|
||||
elements,
|
||||
utils,
|
||||
resources,
|
||||
config: [
|
||||
{
|
||||
unit: "btc",
|
||||
@@ -765,9 +757,7 @@ export function init({
|
||||
colors,
|
||||
id: `average-price`,
|
||||
fitContent: true,
|
||||
vecsResources,
|
||||
utils,
|
||||
elements,
|
||||
resources,
|
||||
config: [
|
||||
{
|
||||
unit: "usd",
|
||||
@@ -794,11 +784,9 @@ export function init({
|
||||
parent: resultsElement,
|
||||
signals,
|
||||
colors,
|
||||
vecsResources,
|
||||
resources,
|
||||
id: `return-ratio`,
|
||||
fitContent: true,
|
||||
utils,
|
||||
elements,
|
||||
config: [
|
||||
{
|
||||
unit: "usd",
|
||||
@@ -820,9 +808,7 @@ export function init({
|
||||
colors,
|
||||
id: `simulation-profitability-ratios`,
|
||||
fitContent: true,
|
||||
vecsResources,
|
||||
utils,
|
||||
elements,
|
||||
resources,
|
||||
config: [
|
||||
{
|
||||
unit: "percentage",
|
||||
@@ -844,8 +830,8 @@ export function init({
|
||||
],
|
||||
});
|
||||
|
||||
vecsResources
|
||||
.getOrCreate(/** @satisfies {DateIndex} */ (0), "price_close")
|
||||
resources.metrics
|
||||
.getOrCreate("price_close", "dateindex")
|
||||
.fetch()
|
||||
.then((_closes) => {
|
||||
if (!_closes) return;
|
||||
@@ -1055,11 +1041,11 @@ export function init({
|
||||
});
|
||||
});
|
||||
|
||||
const f = utils.locale.numberToUSFormat;
|
||||
const f = numberToUSNumber;
|
||||
/** @param {number} v */
|
||||
const fd = (v) => utils.formatters.dollars.format(v);
|
||||
const fd = (v) => numberToDollars.format(v);
|
||||
/** @param {number} v */
|
||||
const fp = (v) => utils.formatters.percentage.format(v);
|
||||
const fp = (v) => numberToPercentage.format(v);
|
||||
/**
|
||||
* @param {ColorName} c
|
||||
* @param {string} t
|
||||
@@ -1098,45 +1084,6 @@ export function init({
|
||||
|
||||
p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
|
||||
|
||||
signals.createEffect(
|
||||
() => 0.073,
|
||||
(lowestAnnual4YReturn) => {
|
||||
const serLowestAnnual4YReturn = c(
|
||||
"cyan",
|
||||
`${fp(lowestAnnual4YReturn)}`,
|
||||
);
|
||||
|
||||
const lowestAnnual4YReturnPercentage = 1 + lowestAnnual4YReturn;
|
||||
/**
|
||||
* @param {number} power
|
||||
*/
|
||||
function bitcoinValueReturn(power) {
|
||||
return (
|
||||
bitcoinValue *
|
||||
Math.pow(lowestAnnual4YReturnPercentage, power)
|
||||
);
|
||||
}
|
||||
const bitcoinValueAfter4y = bitcoinValueReturn(4);
|
||||
const serBitcoinValueAfter4y = c(
|
||||
"purple",
|
||||
fd(bitcoinValueAfter4y),
|
||||
);
|
||||
const bitcoinValueAfter10y = bitcoinValueReturn(10);
|
||||
const serBitcoinValueAfter10y = c(
|
||||
"fuchsia",
|
||||
fd(bitcoinValueAfter10y),
|
||||
);
|
||||
const bitcoinValueAfter21y = bitcoinValueReturn(21);
|
||||
const serBitcoinValueAfter21y = c(
|
||||
"pink",
|
||||
fd(bitcoinValueAfter21y),
|
||||
);
|
||||
|
||||
/** @param {number} v */
|
||||
p4.innerHTML = `The lowest annual return after 4 years has historically been ${serLowestAnnual4YReturn}.<br/>Using it as the baseline, your Bitcoin would be worth ${serBitcoinValueAfter4y} after 4 years, ${serBitcoinValueAfter10y} after 10 years and ${serBitcoinValueAfter21y} after 21 years.`;
|
||||
},
|
||||
);
|
||||
|
||||
totalInvestedAmountData.set((a) => a);
|
||||
bitcoinValueData.set((a) => a);
|
||||
bitcoinData.set((a) => a);
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { randomFromArray } from "../core/array";
|
||||
import { createButtonElement, createHeader, createSelect } from "../core/dom";
|
||||
import { tableElement } from "../core/elements";
|
||||
import { serdeMetrics, serdeString, serdeUnit } from "../core/serde";
|
||||
import { resetParams } from "../core/url";
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Option} args.option
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Signals} args.signals
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {BRK} args.brk
|
||||
* @param {Resources} args.resources
|
||||
*/
|
||||
function createTable({
|
||||
utils,
|
||||
metricToIndexes,
|
||||
signals,
|
||||
option,
|
||||
vecsResources,
|
||||
}) {
|
||||
function createTable({ brk, signals, option, resources }) {
|
||||
const indexToMetrics = createIndexToMetrics(metricToIndexes);
|
||||
|
||||
const serializedIndexes = createSerializedIndexes();
|
||||
@@ -150,7 +144,7 @@ function createTable({
|
||||
let from = 0;
|
||||
let to = 0;
|
||||
|
||||
vecsResources
|
||||
resources
|
||||
.getOrCreate(index, serializedIndex())
|
||||
.fetch()
|
||||
.then((vec) => {
|
||||
@@ -292,11 +286,11 @@ function createTable({
|
||||
const unit = serdeUnit.deserialize(metric);
|
||||
th.setUnit(unit);
|
||||
|
||||
const vec = vecsResources.getOrCreate(index, metric);
|
||||
const vec = resources.getOrCreate(index, metric);
|
||||
|
||||
vec.fetch({ from, to });
|
||||
|
||||
const fetchedKey = vecsResources.genFetchedKey({ from, to });
|
||||
const fetchedKey = resources.genFetchedKey({ from, to });
|
||||
|
||||
columns.set((l) => {
|
||||
const i = l.indexOf(prevMetric ?? metric);
|
||||
@@ -355,21 +349,12 @@ function createTable({
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Option} args.option
|
||||
* @param {Elements} args.elements
|
||||
* @param {VecsResources} args.vecsResources
|
||||
* @param {MetricToIndexes} args.metricToIndexes
|
||||
* @param {Resources} args.resources
|
||||
* @param {BRK} args.brk
|
||||
*/
|
||||
export function init({
|
||||
elements,
|
||||
signals,
|
||||
option,
|
||||
utils,
|
||||
vecsResources,
|
||||
metricToIndexes,
|
||||
}) {
|
||||
const parent = elements.table;
|
||||
export function init({ signals, option, resources, brk }) {
|
||||
const parent = tableElement;
|
||||
const { headerElement } = createHeader("Table");
|
||||
parent.append(headerElement);
|
||||
|
||||
@@ -378,9 +363,8 @@ export function init({
|
||||
|
||||
const table = createTable({
|
||||
signals,
|
||||
utils,
|
||||
metricToIndexes,
|
||||
vecsResources,
|
||||
brk,
|
||||
resources,
|
||||
option,
|
||||
});
|
||||
div.append(table.element);
|
||||
@@ -398,103 +382,6 @@ export function init({
|
||||
);
|
||||
}
|
||||
|
||||
function createSerializedIndexes() {
|
||||
return /** @type {const} */ ([
|
||||
/** @satisfies {Metric} */ ("dateindex"),
|
||||
/** @satisfies {Metric} */ ("decadeindex"),
|
||||
/** @satisfies {Metric} */ ("difficultyepoch"),
|
||||
/** @satisfies {Metric} */ ("emptyoutputindex"),
|
||||
/** @satisfies {Metric} */ ("halvingepoch"),
|
||||
/** @satisfies {Metric} */ ("height"),
|
||||
/** @satisfies {Metric} */ ("inputindex"),
|
||||
/** @satisfies {Metric} */ ("monthindex"),
|
||||
/** @satisfies {Metric} */ ("opreturnindex"),
|
||||
/** @satisfies {Metric} */ ("semesterindex"),
|
||||
/** @satisfies {Metric} */ ("outputindex"),
|
||||
/** @satisfies {Metric} */ ("p2aaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2msoutputindex"),
|
||||
/** @satisfies {Metric} */ ("p2pk33addressindex"),
|
||||
/** @satisfies {Metric} */ ("p2pk65addressindex"),
|
||||
/** @satisfies {Metric} */ ("p2pkhaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2shaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2traddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2wpkhaddressindex"),
|
||||
/** @satisfies {Metric} */ ("p2wshaddressindex"),
|
||||
/** @satisfies {Metric} */ ("quarterindex"),
|
||||
/** @satisfies {Metric} */ ("txindex"),
|
||||
/** @satisfies {Metric} */ ("unknownoutputindex"),
|
||||
/** @satisfies {Metric} */ ("weekindex"),
|
||||
/** @satisfies {Metric} */ ("yearindex"),
|
||||
/** @satisfies {Metric} */ ("loadedaddressindex"),
|
||||
/** @satisfies {Metric} */ ("emptyaddressindex"),
|
||||
]);
|
||||
}
|
||||
/** @typedef {ReturnType<typeof createSerializedIndexes>} SerializedIndexes */
|
||||
/** @typedef {SerializedIndexes[number]} SerializedIndex */
|
||||
|
||||
/**
|
||||
* @param {SerializedIndex} serializedIndex
|
||||
* @returns {Index}
|
||||
*/
|
||||
function serializedIndexToIndex(serializedIndex) {
|
||||
switch (serializedIndex) {
|
||||
case "height":
|
||||
return /** @satisfies {Height} */ (5);
|
||||
case "dateindex":
|
||||
return /** @satisfies {DateIndex} */ (0);
|
||||
case "weekindex":
|
||||
return /** @satisfies {WeekIndex} */ (23);
|
||||
case "difficultyepoch":
|
||||
return /** @satisfies {DifficultyEpoch} */ (2);
|
||||
case "monthindex":
|
||||
return /** @satisfies {MonthIndex} */ (7);
|
||||
case "quarterindex":
|
||||
return /** @satisfies {QuarterIndex} */ (19);
|
||||
case "semesterindex":
|
||||
return /** @satisfies {SemesterIndex} */ (20);
|
||||
case "yearindex":
|
||||
return /** @satisfies {YearIndex} */ (24);
|
||||
case "decadeindex":
|
||||
return /** @satisfies {DecadeIndex} */ (1);
|
||||
case "halvingepoch":
|
||||
return /** @satisfies {HalvingEpoch} */ (4);
|
||||
case "txindex":
|
||||
return /** @satisfies {TxIndex} */ (21);
|
||||
case "inputindex":
|
||||
return /** @satisfies {InputIndex} */ (6);
|
||||
case "outputindex":
|
||||
return /** @satisfies {OutputIndex} */ (9);
|
||||
case "p2pk33addressindex":
|
||||
return /** @satisfies {P2PK33AddressIndex} */ (12);
|
||||
case "p2pk65addressindex":
|
||||
return /** @satisfies {P2PK65AddressIndex} */ (13);
|
||||
case "p2pkhaddressindex":
|
||||
return /** @satisfies {P2PKHAddressIndex} */ (14);
|
||||
case "p2shaddressindex":
|
||||
return /** @satisfies {P2SHAddressIndex} */ (15);
|
||||
case "p2traddressindex":
|
||||
return /** @satisfies {P2TRAddressIndex} */ (16);
|
||||
case "p2wpkhaddressindex":
|
||||
return /** @satisfies {P2WPKHAddressIndex} */ (17);
|
||||
case "p2wshaddressindex":
|
||||
return /** @satisfies {P2WSHAddressIndex} */ (18);
|
||||
case "p2aaddressindex":
|
||||
return /** @satisfies {P2AAddressIndex} */ (10);
|
||||
case "p2msoutputindex":
|
||||
return /** @satisfies {P2MSOutputIndex} */ (11);
|
||||
case "opreturnindex":
|
||||
return /** @satisfies {OpReturnIndex} */ (8);
|
||||
case "emptyoutputindex":
|
||||
return /** @satisfies {EmptyOutputIndex} */ (3);
|
||||
case "unknownoutputindex":
|
||||
return /** @satisfies {UnknownOutputIndex} */ (22);
|
||||
case "emptyaddressindex":
|
||||
return /** @satisfies {EmptyAddressIndex} */ (26);
|
||||
case "loadedaddressindex":
|
||||
return /** @satisfies {LoadedAddressIndex} */ (25);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MetricToIndexes} metricToIndexes
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user