mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-14 12:38:13 -07:00
general: snapshot
This commit is contained in:
@@ -28,7 +28,6 @@ export function createPresets({
|
||||
{
|
||||
title: `Total Non Empty Address`,
|
||||
color: colors.bitcoin,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets[scale].address_count,
|
||||
},
|
||||
],
|
||||
@@ -67,7 +66,6 @@ export function createPresets({
|
||||
{
|
||||
title: `Total Addresses Created`,
|
||||
color: colors.bitcoin,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets[scale].created_addresses,
|
||||
},
|
||||
],
|
||||
@@ -87,7 +85,6 @@ export function createPresets({
|
||||
{
|
||||
title: `Total Empty Addresses`,
|
||||
color: colors.darkWhite,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets[scale].empty_addresses,
|
||||
},
|
||||
],
|
||||
@@ -143,25 +140,46 @@ function createAddressPresetFolder<Scale extends ResourceScale>({
|
||||
color,
|
||||
datasetKey,
|
||||
}),
|
||||
{
|
||||
name: `Split By Liquidity`,
|
||||
tree: liquidities.map(
|
||||
(liquidity): PartialPresetFolder => ({
|
||||
name: liquidity.name,
|
||||
tree: createCohortPresetList({
|
||||
title: `${liquidity.name} ${name}`,
|
||||
name: `${liquidity.name} ${name}`,
|
||||
scale,
|
||||
color,
|
||||
datasetKey: `${liquidity.key}_${datasetKey}`,
|
||||
}),
|
||||
}),
|
||||
),
|
||||
},
|
||||
createLiquidityFolder({
|
||||
scale,
|
||||
name,
|
||||
datasetKey,
|
||||
color,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function createLiquidityFolder<Scale extends ResourceScale>({
|
||||
scale,
|
||||
color,
|
||||
name,
|
||||
datasetKey,
|
||||
}: {
|
||||
scale: Scale;
|
||||
name: string;
|
||||
datasetKey: AddressCohortKey | "";
|
||||
color: string;
|
||||
}): PartialPresetFolder {
|
||||
return {
|
||||
name: `Split By Liquidity`,
|
||||
tree: liquidities.map(
|
||||
(liquidity): PartialPresetFolder => ({
|
||||
name: liquidity.name,
|
||||
tree: createCohortPresetList({
|
||||
title: `${liquidity.name} ${name}`,
|
||||
name: `${liquidity.name} ${name}`,
|
||||
scale,
|
||||
color,
|
||||
datasetKey: !datasetKey
|
||||
? liquidity.key
|
||||
: `${liquidity.key}_${datasetKey}`,
|
||||
}),
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function createAddressCountPreset<Scale extends ResourceScale>({
|
||||
scale,
|
||||
color,
|
||||
|
||||
+362
-406
@@ -1,10 +1,10 @@
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { chunkIdToIndex } from "../datasets/resource";
|
||||
import { createChart } from "../lightweightCharts/chart/create";
|
||||
import { chartState } from "../lightweightCharts/chart/state";
|
||||
import { setMinMaxMarkers } from "../lightweightCharts/chart/markers";
|
||||
import { initTimeScale } from "../lightweightCharts/chart/time";
|
||||
import { setWhitespace } from "../lightweightCharts/chart/whitespace";
|
||||
import { createAreaSeries } from "../lightweightCharts/series/creators/area";
|
||||
import {
|
||||
createBaseLineSeries,
|
||||
DEFAULT_BASELINE_COLORS,
|
||||
@@ -16,13 +16,12 @@ import { createLineSeries } from "../lightweightCharts/series/creators/line";
|
||||
import { colors } from "../utils/colors";
|
||||
import { debounce } from "../utils/debounce";
|
||||
import { stringToId } from "../utils/id";
|
||||
import { webSockets } from "../ws";
|
||||
|
||||
export enum SeriesType {
|
||||
Normal,
|
||||
Line,
|
||||
Based,
|
||||
Area,
|
||||
Histogram,
|
||||
Candlestick,
|
||||
}
|
||||
|
||||
type SeriesConfig<Scale extends ResourceScale> =
|
||||
@@ -33,6 +32,7 @@ type SeriesConfig<Scale extends ResourceScale> =
|
||||
seriesType: SeriesType.Based;
|
||||
title: string;
|
||||
options?: BaselineSeriesOptions;
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
defaultVisible?: boolean;
|
||||
}
|
||||
| {
|
||||
@@ -42,15 +42,27 @@ type SeriesConfig<Scale extends ResourceScale> =
|
||||
seriesType: SeriesType.Histogram;
|
||||
title: string;
|
||||
options?: DeepPartialHistogramOptions;
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
defaultVisible?: boolean;
|
||||
}
|
||||
| {
|
||||
dataset: ResourceDataset<Scale>;
|
||||
seriesType: SeriesType.Candlestick;
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
colors?: undefined;
|
||||
color?: undefined;
|
||||
options?: DeepPartialLineOptions;
|
||||
defaultVisible?: boolean;
|
||||
title: string;
|
||||
}
|
||||
| {
|
||||
dataset: ResourceDataset<Scale>;
|
||||
color: string;
|
||||
colors?: undefined;
|
||||
seriesType?: SeriesType.Normal | SeriesType.Area;
|
||||
seriesType?: SeriesType.Line;
|
||||
title: string;
|
||||
options?: DeepPartialLineOptions;
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
defaultVisible?: boolean;
|
||||
};
|
||||
|
||||
@@ -65,18 +77,28 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
priceDataset,
|
||||
priceOptions,
|
||||
legendSetter,
|
||||
dark,
|
||||
activeRange,
|
||||
}: {
|
||||
charts: RWS<IChartApi[]>;
|
||||
parentDiv: HTMLDivElement;
|
||||
preset: Preset;
|
||||
legendSetter: Setter<PresetLegend>;
|
||||
legendSetter: Setter<SeriesLegend[]>;
|
||||
priceDataset?: ResourceDataset<Scale>;
|
||||
priceOptions?: PriceSeriesOptions;
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
top?: SeriesConfig<Scale>[];
|
||||
bottom?: SeriesConfig<Scale>[];
|
||||
datasets: Datasets;
|
||||
dark: boolean;
|
||||
activeRange: RWS<number[]>;
|
||||
}) {
|
||||
// ---
|
||||
// Reset states
|
||||
// ---
|
||||
|
||||
legendSetter([]);
|
||||
|
||||
reactiveChartList.set((charts) => {
|
||||
charts.forEach((chart) => {
|
||||
chart.remove();
|
||||
@@ -85,29 +107,36 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
return [];
|
||||
});
|
||||
|
||||
activeRange.set([]);
|
||||
|
||||
parentDiv.replaceChildren();
|
||||
|
||||
// ---
|
||||
// Done
|
||||
// ---
|
||||
|
||||
const scale = preset.scale;
|
||||
|
||||
const legendList: PresetLegend = [];
|
||||
const presetLegend: SeriesLegend[] = [];
|
||||
|
||||
const priceSeriesType = createRWS<"Candlestick" | "Line">("Candlestick");
|
||||
const priceSeriesType = createRWS<PriceSeriesType>("Candlestick");
|
||||
|
||||
// const valuesSkipped = createRWS(0);
|
||||
const activeDatasets: ResourceDataset<any, any>[] = [];
|
||||
|
||||
const activeDatasets: Set<ResourceDataset<any, any>> = new Set();
|
||||
const lastActiveIndex = createMemo(() => {
|
||||
const last = activeRange().at(-1);
|
||||
return last !== undefined ? chunkIdToIndex(scale, last) : undefined;
|
||||
});
|
||||
|
||||
const exactRange = createRWS(undefined as TimeRange | undefined);
|
||||
|
||||
const charts = [top || [], bottom]
|
||||
.flatMap((list) => (list ? [list] : []))
|
||||
.flatMap((seriesConfigList, index, array) => {
|
||||
.flatMap((seriesConfigList, index) => {
|
||||
if (index !== 0 && seriesConfigList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const isAnyArea = seriesConfigList.find(
|
||||
(config) => config.seriesType === SeriesType.Area,
|
||||
);
|
||||
|
||||
const div = document.createElement("div");
|
||||
|
||||
div.className = "w-full cursor-crosshair min-h-0 border-orange-200/10";
|
||||
@@ -115,28 +144,40 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
parentDiv.appendChild(div);
|
||||
|
||||
const chart = createChart(scale, div, {
|
||||
...priceScaleOptions,
|
||||
...(isAnyArea
|
||||
? {
|
||||
scaleMargins: {
|
||||
bottom: 0,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
dark,
|
||||
priceScaleOptions: {
|
||||
...priceScaleOptions,
|
||||
},
|
||||
});
|
||||
|
||||
chartState.chart = chart;
|
||||
|
||||
if (!chart) {
|
||||
console.log("chart: undefined");
|
||||
return [];
|
||||
}
|
||||
|
||||
setWhitespace(chart, scale);
|
||||
const whitespace = setWhitespace(chart, scale);
|
||||
|
||||
const seriesList: ISeriesApi<any>[] = [];
|
||||
// return [
|
||||
// {
|
||||
// scale,
|
||||
// div,
|
||||
// chart,
|
||||
// whitespace,
|
||||
// legendList: [],
|
||||
// debouncedSetMinMaxMarkers: () => {},
|
||||
// },
|
||||
// ];
|
||||
|
||||
const _legendList: PresetLegend = [];
|
||||
const chartLegend: SeriesLegend[] = [];
|
||||
|
||||
const debouncedSetMinMaxMarkers = debounce(() => {
|
||||
setMinMaxMarkers({
|
||||
scale,
|
||||
visibleRange: exactRange(),
|
||||
legendList: chartLegend,
|
||||
activeRange,
|
||||
});
|
||||
}, 20);
|
||||
|
||||
if (index === 0) {
|
||||
const dataset =
|
||||
@@ -145,102 +186,93 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
typeof priceDataset
|
||||
>);
|
||||
|
||||
activeDatasets.add(dataset);
|
||||
activeDatasets.push(dataset);
|
||||
|
||||
const price = applyPriceSeries({
|
||||
chart,
|
||||
preset,
|
||||
seriesType: priceSeriesType,
|
||||
// valuesSkipped,
|
||||
dataset,
|
||||
options: priceOptions,
|
||||
const title = priceOptions?.title || "Price";
|
||||
|
||||
const priceScaleOptions: DeepPartialPriceScaleOptions = {
|
||||
mode: 1,
|
||||
...priceOptions?.priceScaleOptions,
|
||||
};
|
||||
|
||||
function createPriceSeries(seriesType: PriceSeriesType) {
|
||||
let seriesConfig: SeriesConfig<Scale>;
|
||||
|
||||
if (seriesType === "Candlestick") {
|
||||
seriesConfig = {
|
||||
dataset,
|
||||
title,
|
||||
seriesType: SeriesType.Candlestick,
|
||||
options: priceOptions,
|
||||
priceScaleOptions,
|
||||
};
|
||||
} else {
|
||||
seriesConfig = {
|
||||
dataset,
|
||||
title,
|
||||
color: colors.white,
|
||||
options: priceOptions?.seriesOptions,
|
||||
priceScaleOptions,
|
||||
};
|
||||
}
|
||||
|
||||
return createSeriesGroup({
|
||||
activeRange,
|
||||
seriesConfig,
|
||||
chart,
|
||||
chartLegend,
|
||||
lastActiveIndex,
|
||||
preset,
|
||||
disabled: () => priceSeriesType() !== seriesType,
|
||||
debouncedSetMinMaxMarkers,
|
||||
});
|
||||
}
|
||||
|
||||
const priceCandlestickLegend = createPriceSeries("Candlestick");
|
||||
const priceLineLegend = createPriceSeries("Line");
|
||||
|
||||
createEffect(() => {
|
||||
priceCandlestickLegend.visible.set(priceLineLegend.visible());
|
||||
});
|
||||
|
||||
_legendList.push(price.lineLegend, price.ohlcLegend);
|
||||
|
||||
seriesList.push(price.lineLegend.series, price.ohlcLegend.series);
|
||||
createEffect(() => {
|
||||
priceLineLegend.visible.set(priceCandlestickLegend.visible());
|
||||
});
|
||||
}
|
||||
|
||||
seriesList.push(
|
||||
...seriesConfigList
|
||||
.reverse()
|
||||
.map(
|
||||
({
|
||||
dataset,
|
||||
color,
|
||||
colors,
|
||||
seriesType: type,
|
||||
title,
|
||||
options,
|
||||
defaultVisible,
|
||||
}) => {
|
||||
activeDatasets.add(dataset);
|
||||
seriesConfigList.reverse().forEach((seriesConfig) => {
|
||||
activeDatasets.push(seriesConfig.dataset);
|
||||
|
||||
let series: ISeriesApi<
|
||||
"Baseline" | "Line" | "Area" | "Histogram"
|
||||
>;
|
||||
|
||||
if (type === SeriesType.Based) {
|
||||
series = createBaseLineSeries(chart, {
|
||||
color,
|
||||
...options,
|
||||
});
|
||||
} else if (type === SeriesType.Area) {
|
||||
series = createAreaSeries(chart, {
|
||||
color,
|
||||
autoscaleInfoProvider: (
|
||||
getInfo: () => AutoscaleInfo | null,
|
||||
) => {
|
||||
const info = getInfo();
|
||||
if (info) {
|
||||
info.priceRange.minValue = 0;
|
||||
}
|
||||
return info;
|
||||
},
|
||||
...options,
|
||||
});
|
||||
} else if (type === SeriesType.Histogram) {
|
||||
series = createHistogramSeries(chart, {
|
||||
color,
|
||||
...options,
|
||||
});
|
||||
} else {
|
||||
series = createLineSeries(chart, {
|
||||
color,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
_legendList.push(
|
||||
createSeriesLegend({
|
||||
id: stringToId(title),
|
||||
presetId: preset.id,
|
||||
title,
|
||||
series,
|
||||
color: () => colors || color || DEFAULT_BASELINE_COLORS,
|
||||
defaultVisible,
|
||||
url: dataset.url,
|
||||
}),
|
||||
);
|
||||
|
||||
createEffect(() => {
|
||||
const values = dataset.values();
|
||||
console.log(values.length);
|
||||
|
||||
series.setData(values);
|
||||
});
|
||||
|
||||
return series;
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
_legendList.forEach((legend) => {
|
||||
legendList.splice(0, 0, legend);
|
||||
createSeriesGroup({
|
||||
activeRange,
|
||||
seriesConfig,
|
||||
chartLegend,
|
||||
chart,
|
||||
preset,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
});
|
||||
});
|
||||
|
||||
return [{ div, chart, seriesList, legendList: _legendList }];
|
||||
});
|
||||
chartLegend.forEach((legend) => {
|
||||
presetLegend.splice(0, 0, legend);
|
||||
|
||||
createEffect(on(legend.visible, debouncedSetMinMaxMarkers));
|
||||
});
|
||||
|
||||
createEffect(on(exactRange, debouncedSetMinMaxMarkers));
|
||||
|
||||
return [
|
||||
{
|
||||
scale,
|
||||
div,
|
||||
chart,
|
||||
whitespace,
|
||||
legendList: chartLegend,
|
||||
debouncedSetMinMaxMarkers,
|
||||
},
|
||||
];
|
||||
}) satisfies ChartObject[];
|
||||
|
||||
createEffect(() => {
|
||||
const visibleCharts: typeof charts = [];
|
||||
@@ -267,344 +299,268 @@ export function applySeriesList<Scale extends ResourceScale>({
|
||||
});
|
||||
});
|
||||
|
||||
// const seriesType = createRWS(
|
||||
// checkIfUpClose(chart, chartState.range) || "Candlestick",
|
||||
// );
|
||||
|
||||
function updateVisibleRangeRatio(
|
||||
chart: IChartApi,
|
||||
range: LogicalRange | null,
|
||||
) {
|
||||
if (!range) return;
|
||||
|
||||
try {
|
||||
const width = chart.timeScale().width();
|
||||
|
||||
const ratio = (range.to - range.from) / width;
|
||||
|
||||
if (ratio <= 0.5) {
|
||||
// valuesSkipped.set(0);
|
||||
|
||||
priceSeriesType.set("Candlestick");
|
||||
} else {
|
||||
priceSeriesType.set("Line");
|
||||
|
||||
// valuesSkipped.set(Math.floor(ratio / 1.25));
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const debouncedUpdateVisibleRangeRatio = debounce(
|
||||
updateVisibleRangeRatio,
|
||||
const debouncedUpdateVisiblePriceSeriesType = debounce(
|
||||
updateVisiblePriceSeriesType,
|
||||
50,
|
||||
);
|
||||
|
||||
initTimeScale({
|
||||
activeDatasets,
|
||||
scale,
|
||||
charts,
|
||||
activeRange,
|
||||
exactRange,
|
||||
});
|
||||
|
||||
charts.forEach(({ chart }, index) => {
|
||||
const activeDatasetsLength = activeDatasets.length;
|
||||
createEffect(() => {
|
||||
const range = activeRange();
|
||||
|
||||
untrack(() => {
|
||||
for (let i = 0; i < range.length; i++) {
|
||||
const id = range[i];
|
||||
for (let j = 0; j < activeDatasetsLength; j++) {
|
||||
activeDatasets[j].fetch(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const lastChartIndex = charts.length - 1;
|
||||
|
||||
for (let i = 0; i < charts.length; i++) {
|
||||
const chart = charts[i].chart;
|
||||
|
||||
chart.timeScale().subscribeVisibleLogicalRangeChange((timeRange) => {
|
||||
// Last chart otherwise length of timescale is Infinity
|
||||
if (index === charts.length - 1) {
|
||||
debouncedUpdateVisibleRangeRatio(chart, timeRange);
|
||||
if (!timeRange) return;
|
||||
|
||||
// Must be the chart with the visible timeScale
|
||||
if (i === lastChartIndex) {
|
||||
debouncedUpdateVisiblePriceSeriesType(
|
||||
chart,
|
||||
timeRange,
|
||||
priceSeriesType,
|
||||
);
|
||||
}
|
||||
|
||||
charts.forEach(({ chart: _chart }, _index) => {
|
||||
if (timeRange && index !== _index) {
|
||||
_chart.timeScale().setVisibleLogicalRange(timeRange);
|
||||
for (let j = 0; j <= lastChartIndex; j++) {
|
||||
if (i !== j) {
|
||||
charts[j].chart.timeScale().setVisibleLogicalRange(timeRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
chart.subscribeCrosshairMove(({ time, sourceEvent }) => {
|
||||
// Don't override crosshair position from scroll event
|
||||
if (time && !sourceEvent) return;
|
||||
|
||||
charts.forEach(({ chart: _chart, seriesList }, _index) => {
|
||||
const first = seriesList.at(0);
|
||||
for (let j = 0; j <= lastChartIndex; j++) {
|
||||
const whitespace = charts[j].whitespace;
|
||||
const otherChart = charts[j].chart;
|
||||
|
||||
if (first && index !== _index) {
|
||||
if (whitespace && i !== j) {
|
||||
if (time) {
|
||||
_chart.setCrosshairPosition(NaN, time, first);
|
||||
otherChart.setCrosshairPosition(NaN, time, whitespace);
|
||||
} else {
|
||||
// No time when mouse goes outside the chart
|
||||
_chart.clearCrosshairPosition();
|
||||
otherChart.clearCrosshairPosition();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
legendSetter(legendList);
|
||||
legendSetter(presetLegend);
|
||||
|
||||
reactiveChartList.set(() => charts.map(({ chart }) => chart));
|
||||
}
|
||||
|
||||
function applyPriceSeries<
|
||||
Scale extends ResourceScale,
|
||||
T extends OHLC | number,
|
||||
>({
|
||||
chart,
|
||||
preset,
|
||||
dataset,
|
||||
seriesType,
|
||||
// valuesSkipped,
|
||||
options,
|
||||
}: {
|
||||
chart: IChartApi;
|
||||
preset: Preset;
|
||||
// valuesSkipped: Accessor<number>;
|
||||
seriesType: Accessor<"Candlestick" | "Line">;
|
||||
dataset: ResourceDataset<Scale, T>;
|
||||
options?: PriceSeriesOptions;
|
||||
}) {
|
||||
// console.time("series add");
|
||||
function updateVisiblePriceSeriesType(
|
||||
chart: IChartApi,
|
||||
range: LogicalRange,
|
||||
priceSeriesType: RWS<PriceSeriesType>,
|
||||
) {
|
||||
try {
|
||||
const width = chart.timeScale().width();
|
||||
|
||||
const id = options?.id || "price";
|
||||
const title = options?.title || "Price";
|
||||
const ratio = (range.to - range.from) / width;
|
||||
|
||||
const url = "url" in dataset ? (dataset as any).url : undefined;
|
||||
|
||||
const priceScaleOptions: DeepPartialPriceScaleOptions = {
|
||||
mode: 1,
|
||||
...options?.priceScaleOptions,
|
||||
};
|
||||
|
||||
let [ohlcSeries, ohlcColors] = createCandlesticksSeries(chart, options);
|
||||
|
||||
const ohlcLegend = createSeriesLegend({
|
||||
id,
|
||||
presetId: preset.id,
|
||||
title,
|
||||
color: () => ohlcColors,
|
||||
series: ohlcSeries,
|
||||
disabled: () => seriesType() !== "Candlestick",
|
||||
url,
|
||||
});
|
||||
|
||||
ohlcSeries.priceScale().applyOptions(priceScaleOptions);
|
||||
|
||||
// ---
|
||||
|
||||
const lineColor = colors.white;
|
||||
|
||||
let lineSeries = createLineSeries(chart, {
|
||||
color: lineColor,
|
||||
...options?.seriesOptions,
|
||||
});
|
||||
|
||||
const lineLegend = createSeriesLegend({
|
||||
id,
|
||||
presetId: preset.id,
|
||||
title,
|
||||
color: () => lineColor,
|
||||
series: lineSeries,
|
||||
disabled: () => seriesType() !== "Line",
|
||||
visible: ohlcLegend.visible,
|
||||
url,
|
||||
});
|
||||
|
||||
lineSeries.priceScale().applyOptions(priceScaleOptions);
|
||||
|
||||
// console.timeEnd("series add");
|
||||
|
||||
// lineSeries.setData(whitespaceHeightDataset);
|
||||
// ohlcSeries.setData({ time: 0, value: NaN });
|
||||
|
||||
// ---
|
||||
|
||||
// setMinMaxMarkers({
|
||||
// scale: preset.scale,
|
||||
// candlesticks:
|
||||
// dataset?.values() || datasets[preset.scale].price.values() || ([] as any),
|
||||
// range: chartState.range,
|
||||
// lowerOpacity,
|
||||
// });
|
||||
|
||||
// const startingValue = {
|
||||
// number: -1,
|
||||
// time: -1,
|
||||
// open: NaN,
|
||||
// high: NaN,
|
||||
// low: NaN,
|
||||
// close: NaN,
|
||||
// value: NaN,
|
||||
// };
|
||||
// lineSeries.update(startingValue);
|
||||
// ohlcSeries.update(startingValue);
|
||||
|
||||
// const callback = (
|
||||
// chunks: any[],
|
||||
// valuesSkippedPlus1: number,
|
||||
// length: number,
|
||||
// ) => {
|
||||
// console.time("t");
|
||||
// console.time("a");
|
||||
// // chart.removeSeries(ohlcSeries);
|
||||
// // chart.removeSeries(lineSeries);
|
||||
|
||||
// // ohlcSeries = createCandlesticksSeries(chart, options)[0];
|
||||
|
||||
// // ohlcSeries.priceScale().applyOptions(priceScaleOptions);
|
||||
|
||||
// // lineSeries = createLineSeries(chart, {
|
||||
// // color: lineColor,
|
||||
// // ...options?.seriesOptions,
|
||||
// // });
|
||||
|
||||
// // lineSeries.priceScale().applyOptions(priceScaleOptions);
|
||||
|
||||
// const values = new Array(length);
|
||||
|
||||
// let i = 0;
|
||||
// for (let k = 0; k < chunks.length; k++) {
|
||||
// const chunk = chunks[k];
|
||||
// // const chunk =
|
||||
// // fetchedJSONs[chunkIdToIndex(dataset.scale, activeRange[k])]?.vec?.() ||
|
||||
// // [];
|
||||
|
||||
// for (let j = 0; j < chunk.length; j += valuesSkippedPlus1) {
|
||||
// values[i++] = chunk[j];
|
||||
// // console.log(chunk[j]);
|
||||
// // callback(chunk[j]);
|
||||
// // for (let i = 0; i < seriesList.length; i++) {
|
||||
// // seriesList[i].update(chunk[j]);
|
||||
// // }
|
||||
// // const value = chunk[j];
|
||||
// // console.log(value.time);
|
||||
// // lineSeries.update(value);
|
||||
// // ohlcSeries.update(value);
|
||||
|
||||
// // i++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// console.log(values.length);
|
||||
// console.timeEnd("t");
|
||||
|
||||
// lineSeries.setData(values);
|
||||
|
||||
// console.timeEnd("a");
|
||||
// };
|
||||
|
||||
// const debouncedCallback = debounce(callback, 200);
|
||||
|
||||
createEffect(() => {
|
||||
const values = dataset.values();
|
||||
console.log(values.length);
|
||||
lineSeries.setData(values);
|
||||
ohlcSeries.setData(values);
|
||||
});
|
||||
// createEffect(() =>
|
||||
// computeDrawnSeriesValues(
|
||||
// dataset,
|
||||
// valuesSkipped(),
|
||||
// debouncedCallback,
|
||||
// // [lineSeries, ohlcSeries],
|
||||
// // (value) => {
|
||||
// // try {
|
||||
// // console.log(value);
|
||||
// // lineSeries.update(value);
|
||||
// // ohlcSeries.update(value);
|
||||
// // } catch {}
|
||||
// // }),
|
||||
// ),
|
||||
// );
|
||||
|
||||
createEffect(() => {
|
||||
if (preset.scale === "date") {
|
||||
const latest = webSockets.liveKrakenCandle.latest();
|
||||
|
||||
if (latest) {
|
||||
ohlcSeries.update(latest);
|
||||
lineSeries.update(latest);
|
||||
}
|
||||
if (ratio <= 0.5) {
|
||||
priceSeriesType.set("Candlestick");
|
||||
} else {
|
||||
priceSeriesType.set("Line");
|
||||
}
|
||||
});
|
||||
|
||||
return { ohlcLegend, lineLegend };
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// // const computeDrawnSeriesValues = debounce(_computeDrawnSeriesValues, 100);
|
||||
function createSeriesGroup<Scale extends ResourceScale>({
|
||||
activeRange,
|
||||
seriesConfig,
|
||||
preset,
|
||||
chartLegend,
|
||||
chart,
|
||||
disabled,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
}: {
|
||||
activeRange: Accessor<number[]>;
|
||||
seriesConfig: SeriesConfig<Scale>;
|
||||
preset: Preset;
|
||||
chart: IChartApi;
|
||||
chartLegend: SeriesLegend[];
|
||||
lastActiveIndex: Accessor<number | undefined>;
|
||||
disabled?: Accessor<boolean>;
|
||||
debouncedSetMinMaxMarkers: VoidFunction;
|
||||
}) {
|
||||
const {
|
||||
dataset,
|
||||
title,
|
||||
colors,
|
||||
color,
|
||||
defaultVisible,
|
||||
seriesType: type,
|
||||
options,
|
||||
priceScaleOptions,
|
||||
} = seriesConfig;
|
||||
|
||||
// function computeDrawnSeriesValues<
|
||||
// S extends ResourceScale,
|
||||
// T extends OHLC | number,
|
||||
// >(
|
||||
// dataset: ResourceDataset<S, T>,
|
||||
// valuesSkipped: number,
|
||||
// callback: (chunks: any, v: number, l: number) => void,
|
||||
// // seriesList: ISeriesApi<any>[],
|
||||
// ) {
|
||||
// // console.time(dataset.url);
|
||||
const scale = preset.scale;
|
||||
|
||||
// const { fetchedJSONs, activeRange: _activeRange } = dataset;
|
||||
const seriesList: RWS<
|
||||
ISeriesApi<"Baseline" | "Line" | "Histogram" | "Candlestick"> | undefined
|
||||
>[] = new Array(dataset.fetchedJSONs.length);
|
||||
|
||||
// const activeRange = _activeRange();
|
||||
let defaultSeriesColor: string | string[] | undefined = undefined;
|
||||
|
||||
// const valuesSkippedPlus1 = valuesSkipped + 1;
|
||||
const legend = createSeriesLegend({
|
||||
id: stringToId(title),
|
||||
presetId: preset.id,
|
||||
title,
|
||||
seriesList,
|
||||
color: () =>
|
||||
colors || color || defaultSeriesColor || DEFAULT_BASELINE_COLORS,
|
||||
defaultVisible,
|
||||
disabled,
|
||||
dataset,
|
||||
});
|
||||
|
||||
// if (valuesSkippedPlus1 === 1) {
|
||||
// console.log("todo valuesSkippedPlus1===1, skip for now");
|
||||
// }
|
||||
chartLegend.push(legend);
|
||||
|
||||
// // for (let i = 0; i < seriesList.length; i++) {
|
||||
// // seriesList[i].
|
||||
// // }
|
||||
dataset.fetchedJSONs.forEach((json, index) => {
|
||||
const series: (typeof seriesList)[number] = createRWS(undefined);
|
||||
|
||||
// const chunks = new Array(activeRange.length);
|
||||
// let length = 0;
|
||||
seriesList[index] = series;
|
||||
|
||||
// for (let i = 0; i < chunks.length; i++) {
|
||||
// const chunk =
|
||||
// fetchedJSONs[chunkIdToIndex(dataset.scale, activeRange[i])]?.vec?.() ||
|
||||
// [];
|
||||
createEffect(() => {
|
||||
const values = json.vec();
|
||||
if (!values) return;
|
||||
|
||||
// chunks[i] = chunk;
|
||||
untrack(() => {
|
||||
let s = series();
|
||||
|
||||
// length += Math.ceil(chunk.length / valuesSkippedPlus1);
|
||||
// }
|
||||
if (!s) {
|
||||
switch (type) {
|
||||
case SeriesType.Based: {
|
||||
s = createBaseLineSeries(chart, {
|
||||
color,
|
||||
...options,
|
||||
});
|
||||
|
||||
// callback(chunks, valuesSkippedPlus1, length);
|
||||
break;
|
||||
}
|
||||
case SeriesType.Candlestick: {
|
||||
const candlestickSeries = createCandlesticksSeries(
|
||||
chart,
|
||||
options,
|
||||
);
|
||||
|
||||
// // setValues(chunks, valuesSkippedPlus1, length, callback);
|
||||
// // }
|
||||
s = candlestickSeries[0];
|
||||
defaultSeriesColor = candlestickSeries[1];
|
||||
|
||||
// // // const debouncedSetValues = debounce(setValues, 50);
|
||||
// // function setValues(
|
||||
// // chunks: any[],
|
||||
// // valuesSkippedPlus1: number,
|
||||
// // length: number,
|
||||
// // callback: (values: any[]) => void,
|
||||
// // ) {
|
||||
// // const values = new Array(length);
|
||||
break;
|
||||
}
|
||||
case SeriesType.Histogram: {
|
||||
s = createHistogramSeries(chart, {
|
||||
color,
|
||||
...options,
|
||||
});
|
||||
|
||||
// // let i = 0;
|
||||
// // for (let k = 0; k < activeRange.length; k++) {
|
||||
// // const chunk = chunks[k];
|
||||
// // // const chunk =
|
||||
// // // fetchedJSONs[chunkIdToIndex(dataset.scale, activeRange[k])]?.vec?.() ||
|
||||
// // // [];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case SeriesType.Line: {
|
||||
s = createLineSeries(chart, {
|
||||
color,
|
||||
...options,
|
||||
});
|
||||
|
||||
// // for (let j = 0; j < chunk.length; j += valuesSkippedPlus1) {
|
||||
// // // values[i++] = chunk[j];
|
||||
// // // console.log(chunk[j]);
|
||||
// // // callback(chunk[j]);
|
||||
// // for (let i = 0; i < seriesList.length; i++) {
|
||||
// // seriesList[i].update(chunk[j]);
|
||||
// // }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// // // i++;
|
||||
// // }
|
||||
// // }
|
||||
if (priceScaleOptions) {
|
||||
s.priceScale().applyOptions(priceScaleOptions);
|
||||
}
|
||||
|
||||
// // console.log(i);
|
||||
series.set(s);
|
||||
}
|
||||
|
||||
// // if (i !== values.length) {
|
||||
// // console.log({ n: i, values });
|
||||
// // throw Error("error");
|
||||
// // }
|
||||
s.setData(values);
|
||||
|
||||
// // console.timeEnd(dataset.url);
|
||||
// }
|
||||
untrack(() => {
|
||||
debouncedSetMinMaxMarkers();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const s = series();
|
||||
const currentVec = dataset.fetchedJSONs.at(index)?.vec();
|
||||
const nextVec = dataset.fetchedJSONs.at(index + 1)?.vec();
|
||||
|
||||
if (s && currentVec?.length && nextVec?.length) {
|
||||
s.update(nextVec[0]);
|
||||
}
|
||||
});
|
||||
|
||||
const isLast = createMemo(() => {
|
||||
const last = lastActiveIndex();
|
||||
return last !== undefined && last === index;
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
series()?.applyOptions({
|
||||
lastValueVisible: legend.drawn() && isLast(),
|
||||
});
|
||||
});
|
||||
|
||||
const inRange = createMemo(() => {
|
||||
const range = activeRange();
|
||||
|
||||
if (range.length) {
|
||||
const start = chunkIdToIndex(scale, range.at(0)!);
|
||||
const end = chunkIdToIndex(scale, range.at(-1)!);
|
||||
|
||||
if (index >= start && index <= end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
const visible = createMemo((previous: boolean) => {
|
||||
if (legend.disabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return previous || inRange();
|
||||
}, false);
|
||||
|
||||
createEffect(() => {
|
||||
series()?.applyOptions({
|
||||
visible: legend.drawn() && visible(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return legend;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,6 @@ export function createPresets() {
|
||||
{
|
||||
title: "Mined",
|
||||
color: colors.bitcoin,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets.date.total_blocks_mined,
|
||||
},
|
||||
],
|
||||
@@ -184,7 +183,6 @@ export function createPresets() {
|
||||
{
|
||||
title: "Size (MB)",
|
||||
color: colors.darkWhite,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets.date.cumulative_block_size,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,7 +4,10 @@ import { colors } from "../utils/colors";
|
||||
import { replaceHistory } from "../utils/history";
|
||||
import { stringToId } from "../utils/id";
|
||||
import { resetURLParams } from "../utils/urlParams";
|
||||
import { createPresets as createAddressesPresets } from "./addresses";
|
||||
import {
|
||||
createPresets as createAddressesPresets,
|
||||
createLiquidityFolder,
|
||||
} from "./addresses";
|
||||
import { createPresets as createBlocksPresets } from "./blocks";
|
||||
import { createPresets as createCoinblocksPresets } from "./coinblocks";
|
||||
import { createPresets as createHodlersPresets } from "./hodlers";
|
||||
@@ -31,7 +34,7 @@ export function createPresets(): Presets {
|
||||
{
|
||||
name: "By Date",
|
||||
tree: [
|
||||
createMarketPresets({ scale: "date" }),
|
||||
createMarketPresets("date"),
|
||||
createBlocksPresets(),
|
||||
createMinersPresets("date"),
|
||||
createTransactionsPresets("date"),
|
||||
@@ -42,6 +45,12 @@ export function createPresets(): Presets {
|
||||
name: "",
|
||||
title: "",
|
||||
}),
|
||||
createLiquidityFolder({
|
||||
scale: "date",
|
||||
color: colors.bitcoin,
|
||||
datasetKey: "",
|
||||
name: "",
|
||||
}),
|
||||
createHodlersPresets({ scale: "date" }),
|
||||
createAddressesPresets({ scale: "date" }),
|
||||
createCoinblocksPresets({ scale: "date" }),
|
||||
@@ -50,7 +59,7 @@ export function createPresets(): Presets {
|
||||
{
|
||||
name: "By Height",
|
||||
tree: [
|
||||
createMarketPresets({ scale: "height" }),
|
||||
createMarketPresets("height"),
|
||||
createMinersPresets("height"),
|
||||
createTransactionsPresets("height"),
|
||||
...createCohortPresetList({
|
||||
@@ -60,6 +69,12 @@ export function createPresets(): Presets {
|
||||
datasetKey: "",
|
||||
title: "",
|
||||
}),
|
||||
createLiquidityFolder({
|
||||
scale: "height",
|
||||
color: colors.bitcoin,
|
||||
datasetKey: "",
|
||||
name: "",
|
||||
}),
|
||||
createHodlersPresets({ scale: "height" }),
|
||||
createAddressesPresets({ scale: "height" }),
|
||||
createCoinblocksPresets({ scale: "height" }),
|
||||
@@ -101,7 +116,7 @@ export function createPresets(): Presets {
|
||||
const serializedHistory: SerializedPresetsHistory = history().map(
|
||||
({ preset, date }) => ({
|
||||
p: preset.id,
|
||||
d: date.valueOf(),
|
||||
d: date.getTime(),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createPresets as createAveragesPresets } from "./averages";
|
||||
import { createPresets as createIndicatorsPresets } from "./indicators";
|
||||
import { createPresets as createReturnsPresets } from "./returns";
|
||||
|
||||
export function createPresets({ scale }: { scale: ResourceScale }) {
|
||||
export function createPresets(scale: ResourceScale) {
|
||||
return {
|
||||
name: "Market",
|
||||
tree: [
|
||||
@@ -18,25 +18,6 @@ export function createPresets({ scale }: { scale: ResourceScale }) {
|
||||
},
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
scale,
|
||||
icon: IconTablerPercentage,
|
||||
name: "Performance",
|
||||
title: "Market Performance",
|
||||
applyPreset(params) {
|
||||
return applySeriesList({
|
||||
...params,
|
||||
priceOptions: {
|
||||
id: "performance",
|
||||
title: "Performance",
|
||||
priceScaleOptions: {
|
||||
mode: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
description: "",
|
||||
},
|
||||
{
|
||||
scale,
|
||||
icon: IconTablerInfinity,
|
||||
|
||||
@@ -60,7 +60,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Count",
|
||||
color,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets[scale][`${datasetPrefix}utxo_count`],
|
||||
},
|
||||
],
|
||||
@@ -105,7 +104,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: `${name} Realized Cap.`,
|
||||
color,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][`${datasetPrefix}realized_cap`],
|
||||
},
|
||||
@@ -160,7 +158,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
dataset:
|
||||
params.datasets[scale][`${datasetPrefix}realized_profit`],
|
||||
color: colors.profit,
|
||||
seriesType: SeriesType.Area,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -181,7 +178,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
dataset:
|
||||
params.datasets[scale][`${datasetPrefix}realized_loss`],
|
||||
color: colors.loss,
|
||||
seriesType: SeriesType.Area,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -274,7 +270,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Cumulative Realized Profit",
|
||||
color: colors.profit,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}cumulative_realized_profit`
|
||||
@@ -297,7 +292,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Cumulative Realized Loss",
|
||||
color: colors.loss,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}cumulative_realized_loss`
|
||||
@@ -371,7 +365,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
dataset:
|
||||
params.datasets[scale][`${datasetPrefix}unrealized_profit`],
|
||||
color: colors.profit,
|
||||
seriesType: SeriesType.Area,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -393,7 +386,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
dataset:
|
||||
params.datasets[scale][`${datasetPrefix}unrealized_loss`],
|
||||
color: colors.loss,
|
||||
seriesType: SeriesType.Area,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -539,7 +531,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Supply",
|
||||
color,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset: params.datasets[scale][`${datasetPrefix}supply`],
|
||||
},
|
||||
],
|
||||
@@ -558,7 +549,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Supply",
|
||||
color: colors.profit,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}supply_in_profit`
|
||||
@@ -581,7 +571,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Supply",
|
||||
color: colors.loss,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}supply_in_loss`
|
||||
@@ -658,7 +647,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Supply",
|
||||
color,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}supply_to_circulating_supply_ratio`
|
||||
@@ -681,7 +669,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Supply",
|
||||
color: colors.profit,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}supply_in_profit_to_circulating_supply_ratio`
|
||||
@@ -703,7 +690,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
bottom: [
|
||||
{
|
||||
title: "Supply",
|
||||
seriesType: SeriesType.Area,
|
||||
color: colors.loss,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
@@ -779,7 +765,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
{
|
||||
title: "Supply",
|
||||
color: colors.profit,
|
||||
seriesType: SeriesType.Area,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
`${datasetPrefix}supply_in_profit_to_own_supply_ratio`
|
||||
@@ -801,7 +786,6 @@ export function createCohortPresetList<Scale extends ResourceScale>({
|
||||
bottom: [
|
||||
{
|
||||
title: "Supply",
|
||||
seriesType: SeriesType.Area,
|
||||
color: colors.loss,
|
||||
dataset:
|
||||
params.datasets[scale][
|
||||
|
||||
Vendored
+13
-2
@@ -24,7 +24,9 @@ type ApplyPreset = (params: {
|
||||
parentDiv: HTMLDivElement;
|
||||
datasets: Datasets;
|
||||
preset: Preset;
|
||||
legendSetter: Setter<PresetLegend>;
|
||||
legendSetter: Setter<SeriesLegend[]>;
|
||||
dark: boolean;
|
||||
activeRange: RWS<number[]>;
|
||||
}) => void;
|
||||
|
||||
interface PartialPresetFolder {
|
||||
@@ -58,4 +60,13 @@ interface Presets {
|
||||
select(preset: Preset): void;
|
||||
}
|
||||
|
||||
type PresetLegend = SeriesLegend[];
|
||||
type PriceSeriesType = "Candlestick" | "Line";
|
||||
|
||||
interface ChartObject {
|
||||
scale: ResourceScale;
|
||||
div: HTMLDivElement;
|
||||
chart: IChartApi;
|
||||
whitespace: ISeriesApi<"Line">;
|
||||
legendList: SeriesLegend[];
|
||||
debouncedSetMinMaxMarkers: VoidFunction;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user