mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 22:59:58 -07:00
1104 lines
32 KiB
JavaScript
1104 lines
32 KiB
JavaScript
// // @ts-nocheck
|
|
|
|
// import {
|
|
// createDateRange,
|
|
// dateToDateIndex,
|
|
// differenceBetweenDates,
|
|
// } from "../utils/date.js";
|
|
// import {
|
|
// createButtonElement,
|
|
// createFieldElement,
|
|
// createHeader,
|
|
// createSelect,
|
|
// } from "../utils/dom.js";
|
|
// import { simulationElement } from "../utils/elements.js";
|
|
// import {
|
|
// numberToDollars,
|
|
// numberToPercentage,
|
|
// numberToUSNumber,
|
|
// } from "../utils/format.js";
|
|
// import { serdeDate, serdeOptDate, serdeOptNumber } from "../utils/serde.js";
|
|
// import signals from "../signals.js";
|
|
// import { createChart } from "../chart/index.js";
|
|
// import { resources } from "../resources.js";
|
|
// import { colors } from "../chart/colors.js";
|
|
|
|
// export function init() {
|
|
// /**
|
|
// * @typedef {Object} Frequency
|
|
// * @property {string} name
|
|
// * @property {string} value
|
|
// * @property {(date: Date) => boolean} isTriggerDay
|
|
// *
|
|
// * @typedef {Object} Frequencies
|
|
// * @property {string} name
|
|
// * @property {Frequency[]} list
|
|
// */
|
|
|
|
// const domExtended = {
|
|
// /**
|
|
// * @param {Object} args
|
|
// * @param {string} args.id
|
|
// * @param {string} args.title
|
|
// * @param {string} args.placeholder
|
|
// * @param {Signal<number | null>} args.signal
|
|
// * @param {number} args.min
|
|
// * @param {number} args.step
|
|
// * @param {number} [args.max]
|
|
// * @param {Signals} args.signals
|
|
// */
|
|
// createInputNumberElement({
|
|
// id,
|
|
// title,
|
|
// signal,
|
|
// min,
|
|
// max,
|
|
// step,
|
|
// placeholder,
|
|
// signals,
|
|
// }) {
|
|
// const input = window.document.createElement("input");
|
|
// if (!id || !title || !placeholder) throw Error("input attribute missing");
|
|
// input.id = id;
|
|
// input.title = title;
|
|
// input.placeholder = placeholder;
|
|
// input.type = "number";
|
|
// input.min = String(min);
|
|
// if (max) {
|
|
// input.max = String(max);
|
|
// }
|
|
// input.step = String(step);
|
|
|
|
// let stateValue = /** @type {string | null} */ (null);
|
|
|
|
// signals.createScopedEffect(
|
|
// () => {
|
|
// const value = signal();
|
|
// return value ? String(value) : "";
|
|
// },
|
|
// (value) => {
|
|
// if (stateValue !== value) {
|
|
// input.value = value;
|
|
// stateValue = value;
|
|
// }
|
|
// },
|
|
// );
|
|
|
|
// input.addEventListener("input", () => {
|
|
// const valueSer = input.value;
|
|
// stateValue = valueSer;
|
|
// const value = Number(valueSer);
|
|
// if (value >= min && (max ? value <= max : true)) {
|
|
// signal.set(value);
|
|
// }
|
|
// });
|
|
|
|
// return { input, signal };
|
|
// },
|
|
// /**
|
|
// * @param {Object} args
|
|
// * @param {string} args.id
|
|
// * @param {string} args.title
|
|
// * @param {Signal<number | null>} args.signal
|
|
// * @param {Signals} args.signals
|
|
// */
|
|
// createInputDollar({ id, title, signal, signals }) {
|
|
// return this.createInputNumberElement({
|
|
// id,
|
|
// placeholder: "USD",
|
|
// min: 0,
|
|
// title,
|
|
// signal,
|
|
// signals,
|
|
// step: 1,
|
|
// });
|
|
// },
|
|
// /**
|
|
// * @param {Object} args
|
|
// * @param {string} args.id
|
|
// * @param {string} args.title
|
|
// * @param {Signal<Date | null>} args.signal
|
|
// * @param {Signals} 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 = serdeDate.serialize(maxDate);
|
|
// input.min = min;
|
|
// input.max = max;
|
|
|
|
// let stateValue = /** @type {string | null} */ (null);
|
|
|
|
// signals.createScopedEffect(
|
|
// () => {
|
|
// const dateSignal = signal();
|
|
// return dateSignal ? serdeDate.serialize(dateSignal) : "";
|
|
// },
|
|
// (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, 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 = createButtonElement({
|
|
// onClick: signal.reset,
|
|
// inside: "Reset",
|
|
// title: "Reset field",
|
|
// });
|
|
// button.type = "reset";
|
|
|
|
// div.append(button);
|
|
|
|
// return div;
|
|
// },
|
|
// };
|
|
|
|
// const parametersElement = window.document.createElement("div");
|
|
// simulationElement.append(parametersElement);
|
|
// const resultsElement = window.document.createElement("div");
|
|
// simulationElement.append(resultsElement);
|
|
|
|
// function computeFrequencies() {
|
|
// const weekDays = [
|
|
// "Monday",
|
|
// "Tuesday",
|
|
// "Wednesday",
|
|
// "Thursday",
|
|
// "Friday",
|
|
// "Saturday",
|
|
// "Sunday",
|
|
// ];
|
|
// const maxDays = 28;
|
|
|
|
// /** @param {number} day */
|
|
// function getOrdinalDay(day) {
|
|
// const rest = (day % 30) % 20;
|
|
|
|
// return `${day}${
|
|
// rest === 1 ? "st" : rest === 2 ? "nd" : rest === 3 ? "rd" : "th"
|
|
// }`;
|
|
// }
|
|
|
|
// /** @satisfies {([Frequency, Frequencies, Frequencies, Frequencies])} */
|
|
// const list = [
|
|
// {
|
|
// name: "Every day",
|
|
// value: "every-day",
|
|
// /** @param {Date} _ */
|
|
// isTriggerDay(_) {
|
|
// return true;
|
|
// },
|
|
// },
|
|
// {
|
|
// name: "Once a week",
|
|
// list: weekDays.map((day, index) => ({
|
|
// name: day,
|
|
// value: day.toLowerCase(),
|
|
// /** @param {Date} date */
|
|
// isTriggerDay(date) {
|
|
// let day = date.getUTCDay() - 1;
|
|
// if (day === -1) {
|
|
// day = 6;
|
|
// }
|
|
// return day === index;
|
|
// },
|
|
// })),
|
|
// },
|
|
// {
|
|
// name: "Every two weeks",
|
|
// list: [...Array(Math.round(maxDays / 2)).keys()].map((day) => {
|
|
// const day1 = day + 1;
|
|
// const day2 = day + 15;
|
|
|
|
// return {
|
|
// value: `${day1}+${day2}`,
|
|
// name: `The ${getOrdinalDay(day1)} and the ${getOrdinalDay(day2)}`,
|
|
// /** @param {Date} date */
|
|
// isTriggerDay(date) {
|
|
// const d = date.getUTCDate();
|
|
// return d === day1 || d === day2;
|
|
// },
|
|
// };
|
|
// }),
|
|
// },
|
|
// {
|
|
// name: "Once a month",
|
|
// list: [...Array(maxDays).keys()].map((day) => {
|
|
// day++;
|
|
|
|
// return {
|
|
// name: `The ${getOrdinalDay(day)}`,
|
|
// value: String(day),
|
|
// /** @param {Date} date */
|
|
// isTriggerDay(date) {
|
|
// const d = date.getUTCDate();
|
|
// return d === day;
|
|
// },
|
|
// };
|
|
// }),
|
|
// },
|
|
// ];
|
|
|
|
// /** @type {Record<string, Frequency>} */
|
|
// const idToFrequency = {};
|
|
|
|
// list.forEach((anyFreq) => {
|
|
// if ("list" in anyFreq) {
|
|
// anyFreq.list?.forEach((freq) => {
|
|
// idToFrequency[freq.value] = freq;
|
|
// });
|
|
// } else {
|
|
// idToFrequency[anyFreq.value] = anyFreq;
|
|
// }
|
|
// });
|
|
|
|
// const serde = {
|
|
// /**
|
|
// * @param {Frequency} v
|
|
// */
|
|
// serialize(v) {
|
|
// return v.value;
|
|
// },
|
|
// /**
|
|
// * @param {string} v
|
|
// */
|
|
// deserialize(v) {
|
|
// const freq = idToFrequency[v];
|
|
// if (!freq) throw "Freq not found";
|
|
// return freq;
|
|
// },
|
|
// };
|
|
|
|
// return { list, serde };
|
|
// }
|
|
|
|
// const frequencies = computeFrequencies();
|
|
|
|
// const keyPrefix = "save-in-bitcoin";
|
|
// const settings = {
|
|
// dollars: {
|
|
// initial: {
|
|
// amount: signals.createSignal(/** @type {number | null} */ (1000), {
|
|
// save: {
|
|
// ...serdeOptNumber,
|
|
// keyPrefix,
|
|
// key: "initial-amount",
|
|
// },
|
|
// }),
|
|
// },
|
|
// topUp: {
|
|
// amount: signals.createSignal(/** @type {number | null} */ (150), {
|
|
// save: {
|
|
// ...serdeOptNumber,
|
|
// keyPrefix,
|
|
// key: "top-up-amount",
|
|
// },
|
|
// }),
|
|
// frenquency: signals.createSignal(
|
|
// /** @type {Frequency} */ (frequencies.list[3].list[0]),
|
|
// {
|
|
// save: {
|
|
// ...frequencies.serde,
|
|
// keyPrefix,
|
|
// key: "top-up-freq",
|
|
// },
|
|
// },
|
|
// ),
|
|
// },
|
|
// },
|
|
// bitcoin: {
|
|
// investment: {
|
|
// initial: signals.createSignal(/** @type {number | null} */ (1000), {
|
|
// save: {
|
|
// ...serdeOptNumber,
|
|
// keyPrefix,
|
|
// key: "initial-swap",
|
|
// },
|
|
// }),
|
|
// recurrent: signals.createSignal(/** @type {number | null} */ (5), {
|
|
// save: {
|
|
// ...serdeOptNumber,
|
|
// keyPrefix,
|
|
// key: "recurrent-swap",
|
|
// },
|
|
// }),
|
|
// frequency: signals.createSignal(
|
|
// /** @type {Frequency} */ (frequencies.list[0]),
|
|
// {
|
|
// save: {
|
|
// ...frequencies.serde,
|
|
// keyPrefix,
|
|
// key: "swap-freq",
|
|
// },
|
|
// },
|
|
// ),
|
|
// },
|
|
// },
|
|
// interval: {
|
|
// start: signals.createSignal(
|
|
// /** @type {Date | null} */ (new Date("2021-04-15")),
|
|
// {
|
|
// save: {
|
|
// ...serdeOptDate,
|
|
// keyPrefix,
|
|
// key: "interval-start",
|
|
// },
|
|
// },
|
|
// ),
|
|
// end: signals.createSignal(/** @type {Date | null} */ (new Date()), {
|
|
// save: {
|
|
// ...serdeOptDate,
|
|
// keyPrefix,
|
|
// key: "interval-end",
|
|
// },
|
|
// }),
|
|
// },
|
|
// fees: {
|
|
// percentage: signals.createSignal(/** @type {number | null} */ (1), {
|
|
// save: {
|
|
// ...serdeOptNumber,
|
|
// keyPrefix,
|
|
// key: "percentage",
|
|
// },
|
|
// }),
|
|
// },
|
|
// };
|
|
|
|
// parametersElement.append(createHeader("Save in Bitcoin").headerElement);
|
|
|
|
// /**
|
|
// * @param {Object} param0
|
|
// * @param {ColorName} param0.color
|
|
// * @param {string} param0.type
|
|
// * @param {string} param0.text
|
|
// */
|
|
// function createColoredTypeHTML({ color, type, text }) {
|
|
// return `${createColoredSpan({ color, text: `${type}:` })} ${text}`;
|
|
// }
|
|
|
|
// /**
|
|
// * @param {Object} param0
|
|
// * @param {ColorName} param0.color
|
|
// * @param {string} param0.text
|
|
// */
|
|
// function createColoredSpan({ color, text }) {
|
|
// return `<span style="color: ${colors[
|
|
// color
|
|
// ]()}; font-weight: 500; text-transform: uppercase;
|
|
// font-size: var(--font-size-sm);">${text}</span>`;
|
|
// }
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "green",
|
|
// type: "Dollars",
|
|
// text: "Initial Amount",
|
|
// }),
|
|
// description:
|
|
// "The amount of dollars you have ready on the exchange on day one.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputDollar({
|
|
// id: "simulation-dollars-initial",
|
|
// title: "Initial Dollar Amount",
|
|
// signal: settings.dollars.initial.amount,
|
|
// signals,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "green",
|
|
// type: "Dollars",
|
|
// text: "Top Up Frequency",
|
|
// }),
|
|
// description:
|
|
// "The frequency at which you'll top up your account at the exchange.",
|
|
// input: domExtended.createResetableInput(
|
|
// createSelect({
|
|
// id: "top-up-frequency",
|
|
// list: frequencies.list,
|
|
// signal: settings.dollars.topUp.frenquency,
|
|
// deep: true,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "green",
|
|
// type: "Dollars",
|
|
// text: "Top Up Amount",
|
|
// }),
|
|
// description:
|
|
// "The recurrent amount of dollars you'll be transfering to said exchange.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputDollar({
|
|
// id: "simulation-dollars-top-up-amount",
|
|
// title: "Top Up Dollar Amount",
|
|
// signal: settings.dollars.topUp.amount,
|
|
// signals,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "orange",
|
|
// type: "Bitcoin",
|
|
// text: "Initial Investment",
|
|
// }),
|
|
// description:
|
|
// "The amount, if available, of dollars that will be used to buy Bitcoin on day one.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputDollar({
|
|
// id: "simulation-bitcoin-initial-investment",
|
|
// title: "Initial Swap Amount",
|
|
// signal: settings.bitcoin.investment.initial,
|
|
// signals,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "orange",
|
|
// type: "Bitcoin",
|
|
// text: "Investment Frequency",
|
|
// }),
|
|
// description: "The frequency at which you'll be buying Bitcoin.",
|
|
// input: domExtended.createResetableInput(
|
|
// createSelect({
|
|
// id: "investment-frequency",
|
|
// list: frequencies.list,
|
|
// signal: settings.bitcoin.investment.frequency,
|
|
// deep: true,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "orange",
|
|
// type: "Bitcoin",
|
|
// text: "Recurrent Investment",
|
|
// }),
|
|
// description:
|
|
// "The recurrent amount, if available, of dollars that will be used to buy Bitcoin.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputDollar({
|
|
// id: "simulation-bitcoin-recurrent-investment",
|
|
// title: "Bitcoin Recurrent Investment",
|
|
// signal: settings.bitcoin.investment.recurrent,
|
|
// signals,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "sky",
|
|
// type: "Interval",
|
|
// text: "Start",
|
|
// }),
|
|
// description: "The first day of the simulation.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputDate({
|
|
// id: "simulation-inverval-start",
|
|
// title: "First Simulation Date",
|
|
// signal: settings.interval.start,
|
|
// signals,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "sky",
|
|
// type: "Interval",
|
|
// text: "End",
|
|
// }),
|
|
// description: "The last day of the simulation.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputDate({
|
|
// id: "simulation-inverval-end",
|
|
// title: "Last Simulation Day",
|
|
// signal: settings.interval.end,
|
|
// signals,
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// parametersElement.append(
|
|
// createFieldElement({
|
|
// title: createColoredTypeHTML({
|
|
// color: "red",
|
|
// type: "Fees",
|
|
// text: "Exchange",
|
|
// }),
|
|
// description: "The amount of trading fees (in %) at the exchange.",
|
|
// input: domExtended.createResetableInput(
|
|
// domExtended.createInputNumberElement({
|
|
// id: "simulation-fees",
|
|
// title: "Exchange Fees",
|
|
// signal: settings.fees.percentage,
|
|
// min: 0,
|
|
// max: 50,
|
|
// step: 0.01,
|
|
// signals,
|
|
// placeholder: "Fees",
|
|
// }),
|
|
// ),
|
|
// }),
|
|
// );
|
|
|
|
// const p1 = window.document.createElement("p");
|
|
// resultsElement.append(p1);
|
|
// const p2 = window.document.createElement("p");
|
|
// resultsElement.append(p2);
|
|
// const p3 = window.document.createElement("p");
|
|
// resultsElement.append(p3);
|
|
|
|
// const owner = signals.getOwner();
|
|
|
|
// const totalInvestedAmountData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const bitcoinValueData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const bitcoinData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const resultData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const dollarsLeftData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const totalValueData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const investmentData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const bitcoinAddedData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const averageCostBasisData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const bitcoinPriceData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const buyCountData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const totalFeesPaidData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const daysCountData = signals.createSignal(/** @type {LineData[]} */ ([]), {
|
|
// equals: false,
|
|
// });
|
|
// const profitableDaysRatioData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
// const unprofitableDaysRatioData = signals.createSignal(
|
|
// /** @type {LineData[]} */ ([]),
|
|
// {
|
|
// equals: false,
|
|
// },
|
|
// );
|
|
|
|
// /** @type {() => IndexName} */
|
|
// const index = () => "dateindex";
|
|
|
|
// createChart({
|
|
// index,
|
|
// parent: resultsElement,
|
|
// signals,
|
|
// colors,
|
|
// id: `result`,
|
|
// fitContent: true,
|
|
// resources,
|
|
// config: [
|
|
// {
|
|
// unit: "usd",
|
|
// blueprints: [
|
|
// {
|
|
// title: "Bitcoin Value",
|
|
// type: "Line",
|
|
// color: colors.amber,
|
|
// data: bitcoinValueData,
|
|
// },
|
|
// {
|
|
// title: "Dollars Converted",
|
|
// type: "Line",
|
|
// color: colors.green,
|
|
// data: totalInvestedAmountData,
|
|
// },
|
|
// {
|
|
// title: "Dollars Left",
|
|
// type: "Line",
|
|
// color: colors.emerald,
|
|
// data: dollarsLeftData,
|
|
// defaultActive: false,
|
|
// },
|
|
// {
|
|
// title: "Fees Paid",
|
|
// type: "Line",
|
|
// color: colors.rose,
|
|
// data: totalFeesPaidData,
|
|
// defaultActive: false,
|
|
// },
|
|
// ],
|
|
// },
|
|
// ],
|
|
// });
|
|
|
|
// createChart({
|
|
// index,
|
|
// parent: resultsElement,
|
|
// signals,
|
|
// colors,
|
|
// id: `bitcoin`,
|
|
// fitContent: true,
|
|
// resources,
|
|
// config: [
|
|
// {
|
|
// unit: "btc",
|
|
// blueprints: [
|
|
// {
|
|
// title: "Bitcoin Stack",
|
|
// type: "Line",
|
|
// color: colors.orange,
|
|
// data: bitcoinData,
|
|
// },
|
|
// ],
|
|
// },
|
|
// ],
|
|
// });
|
|
|
|
// createChart({
|
|
// index,
|
|
// parent: resultsElement,
|
|
// signals,
|
|
// colors,
|
|
// id: `average-price`,
|
|
// fitContent: true,
|
|
// resources,
|
|
// config: [
|
|
// {
|
|
// unit: "usd",
|
|
// blueprints: [
|
|
// {
|
|
// title: "Bitcoin Price",
|
|
// type: "Line",
|
|
// color: colors.default,
|
|
// data: bitcoinPriceData,
|
|
// },
|
|
// {
|
|
// title: "Average Cost Basis",
|
|
// type: "Line",
|
|
// color: colors.lime,
|
|
// data: averageCostBasisData,
|
|
// },
|
|
// ],
|
|
// },
|
|
// ],
|
|
// });
|
|
|
|
// createChart({
|
|
// index,
|
|
// parent: resultsElement,
|
|
// signals,
|
|
// colors,
|
|
// resources,
|
|
// id: `return-ratio`,
|
|
// fitContent: true,
|
|
// config: [
|
|
// {
|
|
// unit: "usd",
|
|
// blueprints: [
|
|
// {
|
|
// title: "Return Of Investment",
|
|
// type: "Baseline",
|
|
// data: resultData,
|
|
// },
|
|
// ],
|
|
// },
|
|
// ],
|
|
// });
|
|
|
|
// createChart({
|
|
// index,
|
|
// parent: resultsElement,
|
|
// signals,
|
|
// colors,
|
|
// id: `simulation-profitability-ratios`,
|
|
// fitContent: true,
|
|
// resources,
|
|
// config: [
|
|
// {
|
|
// unit: "percentage",
|
|
// blueprints: [
|
|
// {
|
|
// title: "Profitable Days Ratio",
|
|
// type: "Line",
|
|
// color: colors.green,
|
|
// data: profitableDaysRatioData,
|
|
// },
|
|
// {
|
|
// title: "Unprofitable Days Ratio",
|
|
// type: "Line",
|
|
// color: colors.red,
|
|
// data: unprofitableDaysRatioData,
|
|
// },
|
|
// ],
|
|
// },
|
|
// ],
|
|
// });
|
|
|
|
// resources.metrics
|
|
// .getOrCreate("price_close", "dateindex")
|
|
// .fetch()
|
|
// .then((_closes) => {
|
|
// if (!_closes) return;
|
|
// const closes = /** @type {number[]} */ (_closes);
|
|
|
|
// signals.runWithOwner(owner, () => {
|
|
// signals.createScopedEffect(
|
|
// () => ({
|
|
// initialDollarAmount: settings.dollars.initial.amount() || 0,
|
|
// topUpAmount: settings.dollars.topUp.amount() || 0,
|
|
// topUpFrequency: settings.dollars.topUp.frenquency(),
|
|
// initialSwap: settings.bitcoin.investment.initial() || 0,
|
|
// recurrentSwap: settings.bitcoin.investment.recurrent() || 0,
|
|
// swapFrequency: settings.bitcoin.investment.frequency(),
|
|
// start: settings.interval.start(),
|
|
// end: settings.interval.end(),
|
|
// fees: settings.fees.percentage(),
|
|
// }),
|
|
// ({
|
|
// initialDollarAmount,
|
|
// topUpAmount,
|
|
// topUpFrequency,
|
|
// initialSwap,
|
|
// recurrentSwap,
|
|
// swapFrequency,
|
|
// start,
|
|
// end,
|
|
// fees,
|
|
// }) => {
|
|
// if (!start || !end || start > end) return;
|
|
|
|
// const range = createDateRange(start, end);
|
|
|
|
// totalInvestedAmountData().length = 0;
|
|
// bitcoinValueData().length = 0;
|
|
// bitcoinData().length = 0;
|
|
// resultData().length = 0;
|
|
// dollarsLeftData().length = 0;
|
|
// totalValueData().length = 0;
|
|
// investmentData().length = 0;
|
|
// bitcoinAddedData().length = 0;
|
|
// averageCostBasisData().length = 0;
|
|
// bitcoinPriceData().length = 0;
|
|
// buyCountData().length = 0;
|
|
// totalFeesPaidData().length = 0;
|
|
// daysCountData().length = 0;
|
|
// profitableDaysRatioData().length = 0;
|
|
// unprofitableDaysRatioData().length = 0;
|
|
|
|
// let bitcoin = 0;
|
|
// let sats = 0;
|
|
// let dollars = initialDollarAmount;
|
|
// let investedAmount = 0;
|
|
// let postFeesInvestedAmount = 0;
|
|
// let buyCount = 0;
|
|
// let averageCostBasis = 0;
|
|
// let bitcoinValue = 0;
|
|
// let roi = 0;
|
|
// let totalValue = 0;
|
|
// let totalFeesPaid = 0;
|
|
// let daysCount = range.length;
|
|
// let profitableDays = 0;
|
|
// let unprofitableDays = 0;
|
|
// let profitableDaysRatio = 0;
|
|
// let unprofitableDaysRatio = 0;
|
|
// let lastInvestDay = range[0];
|
|
// let dailyInvestment = 0;
|
|
// let bitcoinAdded = 0;
|
|
// let satsAdded = 0;
|
|
// let lastSatsAdded = 0;
|
|
|
|
// range.forEach((date, index) => {
|
|
// const time = date.valueOf() / 1000;
|
|
|
|
// if (topUpFrequency.isTriggerDay(date)) {
|
|
// dollars += topUpAmount;
|
|
// }
|
|
|
|
// const close = closes[dateToDateIndex(date)];
|
|
|
|
// if (!close) return;
|
|
|
|
// dailyInvestment = 0;
|
|
// /** @param {number} value */
|
|
// function invest(value) {
|
|
// value = Math.min(dollars, value);
|
|
// dailyInvestment += value;
|
|
// dollars -= value;
|
|
// buyCount += 1;
|
|
// lastInvestDay = date;
|
|
// }
|
|
// if (!index) {
|
|
// invest(initialSwap);
|
|
// }
|
|
// if (swapFrequency.isTriggerDay(date) && dollars > 0) {
|
|
// invest(recurrentSwap);
|
|
// }
|
|
|
|
// investedAmount += dailyInvestment;
|
|
|
|
// let dailyInvestmentPostFees =
|
|
// dailyInvestment * (1 - (fees || 0) / 100);
|
|
|
|
// totalFeesPaid += dailyInvestment - dailyInvestmentPostFees;
|
|
|
|
// bitcoinAdded = dailyInvestmentPostFees / close;
|
|
// bitcoin += bitcoinAdded;
|
|
// satsAdded = Math.floor(bitcoinAdded * 100_000_000);
|
|
// if (satsAdded > 0) {
|
|
// lastSatsAdded = satsAdded;
|
|
// }
|
|
// sats += satsAdded;
|
|
|
|
// postFeesInvestedAmount += dailyInvestmentPostFees;
|
|
|
|
// bitcoinValue = close * bitcoin;
|
|
|
|
// totalValue = dollars + bitcoinValue;
|
|
|
|
// averageCostBasis = postFeesInvestedAmount / bitcoin;
|
|
|
|
// roi = (bitcoinValue / postFeesInvestedAmount - 1) * 100;
|
|
|
|
// const daysCount = index + 1;
|
|
// profitableDaysRatio = profitableDays / daysCount;
|
|
// unprofitableDaysRatio = unprofitableDays / daysCount;
|
|
|
|
// if (roi >= 0) {
|
|
// profitableDays += 1;
|
|
// } else {
|
|
// unprofitableDays += 1;
|
|
// }
|
|
|
|
// bitcoinPriceData().push({
|
|
// time,
|
|
// value: close,
|
|
// });
|
|
|
|
// bitcoinData().push({
|
|
// time,
|
|
// value: bitcoin,
|
|
// });
|
|
|
|
// totalInvestedAmountData().push({
|
|
// time,
|
|
// value: investedAmount,
|
|
// });
|
|
|
|
// bitcoinValueData().push({
|
|
// time,
|
|
// value: bitcoinValue,
|
|
// });
|
|
|
|
// resultData().push({
|
|
// time,
|
|
// value: roi,
|
|
// });
|
|
|
|
// dollarsLeftData().push({
|
|
// time,
|
|
// value: dollars,
|
|
// });
|
|
|
|
// totalValueData().push({
|
|
// time,
|
|
// value: totalValue,
|
|
// });
|
|
|
|
// investmentData().push({
|
|
// time,
|
|
// value: dailyInvestment,
|
|
// });
|
|
|
|
// bitcoinAddedData().push({
|
|
// time,
|
|
// value: bitcoinAdded,
|
|
// });
|
|
|
|
// averageCostBasisData().push({
|
|
// time,
|
|
// value: averageCostBasis,
|
|
// });
|
|
|
|
// buyCountData().push({
|
|
// time,
|
|
// value: buyCount,
|
|
// });
|
|
|
|
// totalFeesPaidData().push({
|
|
// time,
|
|
// value: totalFeesPaid,
|
|
// });
|
|
|
|
// daysCountData().push({
|
|
// time,
|
|
// value: daysCount,
|
|
// });
|
|
|
|
// profitableDaysRatioData().push({
|
|
// time,
|
|
// value: profitableDaysRatio * 100,
|
|
// });
|
|
|
|
// unprofitableDaysRatioData().push({
|
|
// time,
|
|
// value: unprofitableDaysRatio * 100,
|
|
// });
|
|
// });
|
|
|
|
// const f = numberToUSNumber;
|
|
// /** @param {number} v */
|
|
// const fd = (v) => numberToDollars.format(v);
|
|
// /** @param {number} v */
|
|
// const fp = (v) => numberToPercentage.format(v);
|
|
// /**
|
|
// * @param {ColorName} c
|
|
// * @param {string} t
|
|
// */
|
|
// const c = (c, t) => createColoredSpan({ color: c, text: t });
|
|
|
|
// const serInvestedAmount = c("green", fd(investedAmount));
|
|
// const serDaysCount = c("sky", f(daysCount));
|
|
// const serSats = c("orange", f(sats));
|
|
// const serBitcoin = c("orange", `~${f(bitcoin)}`);
|
|
// const serBitcoinValue = c("amber", fd(bitcoinValue));
|
|
// const serAverageCostBasis = c("lime", fd(averageCostBasis));
|
|
// const serRoi = c("yellow", fp(roi / 100));
|
|
// const serDollars = c("emerald", fd(dollars));
|
|
// const serTotalFeesPaid = c("rose", fd(totalFeesPaid));
|
|
|
|
// p1.innerHTML = `After exchanging ${serInvestedAmount} in the span of ${serDaysCount} days, you would have accumulated ${serSats} Satoshis (${serBitcoin} Bitcoin) worth today ${serBitcoinValue} at an average cost basis of ${serAverageCostBasis} per Bitcoin with a return of investment of ${serRoi}, have ${serDollars} left and paid a total of ${serTotalFeesPaid} in fees.`;
|
|
|
|
// const dayDiff = Math.floor(
|
|
// differenceBetweenDates(new Date(), lastInvestDay),
|
|
// );
|
|
// const serDailyInvestment = c("emerald", fd(dailyInvestment));
|
|
// const setLastSatsAdded = c("orange", f(lastSatsAdded));
|
|
// p2.innerHTML = `You would've last bought ${c(
|
|
// "blue",
|
|
// dayDiff
|
|
// ? `${f(dayDiff)} ${dayDiff > 1 ? "days" : "day"} ago`
|
|
// : "today",
|
|
// )} and exchanged ${serDailyInvestment} for approximately ${setLastSatsAdded} Satoshis`;
|
|
|
|
// const serProfitableDaysRatio = c("green", fp(profitableDaysRatio));
|
|
// const serUnprofitableDaysRatio = c(
|
|
// "red",
|
|
// fp(unprofitableDaysRatio),
|
|
// );
|
|
|
|
// p3.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
|
|
|
|
// totalInvestedAmountData.set((a) => a);
|
|
// bitcoinValueData.set((a) => a);
|
|
// bitcoinData.set((a) => a);
|
|
// resultData.set((a) => a);
|
|
// dollarsLeftData.set((a) => a);
|
|
// totalValueData.set((a) => a);
|
|
// investmentData.set((a) => a);
|
|
// bitcoinAddedData.set((a) => a);
|
|
// averageCostBasisData.set((a) => a);
|
|
// bitcoinPriceData.set((a) => a);
|
|
// buyCountData.set((a) => a);
|
|
// totalFeesPaidData.set((a) => a);
|
|
// daysCountData.set((a) => a);
|
|
// profitableDaysRatioData.set((a) => a);
|
|
// unprofitableDaysRatioData.set((a) => a);
|
|
// },
|
|
// );
|
|
// });
|
|
// });
|
|
// }
|