Files
brk/website/scripts/options/market/investing.js
2026-01-25 20:11:32 +01:00

236 lines
7.1 KiB
JavaScript

/** Investing section (DCA) */
import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js";
import { line, baseline } from "../series.js";
import { satsBtcUsd } from "../shared.js";
import { periodIdToName } from "./utils.js";
/**
* Build DCA classes data array
* @param {Colors} colors
* @param {MarketDca} dca
*/
export function buildDcaClasses(colors, dca) {
return /** @type {const} */ ([
[2026, "rose", true],
[2025, "pink", true],
[2024, "fuchsia", true],
[2023, "purple", true],
[2022, "blue", true],
[2021, "sky", true],
[2020, "teal", true],
[2019, "green", true],
[2018, "yellow", true],
[2017, "orange", true],
[2016, "red", false],
[2015, "pink", false],
]).map(([year, colorKey, defaultActive]) => ({
year,
color: colors[colorKey],
defaultActive,
costBasis: dca.classAveragePrice[`_${year}`],
returns: dca.classReturns[`_${year}`],
stack: dca.classStack[`_${year}`],
}));
}
/**
* Create Investing section
* @param {PartialContext} ctx
* @param {Object} args
* @param {Market["dca"]} args.dca
* @param {Market["lookback"]} args.lookback
* @param {Market["returns"]} args.returns
*/
export function createInvestingSection(ctx, { dca, lookback, returns }) {
const { colors } = ctx;
const dcaClasses = buildDcaClasses(colors, dca);
/**
* @param {string} id
* @param {ShortPeriodKey} key
*/
const createPeriodTree = (id, key) => {
const name = periodIdToName(id, true);
return {
name,
tree: [
{
name: "Cost basis",
title: `${name} Cost Basis`,
top: [
line({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green, unit: Unit.usd }),
line({ metric: lookback[key], name: "Lump sum", color: colors.orange, unit: Unit.usd }),
],
},
{
name: "Returns",
title: `${name} Returns`,
bottom: [
baseline({ metric: dca.periodReturns[key], name: "DCA", unit: Unit.percentage }),
baseline({ metric: returns.priceReturns[key], name: "Lump sum", color: [colors.cyan, colors.orange], unit: Unit.percentage }),
priceLine({ ctx, unit: Unit.percentage }),
],
},
{
name: "Stack",
title: `${name} Stack`,
bottom: [
...satsBtcUsd(dca.periodStack[key], "DCA", colors.green),
...satsBtcUsd(dca.periodLumpSumStack[key], "Lump sum", colors.orange),
],
},
],
};
};
/**
* @param {string} id
* @param {LongPeriodKey} key
*/
const createPeriodTreeWithCagr = (id, key) => {
const name = periodIdToName(id, true);
return {
name,
tree: [
{
name: "Cost basis",
title: `${name} Cost Basis`,
top: [
line({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green, unit: Unit.usd }),
line({ metric: lookback[key], name: "Lump sum", color: colors.orange, unit: Unit.usd }),
],
},
{
name: "Returns",
title: `${name} Returns`,
bottom: [
baseline({ metric: dca.periodReturns[key], name: "DCA", unit: Unit.percentage }),
baseline({ metric: returns.priceReturns[key], name: "Lump sum", color: [colors.cyan, colors.orange], unit: Unit.percentage }),
line({ metric: dca.periodCagr[key], name: "DCA CAGR", color: colors.purple, unit: Unit.percentage, defaultActive: false }),
line({ metric: returns.cagr[key], name: "Lump sum CAGR", color: colors.indigo, unit: Unit.percentage, defaultActive: false }),
priceLine({ ctx, unit: Unit.percentage }),
],
},
{
name: "Stack",
title: `${name} Stack`,
bottom: [
...satsBtcUsd(dca.periodStack[key], "DCA", colors.green),
...satsBtcUsd(dca.periodLumpSumStack[key], "Lump sum", colors.orange),
],
},
],
};
};
return {
name: "Investing",
tree: [
// DCA vs Lump sum
{
name: "DCA vs Lump sum",
tree: [
createPeriodTree("1w", "_1w"),
createPeriodTree("1m", "_1m"),
createPeriodTree("3m", "_3m"),
createPeriodTree("6m", "_6m"),
createPeriodTree("1y", "_1y"),
createPeriodTreeWithCagr("2y", "_2y"),
createPeriodTreeWithCagr("3y", "_3y"),
createPeriodTreeWithCagr("4y", "_4y"),
createPeriodTreeWithCagr("5y", "_5y"),
createPeriodTreeWithCagr("6y", "_6y"),
createPeriodTreeWithCagr("8y", "_8y"),
createPeriodTreeWithCagr("10y", "_10y"),
],
},
// DCA classes
{
name: "DCA classes",
tree: [
// Comparison charts (all years overlaid)
{
name: "Compare",
tree: [
{
name: "Cost basis",
title: "DCA Cost Basis",
top: dcaClasses.map(
({ year, color, defaultActive, costBasis }) =>
line({
metric: costBasis,
name: `${year}`,
color,
defaultActive,
unit: Unit.usd,
}),
),
},
{
name: "Returns",
title: "DCA Returns",
bottom: dcaClasses.map(
({ year, color, defaultActive, returns }) =>
baseline({
metric: returns,
name: `${year}`,
color,
defaultActive,
unit: Unit.percentage,
}),
),
},
{
name: "Stack",
title: "DCA Stack",
bottom: dcaClasses.flatMap(
({ year, color, defaultActive, stack }) =>
satsBtcUsd(stack, `${year}`, color, { defaultActive }),
),
},
],
},
// Individual year charts
...dcaClasses.map(({ year, color, costBasis, returns, stack }) => ({
name: `${year}`,
tree: [
{
name: "Cost basis",
title: `${year} Cost Basis`,
top: [
line({
metric: costBasis,
name: "Cost basis",
color,
unit: Unit.usd,
}),
],
},
{
name: "Returns",
title: `${year} Returns`,
bottom: [
baseline({
metric: returns,
name: "Returns",
color,
unit: Unit.percentage,
}),
],
},
{
name: "Stack",
title: `${year} Stack`,
bottom: satsBtcUsd(stack, "Stack", color),
},
],
})),
],
},
],
};
}