mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-27 08:09:58 -07:00
global: snapshot
This commit is contained in:
@@ -224,14 +224,6 @@ export function init({
|
||||
}
|
||||
createFetchChunksOfVisibleDatasetsEffect();
|
||||
|
||||
function resetChartListElement() {
|
||||
while (
|
||||
elements.chartList.lastElementChild?.classList.contains("chart-wrapper")
|
||||
) {
|
||||
elements.chartList.lastElementChild?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} parent
|
||||
* @param {number} chartIndex
|
||||
@@ -767,7 +759,7 @@ export function init({
|
||||
/** @type {AnyDatasetPath} */
|
||||
const datasetPath = `${s}-to-price`;
|
||||
|
||||
const dataset = datasets.getOrImport(s, datasetPath);
|
||||
const dataset = datasets.getOrCreate(s, datasetPath);
|
||||
|
||||
// Don't trigger reactivity by design
|
||||
activeDatasets().add(dataset);
|
||||
@@ -806,7 +798,7 @@ export function init({
|
||||
});
|
||||
|
||||
function createLiveCandleUpdateEffect() {
|
||||
signals.createEffect(webSockets.krakenCandle.latest, (latest) => {
|
||||
signals.createEffect(webSockets.kraken1dCandle.latest, (latest) => {
|
||||
if (!latest) return;
|
||||
|
||||
const index = utils.chunkIdToIndex(s, latest.year);
|
||||
@@ -825,95 +817,6 @@ export function init({
|
||||
return priceSeries;
|
||||
}
|
||||
|
||||
function resetLegendElement() {
|
||||
elements.legend.innerHTML = "";
|
||||
}
|
||||
|
||||
function initTimeScaleElement() {
|
||||
const GENESIS_DAY = "2009-01-03";
|
||||
|
||||
/**
|
||||
* @param {HTMLButtonElement} button
|
||||
* @param {ChartOption} option
|
||||
*/
|
||||
function setTimeScale(button, option) {
|
||||
const chart = charts.at(-1);
|
||||
if (!chart) return;
|
||||
const timeScale = chart.timeScale();
|
||||
|
||||
const year = button.dataset.year;
|
||||
let days = button.dataset.days;
|
||||
let toHeight = button.dataset.to;
|
||||
|
||||
switch (option.scale) {
|
||||
case "date": {
|
||||
let from = new Date();
|
||||
let to = new Date();
|
||||
to.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
if (!days && typeof button.dataset.yearToDate === "string") {
|
||||
days = String(
|
||||
Math.ceil(
|
||||
(to.getTime() -
|
||||
new Date(`${to.getUTCFullYear()}-01-01`).getTime()) /
|
||||
consts.ONE_DAY_IN_MS,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (year) {
|
||||
from = new Date(`${year}-01-01`);
|
||||
to = new Date(`${year}-12-31`);
|
||||
} else if (days) {
|
||||
from.setDate(from.getUTCDate() - Number(days));
|
||||
} else {
|
||||
from = new Date(GENESIS_DAY);
|
||||
}
|
||||
|
||||
timeScale.setVisibleRange({
|
||||
from: /** @type {Time} */ (from.getTime() / 1000),
|
||||
to: /** @type {Time} */ (to.getTime() / 1000),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "height": {
|
||||
timeScale.setVisibleRange({
|
||||
from: /** @type {Time} */ (0),
|
||||
to: /** @type {Time} */ (Number(toHeight?.slice(0, -1)) * 1_000),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} timeScaleButtons
|
||||
*/
|
||||
function initGoToButtons(timeScaleButtons) {
|
||||
Array.from(timeScaleButtons.children).forEach((button) => {
|
||||
if (button.tagName !== "BUTTON") throw "Expect a button";
|
||||
button.addEventListener("click", () => {
|
||||
const option = options.selected();
|
||||
if (option.kind === "chart") {
|
||||
setTimeScale(/** @type {HTMLButtonElement} */ (button), option);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// initGoToButtons(elements.timeScaleDateButtons);
|
||||
// initGoToButtons(elements.timeScaleHeightButtons);
|
||||
|
||||
// function createScaleButtonsToggleEffect() {
|
||||
// const isDate = signals.createMemo(() => scale() === "date");
|
||||
// signals.createEffect(isDate, (isDate) => {
|
||||
// elements.timeScaleDateButtons.hidden = !isDate;
|
||||
// elements.timeScaleHeightButtons.hidden = isDate;
|
||||
// });
|
||||
// }
|
||||
// createScaleButtonsToggleEffect();
|
||||
}
|
||||
initTimeScaleElement();
|
||||
|
||||
/**
|
||||
* @param {ChartOption} option
|
||||
*/
|
||||
@@ -933,15 +836,12 @@ export function init({
|
||||
(list) => (list ? [list] : []),
|
||||
);
|
||||
|
||||
resetLegendElement();
|
||||
resetChartListElement();
|
||||
|
||||
/** @type {Series[]} */
|
||||
const allSeries = [];
|
||||
|
||||
charts = chartsBlueprints.map((seriesBlueprints, chartIndex) => {
|
||||
const { chartDiv, unitName, chartMode } = createChartDiv(
|
||||
elements.chartList,
|
||||
elements.chartsChartList,
|
||||
chartIndex,
|
||||
);
|
||||
|
||||
@@ -1148,7 +1048,7 @@ export function init({
|
||||
}
|
||||
|
||||
[...seriesBlueprints].reverse().forEach((seriesBlueprint, index) => {
|
||||
const dataset = datasets.getOrImport(
|
||||
const dataset = datasets.getOrCreate(
|
||||
scale,
|
||||
seriesBlueprint.datasetPath,
|
||||
);
|
||||
@@ -1266,11 +1166,31 @@ export function init({
|
||||
});
|
||||
}
|
||||
|
||||
function resetLegendElement() {
|
||||
elements.legend.innerHTML = "";
|
||||
}
|
||||
|
||||
function resetChartListElement() {
|
||||
while (
|
||||
elements.chartsChartList.lastElementChild?.classList.contains(
|
||||
"chart-wrapper",
|
||||
)
|
||||
) {
|
||||
elements.chartsChartList.lastElementChild?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
charts.forEach((chart) => chart.remove());
|
||||
charts = [];
|
||||
resetLegendElement();
|
||||
resetChartListElement();
|
||||
}
|
||||
|
||||
function createApplyChartOptionEffect() {
|
||||
signals.createEffect(selected, (option) => {
|
||||
signals.createRoot(() => {
|
||||
applyChartOption(option);
|
||||
});
|
||||
reset();
|
||||
applyChartOption(option);
|
||||
});
|
||||
}
|
||||
createApplyChartOptionEffect();
|
||||
|
||||
41
website/scripts/live-price.js
Normal file
41
website/scripts/live-price.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @import {Options} from './options';
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Colors} args.colors
|
||||
* @param {Consts} args.consts
|
||||
* @param {LightweightCharts} args.lightweightCharts
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Options} args.options
|
||||
* @param {Datasets} args.datasets
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
* @param {Ids} args.ids
|
||||
* @param {Accessor<boolean>} args.dark
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
consts,
|
||||
dark,
|
||||
datasets,
|
||||
elements,
|
||||
ids,
|
||||
lightweightCharts,
|
||||
options,
|
||||
signals,
|
||||
utils,
|
||||
webSockets,
|
||||
}) {
|
||||
const livePriceElement = elements.livePrice;
|
||||
|
||||
const price = window.document.createElement("h1");
|
||||
livePriceElement.append(price);
|
||||
|
||||
signals.createEffect(webSockets.kraken1dCandle.latest, (candle) => {
|
||||
if (!candle) return;
|
||||
price.innerHTML = utils.formatters.dollars.format(candle.close);
|
||||
});
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
* @import { Option, ResourceDataset, TimeScale, TimeRange, Unit, Marker, Weighted, DatasetPath, OHLC, FetchedJSON, DatasetValue, FetchedResult, AnyDatasetPath, SeriesBlueprint, BaselineSpecificSeriesBlueprint, CandlestickSpecificSeriesBlueprint, LineSpecificSeriesBlueprint, SpecificSeriesBlueprintWithChart, Signal, Color, DatasetCandlestickData, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, AnyPath, SimulationOption } from "./types/self"
|
||||
* @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions} from "../packages/lightweight-charts/v4.2.0/types";
|
||||
* @import * as _ from "../packages/ufuzzy/v1.0.14/types"
|
||||
* @import { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/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";
|
||||
* @import { SignalOptions } from "../packages/solid-signals/2024-11-02/types/core/core"
|
||||
* @import { getOwner as GetOwner, onCleanup as OnCleanup, Owner } from "../packages/solid-signals/2024-11-02/types/core/owner"
|
||||
@@ -19,7 +19,22 @@ function initPackages() {
|
||||
createSolidSignal: /** @type {CreateSignal} */ (
|
||||
_signals.createSignal
|
||||
),
|
||||
createEffect: /** @type {CreateEffect} */ (_signals.createEffect),
|
||||
createSolidEffect: /** @type {CreateEffect} */ (
|
||||
_signals.createEffect
|
||||
),
|
||||
createEffect: /** @type {CreateEffect} */ (compute, effect) => {
|
||||
let dispose = /** @type {VoidFunction | null} */ (null);
|
||||
// @ts-ignore
|
||||
_signals.createEffect(compute, (v) => {
|
||||
dispose?.();
|
||||
signals.createRoot((_dispose) => {
|
||||
dispose = _dispose;
|
||||
effect(v);
|
||||
});
|
||||
signals.onCleanup(() => dispose?.());
|
||||
});
|
||||
signals.onCleanup(() => dispose?.());
|
||||
},
|
||||
createMemo: /** @type {CreateMemo} */ (_signals.createMemo),
|
||||
createRoot: /** @type {CreateRoot} */ (_signals.createRoot),
|
||||
getOwner: /** @type {GetOwner} */ (_signals.getOwner),
|
||||
@@ -220,8 +235,15 @@ function initPackages() {
|
||||
* @param {HTMLElement} args.element
|
||||
* @param {Signals} args.signals
|
||||
* @param {Colors} args.colors
|
||||
* @param {DeepPartial<ChartOptions>} [args.options]
|
||||
*/
|
||||
function createChart({ scale, element, signals, colors }) {
|
||||
function createChart({
|
||||
scale,
|
||||
element,
|
||||
signals,
|
||||
colors,
|
||||
options: _options = {},
|
||||
}) {
|
||||
/** @satisfies {DeepPartial<ChartOptions>} */
|
||||
const options = {
|
||||
autoSize: true,
|
||||
@@ -257,6 +279,7 @@ function initPackages() {
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
..._options,
|
||||
};
|
||||
|
||||
/** @type {IChartApi} */
|
||||
@@ -597,7 +620,6 @@ function initPackages() {
|
||||
let packagePromise = null;
|
||||
|
||||
return function () {
|
||||
let p = null;
|
||||
if (!packagePromise) {
|
||||
// @ts-ignore
|
||||
packagePromise = imports[key]();
|
||||
@@ -640,6 +662,20 @@ const utils = {
|
||||
yield() {
|
||||
return this.sleep(0);
|
||||
},
|
||||
array: {
|
||||
/**
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
*/
|
||||
range(start, end) {
|
||||
const range = [];
|
||||
while (start <= end) {
|
||||
range.push(start);
|
||||
start += 1;
|
||||
}
|
||||
return range;
|
||||
},
|
||||
},
|
||||
dom: {
|
||||
/**
|
||||
* @param {string} id
|
||||
@@ -743,13 +779,16 @@ const utils = {
|
||||
/**
|
||||
* @param {Object} arg
|
||||
* @param {string} arg.text
|
||||
* @param {string} arg.title
|
||||
* @param {VoidFunction} arg.onClick
|
||||
*/
|
||||
createButtonElement({ text, onClick }) {
|
||||
createButtonElement({ text, onClick, title }) {
|
||||
const button = window.document.createElement("button");
|
||||
|
||||
button.innerHTML = text;
|
||||
|
||||
button.title = title;
|
||||
|
||||
button.addEventListener("click", onClick);
|
||||
|
||||
return button;
|
||||
@@ -773,6 +812,8 @@ const utils = {
|
||||
}) {
|
||||
const label = window.document.createElement("label");
|
||||
|
||||
inputId = inputId.toLowerCase();
|
||||
|
||||
const input = window.document.createElement("input");
|
||||
input.type = "radio";
|
||||
input.name = inputName;
|
||||
@@ -898,6 +939,130 @@ const utils = {
|
||||
|
||||
return field;
|
||||
},
|
||||
createUlElement() {
|
||||
return window.document.createElement("ul");
|
||||
},
|
||||
createLiElement() {
|
||||
return window.document.createElement("li");
|
||||
},
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {string} args.id
|
||||
* @param {string} args.title
|
||||
* @param {Signal<number | null>} args.signal
|
||||
* @param {number} args.min
|
||||
* @param {number} args.max
|
||||
* @param {number} args.step
|
||||
* @param {{createEffect: typeof CreateEffect}} args.signals
|
||||
*/
|
||||
createInputNumberElement({ id, title, signal, min, max, step, signals }) {
|
||||
const input = window.document.createElement("input");
|
||||
input.id = id;
|
||||
input.title = title;
|
||||
input.type = "number";
|
||||
input.min = String(min);
|
||||
input.max = String(max);
|
||||
input.step = String(step);
|
||||
|
||||
let stateValue = /** @type {string | null} */ (null);
|
||||
|
||||
signals.createEffect(
|
||||
() => {
|
||||
const value = signal();
|
||||
return value ? String(value) : "";
|
||||
},
|
||||
(value) => {
|
||||
if (stateValue !== value) {
|
||||
input.value = value;
|
||||
stateValue = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
input.addEventListener("input", () => {
|
||||
const valueSer = input.value;
|
||||
const value = Number(valueSer);
|
||||
if (value >= min && value <= max) {
|
||||
stateValue = valueSer;
|
||||
signal.set(value);
|
||||
}
|
||||
});
|
||||
|
||||
return input;
|
||||
},
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {string} args.id
|
||||
* @param {string} args.title
|
||||
* @param {Signal<Date | null>} args.signal
|
||||
* @param {{createEffect: typeof CreateEffect}} args.signals
|
||||
*/
|
||||
createInputDate({ id, title, signal, signals }) {
|
||||
const input = window.document.createElement("input");
|
||||
input.id = id;
|
||||
input.title = title;
|
||||
input.type = "date";
|
||||
const min = "2011-01-01";
|
||||
const minDate = new Date(min);
|
||||
const maxDate = new Date();
|
||||
const max = utils.date.toString(maxDate);
|
||||
input.min = min;
|
||||
input.max = max;
|
||||
|
||||
let stateValue = /** @type {string | null} */ (null);
|
||||
|
||||
signals.createEffect(
|
||||
() => {
|
||||
const date = signal();
|
||||
return date ? utils.date.toString(date) : "";
|
||||
},
|
||||
(value) => {
|
||||
if (stateValue !== value) {
|
||||
input.value = value;
|
||||
stateValue = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
input.addEventListener("change", () => {
|
||||
const value = input.value;
|
||||
const date = new Date(value);
|
||||
if (date >= minDate && date <= maxDate) {
|
||||
stateValue = value;
|
||||
signal.set(value ? date : null);
|
||||
}
|
||||
});
|
||||
|
||||
return input;
|
||||
},
|
||||
/**
|
||||
* @param {Object} param0
|
||||
* @param {string} param0.title
|
||||
* @param {string} param0.description
|
||||
*/
|
||||
createHeader({ title, description }) {
|
||||
const headerElement = window.document.createElement("header");
|
||||
|
||||
const div = window.document.createElement("div");
|
||||
headerElement.append(div);
|
||||
|
||||
const h1 = window.document.createElement("h1");
|
||||
div.append(h1);
|
||||
|
||||
const titleElement = window.document.createElement("span");
|
||||
titleElement.append(title);
|
||||
h1.append(titleElement);
|
||||
|
||||
const descriptionElement = window.document.createElement("small");
|
||||
descriptionElement.append(description);
|
||||
h1.append(descriptionElement);
|
||||
|
||||
return {
|
||||
headerElement,
|
||||
titleElement,
|
||||
descriptionElement,
|
||||
};
|
||||
},
|
||||
},
|
||||
url: {
|
||||
chartParamsWhitelist: ["from", "to"],
|
||||
@@ -1124,6 +1289,20 @@ const utils = {
|
||||
return Number(v);
|
||||
},
|
||||
},
|
||||
date: {
|
||||
/**
|
||||
* @param {Date} v
|
||||
*/
|
||||
serialize(v) {
|
||||
return utils.date.toString(v);
|
||||
},
|
||||
/**
|
||||
* @param {string} v
|
||||
*/
|
||||
deserialize(v) {
|
||||
return new Date(v);
|
||||
},
|
||||
},
|
||||
},
|
||||
formatters: {
|
||||
dollars: new Intl.NumberFormat("en-US", {
|
||||
@@ -1168,6 +1347,25 @@ const utils = {
|
||||
: // @ts-ignore
|
||||
new Date(time.year, time.month, time.day);
|
||||
},
|
||||
/**
|
||||
* @param {Date} start
|
||||
*/
|
||||
getRangeUpToToday(start) {
|
||||
return this.getRange(start, new Date());
|
||||
},
|
||||
/**
|
||||
* @param {Date} start
|
||||
* @param {Date} end
|
||||
*/
|
||||
getRange(start, end) {
|
||||
const dates = /** @type {Date[]} */ ([]);
|
||||
let currentDate = new Date(start);
|
||||
while (currentDate <= end) {
|
||||
dates.push(new Date(currentDate));
|
||||
currentDate.setUTCDate(currentDate.getUTCDate() + 1);
|
||||
}
|
||||
return dates;
|
||||
},
|
||||
},
|
||||
/**
|
||||
*
|
||||
@@ -1328,14 +1526,14 @@ const elements = {
|
||||
selectedTitle: utils.dom.getElementById("selected-title"),
|
||||
selectedDescription: utils.dom.getElementById("selected-description"),
|
||||
selectors: utils.dom.getElementById("frame-selectors"),
|
||||
chartList: utils.dom.getElementById("chart-list"),
|
||||
chartsChartList: utils.dom.getElementById("charts-chart-list"),
|
||||
legend: utils.dom.getElementById("legend"),
|
||||
style: getComputedStyle(window.document.documentElement),
|
||||
// timeScaleDateButtons: utils.dom.getElementById("timescale-date-buttons"),
|
||||
// timeScaleHeightButtons: utils.dom.getElementById("timescale-height-buttons"),
|
||||
selectedHeader: utils.dom.getElementById("selected-header"),
|
||||
charts: utils.dom.getElementById("charts"),
|
||||
simulation: utils.dom.getElementById("simulation"),
|
||||
livePrice: utils.dom.getElementById("live-price"),
|
||||
moscowTime: utils.dom.getElementById("moscow-time"),
|
||||
};
|
||||
/** @typedef {typeof elements} Elements */
|
||||
|
||||
@@ -1951,6 +2149,21 @@ function createDatasets(signals) {
|
||||
scale,
|
||||
url: baseURL,
|
||||
fetch: _fetch,
|
||||
fetchRange(start, end) {
|
||||
const promises = /** @type {Promise<void>[]} */ ([]);
|
||||
switch (scale) {
|
||||
case "date": {
|
||||
utils.array.range(start, end).forEach((year) => {
|
||||
promises.push(this.fetch(year));
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw "Unsupported";
|
||||
}
|
||||
}
|
||||
return Promise.all(promises);
|
||||
},
|
||||
fetchedJSONs,
|
||||
// drop() {
|
||||
// dispose();
|
||||
@@ -1985,7 +2198,7 @@ function createDatasets(signals) {
|
||||
* @param {DatasetPath<S>} path
|
||||
* @returns {ResourceDataset<S>}
|
||||
*/
|
||||
function getOrImport(scale, path) {
|
||||
function getOrCreate(scale, path) {
|
||||
if (scale === "date") {
|
||||
const found = date.get(/** @type {DatePath} */ (path));
|
||||
if (found) return /** @type {ResourceDataset<S>} */ (found);
|
||||
@@ -2018,7 +2231,7 @@ function createDatasets(signals) {
|
||||
}
|
||||
|
||||
return {
|
||||
getOrImport,
|
||||
getOrCreate,
|
||||
};
|
||||
}
|
||||
/** @typedef {ReturnType<typeof createDatasets>} Datasets */
|
||||
@@ -2088,18 +2301,18 @@ function initWebSockets(signals) {
|
||||
|
||||
/**
|
||||
* @param {(candle: DatasetCandlestickData) => void} callback
|
||||
* @returns
|
||||
* @param {number} interval
|
||||
*/
|
||||
function krakenCandleWebSocketCreator(callback) {
|
||||
const ws = new WebSocket("wss://ws.kraken.com");
|
||||
function krakenCandleWebSocketCreator(callback, interval) {
|
||||
const ws = new WebSocket("wss://ws.kraken.com/v2");
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "subscribe",
|
||||
pair: ["XBT/USD"],
|
||||
subscription: {
|
||||
name: "ohlc",
|
||||
method: "subscribe",
|
||||
params: {
|
||||
channel: "ohlc",
|
||||
symbol: ["BTC/USD"],
|
||||
interval: 1440,
|
||||
},
|
||||
}),
|
||||
@@ -2109,11 +2322,11 @@ function initWebSockets(signals) {
|
||||
ws.addEventListener("message", (message) => {
|
||||
const result = JSON.parse(message.data);
|
||||
|
||||
if (!Array.isArray(result)) return;
|
||||
if (result.channel !== "ohlc") return;
|
||||
|
||||
const [timestamp, _, open, high, low, close, __, volume] = result[1];
|
||||
const { timestamp, open, high, low, close } = result.data.at(-1);
|
||||
|
||||
const date = new Date(Number(timestamp) * 1000);
|
||||
const date = new Date(timestamp);
|
||||
|
||||
const dateStr = utils.date.toString(date);
|
||||
|
||||
@@ -2134,12 +2347,18 @@ function initWebSockets(signals) {
|
||||
return ws;
|
||||
}
|
||||
|
||||
const krakenCandle = createWebsocket(krakenCandleWebSocketCreator);
|
||||
const kraken1dCandle = createWebsocket((callback) =>
|
||||
krakenCandleWebSocketCreator(callback, 1440),
|
||||
);
|
||||
const kraken5mnCandle = createWebsocket((callback) =>
|
||||
krakenCandleWebSocketCreator(callback, 5),
|
||||
);
|
||||
|
||||
krakenCandle.open();
|
||||
kraken1dCandle.open();
|
||||
kraken5mnCandle.open();
|
||||
|
||||
function createDocumentTitleEffect() {
|
||||
signals.createEffect(krakenCandle.latest, (latest) => {
|
||||
signals.createEffect(kraken5mnCandle.latest, (latest) => {
|
||||
if (latest) {
|
||||
const close = latest.close;
|
||||
console.log("close:", close);
|
||||
@@ -2153,7 +2372,8 @@ function initWebSockets(signals) {
|
||||
createDocumentTitleEffect();
|
||||
|
||||
return {
|
||||
krakenCandle,
|
||||
kraken1dCandle,
|
||||
// kraken5mnCandle,
|
||||
};
|
||||
}
|
||||
/** @typedef {ReturnType<typeof initWebSockets>} WebSockets */
|
||||
@@ -2252,6 +2472,8 @@ packages.signals().then((signals) =>
|
||||
);
|
||||
let firstChartOption = true;
|
||||
let firstSimulationOption = true;
|
||||
let firstLivePriceOption = true;
|
||||
let firstMoscowTimeOption = true;
|
||||
|
||||
signals.createEffect(options.selected, (option) => {
|
||||
if (previousElement) {
|
||||
@@ -2262,7 +2484,13 @@ packages.signals().then((signals) =>
|
||||
utils.url.replaceHistory({ pathname: option.id });
|
||||
}
|
||||
|
||||
const hideTop = option.kind === "home" || option.kind === "pdf";
|
||||
const hideTop =
|
||||
option.kind === "home" ||
|
||||
option.kind === "pdf" ||
|
||||
option.kind === "live-price" ||
|
||||
option.kind === "converter" ||
|
||||
option.kind === "moscow-time";
|
||||
|
||||
elements.selectedHeader.hidden = hideTop;
|
||||
|
||||
elements.selectedTitle.innerHTML = option.title;
|
||||
@@ -2277,6 +2505,8 @@ packages.signals().then((signals) =>
|
||||
// break;
|
||||
// }
|
||||
case "chart": {
|
||||
console.log("chart", option);
|
||||
|
||||
element = elements.charts;
|
||||
|
||||
lastChartOption.set(option);
|
||||
@@ -2333,7 +2563,7 @@ packages.signals().then((signals) =>
|
||||
ids,
|
||||
lightweightCharts,
|
||||
options,
|
||||
selected: /** @type {any} */ (lastChartOption),
|
||||
selected: option,
|
||||
signals,
|
||||
utils,
|
||||
webSockets,
|
||||
@@ -2347,7 +2577,77 @@ packages.signals().then((signals) =>
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
case "live-price": {
|
||||
console.log("live-price");
|
||||
|
||||
element = elements.livePrice;
|
||||
|
||||
if (firstLivePriceOption) {
|
||||
const lightweightCharts = packages.lightweightCharts();
|
||||
const script = import("./live-price.js");
|
||||
|
||||
utils.dom.importStyleAndThen("/styles/live-price.css", () =>
|
||||
script.then(({ init }) =>
|
||||
lightweightCharts.then((lightweightCharts) =>
|
||||
signals.runWithOwner(owner, () =>
|
||||
init({
|
||||
colors,
|
||||
consts,
|
||||
dark,
|
||||
datasets,
|
||||
elements,
|
||||
ids,
|
||||
lightweightCharts,
|
||||
options,
|
||||
signals,
|
||||
utils,
|
||||
webSockets,
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
firstLivePriceOption = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case "moscow-time": {
|
||||
console.log("moscow-time");
|
||||
|
||||
element = elements.moscowTime;
|
||||
|
||||
if (firstLivePriceOption) {
|
||||
const lightweightCharts = packages.lightweightCharts();
|
||||
const script = import("./moscow-time.js");
|
||||
|
||||
utils.dom.importStyleAndThen("/styles/moscow-time.css", () =>
|
||||
script.then(({ init }) =>
|
||||
signals.runWithOwner(owner, () =>
|
||||
init({
|
||||
colors,
|
||||
consts,
|
||||
dark,
|
||||
datasets,
|
||||
elements,
|
||||
ids,
|
||||
options,
|
||||
signals,
|
||||
utils,
|
||||
webSockets,
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
firstLivePriceOption = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case "converter":
|
||||
case "home":
|
||||
case "pdf":
|
||||
case "url": {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
45
website/scripts/moscow-time.js
Normal file
45
website/scripts/moscow-time.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @import {Options} from './options';
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {Colors} args.colors
|
||||
* @param {Consts} args.consts
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Options} args.options
|
||||
* @param {Datasets} args.datasets
|
||||
* @param {WebSockets} args.webSockets
|
||||
* @param {Elements} args.elements
|
||||
* @param {Ids} args.ids
|
||||
* @param {Accessor<boolean>} args.dark
|
||||
*/
|
||||
export function init({
|
||||
colors,
|
||||
consts,
|
||||
dark,
|
||||
datasets,
|
||||
elements,
|
||||
ids,
|
||||
options,
|
||||
signals,
|
||||
utils,
|
||||
webSockets,
|
||||
}) {
|
||||
const moscowTimeElement = elements.moscowTime;
|
||||
|
||||
const satsPerDollar = signals.createMemo(
|
||||
() =>
|
||||
100_000_000 /
|
||||
// webSockets.kraken5mnCandle.latest()?.close ||
|
||||
(webSockets.kraken1dCandle.latest()?.close || 0),
|
||||
);
|
||||
|
||||
const p = window.document.createElement("h1");
|
||||
moscowTimeElement.append(p);
|
||||
|
||||
signals.createEffect(satsPerDollar, (satsPerDollar) => {
|
||||
p.innerHTML = utils.formatters.dollars.format(satsPerDollar);
|
||||
});
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
* @param {Colors} args.colors
|
||||
* @param {Consts} args.consts
|
||||
* @param {LightweightCharts} args.lightweightCharts
|
||||
* @param {Accessor<ChartOption>} args.selected
|
||||
* @param {SimulationOption} args.selected
|
||||
* @param {Signals} args.signals
|
||||
* @param {Utilities} args.utils
|
||||
* @param {Options} args.options
|
||||
@@ -38,22 +38,27 @@ export function init({
|
||||
const resultsElement = window.document.createElement("div");
|
||||
simulationElement.append(resultsElement);
|
||||
|
||||
const getDefaultIntervalStart = () => new Date("2021-04-15");
|
||||
const getDefaultIntervalEnd = () => new Date();
|
||||
|
||||
const storagePrefix = "save-in-bitcoin";
|
||||
const settings = {
|
||||
initial: signals.createSignal(/** @type {number | null} */ (1000), {
|
||||
save: {
|
||||
...utils.serde.number,
|
||||
id: `${storagePrefix}-initial-amount`,
|
||||
param: "initial-amount",
|
||||
},
|
||||
}),
|
||||
later: signals.createSignal(/** @type {number | null} */ (0), {
|
||||
save: {
|
||||
...utils.serde.number,
|
||||
id: `${storagePrefix}-later-amount`,
|
||||
param: "later-amount",
|
||||
},
|
||||
}),
|
||||
initial: {
|
||||
firstDay: signals.createSignal(/** @type {number | null} */ (1000), {
|
||||
save: {
|
||||
...utils.serde.number,
|
||||
id: `${storagePrefix}-initial-amount`,
|
||||
param: "initial-amount",
|
||||
},
|
||||
}),
|
||||
overTime: signals.createSignal(/** @type {number | null} */ (0), {
|
||||
save: {
|
||||
...utils.serde.number,
|
||||
id: `${storagePrefix}-later-amount`,
|
||||
param: "later-amount",
|
||||
},
|
||||
}),
|
||||
},
|
||||
recurrent: {
|
||||
amount: signals.createSignal(/** @type {number | null} */ (100), {
|
||||
save: {
|
||||
@@ -63,8 +68,45 @@ export function init({
|
||||
},
|
||||
}),
|
||||
},
|
||||
interval: {
|
||||
start: signals.createSignal(
|
||||
/** @type {Date | null} */ (getDefaultIntervalStart()),
|
||||
{
|
||||
save: {
|
||||
...utils.serde.date,
|
||||
id: `${storagePrefix}-interval-start`,
|
||||
param: "interval-start",
|
||||
},
|
||||
},
|
||||
),
|
||||
end: signals.createSignal(
|
||||
/** @type {Date | null} */ (getDefaultIntervalEnd()),
|
||||
{
|
||||
save: {
|
||||
...utils.serde.date,
|
||||
id: `${storagePrefix}-interval-end`,
|
||||
param: "interval-end",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
fees: {
|
||||
percentage: signals.createSignal(/** @type {number | null} */ (0.25), {
|
||||
save: {
|
||||
...utils.serde.number,
|
||||
id: `${storagePrefix}-percentage`,
|
||||
param: "percentage",
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const { headerElement } = utils.dom.createHeader({
|
||||
title: selected.title,
|
||||
description: selected.serializedPath,
|
||||
});
|
||||
parametersElement.append(headerElement);
|
||||
|
||||
const initialGroup = createParameterGroup({
|
||||
title: "Initial",
|
||||
description:
|
||||
@@ -78,7 +120,7 @@ export function init({
|
||||
input: createInputDollar({
|
||||
id: "simulation-dollars-initial",
|
||||
title: "Initial amount of dollars converted",
|
||||
signal: settings.initial,
|
||||
signal: settings.initial.firstDay,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
@@ -89,12 +131,17 @@ export function init({
|
||||
input: createInputDollar({
|
||||
id: "simulation-dollars-later",
|
||||
title: "Dollars to spread over time",
|
||||
signal: settings.later,
|
||||
signal: settings.initial.overTime,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
parametersElement.append(createHrElement());
|
||||
const topUpGroup = createParameterGroup({
|
||||
title: "Top Up",
|
||||
description:
|
||||
"The topUp amount of dollars you're willing to eventually save in Bitcoin.",
|
||||
});
|
||||
parametersElement.append(topUpGroup);
|
||||
|
||||
const recurrentGroup = createParameterGroup({
|
||||
title: "Recurrent",
|
||||
@@ -105,7 +152,7 @@ export function init({
|
||||
|
||||
recurrentGroup.append(
|
||||
createInputField({
|
||||
name: "Amount",
|
||||
name: "Maximum Amount",
|
||||
input: createInputDollar({
|
||||
id: "simulation-dollars-recurrent",
|
||||
title: "Recurrent dollar amount",
|
||||
@@ -114,34 +161,92 @@ export function init({
|
||||
}),
|
||||
);
|
||||
|
||||
const frequencyUL = appendUl({ parent: recurrentGroup });
|
||||
|
||||
[{ name: "Daily" }, { name: "Weekly" }, { name: "Monthly" }].forEach(
|
||||
({ name }) => {
|
||||
const li = appendLi({ name, parent: frequencyUL });
|
||||
},
|
||||
);
|
||||
|
||||
const frequencyChoiceUL = appendUl({ parent: recurrentGroup });
|
||||
const frequencyUL = utils.dom.createUlElement();
|
||||
recurrentGroup.append(frequencyUL);
|
||||
|
||||
[
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
].forEach((name) => {
|
||||
const li = appendLi({ name, parent: frequencyChoiceUL });
|
||||
{ name: "Daily" },
|
||||
{
|
||||
name: "Weekly",
|
||||
sub: [
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
"Sunday",
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Monthly",
|
||||
sub: [
|
||||
"The 1st",
|
||||
"The 2nd",
|
||||
"The 3rd",
|
||||
"The 4th",
|
||||
"The 5th",
|
||||
"The 6th",
|
||||
"The 7th",
|
||||
"The 8th",
|
||||
"The 9th",
|
||||
"The 10th",
|
||||
"The 11th",
|
||||
"The 12th",
|
||||
"The 13th",
|
||||
"The 14th",
|
||||
"The 15th",
|
||||
"The 16th",
|
||||
"The 17th",
|
||||
"The 18th",
|
||||
"The 19th",
|
||||
"The 20th",
|
||||
"The 21st",
|
||||
"The 22nd",
|
||||
"The 23rd",
|
||||
"The 24th",
|
||||
"The 25th",
|
||||
"The 26th",
|
||||
"The 27th",
|
||||
"The 28th",
|
||||
],
|
||||
},
|
||||
].forEach(({ name, sub }, index) => {
|
||||
const li = utils.dom.createLiElement();
|
||||
const { label, input } = utils.dom.createLabeledInput({
|
||||
inputId: `frequency-${name}`,
|
||||
inputName: "frequency",
|
||||
inputValue: name.toLowerCase(),
|
||||
labelTitle: name,
|
||||
inputChecked: !index,
|
||||
onClick: () => {},
|
||||
});
|
||||
label.append(name);
|
||||
li.append(label);
|
||||
if (sub) {
|
||||
const parentName = name;
|
||||
const ul = utils.dom.createUlElement();
|
||||
li.append(ul);
|
||||
sub.forEach((name) => {
|
||||
const li = utils.dom.createLiElement();
|
||||
const { label, input } = utils.dom.createLabeledInput({
|
||||
inputId: `frequency-${parentName}-${name}`,
|
||||
inputName: `frequency-${parentName}`,
|
||||
inputValue: name.toLowerCase(),
|
||||
labelTitle: name,
|
||||
inputChecked: !index,
|
||||
onClick: () => {},
|
||||
});
|
||||
label.append(name);
|
||||
li.append(label);
|
||||
ul.append(li);
|
||||
});
|
||||
}
|
||||
frequencyUL.append(li);
|
||||
});
|
||||
|
||||
parametersElement.append(createHrElement());
|
||||
|
||||
const today = signals.createSignal(utils.date.todayUTC());
|
||||
setInterval(() => {
|
||||
today.set(utils.date.todayUTC());
|
||||
}, consts.FIVE_SECONDS_IN_MS);
|
||||
const frequencyChoiceUL = utils.dom.createUlElement();
|
||||
recurrentGroup.append(frequencyChoiceUL);
|
||||
|
||||
const intervalGroup = createParameterGroup({
|
||||
title: "Interval",
|
||||
@@ -149,42 +254,22 @@ export function init({
|
||||
});
|
||||
parametersElement.append(intervalGroup);
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {HTMLElement} args.parent
|
||||
*/
|
||||
function appendDiv({ parent }) {
|
||||
const div = window.document.createElement("div");
|
||||
parent.append(div);
|
||||
return div;
|
||||
}
|
||||
console.log("weofpwklfpwkofwepokf");
|
||||
|
||||
function createInputDateField() {
|
||||
const div = appendDiv({ parent: intervalGroup });
|
||||
|
||||
appendInputDate({
|
||||
id: "",
|
||||
title: "",
|
||||
value: "2021-04-15",
|
||||
parent: div,
|
||||
signals,
|
||||
today,
|
||||
});
|
||||
|
||||
appendButton({
|
||||
onClick: () => {},
|
||||
text: "Reset",
|
||||
title: "",
|
||||
parent: div,
|
||||
});
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
createInputDateField();
|
||||
createInputDateField();
|
||||
|
||||
parametersElement.append(createHrElement());
|
||||
createInputDateField({
|
||||
signal: settings.interval.start,
|
||||
getDefault: getDefaultIntervalStart,
|
||||
parent: intervalGroup,
|
||||
signals,
|
||||
utils,
|
||||
});
|
||||
createInputDateField({
|
||||
signal: settings.interval.end,
|
||||
getDefault: getDefaultIntervalEnd,
|
||||
parent: intervalGroup,
|
||||
signals,
|
||||
utils,
|
||||
});
|
||||
|
||||
const feesGroup = createParameterGroup({
|
||||
title: "Fees",
|
||||
@@ -193,39 +278,275 @@ export function init({
|
||||
});
|
||||
parametersElement.append(feesGroup);
|
||||
|
||||
createInputNumber({
|
||||
const input = utils.dom.createInputNumberElement({
|
||||
id: "",
|
||||
title: "",
|
||||
value: 0.25,
|
||||
parent: feesGroup,
|
||||
signal: settings.fees.percentage,
|
||||
min: 0,
|
||||
max: 10,
|
||||
max: 50,
|
||||
step: 0.01,
|
||||
signals,
|
||||
});
|
||||
feesGroup.append(input);
|
||||
|
||||
// parametersElement.append(utils.dom.createHrElement());
|
||||
|
||||
// const strategyGroup = createParameterGroup({
|
||||
// title: "Strategy",
|
||||
// description: "The strategy used to convert your fiat into Bitcoin",
|
||||
// });
|
||||
// parametersElement.append(strategyGroup);
|
||||
|
||||
// const ulStrategies = utils.dom.createUlElement();
|
||||
// strategyGroup.append(ulStrategies);
|
||||
|
||||
// ["All in", "Weighted Local", "Weighted Cycle"].forEach((strategy) => {
|
||||
// const li = utils.dom.createLiElement();
|
||||
// li.append(strategy);
|
||||
// ulStrategies.append(li);
|
||||
// });
|
||||
|
||||
const parent = window.document.createElement("div");
|
||||
parent.classList.add("chart-list");
|
||||
resultsElement.append(parent);
|
||||
|
||||
signals.createEffect(settings.interval.start, (start) => {
|
||||
console.log("start", start);
|
||||
});
|
||||
|
||||
parametersElement.append(createHrElement());
|
||||
|
||||
const strategyGroup = createParameterGroup({
|
||||
title: "Strategy",
|
||||
description: "The strategy used to convert your fiat into Bitcoin",
|
||||
signals.createEffect(settings.interval.end, (end) => {
|
||||
console.log("end", end);
|
||||
});
|
||||
parametersElement.append(strategyGroup);
|
||||
|
||||
const ulStrategies = appendUl({ parent: strategyGroup });
|
||||
const owner = signals.getOwner();
|
||||
|
||||
["All in", "Weighted Local", "Weighted Cycle"].forEach((strategy) => {
|
||||
appendLi({
|
||||
name: strategy,
|
||||
parent: ulStrategies,
|
||||
const closes = datasets.getOrCreate("date", "date-to-close");
|
||||
closes.fetchRange(2009, new Date().getUTCFullYear()).then(() => {
|
||||
signals.runWithOwner(owner, () => {
|
||||
signals.createEffect(
|
||||
() => ({
|
||||
initialAmount: settings.initial.firstDay() || 0,
|
||||
recurrentAmount: settings.recurrent.amount() || 0,
|
||||
dollarsLeft: settings.initial.overTime() || 0,
|
||||
start: settings.interval.start(),
|
||||
end: settings.interval.end(),
|
||||
fees: settings.fees.percentage(),
|
||||
}),
|
||||
// ({ initialAmount, recurrentAmount, dollarsLeft, start, end }) => {
|
||||
// console.log({
|
||||
// start,
|
||||
// end,
|
||||
// });
|
||||
// },
|
||||
({ initialAmount, recurrentAmount, dollarsLeft, start, end, fees }) => {
|
||||
console.log({ start, end });
|
||||
parent.innerHTML = "";
|
||||
|
||||
if (!start || !end || start > end) return;
|
||||
|
||||
const range = utils.date.getRange(start, end);
|
||||
|
||||
let investedAmount = 0;
|
||||
|
||||
/** @type {LineData<Time>[]} */
|
||||
const investedData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const returnData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const bitcoinData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const resultData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const dollarsData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const totalData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const investmentData = [];
|
||||
/** @type {LineData<Time>[]} */
|
||||
const bitcoinAddedData = [];
|
||||
|
||||
let bitcoin = 0;
|
||||
|
||||
let feesPaid = 0;
|
||||
|
||||
range.forEach((date, index) => {
|
||||
const year = date.getUTCFullYear();
|
||||
const time = utils.date.toString(date);
|
||||
|
||||
const close = closes.fetchedJSONs
|
||||
.at(utils.chunkIdToIndex("date", year))
|
||||
?.json()?.dataset.map[utils.date.toString(date)];
|
||||
|
||||
if (!close) return;
|
||||
|
||||
let investmentPreFees =
|
||||
(!index ? initialAmount : 0) + recurrentAmount;
|
||||
|
||||
if (dollarsLeft > 0) {
|
||||
if (dollarsLeft >= recurrentAmount) {
|
||||
investmentPreFees += recurrentAmount;
|
||||
dollarsLeft -= recurrentAmount;
|
||||
} else {
|
||||
investmentPreFees += dollarsLeft;
|
||||
dollarsLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let investment = investmentPreFees * (1 - (fees || 0) / 100);
|
||||
feesPaid += investmentPreFees - investment;
|
||||
|
||||
const bitcoinAdded = investment / close;
|
||||
bitcoin += bitcoinAdded;
|
||||
|
||||
investedAmount += investment;
|
||||
|
||||
const _return = close * bitcoin;
|
||||
|
||||
bitcoinData.push({
|
||||
time,
|
||||
value: bitcoin,
|
||||
});
|
||||
|
||||
investedData.push({
|
||||
time,
|
||||
value: investedAmount,
|
||||
});
|
||||
|
||||
returnData.push({
|
||||
time,
|
||||
value: _return,
|
||||
});
|
||||
|
||||
resultData.push({
|
||||
time,
|
||||
value: (_return / investedAmount - 1) * 100,
|
||||
});
|
||||
|
||||
dollarsData.push({
|
||||
time,
|
||||
value: dollarsLeft,
|
||||
});
|
||||
|
||||
totalData.push({
|
||||
time,
|
||||
value: dollarsLeft + _return,
|
||||
});
|
||||
|
||||
investmentData.push({
|
||||
time,
|
||||
value: investment,
|
||||
});
|
||||
|
||||
bitcoinAddedData.push({
|
||||
time,
|
||||
value: bitcoinAdded,
|
||||
});
|
||||
});
|
||||
|
||||
(() => {
|
||||
const chartWrapper = window.document.createElement("div");
|
||||
chartWrapper.classList.add("chart-wrapper");
|
||||
parent.append(chartWrapper);
|
||||
|
||||
const chartDiv = window.document.createElement("div");
|
||||
chartDiv.classList.add("chart-div");
|
||||
chartWrapper.append(chartDiv);
|
||||
|
||||
const chart = lightweightCharts.createChart({
|
||||
scale: "date",
|
||||
element: chartDiv,
|
||||
signals,
|
||||
colors,
|
||||
options: {
|
||||
handleScale: false,
|
||||
handleScroll: false,
|
||||
},
|
||||
});
|
||||
|
||||
const line = chart.addLineSeries();
|
||||
|
||||
line.setData(investedData);
|
||||
|
||||
const line2 = chart.addLineSeries();
|
||||
|
||||
line2.setData(returnData);
|
||||
|
||||
const line3 = chart.addLineSeries();
|
||||
|
||||
line3.setData(dollarsData);
|
||||
|
||||
const line4 = chart.addLineSeries();
|
||||
|
||||
line4.setData(totalData);
|
||||
|
||||
const line5 = chart.addLineSeries();
|
||||
|
||||
line5.setData(investmentData);
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const chartWrapper = window.document.createElement("div");
|
||||
chartWrapper.classList.add("chart-wrapper");
|
||||
parent.append(chartWrapper);
|
||||
|
||||
const chartDiv = window.document.createElement("div");
|
||||
chartDiv.classList.add("chart-div");
|
||||
chartWrapper.append(chartDiv);
|
||||
|
||||
const chart = lightweightCharts.createChart({
|
||||
scale: "date",
|
||||
element: chartDiv,
|
||||
signals,
|
||||
colors,
|
||||
options: {
|
||||
handleScale: false,
|
||||
handleScroll: false,
|
||||
},
|
||||
});
|
||||
|
||||
const line = chart.addLineSeries();
|
||||
|
||||
line.setData(bitcoinData);
|
||||
|
||||
const line2 = chart.addLineSeries();
|
||||
|
||||
line2.setData(bitcoinAddedData);
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
})();
|
||||
|
||||
(() => {
|
||||
const chartWrapper = window.document.createElement("div");
|
||||
chartWrapper.classList.add("chart-wrapper");
|
||||
parent.append(chartWrapper);
|
||||
|
||||
const chartDiv = window.document.createElement("div");
|
||||
chartDiv.classList.add("chart-div");
|
||||
chartWrapper.append(chartDiv);
|
||||
|
||||
const chart = lightweightCharts.createChart({
|
||||
scale: "date",
|
||||
element: chartDiv,
|
||||
signals,
|
||||
colors,
|
||||
options: {
|
||||
handleScale: false,
|
||||
handleScroll: false,
|
||||
},
|
||||
});
|
||||
|
||||
const line = chart.addLineSeries();
|
||||
|
||||
line.setData(resultData);
|
||||
|
||||
chart.timeScale().fitContent();
|
||||
})();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// On the side
|
||||
// Value in Bitcoin
|
||||
// Value in Dollars + total converted
|
||||
//
|
||||
// Value min estimated value in 4 years
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,31 +590,6 @@ function createParameterGroup({ title, description }) {
|
||||
return div;
|
||||
}
|
||||
|
||||
function createHrElement() {
|
||||
return window.document.createElement("hr");
|
||||
}
|
||||
|
||||
/**
|
||||
*@param {Object} args
|
||||
*@param {string} args.id
|
||||
*@param {string} args.title
|
||||
*@param {number} args.value
|
||||
*@param {HTMLElement} args.parent
|
||||
*@param {number} args.min
|
||||
*@param {number} args.max
|
||||
*/
|
||||
function createInputNumber({ id, title, value, parent, min, max }) {
|
||||
const input = window.document.createElement("input");
|
||||
input.id = id;
|
||||
input.title = title;
|
||||
input.type = "number";
|
||||
input.value = String(value);
|
||||
input.min = String(min);
|
||||
input.max = String(max);
|
||||
parent.append(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {string} args.id
|
||||
@@ -320,90 +616,36 @@ function createInputDollar({ id, title, signal }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {string} args.id
|
||||
* @param {string} args.title
|
||||
* @param {Signal<number | null>} args.signal
|
||||
*
|
||||
* @param {Object} arg
|
||||
* @param {Signal<Date | null>} arg.signal
|
||||
* @param {() => Date | null} arg.getDefault
|
||||
* @param {HTMLElement} arg.parent
|
||||
* @param {Utilities} arg.utils
|
||||
* @param {Signals} arg.signals
|
||||
*/
|
||||
function createInputRangePercentage({ id, title, signal }) {
|
||||
const input = window.document.createElement("input");
|
||||
input.id = id;
|
||||
input.title = title;
|
||||
input.type = "range";
|
||||
input.min = "0";
|
||||
input.max = "100";
|
||||
function createInputDateField({ signal, getDefault, parent, signals, utils }) {
|
||||
const div = window.document.createElement("div");
|
||||
parent.append(div);
|
||||
|
||||
const value = signal();
|
||||
input.value = value !== null ? String(value) : "";
|
||||
div.append(
|
||||
utils.dom.createInputDate({
|
||||
id: "",
|
||||
title: "",
|
||||
signal,
|
||||
signals,
|
||||
}),
|
||||
);
|
||||
|
||||
input.addEventListener("input", () => {
|
||||
const value = input.value;
|
||||
signal.set(value ? Number(value) : null);
|
||||
const button = utils.dom.createButtonElement({
|
||||
onClick: () => {
|
||||
signal.set(getDefault());
|
||||
},
|
||||
text: "Reset",
|
||||
title: "Reset field",
|
||||
});
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {HTMLElement} args.parent
|
||||
*/
|
||||
function appendUl({ parent }) {
|
||||
const ul = window.document.createElement("ul");
|
||||
parent.append(ul);
|
||||
return ul;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {string} args.name
|
||||
* @param {HTMLUListElement} args.parent
|
||||
*/
|
||||
function appendLi({ name, parent }) {
|
||||
const li = window.document.createElement("li");
|
||||
li.innerHTML = name;
|
||||
parent.append(li);
|
||||
return li;
|
||||
}
|
||||
|
||||
/**
|
||||
*@param {Object} args
|
||||
*@param {string} args.id
|
||||
*@param {string} args.title
|
||||
*@param {string} args.value
|
||||
*@param {HTMLElement} args.parent
|
||||
*@param {Accessor<Date>} args.today
|
||||
*@param {Signals} args.signals
|
||||
*/
|
||||
function appendInputDate({ id, title, value, parent, today, signals }) {
|
||||
const input = window.document.createElement("input");
|
||||
input.id = id;
|
||||
input.title = title;
|
||||
input.type = "date";
|
||||
input.value = value;
|
||||
|
||||
input.min = "2011-01-01";
|
||||
|
||||
signals.createEffect(today, (today) => {
|
||||
input.max = today.toJSON().split("T")[0];
|
||||
});
|
||||
|
||||
parent.append(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
*@param {Object} args
|
||||
*@param {string} args.title
|
||||
*@param {string} args.text
|
||||
*@param {HTMLElement} args.parent
|
||||
*@param {VoidFunction} args.onClick
|
||||
*/
|
||||
function appendButton({ title, text, onClick, parent }) {
|
||||
const button = window.document.createElement("button");
|
||||
button.title = title;
|
||||
button.innerHTML = text;
|
||||
button.addEventListener("click", onClick);
|
||||
parent.append(button);
|
||||
return button;
|
||||
div.append(button);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
25
website/scripts/types/self.d.ts
vendored
25
website/scripts/types/self.d.ts
vendored
@@ -101,7 +101,6 @@ type Unit =
|
||||
| "Weight";
|
||||
|
||||
interface PartialOption {
|
||||
// icon: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -111,6 +110,18 @@ interface PartialHomeOption extends PartialOption {
|
||||
name: "Home";
|
||||
}
|
||||
|
||||
interface PartialLivePriceOption extends PartialOption {
|
||||
kind: "live-price";
|
||||
}
|
||||
|
||||
interface PartialMoscowTimeOption extends PartialOption {
|
||||
kind: "moscow-time";
|
||||
}
|
||||
|
||||
interface PartialConverterOption extends PartialOption {
|
||||
kind: "converter";
|
||||
}
|
||||
|
||||
interface PartialChartOption extends PartialOption {
|
||||
scale: TimeScale;
|
||||
title: string;
|
||||
@@ -147,6 +158,9 @@ interface PartialOptionsGroup {
|
||||
|
||||
type AnyPartialOption =
|
||||
| PartialHomeOption
|
||||
| PartialLivePriceOption
|
||||
| PartialMoscowTimeOption
|
||||
| PartialConverterOption
|
||||
| PartialChartOption
|
||||
| PartialSimulationOption
|
||||
| PartialPdfOption
|
||||
@@ -168,6 +182,9 @@ type OptionPath = {
|
||||
|
||||
type HomeOption = PartialHomeOption & ProcessedOptionAddons;
|
||||
type SimulationOption = PartialSimulationOption & ProcessedOptionAddons;
|
||||
type LivePriceOption = PartialLivePriceOption & ProcessedOptionAddons;
|
||||
type MoscowTimeOption = PartialMoscowTimeOption & ProcessedOptionAddons;
|
||||
type ConverterOption = PartialConverterOption & ProcessedOptionAddons;
|
||||
|
||||
interface PdfOption extends PartialPdfOption, ProcessedOptionAddons {
|
||||
kind: "pdf";
|
||||
@@ -183,6 +200,9 @@ interface ChartOption extends PartialChartOption, ProcessedOptionAddons {
|
||||
|
||||
type Option =
|
||||
| HomeOption
|
||||
| LivePriceOption
|
||||
| MoscowTimeOption
|
||||
| ConverterOption
|
||||
| PdfOption
|
||||
| UrlOption
|
||||
| ChartOption
|
||||
@@ -208,7 +228,8 @@ interface ResourceDataset<
|
||||
> {
|
||||
scale: Scale;
|
||||
url: string;
|
||||
fetch: (id: number) => void;
|
||||
fetch: (id: number) => Promise<void>;
|
||||
fetchRange: (start: number, end: number) => Promise<void[]>;
|
||||
fetchedJSONs: FetchedResult<Scale, Type>[];
|
||||
// drop: VoidFunction;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user