mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
1092 lines
30 KiB
JavaScript
1092 lines
30 KiB
JavaScript
/** Market section */
|
|
|
|
import { colors } from "../utils/colors.js";
|
|
import { brk } from "../client.js";
|
|
import { includes } from "../utils/array.js";
|
|
import { Unit } from "../utils/units.js";
|
|
import { priceLine, priceLines } from "./constants.js";
|
|
import {
|
|
baseline,
|
|
deltaTree,
|
|
histogram,
|
|
line,
|
|
price,
|
|
percentRatio,
|
|
percentRatioBaseline,
|
|
ROLLING_WINDOWS,
|
|
ROLLING_WINDOWS_TO_1M,
|
|
} from "./series.js";
|
|
import { simplePriceRatioTree } from "./shared.js";
|
|
import { periodIdToName } from "./utils.js";
|
|
|
|
/**
|
|
* @typedef {Object} Period
|
|
* @property {string} id
|
|
* @property {Color} color
|
|
* @property {{ percent: AnySeriesPattern, ratio: AnySeriesPattern }} returns
|
|
* @property {AnyPricePattern} lookback
|
|
* @property {boolean} [defaultActive]
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Period & { cagr: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }} PeriodWithCagr
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} MaPeriod
|
|
* @property {string} id
|
|
* @property {Color} color
|
|
* @property {MaPriceRatioPattern} ratio
|
|
*/
|
|
|
|
/**
|
|
* Create index (percent) + ratio line pair from a BpsPercentRatioPattern
|
|
* @param {{ pattern: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, name: string, color?: Color, defaultActive?: boolean }} args
|
|
* @returns {AnyFetchedSeriesBlueprint[]}
|
|
*/
|
|
function indexRatio({ pattern, name, color, defaultActive }) {
|
|
return [
|
|
line({
|
|
series: pattern.percent,
|
|
name,
|
|
color,
|
|
defaultActive,
|
|
unit: Unit.index,
|
|
}),
|
|
line({
|
|
series: pattern.ratio,
|
|
name,
|
|
color,
|
|
defaultActive,
|
|
unit: Unit.ratio,
|
|
}),
|
|
];
|
|
}
|
|
|
|
const commonMaIds = /** @type {const} */ ([
|
|
"1w",
|
|
"1m",
|
|
"200d",
|
|
"1y",
|
|
"200w",
|
|
"4y",
|
|
]);
|
|
|
|
/**
|
|
* @param {string} label
|
|
* @param {MaPeriod[]} averages
|
|
*/
|
|
function createMaSubSection(label, averages) {
|
|
const common = averages.filter((a) => includes(commonMaIds, a.id));
|
|
const more = averages.filter((a) => !includes(commonMaIds, a.id));
|
|
|
|
/** @param {MaPeriod} a */
|
|
const toFolder = (a) => ({
|
|
name: periodIdToName(a.id, true),
|
|
tree: simplePriceRatioTree({
|
|
pattern: a.ratio,
|
|
title: `${periodIdToName(a.id, true)} ${label}`,
|
|
legend: "average",
|
|
color: a.color,
|
|
}),
|
|
});
|
|
|
|
return {
|
|
name: label,
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: `Price ${label}s`,
|
|
top: averages.map((a) =>
|
|
price({
|
|
series: a.ratio,
|
|
name: a.id,
|
|
color: a.color,
|
|
defaultActive: includes(commonMaIds, a.id),
|
|
}),
|
|
),
|
|
},
|
|
...common.map(toFolder),
|
|
{ name: "More...", tree: more.map(toFolder) },
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {Period[]} periods
|
|
*/
|
|
function returnsSubSection(name, periods) {
|
|
return {
|
|
name,
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: `${name} Returns`,
|
|
bottom: periods.flatMap((p) =>
|
|
percentRatioBaseline({
|
|
pattern: p.returns,
|
|
name: p.id,
|
|
color: p.color,
|
|
}),
|
|
),
|
|
},
|
|
...periods.map((p) => ({
|
|
name: periodIdToName(p.id, true),
|
|
title: `${periodIdToName(p.id, true)} Returns`,
|
|
bottom: percentRatioBaseline({ pattern: p.returns, name: "Total" }),
|
|
})),
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {PeriodWithCagr[]} periods
|
|
*/
|
|
function returnsSubSectionWithCagr(name, periods) {
|
|
return {
|
|
name,
|
|
tree: [
|
|
{
|
|
name: "Total",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: `${name} Total Returns`,
|
|
bottom: periods.flatMap((p) =>
|
|
percentRatioBaseline({
|
|
pattern: p.returns,
|
|
name: p.id,
|
|
color: p.color,
|
|
}),
|
|
),
|
|
},
|
|
...periods.map((p) => ({
|
|
name: periodIdToName(p.id, true),
|
|
title: `${periodIdToName(p.id, true)} Total Returns`,
|
|
bottom: percentRatioBaseline({ pattern: p.returns, name: "Total" }),
|
|
})),
|
|
],
|
|
},
|
|
{
|
|
name: "CAGR",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: `${name} CAGR`,
|
|
bottom: periods.flatMap((p) =>
|
|
percentRatioBaseline({
|
|
pattern: p.cagr,
|
|
name: p.id,
|
|
color: p.color,
|
|
}),
|
|
),
|
|
},
|
|
...periods.map((p) => ({
|
|
name: periodIdToName(p.id, true),
|
|
title: `${periodIdToName(p.id, true)} CAGR`,
|
|
bottom: percentRatioBaseline({ pattern: p.cagr, name: "CAGR" }),
|
|
})),
|
|
],
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @param {Period[]} periods
|
|
*/
|
|
function historicalSubSection(name, periods) {
|
|
return {
|
|
name,
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: `${name} Historical`,
|
|
top: periods.map((p) =>
|
|
price({ series: p.lookback, name: p.id, color: p.color }),
|
|
),
|
|
},
|
|
...periods.map((p) => ({
|
|
name: periodIdToName(p.id, true),
|
|
title: `${periodIdToName(p.id, true)} Ago`,
|
|
top: [price({ series: p.lookback, name: "Price" })],
|
|
})),
|
|
],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create Market section
|
|
* @returns {PartialOptionsGroup}
|
|
*/
|
|
export function createMarketSection() {
|
|
const { market, supply, cohorts, prices, indicators } = brk.series;
|
|
const {
|
|
movingAverage: ma,
|
|
ath,
|
|
returns,
|
|
volatility,
|
|
range,
|
|
technical,
|
|
lookback,
|
|
} = market;
|
|
|
|
const shortPeriodsBase = [
|
|
{ id: "24h", returns: returns.periods._24h, lookback: lookback._24h },
|
|
{ id: "1w", returns: returns.periods._1w, lookback: lookback._1w },
|
|
{ id: "1m", returns: returns.periods._1m, lookback: lookback._1m },
|
|
{
|
|
id: "3m",
|
|
returns: returns.periods._3m,
|
|
lookback: lookback._3m,
|
|
defaultActive: false,
|
|
},
|
|
{
|
|
id: "6m",
|
|
returns: returns.periods._6m,
|
|
lookback: lookback._6m,
|
|
defaultActive: false,
|
|
},
|
|
{ id: "1y", returns: returns.periods._1y, lookback: lookback._1y },
|
|
];
|
|
|
|
const longPeriodsBase = [
|
|
{
|
|
id: "2y",
|
|
returns: returns.periods._2y,
|
|
cagr: returns.cagr._2y,
|
|
lookback: lookback._2y,
|
|
defaultActive: false,
|
|
},
|
|
{
|
|
id: "3y",
|
|
returns: returns.periods._3y,
|
|
cagr: returns.cagr._3y,
|
|
lookback: lookback._3y,
|
|
defaultActive: false,
|
|
},
|
|
{
|
|
id: "4y",
|
|
returns: returns.periods._4y,
|
|
cagr: returns.cagr._4y,
|
|
lookback: lookback._4y,
|
|
},
|
|
{
|
|
id: "5y",
|
|
returns: returns.periods._5y,
|
|
cagr: returns.cagr._5y,
|
|
lookback: lookback._5y,
|
|
defaultActive: false,
|
|
},
|
|
{
|
|
id: "6y",
|
|
returns: returns.periods._6y,
|
|
cagr: returns.cagr._6y,
|
|
lookback: lookback._6y,
|
|
defaultActive: false,
|
|
},
|
|
{
|
|
id: "8y",
|
|
returns: returns.periods._8y,
|
|
cagr: returns.cagr._8y,
|
|
lookback: lookback._8y,
|
|
defaultActive: false,
|
|
},
|
|
{
|
|
id: "10y",
|
|
returns: returns.periods._10y,
|
|
cagr: returns.cagr._10y,
|
|
lookback: lookback._10y,
|
|
defaultActive: false,
|
|
},
|
|
];
|
|
|
|
const totalReturnPeriods = shortPeriodsBase.length + longPeriodsBase.length;
|
|
|
|
/** @type {Period[]} */
|
|
const shortPeriods = shortPeriodsBase.map((p, i) => ({
|
|
...p,
|
|
color: colors.at(i, totalReturnPeriods),
|
|
}));
|
|
|
|
/** @type {PeriodWithCagr[]} */
|
|
const longPeriods = longPeriodsBase.map((p, i) => ({
|
|
...p,
|
|
color: colors.at(shortPeriodsBase.length + i, totalReturnPeriods),
|
|
}));
|
|
|
|
/** @type {MaPeriod[]} */
|
|
const sma = [
|
|
{ id: "1w", ratio: ma.sma._1w },
|
|
{ id: "8d", ratio: ma.sma._8d },
|
|
{ id: "13d", ratio: ma.sma._13d },
|
|
{ id: "21d", ratio: ma.sma._21d },
|
|
{ id: "1m", ratio: ma.sma._1m },
|
|
{ id: "34d", ratio: ma.sma._34d },
|
|
{ id: "55d", ratio: ma.sma._55d },
|
|
{ id: "89d", ratio: ma.sma._89d },
|
|
{ id: "111d", ratio: ma.sma._111d },
|
|
{ id: "144d", ratio: ma.sma._144d },
|
|
{ id: "200d", ratio: ma.sma._200d },
|
|
{ id: "350d", ratio: ma.sma._350d },
|
|
{ id: "1y", ratio: ma.sma._1y },
|
|
{ id: "2y", ratio: ma.sma._2y },
|
|
{ id: "200w", ratio: ma.sma._200w },
|
|
{ id: "4y", ratio: ma.sma._4y },
|
|
].map((p, i, arr) => ({ ...p, color: colors.at(i, arr.length) }));
|
|
|
|
/** @type {MaPeriod[]} */
|
|
const ema = [
|
|
{ id: "1w", ratio: ma.ema._1w },
|
|
{ id: "8d", ratio: ma.ema._8d },
|
|
{ id: "12d", ratio: ma.ema._12d },
|
|
{ id: "13d", ratio: ma.ema._13d },
|
|
{ id: "21d", ratio: ma.ema._21d },
|
|
{ id: "26d", ratio: ma.ema._26d },
|
|
{ id: "1m", ratio: ma.ema._1m },
|
|
{ id: "34d", ratio: ma.ema._34d },
|
|
{ id: "55d", ratio: ma.ema._55d },
|
|
{ id: "89d", ratio: ma.ema._89d },
|
|
{ id: "144d", ratio: ma.ema._144d },
|
|
{ id: "200d", ratio: ma.ema._200d },
|
|
{ id: "1y", ratio: ma.ema._1y },
|
|
{ id: "2y", ratio: ma.ema._2y },
|
|
{ id: "200w", ratio: ma.ema._200w },
|
|
{ id: "4y", ratio: ma.ema._4y },
|
|
].map((p, i, arr) => ({ ...p, color: colors.at(i, arr.length) }));
|
|
|
|
// SMA vs EMA comparison periods (common periods only)
|
|
const smaVsEma = [
|
|
{
|
|
id: "1w",
|
|
name: "1 Week",
|
|
sma: ma.sma._1w,
|
|
ema: ma.ema._1w,
|
|
},
|
|
{
|
|
id: "1m",
|
|
name: "1 Month",
|
|
sma: ma.sma._1m,
|
|
ema: ma.ema._1m,
|
|
},
|
|
{
|
|
id: "200d",
|
|
name: "200 Day",
|
|
sma: ma.sma._200d,
|
|
ema: ma.ema._200d,
|
|
},
|
|
{
|
|
id: "1y",
|
|
name: "1 Year",
|
|
sma: ma.sma._1y,
|
|
ema: ma.ema._1y,
|
|
},
|
|
{
|
|
id: "200w",
|
|
name: "200 Week",
|
|
sma: ma.sma._200w,
|
|
ema: ma.ema._200w,
|
|
},
|
|
{
|
|
id: "4y",
|
|
name: "4 Year",
|
|
sma: ma.sma._4y,
|
|
ema: ma.ema._4y,
|
|
},
|
|
].map((p, i, arr) => ({ ...p, color: colors.at(i, arr.length) }));
|
|
|
|
return {
|
|
name: "Market",
|
|
tree: [
|
|
// Price
|
|
{ name: "Price", title: "Bitcoin Price" },
|
|
|
|
// Sats/$
|
|
{
|
|
name: "Sats/$",
|
|
title: "Sats per Dollar",
|
|
bottom: [
|
|
line({
|
|
series: prices.spot.sats,
|
|
name: "Sats/$",
|
|
unit: Unit.sats,
|
|
}),
|
|
],
|
|
},
|
|
|
|
// All Time High
|
|
{
|
|
name: "All Time High",
|
|
tree: [
|
|
{
|
|
name: "Drawdown",
|
|
title: "ATH Drawdown",
|
|
top: [price({ series: ath.high, name: "ATH" })],
|
|
bottom: percentRatio({
|
|
pattern: ath.drawdown,
|
|
name: "Drawdown",
|
|
color: colors.loss,
|
|
}),
|
|
},
|
|
{
|
|
name: "Time Since",
|
|
title: "Time Since ATH",
|
|
top: [price({ series: ath.high, name: "ATH" })],
|
|
bottom: [
|
|
line({
|
|
series: ath.daysSince,
|
|
name: "Since",
|
|
unit: Unit.days,
|
|
}),
|
|
line({
|
|
series: ath.yearsSince,
|
|
name: "Since",
|
|
unit: Unit.years,
|
|
}),
|
|
line({
|
|
series: ath.maxDaysBetween,
|
|
name: "Max",
|
|
color: colors.loss,
|
|
unit: Unit.days,
|
|
}),
|
|
line({
|
|
series: ath.maxYearsBetween,
|
|
name: "Max",
|
|
color: colors.loss,
|
|
unit: Unit.years,
|
|
}),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
// Returns
|
|
{
|
|
name: "Returns",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "Returns Comparison",
|
|
bottom: [...shortPeriods, ...longPeriods].flatMap((p) =>
|
|
percentRatioBaseline({
|
|
pattern: p.returns,
|
|
name: p.id,
|
|
color: p.color,
|
|
defaultActive: p.defaultActive,
|
|
}),
|
|
),
|
|
},
|
|
returnsSubSection("Short-term", shortPeriods),
|
|
returnsSubSectionWithCagr("Long-term", longPeriods),
|
|
],
|
|
},
|
|
|
|
// Historical
|
|
{
|
|
name: "Historical",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "Historical Comparison",
|
|
top: [...shortPeriods, ...longPeriods].map((p) =>
|
|
price({
|
|
series: p.lookback,
|
|
name: p.id,
|
|
color: p.color,
|
|
defaultActive: p.defaultActive,
|
|
}),
|
|
),
|
|
},
|
|
historicalSubSection("Short-term", shortPeriods),
|
|
historicalSubSection("Long-term", longPeriods),
|
|
],
|
|
},
|
|
|
|
// Capitalization
|
|
{
|
|
name: "Capitalization",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "Market vs Realized Capitalization",
|
|
bottom: [
|
|
line({
|
|
series: supply.marketCap.usd,
|
|
name: "Market Cap",
|
|
unit: Unit.usd,
|
|
}),
|
|
line({
|
|
series: cohorts.utxo.all.realized.cap.usd,
|
|
name: "Realized Cap",
|
|
color: colors.realized,
|
|
unit: Unit.usd,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Market Cap",
|
|
tree: [
|
|
{
|
|
name: "Value",
|
|
title: "Market Capitalization",
|
|
bottom: [
|
|
line({
|
|
series: supply.marketCap.usd,
|
|
name: "Market Cap",
|
|
unit: Unit.usd,
|
|
}),
|
|
],
|
|
},
|
|
...deltaTree({
|
|
delta: supply.marketCap.delta,
|
|
metric: "Market Cap",
|
|
unit: Unit.usd,
|
|
extract: (v) => v.usd,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Realized Cap",
|
|
tree: [
|
|
{
|
|
name: "Value",
|
|
title: "Realized Capitalization",
|
|
bottom: [
|
|
line({
|
|
series: cohorts.utxo.all.realized.cap.usd,
|
|
name: "Realized Cap",
|
|
color: colors.realized,
|
|
unit: Unit.usd,
|
|
}),
|
|
],
|
|
},
|
|
...deltaTree({
|
|
delta: cohorts.utxo.all.realized.cap.delta,
|
|
metric: "Realized Cap",
|
|
unit: Unit.usd,
|
|
extract: (v) => v.usd,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Growth Rate Spread",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "Capitalization Growth Rate Spread",
|
|
bottom: ROLLING_WINDOWS.map((w) =>
|
|
baseline({
|
|
series: supply.marketMinusRealizedCapGrowthRate[w.key],
|
|
name: w.name,
|
|
color: w.color,
|
|
unit: Unit.percentage,
|
|
}),
|
|
),
|
|
},
|
|
...ROLLING_WINDOWS.map((w) => ({
|
|
name: w.name,
|
|
title: `${w.title} Capitalization Growth Rate Spread`,
|
|
bottom: [
|
|
baseline({
|
|
series: supply.marketMinusRealizedCapGrowthRate[w.key],
|
|
name: w.name,
|
|
unit: Unit.percentage,
|
|
}),
|
|
],
|
|
})),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
// Technical
|
|
{
|
|
name: "Technical",
|
|
tree: [
|
|
// Moving Averages
|
|
{
|
|
name: "Moving Averages",
|
|
tree: [
|
|
{
|
|
name: "SMA vs EMA",
|
|
tree: [
|
|
{
|
|
name: "All Periods",
|
|
title: "SMA vs EMA Comparison",
|
|
top: smaVsEma.flatMap((p) => [
|
|
price({
|
|
series: p.sma,
|
|
name: `${p.id} SMA`,
|
|
color: p.color,
|
|
}),
|
|
price({
|
|
series: p.ema,
|
|
name: `${p.id} EMA`,
|
|
color: p.color,
|
|
style: 1,
|
|
}),
|
|
]),
|
|
},
|
|
...smaVsEma.map((p) => ({
|
|
name: p.name,
|
|
title: `${p.name} SMA vs EMA`,
|
|
top: [
|
|
price({ series: p.sma, name: "SMA", color: p.color }),
|
|
price({
|
|
series: p.ema,
|
|
name: "EMA",
|
|
color: p.color,
|
|
style: 1,
|
|
}),
|
|
],
|
|
})),
|
|
],
|
|
},
|
|
createMaSubSection("SMA", sma),
|
|
createMaSubSection("EMA", ema),
|
|
],
|
|
},
|
|
|
|
// Momentum
|
|
{
|
|
name: "Momentum",
|
|
tree: [
|
|
{
|
|
name: "RSI",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "RSI Comparison",
|
|
bottom: [
|
|
...ROLLING_WINDOWS_TO_1M.flatMap((w) =>
|
|
indexRatio({
|
|
pattern: technical.rsi[w.key].rsi,
|
|
name: w.name,
|
|
color: w.color,
|
|
}),
|
|
),
|
|
priceLine({ unit: Unit.index, number: 70 }),
|
|
priceLine({ unit: Unit.index, number: 30 }),
|
|
],
|
|
},
|
|
...ROLLING_WINDOWS_TO_1M.map((w) => {
|
|
const rsi = technical.rsi[w.key];
|
|
return {
|
|
name: w.name,
|
|
title: `${w.title} RSI`,
|
|
bottom: [
|
|
...indexRatio({
|
|
pattern: rsi.rsi,
|
|
name: "RSI",
|
|
color: colors.indicator.main,
|
|
}),
|
|
priceLine({ unit: Unit.index, number: 70 }),
|
|
priceLine({
|
|
unit: Unit.index,
|
|
number: 50,
|
|
defaultActive: false,
|
|
}),
|
|
priceLine({ unit: Unit.index, number: 30 }),
|
|
],
|
|
};
|
|
}),
|
|
{
|
|
name: "Stochastic",
|
|
tree: ROLLING_WINDOWS_TO_1M.map((w) => {
|
|
const rsi = technical.rsi[w.key];
|
|
return {
|
|
name: w.name,
|
|
title: `${w.title} Stochastic RSI`,
|
|
bottom: [
|
|
...indexRatio({
|
|
pattern: rsi.stochRsiK,
|
|
name: "K",
|
|
color: colors.indicator.fast,
|
|
}),
|
|
...indexRatio({
|
|
pattern: rsi.stochRsiD,
|
|
name: "D",
|
|
color: colors.indicator.slow,
|
|
}),
|
|
...priceLines({
|
|
unit: Unit.index,
|
|
numbers: [80, 20],
|
|
}),
|
|
],
|
|
};
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "MACD",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "MACD Comparison",
|
|
bottom: ROLLING_WINDOWS_TO_1M.map((w) =>
|
|
line({
|
|
series: technical.macd[w.key].line,
|
|
name: w.name,
|
|
color: w.color,
|
|
unit: Unit.usd,
|
|
}),
|
|
),
|
|
},
|
|
...ROLLING_WINDOWS_TO_1M.map((w) => ({
|
|
name: w.name,
|
|
title: `${w.title} MACD`,
|
|
bottom: [
|
|
line({
|
|
series: technical.macd[w.key].line,
|
|
name: "MACD",
|
|
color: colors.indicator.fast,
|
|
unit: Unit.usd,
|
|
}),
|
|
line({
|
|
series: technical.macd[w.key].signal,
|
|
name: "Signal",
|
|
color: colors.indicator.slow,
|
|
unit: Unit.usd,
|
|
}),
|
|
histogram({
|
|
series: technical.macd[w.key].histogram,
|
|
name: "Histogram",
|
|
unit: Unit.usd,
|
|
}),
|
|
],
|
|
})),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
// Volatility
|
|
{
|
|
name: "Volatility",
|
|
tree: [
|
|
{
|
|
name: "Index",
|
|
tree: [
|
|
{
|
|
name: "Compare",
|
|
title: "Volatility Index",
|
|
bottom: ROLLING_WINDOWS.map((w) =>
|
|
line({
|
|
series: volatility[w.key],
|
|
name: w.name,
|
|
color: w.color,
|
|
unit: Unit.percentage,
|
|
}),
|
|
),
|
|
},
|
|
...ROLLING_WINDOWS.map((w) => ({
|
|
name: w.name,
|
|
title: `${w.title} Volatility Index`,
|
|
bottom: [
|
|
line({
|
|
series: volatility[w.key],
|
|
name: w.name,
|
|
color: w.color,
|
|
unit: Unit.percentage,
|
|
}),
|
|
],
|
|
})),
|
|
],
|
|
},
|
|
{
|
|
name: "True Range",
|
|
tree: [
|
|
{
|
|
name: "Daily",
|
|
title: "True Range (Daily)",
|
|
bottom: [
|
|
line({
|
|
series: range.trueRange,
|
|
name: "Daily",
|
|
color: colors.time._24h,
|
|
unit: Unit.usd,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "2 Week Sum",
|
|
title: "True Range (2 Week Sum)",
|
|
bottom: [
|
|
line({
|
|
series: range.trueRangeSum2w,
|
|
name: "2w Sum",
|
|
color: colors.time._1w,
|
|
unit: Unit.usd,
|
|
}),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Choppiness",
|
|
title: "Choppiness Index",
|
|
bottom: [
|
|
...percentRatio({
|
|
pattern: range.choppinessIndex2w,
|
|
name: "2w",
|
|
color: colors.indicator.main,
|
|
}),
|
|
...priceLines({ unit: Unit.index, numbers: [61.8, 38.2] }),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
// Price Bands
|
|
{
|
|
name: "Price Bands",
|
|
tree: [
|
|
{
|
|
name: "Mayer Multiple",
|
|
title: "Mayer Multiple",
|
|
top: [
|
|
price({
|
|
series: ma.sma._200d,
|
|
name: "200d SMA",
|
|
color: colors.indicator.main,
|
|
}),
|
|
price({
|
|
series: ma.sma._200d.x24,
|
|
name: "200d SMA x2.4",
|
|
color: colors.indicator.upper,
|
|
}),
|
|
price({
|
|
series: ma.sma._200d.x08,
|
|
name: "200d SMA x0.8",
|
|
color: colors.indicator.lower,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "MinMax",
|
|
tree: [
|
|
{
|
|
id: "1w",
|
|
name: "1 Week",
|
|
min: range.min._1w,
|
|
max: range.max._1w,
|
|
},
|
|
{
|
|
id: "2w",
|
|
name: "2 Week",
|
|
min: range.min._2w,
|
|
max: range.max._2w,
|
|
},
|
|
{
|
|
id: "1m",
|
|
name: "1 Month",
|
|
min: range.min._1m,
|
|
max: range.max._1m,
|
|
},
|
|
{
|
|
id: "1y",
|
|
name: "1 Year",
|
|
min: range.min._1y,
|
|
max: range.max._1y,
|
|
},
|
|
].map((p) => ({
|
|
name: p.id,
|
|
title: `${p.name} MinMax`,
|
|
top: [
|
|
price({
|
|
series: p.max,
|
|
name: "Max",
|
|
key: "price-max",
|
|
color: colors.stat.max,
|
|
}),
|
|
price({
|
|
series: p.min,
|
|
name: "Min",
|
|
key: "price-min",
|
|
color: colors.stat.min,
|
|
}),
|
|
],
|
|
})),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
// Indicators
|
|
{
|
|
name: "Indicators",
|
|
tree: [
|
|
{
|
|
name: "Valuation",
|
|
tree: [
|
|
{
|
|
name: "NVT",
|
|
title: "NVT Ratio",
|
|
bottom: [
|
|
line({
|
|
series: indicators.nvt.ratio,
|
|
name: "NVT",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Thermocap Multiple",
|
|
title: "Thermocap Multiple",
|
|
bottom: [
|
|
line({
|
|
series: indicators.thermoCapMultiple.ratio,
|
|
name: "Thermocap",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Cycle",
|
|
tree: [
|
|
{
|
|
name: "Pi Cycle",
|
|
title: "Pi Cycle",
|
|
top: [
|
|
price({
|
|
series: ma.sma._111d,
|
|
name: "111d SMA",
|
|
color: colors.indicator.upper,
|
|
}),
|
|
price({
|
|
series: ma.sma._350d.x2,
|
|
name: "350d SMA x2",
|
|
color: colors.indicator.lower,
|
|
}),
|
|
],
|
|
bottom: [
|
|
baseline({
|
|
series: technical.piCycle.ratio,
|
|
name: "Pi Cycle",
|
|
unit: Unit.ratio,
|
|
base: 1,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Stock-to-Flow",
|
|
title: "Stock-to-Flow",
|
|
bottom: [
|
|
line({
|
|
series: indicators.stockToFlow,
|
|
name: "S2F",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Puell Multiple",
|
|
title: "Puell Multiple",
|
|
bottom: [
|
|
line({
|
|
series: indicators.puellMultiple.ratio,
|
|
name: "Puell",
|
|
color: colors.usd,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "RHODL Ratio",
|
|
title: "RHODL Ratio",
|
|
bottom: [
|
|
line({
|
|
series: indicators.rhodlRatio.ratio,
|
|
name: "RHODL",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Activity",
|
|
tree: [
|
|
{
|
|
name: "Dormancy",
|
|
title: "Dormancy",
|
|
bottom: [
|
|
line({
|
|
series: indicators.dormancy.supplyAdjusted,
|
|
name: "Supply Adjusted",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
line({
|
|
series: indicators.dormancy.flow,
|
|
name: "Flow",
|
|
color: colors.usd,
|
|
unit: Unit.ratio,
|
|
defaultActive: false,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "Seller Exhaustion",
|
|
title: "Seller Exhaustion Constant",
|
|
bottom: [
|
|
line({
|
|
series: indicators.sellerExhaustion,
|
|
name: "SEC",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "CDD Supply Adjusted",
|
|
title: "Coindays Destroyed (Supply Adjusted)",
|
|
bottom: [
|
|
line({
|
|
series: indicators.coindaysDestroyedSupplyAdjusted,
|
|
name: "CDD SA",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
{
|
|
name: "CYD Supply Adjusted",
|
|
title: "Coinyears Destroyed (Supply Adjusted)",
|
|
bottom: [
|
|
line({
|
|
series: indicators.coinyearsDestroyedSupplyAdjusted,
|
|
name: "CYD SA",
|
|
color: colors.bitcoin,
|
|
unit: Unit.ratio,
|
|
}),
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "Gini",
|
|
title: "Gini Coefficient",
|
|
bottom: percentRatio({
|
|
pattern: indicators.gini,
|
|
name: "Gini",
|
|
color: colors.loss,
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
}
|