mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-29 22:39:26 -07:00
website: moved more code to lc wrapper
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @import { PriceSeriesType } from '../packages/lightweight-charts/types';
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Colors} args.colors
|
||||
@@ -14,11 +10,9 @@
|
||||
* @param {Datasets} args.datasets
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
* @param {Accessor<boolean>} args.dark
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
dark,
|
||||
datasets,
|
||||
elements,
|
||||
lightweightCharts,
|
||||
@@ -59,10 +53,6 @@ export function init({
|
||||
},
|
||||
);
|
||||
|
||||
const priceSeriesType = signals.createSignal(
|
||||
/** @type {PriceSeriesType} */ ("Candlestick"),
|
||||
);
|
||||
|
||||
function createFetchChunksOfVisibleDatasetsEffect() {
|
||||
signals.createEffect(
|
||||
() => ({
|
||||
@@ -85,90 +75,6 @@ export function init({
|
||||
}
|
||||
createFetchChunksOfVisibleDatasetsEffect();
|
||||
|
||||
/**
|
||||
* @param {Parameters<Chart['getTicksToWidthRatio']>[0]} args
|
||||
*/
|
||||
function updateVisiblePriceSeriesType(args) {
|
||||
const ratio = chart.getTicksToWidthRatio(args);
|
||||
if (ratio) {
|
||||
if (ratio <= 0.5) {
|
||||
priceSeriesType.set("Candlestick");
|
||||
} else {
|
||||
priceSeriesType.set("Line");
|
||||
}
|
||||
}
|
||||
}
|
||||
const debouncedUpdateVisiblePriceSeriesType = utils.debounce(
|
||||
updateVisiblePriceSeriesType,
|
||||
50,
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {PriceSeriesType} args.type
|
||||
* @param {Option} args.option
|
||||
* @param {ChartPane} args.chartPane
|
||||
*/
|
||||
function createPriceSeries({ type, option, chartPane }) {
|
||||
const s = scale();
|
||||
|
||||
/** @type {AnyDatasetPath} */
|
||||
const datasetPath = `${s}-to-price`;
|
||||
|
||||
const dataset = datasets.getOrCreate(s, datasetPath);
|
||||
|
||||
// Don't trigger reactivity by design
|
||||
activeDatasets().add(dataset);
|
||||
|
||||
const title = "BTC Price";
|
||||
|
||||
/** @type {SplitSeriesBlueprint} */
|
||||
let blueprint;
|
||||
|
||||
if (type === "Candlestick") {
|
||||
blueprint = {
|
||||
datasetPath,
|
||||
title,
|
||||
type: "Candlestick",
|
||||
};
|
||||
} else {
|
||||
blueprint = {
|
||||
datasetPath,
|
||||
title,
|
||||
color: colors.default,
|
||||
};
|
||||
}
|
||||
|
||||
const disabled = signals.createMemo(() => priceSeriesType() !== type);
|
||||
|
||||
const priceSeries = chartPane.createSplitSeries({
|
||||
blueprint,
|
||||
dataset,
|
||||
id: option.id,
|
||||
index: -1,
|
||||
disabled,
|
||||
});
|
||||
|
||||
function createLiveCandleUpdateEffect() {
|
||||
signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
|
||||
if (!latest) return;
|
||||
|
||||
const index = utils.chunkIdToIndex(s, latest.year);
|
||||
|
||||
const series = priceSeries.chunks.at(index);
|
||||
|
||||
if (series) {
|
||||
signals.createEffect(series, (series) => {
|
||||
series?.update(latest);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
createLiveCandleUpdateEffect();
|
||||
|
||||
return priceSeries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ChartOption} option
|
||||
*/
|
||||
@@ -189,47 +95,42 @@ export function init({
|
||||
const chartPane = chart.createPane({
|
||||
paneIndex,
|
||||
unit: paneIndex ? option.unit : "US Dollars",
|
||||
whitespace: true,
|
||||
});
|
||||
|
||||
if (!paneIndex) {
|
||||
updateVisiblePriceSeriesType({
|
||||
visibleTimeRange: chart.visibleTimeRange(),
|
||||
/** @type {AnyDatasetPath} */
|
||||
const datasetPath = `${scale}-to-price`;
|
||||
|
||||
const dataset = datasets.getOrCreate(scale, datasetPath);
|
||||
|
||||
// Don't trigger reactivity by design
|
||||
activeDatasets().add(dataset);
|
||||
|
||||
const priceSeries = chartPane.createSplitSeries({
|
||||
blueprint: {
|
||||
datasetPath,
|
||||
title: "BTC Price",
|
||||
type: "Candlestick",
|
||||
},
|
||||
dataset,
|
||||
id: option.id,
|
||||
index: -1,
|
||||
});
|
||||
|
||||
chartPane
|
||||
.timeScale()
|
||||
.subscribeVisibleLogicalRangeChange((logicalRange) => {
|
||||
if (!logicalRange) return;
|
||||
signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
|
||||
if (!latest) return;
|
||||
|
||||
// Must be the chart with the visible timeScale
|
||||
debouncedUpdateVisiblePriceSeriesType({
|
||||
visibleLogicalRange: logicalRange,
|
||||
});
|
||||
const index = utils.chunkIdToIndex(scale, latest.year);
|
||||
|
||||
priceSeries.forEach((splitSeries) => {
|
||||
const series = splitSeries.chunks.at(index);
|
||||
if (series) {
|
||||
signals.createEffect(series, (series) => {
|
||||
series?.update(latest);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/** @param {PriceSeriesType} type */
|
||||
function _createPriceSeries(type) {
|
||||
return createPriceSeries({
|
||||
chartPane,
|
||||
option,
|
||||
type,
|
||||
});
|
||||
}
|
||||
|
||||
const priceCandlestickSeries = _createPriceSeries("Candlestick");
|
||||
const priceLineSeries = _createPriceSeries("Line");
|
||||
|
||||
function createLinkPriceSeriesEffect() {
|
||||
signals.createEffect(priceLineSeries.active, (active) => {
|
||||
priceCandlestickSeries.active.set(active);
|
||||
});
|
||||
|
||||
signals.createEffect(priceCandlestickSeries.active, (active) => {
|
||||
priceLineSeries.active.set(active);
|
||||
});
|
||||
}
|
||||
createLinkPriceSeriesEffect();
|
||||
});
|
||||
}
|
||||
|
||||
[...seriesBlueprints].reverse().forEach((blueprint, index) => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* @import { Option, ResourceDataset, TimeScale, TimeRange, Unit, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, Color, DatasetCandlestickData, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption, Frequency, LastValues } from "./types/self"
|
||||
* @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions } from "../packages/lightweight-charts/v4.2.0/types";
|
||||
* @import { Marker, CreatePaneParameters, HoveredLegend, ChartPane, SplitSeries, SingleSeries, CreateSplitSeriesParameters, LineSeriesBlueprint, CandlestickSeriesBlueprint, BaselineSeriesBlueprint, CreateBaseSeriesParameters, BaseSeries, RemoveSeriesBlueprintFluff, SplitSeriesBlueprint, AnySeries } from "../packages/lightweight-charts/types";
|
||||
* @import { Marker, CreatePaneParameters, HoveredLegend, ChartPane, SplitSeries, SingleSeries, CreateSplitSeriesParameters, LineSeriesBlueprint, CandlestickSeriesBlueprint, BaselineSeriesBlueprint, CreateBaseSeriesParameters, BaseSeries, RemoveSeriesBlueprintFluff, SplitSeriesBlueprint, AnySeries, PriceSeriesType } from "../packages/lightweight-charts/types";
|
||||
* @import * as _ from "../packages/ufuzzy/v1.0.14/types"
|
||||
* @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LineData, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/types"
|
||||
* @import { DatePath, HeightPath, LastPath } from "./types/paths";
|
||||
@@ -598,6 +598,30 @@ const utils = {
|
||||
|
||||
return { select, signal };
|
||||
},
|
||||
/**
|
||||
* @param {Object} param0
|
||||
* @param {Signal<any>} param0.signal
|
||||
* @param {HTMLInputElement} [param0.input]
|
||||
* @param {HTMLSelectElement} [param0.select]
|
||||
*/
|
||||
createResetableInput({ input, select, signal }) {
|
||||
const div = window.document.createElement("div");
|
||||
|
||||
const element = input || select;
|
||||
if (!element) throw "createResetableField element missing";
|
||||
div.append(element);
|
||||
|
||||
const button = this.createButtonElement({
|
||||
onClick: signal.reset,
|
||||
text: "Reset",
|
||||
title: "Reset field",
|
||||
});
|
||||
button.type = "reset";
|
||||
|
||||
div.append(button);
|
||||
|
||||
return div;
|
||||
},
|
||||
/**
|
||||
* @param {'left' | 'bottom' | 'top' | 'right'} position
|
||||
*/
|
||||
@@ -2154,7 +2178,6 @@ packages.signals().then((signals) =>
|
||||
signals.runWithOwner(owner, () =>
|
||||
initChartsElement({
|
||||
colors,
|
||||
dark,
|
||||
datasets,
|
||||
elements,
|
||||
lightweightCharts,
|
||||
|
||||
@@ -158,15 +158,14 @@ export function init({
|
||||
}),
|
||||
description:
|
||||
"The amount of dollars you have ready on the exchange on day one.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputDollar({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputDollar({
|
||||
id: "simulation-dollars-initial",
|
||||
title: "Initial Dollar Amount",
|
||||
signal: settings.dollars.initial.amount,
|
||||
signals,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -179,14 +178,13 @@ export function init({
|
||||
}),
|
||||
description:
|
||||
"The frequency at which you'll top up your account at the exchange.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createSelect({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createSelect({
|
||||
id: "top-up-frequency",
|
||||
list: frequencies.list,
|
||||
signal: settings.dollars.topUp.frenquency,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -199,15 +197,14 @@ export function init({
|
||||
}),
|
||||
description:
|
||||
"The recurrent amount of dollars you'll be transfering to said exchange.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputDollar({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputDollar({
|
||||
id: "simulation-dollars-top-up-amount",
|
||||
title: "Top Up Dollar Amount",
|
||||
signal: settings.dollars.topUp.amount,
|
||||
signals,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -220,15 +217,14 @@ export function init({
|
||||
}),
|
||||
description:
|
||||
"The amount, if available, of dollars that will be used to buy Bitcoin on day one.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputDollar({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputDollar({
|
||||
id: "simulation-bitcoin-initial-investment",
|
||||
title: "Initial Swap Amount",
|
||||
signal: settings.bitcoin.investment.initial,
|
||||
signals,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -240,14 +236,13 @@ export function init({
|
||||
text: "Investment Frequency",
|
||||
}),
|
||||
description: "The frequency at which you'll be buying Bitcoin.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createSelect({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createSelect({
|
||||
id: "investment-frequency",
|
||||
list: frequencies.list,
|
||||
signal: settings.bitcoin.investment.frequency,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -260,15 +255,14 @@ export function init({
|
||||
}),
|
||||
description:
|
||||
"The recurrent amount, if available, of dollars that will be used to buy Bitcoin.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputDollar({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputDollar({
|
||||
id: "simulation-bitcoin-recurrent-investment",
|
||||
title: "Bitcoin Recurrent Investment",
|
||||
signal: settings.bitcoin.investment.recurrent,
|
||||
signals,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -280,15 +274,14 @@ export function init({
|
||||
text: "Start",
|
||||
}),
|
||||
description: "The first day of the simulation.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputDate({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputDate({
|
||||
id: "simulation-inverval-start",
|
||||
title: "First Simulation Date",
|
||||
signal: settings.interval.start,
|
||||
signals,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -300,15 +293,14 @@ export function init({
|
||||
text: "End",
|
||||
}),
|
||||
description: "The last day of the simulation.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputDate({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputDate({
|
||||
id: "simulation-inverval-end",
|
||||
title: "Last Simulation Day",
|
||||
signal: settings.interval.end,
|
||||
signals,
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -320,8 +312,8 @@ export function init({
|
||||
text: "Exchange",
|
||||
}),
|
||||
description: "The amount of trading fees (in %) at the exchange.",
|
||||
input: createResetableInput({
|
||||
...utils.dom.createInputNumberElement({
|
||||
input: utils.dom.createResetableInput(
|
||||
utils.dom.createInputNumberElement({
|
||||
id: "simulation-fees",
|
||||
title: "Exchange Fees",
|
||||
signal: settings.fees.percentage,
|
||||
@@ -331,8 +323,7 @@ export function init({
|
||||
signals,
|
||||
placeholder: "Fees",
|
||||
}),
|
||||
utils,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -851,32 +842,6 @@ function createFieldElement({ title, description, input }) {
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} param0
|
||||
* @param {Signal<any>} param0.signal
|
||||
* @param {HTMLInputElement} [param0.input]
|
||||
* @param {HTMLSelectElement} [param0.select]
|
||||
* @param {Utilities} param0.utils
|
||||
*/
|
||||
function createResetableInput({ input, select, signal, utils }) {
|
||||
const div = window.document.createElement("div");
|
||||
|
||||
const element = input || select;
|
||||
if (!element) throw "createResetableField element missing";
|
||||
div.append(element);
|
||||
|
||||
const button = utils.dom.createButtonElement({
|
||||
onClick: signal.reset,
|
||||
text: "Reset",
|
||||
title: "Reset field",
|
||||
});
|
||||
button.type = "reset";
|
||||
|
||||
div.append(button);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
/** @param {number} day */
|
||||
function getOrdinalDay(day) {
|
||||
const rest = (day % 30) % 20;
|
||||
|
||||
Reference in New Issue
Block a user