website: update

This commit is contained in:
k
2024-11-27 18:36:43 +01:00
parent d39e7584c0
commit 71871901ef
4 changed files with 216 additions and 115 deletions

View File

@@ -252,7 +252,6 @@
--fuchsia: oklch(0.629 0.294 322.523); --fuchsia: oklch(0.629 0.294 322.523);
--pink: oklch(0.624 0.245 357.444); --pink: oklch(0.624 0.245 357.444);
--rose: oklch(0.6155 0.2495 17.012); --rose: oklch(0.6155 0.2495 17.012);
--dollar: var(--green);
--background-color: light-dark(var(--white), var(--black)); --background-color: light-dark(var(--white), var(--black));
--color: light-dark(var(--black), var(--white)); --color: light-dark(var(--black), var(--white));
--off-color: light-dark(var(--light-gray), var(--dark-gray)); --off-color: light-dark(var(--light-gray), var(--dark-gray));
@@ -654,11 +653,11 @@
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
appearance: none; appearance: none;
background: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path d="M7 10l5 5 5-5z" fill="gray"/><path d="M0 0h24v24H0z" fill="none"/></svg>') background: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="gray"><path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>')
100% 50% no-repeat transparent; 100% 50% no-repeat transparent;
&:focus-visible { &:focus-visible {
border: none; border: 0;
} }
} }

View File

@@ -1,7 +1,7 @@
// @ts-check // @ts-check
/** /**
* @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, Frequency, CreatePaneParameters, CreateBaselineSeriesParams, CreateCandlestickSeriesParams, CreateLineSeriesParams } from "./types/self" * @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, Frequency, CreatePaneParameters, CreateBaselineSeriesParams, CreateCandlestickSeriesParams, CreateLineSeriesParams, LastValues } from "./types/self"
* @import {createChart as CreateClassicChart, createChartEx as CreateCustomChart, LineStyleOptions} from "../packages/lightweight-charts/v4.2.0/types"; * @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 * 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 { DeepPartial, ChartOptions, IChartApi, IHorzScaleBehavior, WhitespaceData, SingleValueData, ISeriesApi, Time, LineData, LogicalRange, SeriesMarker, CandlestickData, SeriesType, BaselineStyleOptions, SeriesOptionsCommon } from "../packages/lightweight-charts/v4.2.0/types"
@@ -665,8 +665,7 @@ function initPackages() {
const chartModes = /** @type {const} */ (["Linear", "Log"]); const chartModes = /** @type {const} */ (["Linear", "Log"]);
const chartMode = signals.createSignal( const chartMode = signals.createSignal(
/** @type {Lowercase<typeof chartModes[number]>} */ ( /** @type {Lowercase<typeof chartModes[number]>} */ (
localStorage.getItem(id) || localStorage.getItem(id) || "linear"
chartModes[chartIndex ? 0 : 1].toLowerCase()
), ),
); );
@@ -2048,6 +2047,8 @@ function createColors(dark) {
blue, blue,
rose, rose,
pink, pink,
green,
purple,
_1d: pink, _1d: pink,
_1w: red, _1w: red,
@@ -2138,7 +2139,6 @@ function createColors(dark) {
coinblocksCreated: purple, coinblocksCreated: purple,
coinblocksDestroyed: red, coinblocksDestroyed: red,
coinblocksStored: green, coinblocksStored: green,
momentum: [green, yellow, red],
momentumGreen: green, momentumGreen: green,
momentumYellow: yellow, momentumYellow: yellow,
momentumRed: red, momentumRed: red,
@@ -2703,9 +2703,8 @@ packages.signals().then((signals) =>
} }
const lastHeight = createLastHeightResource(); const lastHeight = createLastHeightResource();
const lastValues = signals.createSignal( const lastValues = signals.createSignal(/** @type {LastValues} */ (null));
/** @type {Record<LastPath, number> | null} */ (null),
);
function createFetchLastValuesWhenNeededEffect() { function createFetchLastValuesWhenNeededEffect() {
let previousHeight = -1; let previousHeight = -1;
signals.createEffect(lastHeight, (lastHeight) => { signals.createEffect(lastHeight, (lastHeight) => {
@@ -2831,17 +2830,12 @@ packages.signals().then((signals) =>
signals.runWithOwner(owner, () => signals.runWithOwner(owner, () =>
init({ init({
colors, colors,
consts,
dark,
datasets, datasets,
elements, elements,
ids,
lightweightCharts, lightweightCharts,
options,
selected: option,
signals, signals,
utils, utils,
webSockets, lastValues,
}), }),
), ),
), ),

View File

@@ -1,35 +1,26 @@
/** /**
* @import { Options } from './options'; * @import { Options } from './options';
* @import { ColorName } from './types/self';
*/ */
/** /**
* @param {Object} args * @param {Object} args
* @param {Colors} args.colors * @param {Colors} args.colors
* @param {Consts} args.consts
* @param {LightweightCharts} args.lightweightCharts * @param {LightweightCharts} args.lightweightCharts
* @param {SimulationOption} args.selected
* @param {Signals} args.signals * @param {Signals} args.signals
* @param {Utilities} args.utils * @param {Utilities} args.utils
* @param {Options} args.options
* @param {Datasets} args.datasets * @param {Datasets} args.datasets
* @param {WebSockets} args.webSockets
* @param {Elements} args.elements * @param {Elements} args.elements
* @param {Ids} args.ids * @param {Signal<LastValues>} args.lastValues
* @param {Accessor<boolean>} args.dark
*/ */
export function init({ export function init({
colors, colors,
consts,
dark,
datasets, datasets,
elements, elements,
ids,
lightweightCharts, lightweightCharts,
options,
selected,
signals, signals,
utils, utils,
webSockets, lastValues,
}) { }) {
const simulationElement = elements.simulation; const simulationElement = elements.simulation;
@@ -137,6 +128,25 @@ export function init({
}).headerElement, }).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: var(--font-weight-bold)">${text}</span>`;
}
parametersElement.append( parametersElement.append(
createFieldElement({ createFieldElement({
title: createColoredTypeHTML({ title: createColoredTypeHTML({
@@ -291,9 +301,10 @@ export function init({
}), }),
); );
const firstParagraph = window.document.createElement("p"); const p1 = window.document.createElement("p");
const p2 = window.document.createElement("p");
const secondParagraph = window.document.createElement("p"); const p3 = window.document.createElement("p");
const p4 = window.document.createElement("p");
const owner = signals.getOwner(); const owner = signals.getOwner();
@@ -326,8 +337,9 @@ export function init({
console.log({ start, end }); console.log({ start, end });
resultsElement.innerHTML = ""; resultsElement.innerHTML = "";
resultsElement.append(firstParagraph); resultsElement.append(p1);
resultsElement.append(secondParagraph); resultsElement.append(p2);
resultsElement.append(p3);
if (!start || !end || start > end) return; if (!start || !end || start > end) return;
@@ -361,6 +373,8 @@ export function init({
const daysCountData = []; const daysCountData = [];
/** @type {LineData<Time>[]} */ /** @type {LineData<Time>[]} */
const profitableDaysRatioData = []; const profitableDaysRatioData = [];
/** @type {LineData<Time>[]} */
const unprofitableDaysRatioData = [];
let bitcoin = 0; let bitcoin = 0;
let dollars = initialDollarAmount; let dollars = initialDollarAmount;
@@ -374,6 +388,8 @@ export function init({
let daysCount = range.length; let daysCount = range.length;
let profitableDays = 0; let profitableDays = 0;
let unprofitableDays = 0; let unprofitableDays = 0;
let profitableDaysRatio = 0;
let unprofitableDaysRatio = 0;
range.forEach((date, index) => { range.forEach((date, index) => {
const year = date.getUTCFullYear(); const year = date.getUTCFullYear();
@@ -423,6 +439,8 @@ export function init({
roi = (bitcoinValue / investedAmount - 1) * 100; roi = (bitcoinValue / investedAmount - 1) * 100;
const daysCount = index + 1; const daysCount = index + 1;
profitableDaysRatio = (profitableDays / daysCount) * 100;
unprofitableDaysRatio = (unprofitableDays / daysCount) * 100;
if (roi >= 0) { if (roi >= 0) {
profitableDays += 1; profitableDays += 1;
@@ -497,20 +515,78 @@ export function init({
profitableDaysRatioData.push({ profitableDaysRatioData.push({
time, time,
value: profitableDays / daysCount, value: profitableDaysRatio,
});
unprofitableDaysRatioData.push({
time,
value: unprofitableDaysRatio,
}); });
}); });
const f = utils.locale.numberToUSFormat; const f = utils.locale.numberToUSFormat;
/** /**
* @param {string} c * @param {ColorName} c
* @param {string} t * @param {string} t
*/ */
const c = (c, t) => createColoredSpan({ color: c, text: t }); const c = (c, t) => createColoredSpan({ color: c, text: t });
firstParagraph.innerHTML = `After exchanging ${c("dollar", `$${f(investedAmount)}`)} in the span of ${c("sky", f(daysCount))} days, you would've accumulated ${c("orange", f(bitcoin))} Bitcoin worth ${c("dollar", `$${f(bitcoinValue)}`)} at an average price of ${c("dollar", `$${f(averagePricePaid)}`)} per Bitcoin with a return of investment of ${c("yellow", `${f(roi)}%`)}.`; const serInvestedAmount = c("dollars", `$${f(investedAmount)}`);
const serDaysCount = c("sky", f(daysCount));
const serBitcoin = c("orange", f(bitcoin));
const serBitcoinValue = c("dollars", `$${f(bitcoinValue)}`);
const serAveragePricePaid = c("dollars", `$${f(averagePricePaid)}`);
const serRoi = c("yellow", `${f(roi)}%`);
secondParagraph.innerHTML = `Work in progress`; p1.innerHTML = `After exchanging ${serInvestedAmount} in the span of ${serDaysCount} days, you would've accumulated ${serBitcoin} Bitcoin worth ${serBitcoinValue} at an average price of ${serAveragePricePaid} per Bitcoin with a return of investment of ${serRoi}.`;
const serProfitableDaysRatio = c(
"green",
`${f(profitableDaysRatio)}%`,
);
const serUnprofitableDaysRatio = c(
"red",
`${f(unprofitableDaysRatio)}%`,
);
p2.innerHTML = `You would've been ${serProfitableDaysRatio} of the time profitable and ${serUnprofitableDaysRatio} of the time unprofitable.`;
signals.createEffect(lastValues, (lastValues) => {
const lowestAnnual4YReturn = 23.68;
// const lowestAnnual4YReturn = lastValues?.["price-4y-compound-return"] || 0
const serLowestAnnual4YReturn = c(
"yellow",
`${f(lowestAnnual4YReturn)}%`,
);
const lowestAnnual4YReturnPercentage =
1 + lowestAnnual4YReturn / 100;
/**
* @param {number} power
*/
function bitcoinValueReturn(power) {
return (
bitcoinValue * Math.pow(lowestAnnual4YReturnPercentage, power)
);
}
const bitcoinValueAfter4y = bitcoinValueReturn(4);
const serBitcoinValueAfter4y = c(
"rose",
`$${f(bitcoinValueAfter4y)}`,
);
const bitcoinValueAfter10y = bitcoinValueReturn(10);
const serBitcoinValueAfter10y = c(
"pink",
`$${f(bitcoinValueAfter10y)}`,
);
const bitcoinValueAfter20y = bitcoinValueReturn(20);
const serBitcoinValueAfter20y = c(
"purple",
`$${f(bitcoinValueAfter20y)}`,
);
p3.innerHTML = `Historically, the lowest annual return after 4 years has been ${serLowestAnnual4YReturn}.<br/>Using this as our baseline and assuming you don't swap any more US Dollars, your Bitcoin would be worth ${serBitcoinValueAfter4y} after 4 years, ${serBitcoinValueAfter10y} after 10 years and ${serBitcoinValueAfter20y} after 20 years.`;
});
lightweightCharts.createChart({ lightweightCharts.createChart({
parent: resultsElement, parent: resultsElement,
@@ -523,12 +599,6 @@ export function init({
unit: "US Dollars", unit: "US Dollars",
scale: "date", scale: "date",
config: [ config: [
{
kind: "line",
color: colors.dollars,
owner,
data: totalInvestedAmountData,
},
{ {
kind: "line", kind: "line",
color: colors.bitcoin, color: colors.bitcoin,
@@ -549,9 +619,9 @@ export function init({
}, },
{ {
kind: "line", kind: "line",
color: colors.yellow, color: colors.dollars,
owner, owner,
data: investmentData, data: totalInvestedAmountData,
}, },
{ {
kind: "line", kind: "line",
@@ -564,6 +634,28 @@ export function init({
], ],
}); });
// lightweightCharts.createChart({
// parent: resultsElement,
// signals,
// colors,
// id: `simulation-0`,
// kind: "static",
// config: [
// {
// unit: "US Dollars",
// scale: "date",
// config: [
// {
// kind: "line",
// color: colors.yellow,
// owner,
// data: investmentData,
// },
// ],
// },
// ],
// });
lightweightCharts.createChart({ lightweightCharts.createChart({
parent: resultsElement, parent: resultsElement,
signals, signals,
@@ -581,43 +673,38 @@ export function init({
owner, owner,
data: bitcoinData, data: bitcoinData,
}, },
{
kind: "line",
color: colors.lightBitcoin,
owner,
data: bitcoinAddedData,
},
], ],
}, },
], ],
}); });
lightweightCharts.createChart({ // lightweightCharts.createChart({
parent: resultsElement, // parent: resultsElement,
signals, // signals,
colors, // colors,
id: `simulation-2`, // id: `simulation-1`,
kind: "static", // kind: "static",
config: [ // config: [
{ // {
unit: "US Dollars", // unit: "US Dollars",
scale: "date", // scale: "date",
config: [ // config: [
{ // {
kind: "baseline", // kind: "line",
owner, // color: colors.lightBitcoin,
data: resultData, // owner,
}, // data: bitcoinAddedData,
], // },
}, // ],
], // },
}); // ],
// });
lightweightCharts.createChart({ lightweightCharts.createChart({
parent: resultsElement, parent: resultsElement,
signals, signals,
colors, colors,
id: `simulation-3`, id: `simulation-average-price`,
kind: "static", kind: "static",
config: [ config: [
{ {
@@ -645,7 +732,66 @@ export function init({
parent: resultsElement, parent: resultsElement,
signals, signals,
colors, colors,
id: `simulation-4`, id: `simulation-return-ratio`,
kind: "static",
config: [
{
unit: "US Dollars",
scale: "date",
config: [
{
kind: "baseline",
owner,
data: resultData,
// TODO: Doesn't work for some reason
// options: {
// baseLineColor: "#888",
// baseLineVisible: true,
// baseLineWidth: 1,
// baseValue: {
// price: 0,
// type: "price",
// },
// },
},
],
},
],
});
lightweightCharts.createChart({
parent: resultsElement,
signals,
colors,
id: `simulation-profitability-ratios`,
kind: "static",
config: [
{
unit: "Percentage",
scale: "date",
config: [
{
kind: "line",
owner,
color: colors.pink,
data: unprofitableDaysRatioData,
},
{
kind: "line",
owner,
color: colors.lime,
data: profitableDaysRatioData,
},
],
},
],
});
lightweightCharts.createChart({
parent: resultsElement,
signals,
colors,
id: `simulation-counts`,
kind: "static", kind: "static",
config: [ config: [
{ {
@@ -668,28 +814,6 @@ export function init({
}, },
], ],
}); });
lightweightCharts.createChart({
parent: resultsElement,
signals,
colors,
id: `simulation-5`,
kind: "static",
config: [
{
unit: "Percentage",
scale: "date",
config: [
{
kind: "line",
owner,
color: colors.pink,
data: profitableDaysRatioData,
},
],
},
],
});
}, },
); );
}); });
@@ -804,25 +928,6 @@ function getOrdinalDay(day) {
}`; }`;
} }
/**
* @param {Object} param0
* @param {string} 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 {string} param0.color
* @param {string} param0.text
*/
function createColoredSpan({ color, text }) {
return `<span style="color: var(--${color}); font-weight: var(--font-weight-bold)">${text}</span>`;
}
function computeFrequencies() { function computeFrequencies() {
const weekDays = [ const weekDays = [
"Monday", "Monday",

View File

@@ -42,6 +42,7 @@ type AnyDatasetPath = import("./paths").DatePath | import("./paths").HeightPath;
type AnyPath = AnyDatasetPath | LastPath; type AnyPath = AnyDatasetPath | LastPath;
type Color = () => string; type Color = () => string;
type ColorName = keyof Colors;
interface BaselineSpecificSeriesBlueprint { interface BaselineSpecificSeriesBlueprint {
type: "Baseline"; type: "Baseline";
@@ -399,3 +400,5 @@ interface CreatePaneParameters {
| ({ kind: "baseline" } & CreateBaselineSeriesParams) | ({ kind: "baseline" } & CreateBaselineSeriesParams)
)[]; )[];
} }
type LastValues = Record<LastPath, number> | null;