diff --git a/crates/brk_computer/src/vecs/stateful/mod.rs b/crates/brk_computer/src/vecs/stateful/mod.rs index 735cd3fa9..eb5126271 100644 --- a/crates/brk_computer/src/vecs/stateful/mod.rs +++ b/crates/brk_computer/src/vecs/stateful/mod.rs @@ -1443,10 +1443,7 @@ impl Vecs { let height = txindex_to_height .get_or_read(input_txindex, &txindex_to_height_mmap) .unwrap() - .unwrap_or_else(|| { - dbg!(input_txindex, txindex_to_height.len()); - unreachable!("") - }) + .unwrap() .into_inner(); (height, value, input_type) diff --git a/crates/brk_vec/src/structs/format.rs b/crates/brk_vec/src/structs/format.rs index 1ed90739a..1ba767b37 100644 --- a/crates/brk_vec/src/structs/format.rs +++ b/crates/brk_vec/src/structs/format.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, ValueEnum, )] pub enum Format { - #[default] Compressed, + #[default] Raw, } diff --git a/websites/default/packages/lightweight-charts/wrapper.js b/websites/default/packages/lightweight-charts/wrapper.js index ceb8b5297..ec81de792 100644 --- a/websites/default/packages/lightweight-charts/wrapper.js +++ b/websites/default/packages/lightweight-charts/wrapper.js @@ -14,9 +14,10 @@ /** * @typedef {Object} Series - * @property {ISeriesApi} inner + * @property {ISeriesApi} inner * @property {string} id * @property {Signal} active + * @property {Signal} hasData * @property {VoidFunction} remove */ @@ -26,8 +27,12 @@ */ /** - * @typedef {ChartData<_SingleValueData>} SingleValueData - * @typedef {ChartData<_CandlestickData>} CandlestickData + * @typedef {ChartData<_SingleValueData>} SingleValueData + * @typedef {ChartData<_CandlestickData>} CandlestickData + */ + +/** + * @typedef {function({ iseries: ISeriesApi; unit: Unit; index: Index }): void} SetDataCallback */ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { @@ -123,7 +128,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { } : {}), // ..._options, - }), + }) ); ichart.priceScale("right").applyOptions({ @@ -155,7 +160,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { }, }, }); - }, + } ); let timeScaleSet = false; @@ -167,12 +172,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { index === /** @satisfies {MonthIndex} */ (7) ? 1 : index === /** @satisfies {QuarterIndex} */ (19) - ? 3 - : index === /** @satisfies {YearIndex} */ (23) - ? 12 - : index === /** @satisfies {DecadeIndex} */ (1) - ? 120 - : 0.5; + ? 3 + : index === /** @satisfies {YearIndex} */ (23) + ? 12 + : index === /** @satisfies {DecadeIndex} */ (1) + ? 120 + : 0.5; ichart.applyOptions({ timeScale: { @@ -195,12 +200,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { activeResources.forEach((v) => { v.fetch(); }); - }), + }) ); if (fitContent) { new ResizeObserver(() => ichart.timeScale().fitContent()).observe( - chartDiv, + chartDiv ); } @@ -229,7 +234,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { const children = Array.from(parent.childNodes).filter( (element) => /** @type {HTMLElement} */ (element).dataset.position === - position, + position ); if (children.length === 1) { @@ -251,7 +256,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { fieldset.append(createChild(pane)); }), - paneIndex ? 50 : 0, + paneIndex ? 50 : 0 ); } @@ -293,14 +298,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { /** * @param {Object} args - * @param {ISeriesApi} args.iseries + * @param {ISeriesApi} args.iseries * @param {string} args.name * @param {Unit} args.unit * @param {number} args.order * @param {Color[]} args.colors * @param {SeriesType} args.seriesType * @param {VecId} [args.vecId] - * @param {Accessor} [args.data] + * @param {SetDataCallback} [args.setDataCallback] + * @param {Accessor[]>} [args.data] * @param {number} args.paneIndex * @param {boolean} [args.defaultActive] */ @@ -311,6 +317,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { unit, order, seriesType, + setDataCallback, paneIndex, defaultActive, colors, @@ -327,13 +334,15 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { }, }); + const hasData = signals.createSignal(false); + let url = /** @type {string | undefined} */ (undefined); signals.createEffect(active, (active) => // Or remove ? iseries.applyOptions({ visible: active, - }), + }) ); iseries.setSeriesOrder(order); @@ -345,9 +354,11 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { const series = { inner: iseries, active, + hasData, id, remove() { dispose(); + // @ts-ignore chart.inner.removeSeries(iseries); if (_valuesResource) { activeResources.delete(_valuesResource); @@ -361,7 +372,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { index, index === /** @satisfies {Height} */ (5) ? "timestamp-fixed" - : "timestamp", + : "timestamp" ); timeResource.fetch(); @@ -446,6 +457,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { // iseries.update(bar, true); // }); // } + hasData.set(true); + setDataCallback?.({ + iseries, + index, + unit, + }); timeScaleSetCallback?.(() => { if ( @@ -461,7 +478,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { } }); timeScaleSet = true; - }, + } ); } else { activeResources.delete(valuesResource); @@ -471,6 +488,12 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { } else if (data) { signals.createEffect(data, (data) => { iseries.setData(data); + hasData.set(true); + setDataCallback?.({ + iseries, + index: index(), + unit, + }); if (fitContent) { ichart.timeScale().fitContent(); @@ -520,6 +543,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { * @param {number} [args.paneIndex] * @param {boolean} [args.defaultActive] * @param {boolean} [args.inverse] + * @param {SetDataCallback} [args.setDataCallback] * @param {DeepPartial} [args.options] */ addCandlestickSeries({ @@ -529,22 +553,29 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { order, paneIndex = 0, defaultActive, + setDataCallback, data, inverse, }) { const green = inverse ? colors.red : colors.green; const red = inverse ? colors.green : colors.red; - const iseries = ichart.addSeries( - /** @type {SeriesDefinition<'Candlestick'>} */ (lc.CandlestickSeries), - { - upColor: green(), - downColor: red(), - wickUpColor: green(), - wickDownColor: red(), - borderVisible: false, - visible: defaultActive !== false, - }, - paneIndex, + + /** @type {ISeriesApi<'Candlestick', number>} */ + const iseries = /** @type {any} */ ( + ichart.addSeries( + /** @type {SeriesDefinition<'Candlestick'>} */ ( + lc.CandlestickSeries + ), + { + upColor: green(), + downColor: red(), + wickUpColor: green(), + wickDownColor: red(), + borderVisible: false, + visible: defaultActive !== false, + }, + paneIndex + ) ); return addSeries({ @@ -556,6 +587,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { seriesType: "Candlestick", unit, data, + setDataCallback, defaultActive, vecId, }); @@ -565,9 +597,10 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { * @param {string} args.name * @param {Unit} args.unit * @param {number} args.order - * @param {Accessor} [args.data] + * @param {Accessor[]>} [args.data] * @param {VecId} [args.vecId] * @param {Color} [args.color] + * @param {SetDataCallback} [args.setDataCallback] * @param {number} [args.paneIndex] * @param {boolean} [args.defaultActive] * @param {DeepPartial} [args.options] @@ -577,6 +610,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { name, unit, order, + setDataCallback, color, paneIndex = 0, defaultActive, @@ -585,16 +619,19 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { }) { color ||= unit === "USD" ? colors.green : colors.orange; - const iseries = ichart.addSeries( - /** @type {SeriesDefinition<'Line'>} */ (lc.LineSeries), - { - lineWidth: /** @type {any} */ (1.5), - visible: defaultActive !== false, - priceLineVisible: false, - color: color(), - ...options, - }, - paneIndex, + /** @type {ISeriesApi<'Line', number>} */ + const iseries = /** @type {any} */ ( + ichart.addSeries( + /** @type {SeriesDefinition<'Line'>} */ (lc.LineSeries), + { + lineWidth: /** @type {any} */ (1.5), + visible: defaultActive !== false, + priceLineVisible: false, + color: color(), + ...options, + }, + paneIndex + ) ); const priceLineOptions = options?.createPriceLine; @@ -610,6 +647,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { paneIndex, seriesType: "Line", unit, + setDataCallback, data, defaultActive, vecId, @@ -620,8 +658,9 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { * @param {string} args.name * @param {Unit} args.unit * @param {number} args.order - * @param {Accessor} [args.data] + * @param {Accessor[]>} [args.data] * @param {VecId} [args.vecId] + * @param {SetDataCallback} [args.setDataCallback] * @param {number} [args.paneIndex] * @param {boolean} [args.defaultActive] * @param {DeepPartial} [args.options] @@ -632,31 +671,35 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { unit, order, paneIndex: _paneIndex, + setDataCallback, defaultActive, data, options, }) { const paneIndex = _paneIndex ?? 0; - const iseries = ichart.addSeries( - /** @type {SeriesDefinition<'Baseline'>} */ (lc.BaselineSeries), - { - lineWidth: /** @type {any} */ (1.5), - visible: defaultActive !== false, - baseValue: { - price: options?.createPriceLine?.value ?? 0, + /** @type {ISeriesApi<'Baseline', number>} */ + const iseries = /** @type {any} */ ( + ichart.addSeries( + /** @type {SeriesDefinition<'Baseline'>} */ (lc.BaselineSeries), + { + lineWidth: /** @type {any} */ (1.5), + visible: defaultActive !== false, + baseValue: { + price: options?.createPriceLine?.value ?? 0, + }, + ...options, + topLineColor: options?.topLineColor ?? colors.green(), + bottomLineColor: options?.bottomLineColor ?? colors.red(), + priceLineVisible: false, + bottomFillColor1: "transparent", + bottomFillColor2: "transparent", + topFillColor1: "transparent", + topFillColor2: "transparent", + lineVisible: true, }, - ...options, - topLineColor: options?.topLineColor ?? colors.green(), - bottomLineColor: options?.bottomLineColor ?? colors.red(), - priceLineVisible: false, - bottomFillColor1: "transparent", - bottomFillColor2: "transparent", - topFillColor1: "transparent", - topFillColor2: "transparent", - lineVisible: true, - }, - paneIndex, + paneIndex + ) ); const priceLineOptions = options?.createPriceLine; @@ -674,6 +717,7 @@ export default import("./v5.0.7-treeshaked/script.js").then((lc) => { order, paneIndex, seriesType: "Baseline", + setDataCallback, unit, data, defaultActive, @@ -824,7 +868,7 @@ function createLegend({ signals, utils }) { } else { spanColor.style.backgroundColor = tameColor(color); } - }, + } ); }); @@ -930,7 +974,7 @@ function createPaneHeightObserver({ ichart, paneIndex, signals, utils }) { } /** - * @param {ISeriesApi} series + * @param {ISeriesApi} series * @param {DeepPartial} options * @param {Colors} colors */ @@ -978,17 +1022,17 @@ function numberToShortUSFormat(value) { if (modulused === 0) { return `${numberToUSFormat( value / (1_000_000 * 1_000 ** letterIndex), - 3, + 3 )}${letter}`; } else if (modulused === 1) { return `${numberToUSFormat( value / (1_000_000 * 1_000 ** letterIndex), - 2, + 2 )}${letter}`; } else { return `${numberToUSFormat( value / (1_000_000 * 1_000 ** letterIndex), - 1, + 1 )}${letter}`; } } @@ -1038,7 +1082,7 @@ function createOklchToRGBA() { return rgb.map((c) => Math.abs(c) > 0.0031308 ? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055) - : 12.92 * c, + : 12.92 * c ); } /** @@ -1050,7 +1094,7 @@ function createOklchToRGBA() { 1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586, -0.0638541728258133, 1, -0.0894841775298119, -1.2914855480194092, ]), - lab, + lab ); const LMS = /** @type {[number, number, number]} */ ( LMSg.map((val) => val ** 3) @@ -1061,7 +1105,7 @@ function createOklchToRGBA() { -0.0405757452148008, 1.112286803280317, -0.0717110580655164, -0.0763729366746601, -0.4214933324022432, 1.5869240198367816, ]), - LMS, + LMS ); } /** @@ -1074,7 +1118,7 @@ function createOklchToRGBA() { -0.9692436362808796, 1.8759675015077202, 0.04155505740717559, 0.05563007969699366, -0.20397695888897652, 1.0569715142428786, ], - xyz, + xyz ); } @@ -1097,8 +1141,8 @@ function createOklchToRGBA() { }); const rgb = srgbLinear2rgb( xyz2rgbLinear( - oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))), - ), + oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))) + ) ).map((v) => { return Math.max(Math.min(Math.round(v * 255), 255), 0); }); diff --git a/websites/default/scripts/chart.js b/websites/default/scripts/chart.js index c9d91a90b..3d95070c5 100644 --- a/websites/default/scripts/chart.js +++ b/websites/default/scripts/chart.js @@ -1,6 +1,7 @@ // @ts-check const keyPrefix = "chart"; +const ONE_BTC_IN_SATS = 100_000_000; /** * @param {Object} args @@ -34,7 +35,7 @@ export function init({ const { index, fieldset } = createIndexSelector({ signals, utils }); const TIMERANGE_LS_KEY = signals.createMemo( - () => `chart-timerange-${index()}`, + () => `chart-timerange-${index()}` ); let firstRun = true; @@ -89,7 +90,7 @@ export function init({ from.set(t.from); to.set(t.to); } - }), + }) ); elements.charts.append(fieldset); @@ -128,6 +129,88 @@ export function init({ const seriesListTop = /** @type {Series[]} */ ([]); const seriesListBottom = /** @type {Series[]} */ ([]); + /** + * @param {Object} params + * @param {ISeriesApi} params.iseries + * @param {Unit} params.unit + * @param {Index} params.index + */ + function printLatest({ iseries, unit, index }) { + const _latest = webSockets.kraken1dCandle.latest(); + + if (!_latest) return; + + const latest = { ..._latest }; + + if (unit === "Sats") { + latest.open = Math.floor(ONE_BTC_IN_SATS / latest.open); + latest.high = Math.floor(ONE_BTC_IN_SATS / latest.high); + latest.low = Math.floor(ONE_BTC_IN_SATS / latest.low); + latest.close = Math.floor(ONE_BTC_IN_SATS / latest.close); + latest.value = Math.floor(ONE_BTC_IN_SATS / latest.value); + } + + const last_ = iseries.data().at(-1); + if (!last_) return; + const last = { ...last_ }; + + if ("close" in last) { + last.close = latest.close; + } + if ("value" in last) { + last.value = latest.value; + } + const date = new Date(latest.time * 1000); + + switch (index) { + case /** @satisfies {Height} */ (5): { + if ("close" in last) { + last.low = Math.min(last.low, latest.close); + last.high = Math.max(last.high, latest.close); + } + iseries.update(last); + break; + } + case /** @satisfies {DateIndex} */ (0): { + iseries.update(latest); + break; + } + default: { + if (index === /** @satisfies {WeekIndex} */ (22)) { + date.setUTCDate(date.getUTCDate() - ((date.getUTCDay() + 6) % 7)); + } else if (index === /** @satisfies {MonthIndex} */ (7)) { + date.setUTCDate(1); + } else if (index === /** @satisfies {QuarterIndex} */ (19)) { + const month = date.getUTCMonth(); + date.setUTCMonth(month - (month % 3), 1); + } else if (index === /** @satisfies {YearIndex} */ (23)) { + date.setUTCMonth(0, 1); + } else if (index === /** @satisfies {DecadeIndex} */ (1)) { + date.setUTCFullYear( + Math.floor(date.getUTCFullYear() / 10) * 10, + 0, + 1 + ); + } else { + throw Error("Unsupported"); + } + + const time = date.valueOf() / 1000; + + if (time === last.time) { + if ("close" in last) { + last.low = Math.min(last.low, latest.low); + last.high = Math.max(last.high, latest.high); + } + iseries.update(last); + } else { + latest.time = time; + iseries.update(latest); + } + } + } + } + signals.createEffect(selected, (option) => { headingElement.innerHTML = option.title; @@ -166,80 +249,92 @@ export function init({ signals.createEffect(index, (index) => { signals.createEffect( - () => [topUnit(), topSeriesType()], - ([topUnit, topSeriesType]) => { + () => ({ + topUnit: topUnit(), + topSeriesType: topSeriesType(), + }), + ({ topUnit, topSeriesType }) => { + /** @type {Series | undefined} */ + let series; + switch (topUnit) { case "USD": { switch (topSeriesType) { case "Candles": { - const series = chart.addCandlestickSeries({ + series = chart.addCandlestickSeries({ vecId: "ohlc", name: "Price", unit: topUnit, + setDataCallback: printLatest, order: 0, }); - seriesListTop[0]?.remove(); - seriesListTop[0] = series; + break; } case "Line": { - const series = chart.addLineSeries({ + series = chart.addLineSeries({ vecId: "close", name: "Price", unit: topUnit, color: colors.default, + setDataCallback: printLatest, options: { priceLineVisible: true, }, order: 0, }); - seriesListTop[0]?.remove(); - seriesListTop[0] = series; } } - // signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => { - // if (!latest) return; - // const last = /** @type { CandlestickData | undefined} */ ( - // candles.data().at(-1) - // ); - // if (!last) return; - // candles?.update({ ...last, close: latest.close }); - // }); break; } case "Sats": { switch (topSeriesType) { case "Candles": { - const series = chart.addCandlestickSeries({ + series = chart.addCandlestickSeries({ vecId: "ohlc-in-sats", name: "Price", unit: topUnit, inverse: true, + setDataCallback: printLatest, order: 0, }); - seriesListTop[0]?.remove(); - seriesListTop[0] = series; break; } case "Line": { - const series = chart.addLineSeries({ + series = chart.addLineSeries({ vecId: "close-in-sats", name: "Price", unit: topUnit, color: colors.default, + setDataCallback: printLatest, options: { priceLineVisible: true, }, order: 0, }); - seriesListTop[0]?.remove(); - seriesListTop[0] = series; } } break; } } - }, + + if (!series) throw Error("Unreachable"); + + seriesListTop[0]?.remove(); + seriesListTop[0] = series; + + // setDataCallback insimport("./options").tead of hasData + signals.createEffect( + () => ({ + latest: webSockets.kraken1dCandle.latest(), + hasData: series.hasData(), + }), + ({ latest, hasData }) => { + if (!series || !latest || !hasData) return; + printLatest({ iseries: series.inner, unit: topUnit, index }); + } + ); + } ); [ @@ -300,7 +395,7 @@ export function init({ blueprint.color?.() ?? blueprint.colors?.[1](), }, order, - }), + }) ); break; } @@ -318,13 +413,13 @@ export function init({ paneIndex, options: blueprint.options, order, - }), + }) ); } } }); }); - }, + } ); firstRun = false; @@ -379,7 +474,7 @@ function createIndexSelector({ signals, utils }) { case "decade": return /** @satisfies {DecadeIndex} */ (1); } - }, + } ); return { fieldset, index }; diff --git a/websites/default/scripts/main.js b/websites/default/scripts/main.js index 7053ecda5..9005836e0 100644 --- a/websites/default/scripts/main.js +++ b/websites/default/scripts/main.js @@ -1,7 +1,7 @@ // @ts-check /** - * @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex,CreatePriceLineOptions, CreatePriceLine } from "./options" + * @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, ChartableIndex,CreatePriceLineOptions, CreatePriceLine, SeriesType } from "./options" * @import { Valued, SingleValueData, CandlestickData, ChartData, OHLCTuple, Series } from "../packages/lightweight-charts/wrapper" * @import * as _ from "../packages/ufuzzy/v1.0.18/types" * @import { createChart as CreateClassicChart, LineStyleOptions, DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, ISeriesApi, Time, LineData, LogicalRange, BaselineStyleOptions, SeriesOptionsCommon, BaselineData, CandlestickStyleOptions } from "../packages/lightweight-charts/v5.0.7-treeshaked/types" @@ -67,14 +67,14 @@ function initPackages() { const imports = { async signals() { return import("../packages/solid-signals/wrapper.js").then((d) => - d.default.then((d) => d), + d.default.then((d) => d) ); }, async lightweightCharts() { return window.document.fonts.ready.then(() => import("../packages/lightweight-charts/wrapper.js").then((d) => - d.default.then((d) => d), - ), + d.default.then((d) => d) + ) ); }, async leanQr() { @@ -82,7 +82,7 @@ function initPackages() { }, async ufuzzy() { return import("../packages/ufuzzy/v1.0.18/script.js").then( - ({ default: d }) => d, + ({ default: d }) => d ); }, }; @@ -580,7 +580,7 @@ function createUtils() { window.history.pushState( null, "", - `${pathname}?${urlParams.toString()}`, + `${pathname}?${urlParams.toString()}` ); } catch (_) {} }, @@ -597,7 +597,7 @@ function createUtils() { window.history.replaceState( null, "", - `${pathname}?${urlParams.toString()}`, + `${pathname}?${urlParams.toString()}` ); } catch (_) {} }, @@ -1120,8 +1120,8 @@ function createUtils() { today.getUTCDate(), 0, 0, - 0, - ), + 0 + ) ); }, /** @@ -1223,7 +1223,7 @@ function createUtils() { */ function getNumberOfDaysBetweenTwoDates(oldest, youngest) { return Math.round( - Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS), + Math.abs((youngest.getTime() - oldest.getTime()) / date.ONE_DAY_IN_MS) ); } @@ -1497,7 +1497,7 @@ function createVecsResources(signals, utils) { const fetchedRecord = signals.createSignal( /** @type {Map}>} */ ( new Map() - ), + ) ); return { @@ -1545,7 +1545,7 @@ function createVecsResources(signals, utils) { index, id, from, - to, + to ) ); fetched.at = new Date(); @@ -1806,7 +1806,7 @@ function initWebSockets(signals, utils) { window.document.addEventListener( "visibilitychange", - reinitWebSocketIfDocumentNotHidden, + reinitWebSocketIfDocumentNotHidden ); window.document.addEventListener("online", reinitWebSocket); @@ -1815,7 +1815,7 @@ function initWebSockets(signals, utils) { ws?.close(); window.document.removeEventListener( "visibilitychange", - reinitWebSocketIfDocumentNotHidden, + reinitWebSocketIfDocumentNotHidden ); window.document.removeEventListener("online", reinitWebSocket); live.set(false); @@ -1828,9 +1828,8 @@ function initWebSockets(signals, utils) { /** * @param {(candle: CandlestickData) => void} callback - * @param {number} interval */ - function krakenCandleWebSocketCreator(callback, interval) { + function krakenCandleWebSocketCreator(callback) { const ws = new WebSocket("wss://ws.kraken.com/v2"); ws.addEventListener("open", () => { @@ -1842,7 +1841,7 @@ function initWebSockets(signals, utils) { symbol: ["BTC/USD"], interval: 1440, }, - }), + }) ); }); @@ -1853,14 +1852,10 @@ function initWebSockets(signals, utils) { const { interval_begin, open, high, low, close } = result.data.at(-1); - const date = new Date(interval_begin); - - const dateStr = utils.date.toString(date); - /** @type {CandlestickData} */ const candle = { index: -1, - time: dateStr, + time: new Date(interval_begin).valueOf() / 1000, open: Number(open), high: Number(high), low: Number(low), @@ -1874,8 +1869,9 @@ function initWebSockets(signals, utils) { return ws; } + /** @type {ReturnType>} */ const kraken1dCandle = createWebsocket((callback) => - krakenCandleWebSocketCreator(callback, 1440), + krakenCandleWebSocketCreator(callback) ); kraken1dCandle.open(); @@ -1934,7 +1930,7 @@ function main() { } const frame = window.document.getElementById( - /** @type {string} */ (input.value), + /** @type {string} */ (input.value) ); if (!frame) { @@ -2032,23 +2028,23 @@ function main() { function initDark() { const preferredColorSchemeMatchMedia = window.matchMedia( - "(prefers-color-scheme: dark)", + "(prefers-color-scheme: dark)" ); const dark = signals.createSignal( - preferredColorSchemeMatchMedia.matches, + preferredColorSchemeMatchMedia.matches ); preferredColorSchemeMatchMedia.addEventListener( "change", ({ matches }) => { dark.set(matches); - }, + } ); return dark; } const dark = initDark(); const qrcode = signals.createSignal( - /** @type {string | null} */ (null), + /** @type {string | null} */ (null) ); function createLastHeightResource() { @@ -2059,7 +2055,7 @@ function main() { lastHeight.set(h); }, /** @satisfies {Height} */ (5), - "height", + "height" ); } fetchLastHeight(); @@ -2106,10 +2102,10 @@ function main() { const owner = signals.getOwner(); const chartOption = signals.createSignal( - /** @type {ChartOption | null} */ (null), + /** @type {ChartOption | null} */ (null) ); const simOption = signals.createSignal( - /** @type {SimulationOption | null} */ (null), + /** @type {SimulationOption | null} */ (null) ); let previousElement = /** @type {HTMLElement | undefined} */ ( @@ -2156,10 +2152,10 @@ function main() { webSockets, vecsResources, vecIdToIndexes, - }), - ), - ), - ), + }) + ) + ) + ) ); } firstTimeLoadingChart = false; @@ -2182,9 +2178,9 @@ function main() { vecsResources, option, vecIdToIndexes, - }), - ), - ), + }) + ) + ) ); } firstTimeLoadingTable = false; @@ -2212,10 +2208,10 @@ function main() { signals, utils, vecsResources, - }), - ), - ), - ), + }) + ) + ) + ) ); } firstTimeLoadingSimulation = false; @@ -2244,7 +2240,7 @@ function main() { createMobileSwitchEffect(); utils.dom.onFirstIntersection(elements.aside, () => - signals.runWithOwner(owner, initSelectedFrame), + signals.runWithOwner(owner, initSelectedFrame) ); } initSelected(); @@ -2322,7 +2318,7 @@ function main() { if (indexes?.length) { const maxIndex = Math.min( (order || indexes).length - 1, - minIndex + RESULTS_PER_PAGE - 1, + minIndex + RESULTS_PER_PAGE - 1 ); list = Array(maxIndex - minIndex + 1); @@ -2398,7 +2394,7 @@ function main() { haystack, needle, undefined, - infoThresh, + infoThresh ); if (!result?.[0]?.length || !result?.[1]) { @@ -2406,7 +2402,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh, + infoThresh ); } @@ -2415,7 +2411,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh, + infoThresh ); } @@ -2424,7 +2420,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh, + infoThresh ); } @@ -2433,7 +2429,7 @@ function main() { haystack, needle, undefined, - infoThresh, + infoThresh ); } @@ -2442,7 +2438,7 @@ function main() { haystack, needle, outOfOrder, - infoThresh, + infoThresh ); } @@ -2525,7 +2521,7 @@ function main() { shareDiv.hidden = false; }); - }), + }) ); } initShare(); @@ -2547,7 +2543,7 @@ function main() { localStorage.setItem(barWidthLocalStorageKey, String(width)); } else { elements.main.style.width = elements.style.getPropertyValue( - "--default-main-width", + "--default-main-width" ); localStorage.removeItem(barWidthLocalStorageKey); } @@ -2583,9 +2579,9 @@ function main() { window.addEventListener("mouseleave", setResizeFalse); } initDesktopResizeBar(); - }), - ), - ), + }) + ) + ) ); } main(); diff --git a/websites/default/scripts/options.js b/websites/default/scripts/options.js index ae0f29a8e..7f242ba50 100644 --- a/websites/default/scripts/options.js +++ b/websites/default/scripts/options.js @@ -27,7 +27,7 @@ * @property {Color} [color] * @property {[Color, Color]} [colors] * @property {DeepPartial} [options] - * @property {Accessor} [data] + * @property {Accessor[]>} [data] * @typedef {BaseSeriesBlueprint & BaselineSeriesBlueprintSpecific} BaselineSeriesBlueprint * * @typedef {Object} CandlestickSeriesBlueprintSpecific @@ -41,7 +41,7 @@ * @property {"Line"} [type] * @property {Color} [color] * @property {DeepPartial} [options] - * @property {Accessor} [data] + * @property {Accessor[]>} [data] * @typedef {BaseSeriesBlueprint & LineSeriesBlueprintSpecific} LineSeriesBlueprint * * @typedef {BaselineSeriesBlueprint | CandlestickSeriesBlueprint | LineSeriesBlueprint} AnySeriesBlueprint @@ -1430,7 +1430,7 @@ function createPartialOptions(colors) { key: `${fixKey(key)}realized-price`, name, color, - }), + }) ), } : createPriceWithRatio({ @@ -1512,7 +1512,9 @@ function createPartialOptions(colors) { }), /** @satisfies {FetchedBaselineSeriesBlueprint} */ ({ type: "Baseline", - key: `${fixKey(key)}net-realized-profit-and-loss-relative-to-realized-cap`, + key: `${fixKey( + key + )}net-realized-profit-and-loss-relative-to-realized-cap`, title: useGroupName ? name : "Net", color: useGroupName ? color : undefined, options: { @@ -1581,7 +1583,9 @@ function createPartialOptions(colors) { bottom: list.flatMap(({ color, name, key }) => [ /** @satisfies {FetchedBaselineSeriesBlueprint} */ ({ type: "Baseline", - key: `${fixKey(key)}adjusted-spent-output-profit-ratio`, + key: `${fixKey( + key + )}adjusted-spent-output-profit-ratio`, title: useGroupName ? name : "asopr", color: useGroupName ? color : undefined, options: { @@ -1603,7 +1607,7 @@ function createPartialOptions(colors) { key: `${fixKey(key)}sell-side-risk-ratio`, name: useGroupName ? name : "Risk", color: color, - }), + }) ), }, ], @@ -1691,7 +1695,9 @@ function createPartialOptions(colors) { }), /** @satisfies {FetchedBaselineSeriesBlueprint} */ ({ type: "Baseline", - key: `${fixKey(key)}net-unrealized-profit-and-loss-relative-to-market-cap`, + key: `${fixKey( + key + )}net-unrealized-profit-and-loss-relative-to-market-cap`, title: useGroupName ? name : "Net", color: useGroupName ? color : undefined, options: { @@ -1868,7 +1874,7 @@ function createPartialOptions(colors) { key: `${key}-sma`, name: key, color, - }), + }) ), }, ...averages.map(({ key, name, color }) => @@ -1878,7 +1884,7 @@ function createPartialOptions(colors) { title: `${name} Market Price Moving Average`, legend: "average", color, - }), + }) ), ], }, @@ -1969,7 +1975,7 @@ function createPartialOptions(colors) { }, }), ], - }), + }) ), .../** @type {const} */ ([ { name: "2 Year", key: "2y" }, @@ -2040,7 +2046,7 @@ function createPartialOptions(colors) { }, }), ], - }), + }) ), ], }, @@ -2056,7 +2062,7 @@ function createPartialOptions(colors) { name: `${year}`, color, defaultActive, - }), + }) ), }, ...dcaClasses.map( @@ -2083,7 +2089,7 @@ function createPartialOptions(colors) { }, }), ], - }), + }) ), ], }, @@ -2144,10 +2150,10 @@ function createPartialOptions(colors) { bottom: [ ...createAverageSumCumulativeMinMaxPercentilesSeries("fee"), ...createAverageSumCumulativeMinMaxPercentilesSeries( - "fee-in-btc", + "fee-in-btc" ), ...createAverageSumCumulativeMinMaxPercentilesSeries( - "fee-in-usd", + "fee-in-usd" ), ], }, @@ -2911,7 +2917,7 @@ export function initOptions({ const detailsList = []; const treeElement = signals.createSignal( - /** @type {HTMLDivElement | null} */ (null), + /** @type {HTMLDivElement | null} */ (null) ); /** @type {string[] | undefined} */ @@ -3030,7 +3036,7 @@ export function initOptions({ return null; } }, - null, + null ); partialTree.forEach((anyPartial, partialIndex) => { @@ -3053,7 +3059,7 @@ export function initOptions({ if ("tree" in anyPartial) { const folderId = utils.stringToId( - `${(path || []).join(" ")} ${anyPartial.name} folder`, + `${(path || []).join(" ")} ${anyPartial.name} folder` ); /** @type {Omit} */ @@ -3068,13 +3074,13 @@ export function initOptions({ const thisPath = groupAddons.id; const passedDetails = signals.createSignal( - /** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null), + /** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null) ); const childOptionsCount = recursiveProcessPartialTree( anyPartial.tree, passedDetails, - [...(path || []), thisPath], + [...(path || []), thisPath] ); listForSum.push(childOptionsCount); @@ -3206,7 +3212,7 @@ export function initOptions({ }); return signals.createMemo(() => - listForSum.reduce((acc, s) => acc + s(), 0), + listForSum.reduce((acc, s) => acc + s(), 0) ); } recursiveProcessPartialTree(partialOptions, treeElement); @@ -3233,7 +3239,7 @@ export function initOptions({ console.log( [...m.entries()] .filter(([_, value]) => value > 1) - .map(([key, _]) => key), + .map(([key, _]) => key) ); throw Error("ID duplicate");