mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -6,8 +6,9 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use brk_cohort::{
|
||||
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, EPOCH_NAMES, OVER_AMOUNT_NAMES, UNDER_AMOUNT_NAMES,
|
||||
UNDER_AGE_NAMES, OVER_AGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES, CLASS_NAMES,
|
||||
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, EPOCH_NAMES, LOSS_NAMES,
|
||||
OVER_AGE_NAMES, OVER_AMOUNT_NAMES, PROFITABILITY_RANGE_NAMES, PROFIT_NAMES,
|
||||
SPENDABLE_TYPE_NAMES, TERM_NAMES, UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
|
||||
};
|
||||
use brk_types::{Index, PoolSlug, pools};
|
||||
use serde::Serialize;
|
||||
@@ -63,6 +64,9 @@ impl CohortConstants {
|
||||
("AMOUNT_RANGE_NAMES", to_value(&AMOUNT_RANGE_NAMES)),
|
||||
("OVER_AMOUNT_NAMES", to_value(&OVER_AMOUNT_NAMES)),
|
||||
("UNDER_AMOUNT_NAMES", to_value(&UNDER_AMOUNT_NAMES)),
|
||||
("PROFITABILITY_RANGE_NAMES", to_value(&PROFITABILITY_RANGE_NAMES)),
|
||||
("PROFIT_NAMES", to_value(&PROFIT_NAMES)),
|
||||
("LOSS_NAMES", to_value(&LOSS_NAMES)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1871,6 +1871,28 @@ impl BpsCentsRatioSatsUsdPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BtcCentsDeltaSatsUsdPattern {
|
||||
pub btc: MetricPattern1<Bitcoin>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub sats: MetricPattern1<Sats>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl BtcCentsDeltaSatsUsdPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
btc: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
cents: MetricPattern1::new(client.clone(), _m(&acc, "cents")),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
sats: MetricPattern1::new(client.clone(), _m(&acc, "sats")),
|
||||
usd: MetricPattern1::new(client.clone(), _m(&acc, "usd")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BtcCentsRelSatsUsdPattern {
|
||||
pub btc: MetricPattern1<Bitcoin>,
|
||||
@@ -1981,6 +2003,28 @@ impl InvestedMaxMinPercentilesSupplyPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct MvrvNuplRealizedSupplyPattern {
|
||||
pub mvrv: MetricPattern1<StoredF32>,
|
||||
pub nupl: BpsRatioPattern,
|
||||
pub realized_cap: AllSthPattern,
|
||||
pub realized_price: BpsCentsRatioSatsUsdPattern,
|
||||
pub supply: AllSthPattern2,
|
||||
}
|
||||
|
||||
impl MvrvNuplRealizedSupplyPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
mvrv: MetricPattern1::new(client.clone(), _m(&acc, "mvrv")),
|
||||
nupl: BpsRatioPattern::new(client.clone(), _m(&acc, "nupl")),
|
||||
realized_cap: AllSthPattern::new(client.clone(), acc.clone()),
|
||||
realized_price: BpsCentsRatioSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")),
|
||||
supply: AllSthPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct PhsReboundThsPattern {
|
||||
pub phs: MetricPattern1<StoredF32>,
|
||||
@@ -2677,7 +2721,7 @@ impl GreedNetPainPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct LossNuplProfitPattern {
|
||||
pub loss: BaseCumulativeSumPattern3,
|
||||
pub loss: BaseCumulativeNegativeSumPattern,
|
||||
pub nupl: BpsRatioPattern,
|
||||
pub profit: BaseCumulativeSumPattern3,
|
||||
}
|
||||
@@ -2686,7 +2730,7 @@ impl LossNuplProfitPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
loss: BaseCumulativeSumPattern3::new(client.clone(), _m(&acc, "unrealized_loss")),
|
||||
loss: BaseCumulativeNegativeSumPattern::new(client.clone(), acc.clone()),
|
||||
nupl: BpsRatioPattern::new(client.clone(), _m(&acc, "nupl")),
|
||||
profit: BaseCumulativeSumPattern3::new(client.clone(), _m(&acc, "unrealized_profit")),
|
||||
}
|
||||
@@ -2815,6 +2859,38 @@ impl AbsoluteRatePattern2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllSthPattern2 {
|
||||
pub all: BtcCentsDeltaSatsUsdPattern,
|
||||
pub sth: BtcCentsSatsUsdPattern,
|
||||
}
|
||||
|
||||
impl AllSthPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
all: BtcCentsDeltaSatsUsdPattern::new(client.clone(), _m(&acc, "supply")),
|
||||
sth: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "sth_supply")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllSthPattern {
|
||||
pub all: MetricPattern1<Dollars>,
|
||||
pub sth: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl AllSthPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
all: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap")),
|
||||
sth: MetricPattern1::new(client.clone(), _m(&acc, "sth_realized_cap")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BlocksDominancePattern {
|
||||
pub blocks_mined: BaseCumulativeSumPattern2,
|
||||
@@ -2959,22 +3035,6 @@ impl PriceValuePattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RealizedSupplyPattern {
|
||||
pub realized_cap: MetricPattern1<Dollars>,
|
||||
pub supply: MetricPattern1<Sats>,
|
||||
}
|
||||
|
||||
impl RealizedSupplyPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
realized_cap: MetricPattern1::new(client.clone(), _m(&acc, "realized_cap")),
|
||||
supply: MetricPattern1::new(client.clone(), _m(&acc, "supply")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelPattern {
|
||||
pub rel_to_mcap: BpsPercentRatioPattern,
|
||||
@@ -4101,28 +4161,13 @@ impl MetricsTree_Scripts_Count {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Scripts_Value {
|
||||
pub op_return: MetricsTree_Scripts_Value_OpReturn,
|
||||
pub op_return: BaseCumulativeSumPattern4,
|
||||
}
|
||||
|
||||
impl MetricsTree_Scripts_Value {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
op_return: MetricsTree_Scripts_Value_OpReturn::new(client.clone(), format!("{base_path}_op_return")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Scripts_Value_OpReturn {
|
||||
pub base: BtcCentsSatsUsdPattern,
|
||||
pub cumulative: BtcCentsSatsUsdPattern,
|
||||
}
|
||||
|
||||
impl MetricsTree_Scripts_Value_OpReturn {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
base: BtcCentsSatsUsdPattern::new(client.clone(), "op_return_value".to_string()),
|
||||
cumulative: BtcCentsSatsUsdPattern::new(client.clone(), "op_return_value_cumulative".to_string()),
|
||||
op_return: BaseCumulativeSumPattern4::new(client.clone(), "op_return_value".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6448,14 +6493,12 @@ impl MetricsTree_Supply {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Supply_Burned {
|
||||
pub op_return: BaseCumulativeSumPattern4,
|
||||
pub unspendable: BaseCumulativeSumPattern4,
|
||||
}
|
||||
|
||||
impl MetricsTree_Supply_Burned {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
op_return: BaseCumulativeSumPattern4::new(client.clone(), "op_return_supply".to_string()),
|
||||
unspendable: BaseCumulativeSumPattern4::new(client.clone(), "unspendable_supply".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -7140,182 +7183,182 @@ impl MetricsTree_Cohorts_Utxo_Profitability {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cohorts_Utxo_Profitability_Range {
|
||||
pub over_1000pct_in_profit: RealizedSupplyPattern,
|
||||
pub _500pct_to_1000pct_in_profit: RealizedSupplyPattern,
|
||||
pub _300pct_to_500pct_in_profit: RealizedSupplyPattern,
|
||||
pub _200pct_to_300pct_in_profit: RealizedSupplyPattern,
|
||||
pub _100pct_to_200pct_in_profit: RealizedSupplyPattern,
|
||||
pub _90pct_to_100pct_in_profit: RealizedSupplyPattern,
|
||||
pub _80pct_to_90pct_in_profit: RealizedSupplyPattern,
|
||||
pub _70pct_to_80pct_in_profit: RealizedSupplyPattern,
|
||||
pub _60pct_to_70pct_in_profit: RealizedSupplyPattern,
|
||||
pub _50pct_to_60pct_in_profit: RealizedSupplyPattern,
|
||||
pub _40pct_to_50pct_in_profit: RealizedSupplyPattern,
|
||||
pub _30pct_to_40pct_in_profit: RealizedSupplyPattern,
|
||||
pub _20pct_to_30pct_in_profit: RealizedSupplyPattern,
|
||||
pub _10pct_to_20pct_in_profit: RealizedSupplyPattern,
|
||||
pub _0pct_to_10pct_in_profit: RealizedSupplyPattern,
|
||||
pub _0pct_to_10pct_in_loss: RealizedSupplyPattern,
|
||||
pub _10pct_to_20pct_in_loss: RealizedSupplyPattern,
|
||||
pub _20pct_to_30pct_in_loss: RealizedSupplyPattern,
|
||||
pub _30pct_to_40pct_in_loss: RealizedSupplyPattern,
|
||||
pub _40pct_to_50pct_in_loss: RealizedSupplyPattern,
|
||||
pub _50pct_to_60pct_in_loss: RealizedSupplyPattern,
|
||||
pub _60pct_to_70pct_in_loss: RealizedSupplyPattern,
|
||||
pub _70pct_to_80pct_in_loss: RealizedSupplyPattern,
|
||||
pub _80pct_to_90pct_in_loss: RealizedSupplyPattern,
|
||||
pub _90pct_to_100pct_in_loss: RealizedSupplyPattern,
|
||||
pub over_1000pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _500pct_to_1000pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _300pct_to_500pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _200pct_to_300pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _100pct_to_200pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _90pct_to_100pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _80pct_to_90pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _70pct_to_80pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _60pct_to_70pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _50pct_to_60pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _40pct_to_50pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _30pct_to_40pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _20pct_to_30pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _10pct_to_20pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _0pct_to_10pct_in_profit: MvrvNuplRealizedSupplyPattern,
|
||||
pub _0pct_to_10pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _10pct_to_20pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _20pct_to_30pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _30pct_to_40pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _40pct_to_50pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _50pct_to_60pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _60pct_to_70pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _70pct_to_80pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _80pct_to_90pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
pub _90pct_to_100pct_in_loss: MvrvNuplRealizedSupplyPattern,
|
||||
}
|
||||
|
||||
impl MetricsTree_Cohorts_Utxo_Profitability_Range {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
over_1000pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_over_1000pct_in_profit".to_string()),
|
||||
_500pct_to_1000pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_500pct_to_1000pct_in_profit".to_string()),
|
||||
_300pct_to_500pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_300pct_to_500pct_in_profit".to_string()),
|
||||
_200pct_to_300pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_200pct_to_300pct_in_profit".to_string()),
|
||||
_100pct_to_200pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_100pct_to_200pct_in_profit".to_string()),
|
||||
_90pct_to_100pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_90pct_to_100pct_in_profit".to_string()),
|
||||
_80pct_to_90pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_80pct_to_90pct_in_profit".to_string()),
|
||||
_70pct_to_80pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_70pct_to_80pct_in_profit".to_string()),
|
||||
_60pct_to_70pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_60pct_to_70pct_in_profit".to_string()),
|
||||
_50pct_to_60pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_50pct_to_60pct_in_profit".to_string()),
|
||||
_40pct_to_50pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_40pct_to_50pct_in_profit".to_string()),
|
||||
_30pct_to_40pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_30pct_to_40pct_in_profit".to_string()),
|
||||
_20pct_to_30pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_20pct_to_30pct_in_profit".to_string()),
|
||||
_10pct_to_20pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_10pct_to_20pct_in_profit".to_string()),
|
||||
_0pct_to_10pct_in_profit: RealizedSupplyPattern::new(client.clone(), "utxos_0pct_to_10pct_in_profit".to_string()),
|
||||
_0pct_to_10pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_0pct_to_10pct_in_loss".to_string()),
|
||||
_10pct_to_20pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_10pct_to_20pct_in_loss".to_string()),
|
||||
_20pct_to_30pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_20pct_to_30pct_in_loss".to_string()),
|
||||
_30pct_to_40pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_30pct_to_40pct_in_loss".to_string()),
|
||||
_40pct_to_50pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_40pct_to_50pct_in_loss".to_string()),
|
||||
_50pct_to_60pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_50pct_to_60pct_in_loss".to_string()),
|
||||
_60pct_to_70pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_60pct_to_70pct_in_loss".to_string()),
|
||||
_70pct_to_80pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_70pct_to_80pct_in_loss".to_string()),
|
||||
_80pct_to_90pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_80pct_to_90pct_in_loss".to_string()),
|
||||
_90pct_to_100pct_in_loss: RealizedSupplyPattern::new(client.clone(), "utxos_90pct_to_100pct_in_loss".to_string()),
|
||||
over_1000pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_1000pct_in_profit".to_string()),
|
||||
_500pct_to_1000pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_500pct_to_1000pct_in_profit".to_string()),
|
||||
_300pct_to_500pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_300pct_to_500pct_in_profit".to_string()),
|
||||
_200pct_to_300pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_200pct_to_300pct_in_profit".to_string()),
|
||||
_100pct_to_200pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_100pct_to_200pct_in_profit".to_string()),
|
||||
_90pct_to_100pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_90pct_to_100pct_in_profit".to_string()),
|
||||
_80pct_to_90pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_80pct_to_90pct_in_profit".to_string()),
|
||||
_70pct_to_80pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_70pct_to_80pct_in_profit".to_string()),
|
||||
_60pct_to_70pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_60pct_to_70pct_in_profit".to_string()),
|
||||
_50pct_to_60pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_50pct_to_60pct_in_profit".to_string()),
|
||||
_40pct_to_50pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_40pct_to_50pct_in_profit".to_string()),
|
||||
_30pct_to_40pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_30pct_to_40pct_in_profit".to_string()),
|
||||
_20pct_to_30pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_20pct_to_30pct_in_profit".to_string()),
|
||||
_10pct_to_20pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_10pct_to_20pct_in_profit".to_string()),
|
||||
_0pct_to_10pct_in_profit: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_0pct_to_10pct_in_profit".to_string()),
|
||||
_0pct_to_10pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_0pct_to_10pct_in_loss".to_string()),
|
||||
_10pct_to_20pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_10pct_to_20pct_in_loss".to_string()),
|
||||
_20pct_to_30pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_20pct_to_30pct_in_loss".to_string()),
|
||||
_30pct_to_40pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_30pct_to_40pct_in_loss".to_string()),
|
||||
_40pct_to_50pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_40pct_to_50pct_in_loss".to_string()),
|
||||
_50pct_to_60pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_50pct_to_60pct_in_loss".to_string()),
|
||||
_60pct_to_70pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_60pct_to_70pct_in_loss".to_string()),
|
||||
_70pct_to_80pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_70pct_to_80pct_in_loss".to_string()),
|
||||
_80pct_to_90pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_80pct_to_90pct_in_loss".to_string()),
|
||||
_90pct_to_100pct_in_loss: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_90pct_to_100pct_in_loss".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cohorts_Utxo_Profitability_Profit {
|
||||
pub breakeven: RealizedSupplyPattern,
|
||||
pub _10pct: RealizedSupplyPattern,
|
||||
pub _20pct: RealizedSupplyPattern,
|
||||
pub _30pct: RealizedSupplyPattern,
|
||||
pub _40pct: RealizedSupplyPattern,
|
||||
pub _50pct: RealizedSupplyPattern,
|
||||
pub _60pct: RealizedSupplyPattern,
|
||||
pub _70pct: RealizedSupplyPattern,
|
||||
pub _80pct: RealizedSupplyPattern,
|
||||
pub _90pct: RealizedSupplyPattern,
|
||||
pub _100pct: RealizedSupplyPattern,
|
||||
pub _200pct: RealizedSupplyPattern,
|
||||
pub _300pct: RealizedSupplyPattern,
|
||||
pub _500pct: RealizedSupplyPattern,
|
||||
pub breakeven: MvrvNuplRealizedSupplyPattern,
|
||||
pub _10pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _20pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _30pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _40pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _50pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _60pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _70pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _80pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _90pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _100pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _200pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _300pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _500pct: MvrvNuplRealizedSupplyPattern,
|
||||
}
|
||||
|
||||
impl MetricsTree_Cohorts_Utxo_Profitability_Profit {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
breakeven: RealizedSupplyPattern::new(client.clone(), "utxos_in_profit".to_string()),
|
||||
_10pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_10pct_in_profit".to_string()),
|
||||
_20pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_20pct_in_profit".to_string()),
|
||||
_30pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_30pct_in_profit".to_string()),
|
||||
_40pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_40pct_in_profit".to_string()),
|
||||
_50pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_50pct_in_profit".to_string()),
|
||||
_60pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_60pct_in_profit".to_string()),
|
||||
_70pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_70pct_in_profit".to_string()),
|
||||
_80pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_80pct_in_profit".to_string()),
|
||||
_90pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_90pct_in_profit".to_string()),
|
||||
_100pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_100pct_in_profit".to_string()),
|
||||
_200pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_200pct_in_profit".to_string()),
|
||||
_300pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_300pct_in_profit".to_string()),
|
||||
_500pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_500pct_in_profit".to_string()),
|
||||
breakeven: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_in_profit".to_string()),
|
||||
_10pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_10pct_in_profit".to_string()),
|
||||
_20pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_20pct_in_profit".to_string()),
|
||||
_30pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_30pct_in_profit".to_string()),
|
||||
_40pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_40pct_in_profit".to_string()),
|
||||
_50pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_50pct_in_profit".to_string()),
|
||||
_60pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_60pct_in_profit".to_string()),
|
||||
_70pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_70pct_in_profit".to_string()),
|
||||
_80pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_80pct_in_profit".to_string()),
|
||||
_90pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_90pct_in_profit".to_string()),
|
||||
_100pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_100pct_in_profit".to_string()),
|
||||
_200pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_200pct_in_profit".to_string()),
|
||||
_300pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_300pct_in_profit".to_string()),
|
||||
_500pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_500pct_in_profit".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cohorts_Utxo_Profitability_Loss {
|
||||
pub breakeven: RealizedSupplyPattern,
|
||||
pub _10pct: RealizedSupplyPattern,
|
||||
pub _20pct: RealizedSupplyPattern,
|
||||
pub _30pct: RealizedSupplyPattern,
|
||||
pub _40pct: RealizedSupplyPattern,
|
||||
pub _50pct: RealizedSupplyPattern,
|
||||
pub _60pct: RealizedSupplyPattern,
|
||||
pub _70pct: RealizedSupplyPattern,
|
||||
pub _80pct: RealizedSupplyPattern,
|
||||
pub breakeven: MvrvNuplRealizedSupplyPattern,
|
||||
pub _10pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _20pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _30pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _40pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _50pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _60pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _70pct: MvrvNuplRealizedSupplyPattern,
|
||||
pub _80pct: MvrvNuplRealizedSupplyPattern,
|
||||
}
|
||||
|
||||
impl MetricsTree_Cohorts_Utxo_Profitability_Loss {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
breakeven: RealizedSupplyPattern::new(client.clone(), "utxos_in_loss".to_string()),
|
||||
_10pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_10pct_in_loss".to_string()),
|
||||
_20pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_20pct_in_loss".to_string()),
|
||||
_30pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_30pct_in_loss".to_string()),
|
||||
_40pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_40pct_in_loss".to_string()),
|
||||
_50pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_50pct_in_loss".to_string()),
|
||||
_60pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_60pct_in_loss".to_string()),
|
||||
_70pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_70pct_in_loss".to_string()),
|
||||
_80pct: RealizedSupplyPattern::new(client.clone(), "utxos_over_80pct_in_loss".to_string()),
|
||||
breakeven: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_in_loss".to_string()),
|
||||
_10pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_10pct_in_loss".to_string()),
|
||||
_20pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_20pct_in_loss".to_string()),
|
||||
_30pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_30pct_in_loss".to_string()),
|
||||
_40pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_40pct_in_loss".to_string()),
|
||||
_50pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_50pct_in_loss".to_string()),
|
||||
_60pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_60pct_in_loss".to_string()),
|
||||
_70pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_70pct_in_loss".to_string()),
|
||||
_80pct: MvrvNuplRealizedSupplyPattern::new(client.clone(), "utxos_over_80pct_in_loss".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cohorts_Utxo_Matured {
|
||||
pub under_1h: BtcCentsSatsUsdPattern,
|
||||
pub _1h_to_1d: BtcCentsSatsUsdPattern,
|
||||
pub _1d_to_1w: BtcCentsSatsUsdPattern,
|
||||
pub _1w_to_1m: BtcCentsSatsUsdPattern,
|
||||
pub _1m_to_2m: BtcCentsSatsUsdPattern,
|
||||
pub _2m_to_3m: BtcCentsSatsUsdPattern,
|
||||
pub _3m_to_4m: BtcCentsSatsUsdPattern,
|
||||
pub _4m_to_5m: BtcCentsSatsUsdPattern,
|
||||
pub _5m_to_6m: BtcCentsSatsUsdPattern,
|
||||
pub _6m_to_1y: BtcCentsSatsUsdPattern,
|
||||
pub _1y_to_2y: BtcCentsSatsUsdPattern,
|
||||
pub _2y_to_3y: BtcCentsSatsUsdPattern,
|
||||
pub _3y_to_4y: BtcCentsSatsUsdPattern,
|
||||
pub _4y_to_5y: BtcCentsSatsUsdPattern,
|
||||
pub _5y_to_6y: BtcCentsSatsUsdPattern,
|
||||
pub _6y_to_7y: BtcCentsSatsUsdPattern,
|
||||
pub _7y_to_8y: BtcCentsSatsUsdPattern,
|
||||
pub _8y_to_10y: BtcCentsSatsUsdPattern,
|
||||
pub _10y_to_12y: BtcCentsSatsUsdPattern,
|
||||
pub _12y_to_15y: BtcCentsSatsUsdPattern,
|
||||
pub over_15y: BtcCentsSatsUsdPattern,
|
||||
pub under_1h: BaseCumulativeSumPattern4,
|
||||
pub _1h_to_1d: BaseCumulativeSumPattern4,
|
||||
pub _1d_to_1w: BaseCumulativeSumPattern4,
|
||||
pub _1w_to_1m: BaseCumulativeSumPattern4,
|
||||
pub _1m_to_2m: BaseCumulativeSumPattern4,
|
||||
pub _2m_to_3m: BaseCumulativeSumPattern4,
|
||||
pub _3m_to_4m: BaseCumulativeSumPattern4,
|
||||
pub _4m_to_5m: BaseCumulativeSumPattern4,
|
||||
pub _5m_to_6m: BaseCumulativeSumPattern4,
|
||||
pub _6m_to_1y: BaseCumulativeSumPattern4,
|
||||
pub _1y_to_2y: BaseCumulativeSumPattern4,
|
||||
pub _2y_to_3y: BaseCumulativeSumPattern4,
|
||||
pub _3y_to_4y: BaseCumulativeSumPattern4,
|
||||
pub _4y_to_5y: BaseCumulativeSumPattern4,
|
||||
pub _5y_to_6y: BaseCumulativeSumPattern4,
|
||||
pub _6y_to_7y: BaseCumulativeSumPattern4,
|
||||
pub _7y_to_8y: BaseCumulativeSumPattern4,
|
||||
pub _8y_to_10y: BaseCumulativeSumPattern4,
|
||||
pub _10y_to_12y: BaseCumulativeSumPattern4,
|
||||
pub _12y_to_15y: BaseCumulativeSumPattern4,
|
||||
pub over_15y: BaseCumulativeSumPattern4,
|
||||
}
|
||||
|
||||
impl MetricsTree_Cohorts_Utxo_Matured {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
under_1h: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_under_1h_old_matured".to_string()),
|
||||
_1h_to_1d: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_1h_to_1d_old_matured".to_string()),
|
||||
_1d_to_1w: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_1d_to_1w_old_matured".to_string()),
|
||||
_1w_to_1m: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_1w_to_1m_old_matured".to_string()),
|
||||
_1m_to_2m: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_1m_to_2m_old_matured".to_string()),
|
||||
_2m_to_3m: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_2m_to_3m_old_matured".to_string()),
|
||||
_3m_to_4m: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_3m_to_4m_old_matured".to_string()),
|
||||
_4m_to_5m: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_4m_to_5m_old_matured".to_string()),
|
||||
_5m_to_6m: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_5m_to_6m_old_matured".to_string()),
|
||||
_6m_to_1y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_6m_to_1y_old_matured".to_string()),
|
||||
_1y_to_2y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_1y_to_2y_old_matured".to_string()),
|
||||
_2y_to_3y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_2y_to_3y_old_matured".to_string()),
|
||||
_3y_to_4y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_3y_to_4y_old_matured".to_string()),
|
||||
_4y_to_5y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_4y_to_5y_old_matured".to_string()),
|
||||
_5y_to_6y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_5y_to_6y_old_matured".to_string()),
|
||||
_6y_to_7y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_6y_to_7y_old_matured".to_string()),
|
||||
_7y_to_8y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_7y_to_8y_old_matured".to_string()),
|
||||
_8y_to_10y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_8y_to_10y_old_matured".to_string()),
|
||||
_10y_to_12y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_10y_to_12y_old_matured".to_string()),
|
||||
_12y_to_15y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_12y_to_15y_old_matured".to_string()),
|
||||
over_15y: BtcCentsSatsUsdPattern::new(client.clone(), "utxo_over_15y_old_matured".to_string()),
|
||||
under_1h: BaseCumulativeSumPattern4::new(client.clone(), "utxos_under_1h_old_matured_supply".to_string()),
|
||||
_1h_to_1d: BaseCumulativeSumPattern4::new(client.clone(), "utxos_1h_to_1d_old_matured_supply".to_string()),
|
||||
_1d_to_1w: BaseCumulativeSumPattern4::new(client.clone(), "utxos_1d_to_1w_old_matured_supply".to_string()),
|
||||
_1w_to_1m: BaseCumulativeSumPattern4::new(client.clone(), "utxos_1w_to_1m_old_matured_supply".to_string()),
|
||||
_1m_to_2m: BaseCumulativeSumPattern4::new(client.clone(), "utxos_1m_to_2m_old_matured_supply".to_string()),
|
||||
_2m_to_3m: BaseCumulativeSumPattern4::new(client.clone(), "utxos_2m_to_3m_old_matured_supply".to_string()),
|
||||
_3m_to_4m: BaseCumulativeSumPattern4::new(client.clone(), "utxos_3m_to_4m_old_matured_supply".to_string()),
|
||||
_4m_to_5m: BaseCumulativeSumPattern4::new(client.clone(), "utxos_4m_to_5m_old_matured_supply".to_string()),
|
||||
_5m_to_6m: BaseCumulativeSumPattern4::new(client.clone(), "utxos_5m_to_6m_old_matured_supply".to_string()),
|
||||
_6m_to_1y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_6m_to_1y_old_matured_supply".to_string()),
|
||||
_1y_to_2y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_1y_to_2y_old_matured_supply".to_string()),
|
||||
_2y_to_3y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_2y_to_3y_old_matured_supply".to_string()),
|
||||
_3y_to_4y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_3y_to_4y_old_matured_supply".to_string()),
|
||||
_4y_to_5y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_4y_to_5y_old_matured_supply".to_string()),
|
||||
_5y_to_6y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_5y_to_6y_old_matured_supply".to_string()),
|
||||
_6y_to_7y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_6y_to_7y_old_matured_supply".to_string()),
|
||||
_7y_to_8y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_7y_to_8y_old_matured_supply".to_string()),
|
||||
_8y_to_10y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_8y_to_10y_old_matured_supply".to_string()),
|
||||
_10y_to_12y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_10y_to_12y_old_matured_supply".to_string()),
|
||||
_12y_to_15y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_12y_to_15y_old_matured_supply".to_string()),
|
||||
over_15y: BaseCumulativeSumPattern4::new(client.clone(), "utxos_over_15y_old_matured_supply".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::thread;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::Indexes;
|
||||
@@ -15,22 +17,40 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Sequential: time → lookback (dependency chain)
|
||||
self.time
|
||||
.timestamp
|
||||
.compute(indexer, indexes, starting_indexes, exit)?;
|
||||
self.lookback
|
||||
.compute(&self.time, starting_indexes, exit)?;
|
||||
self.count
|
||||
.compute(indexer, starting_indexes, exit)?;
|
||||
self.interval
|
||||
.compute(indexer, starting_indexes, exit)?;
|
||||
self.size
|
||||
.compute(indexer, &self.lookback, starting_indexes, exit)?;
|
||||
self.weight
|
||||
.compute(indexer, starting_indexes, exit)?;
|
||||
self.difficulty
|
||||
.compute(indexer, indexes, starting_indexes, exit)?;
|
||||
self.halving.compute(indexes, starting_indexes, exit)?;
|
||||
|
||||
// Parallel: remaining sub-modules are independent of each other.
|
||||
// size depends on lookback (already computed above).
|
||||
let Vecs {
|
||||
lookback,
|
||||
count,
|
||||
interval,
|
||||
size,
|
||||
weight,
|
||||
difficulty,
|
||||
halving,
|
||||
..
|
||||
} = self;
|
||||
thread::scope(|s| -> Result<()> {
|
||||
let r1 = s.spawn(|| count.compute(indexer, starting_indexes, exit));
|
||||
let r2 = s.spawn(|| interval.compute(indexer, starting_indexes, exit));
|
||||
let r3 = s.spawn(|| weight.compute(indexer, starting_indexes, exit));
|
||||
let r4 =
|
||||
s.spawn(|| difficulty.compute(indexer, indexes, starting_indexes, exit));
|
||||
let r5 = s.spawn(|| halving.compute(indexes, starting_indexes, exit));
|
||||
size.compute(indexer, &*lookback, starting_indexes, exit)?;
|
||||
r1.join().unwrap()?;
|
||||
r2.join().unwrap()?;
|
||||
r3.join().unwrap()?;
|
||||
r4.join().unwrap()?;
|
||||
r5.join().unwrap()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _lock = exit.lock();
|
||||
self.db.compact()?;
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(super) struct CostBasisNode {
|
||||
}
|
||||
|
||||
impl CostBasisNode {
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn new(sats: i64, usd: i128, is_sth: bool) -> Self {
|
||||
Self {
|
||||
all_sats: sats,
|
||||
@@ -237,7 +237,7 @@ impl CostBasisFenwick {
|
||||
|
||||
let mut sat_buckets = [0usize; PERCENTILES_LEN + 2];
|
||||
self.tree
|
||||
.batch_kth(&sat_targets, &sat_field, &mut sat_buckets);
|
||||
.kth(&sat_targets, &sat_field, &mut sat_buckets);
|
||||
|
||||
result.min_price = bucket_to_cents(sat_buckets[0]);
|
||||
(0..PERCENTILES_LEN).for_each(|i| {
|
||||
@@ -254,7 +254,7 @@ impl CostBasisFenwick {
|
||||
|
||||
let mut usd_buckets = [0usize; PERCENTILES_LEN];
|
||||
self.tree
|
||||
.batch_kth(&usd_targets, &usd_field, &mut usd_buckets);
|
||||
.kth(&usd_targets, &usd_field, &mut usd_buckets);
|
||||
|
||||
(0..PERCENTILES_LEN).for_each(|i| {
|
||||
result.usd_prices[i] = bucket_to_cents(usd_buckets[i]);
|
||||
@@ -310,16 +310,16 @@ impl CostBasisFenwick {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Profitability queries (all cohort only)
|
||||
// Profitability queries
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Compute profitability range buckets from current spot price.
|
||||
/// Returns 25 ranges: (sats, usd_raw) per range.
|
||||
/// Returns 25 ranges with all/sth splits.
|
||||
pub(super) fn profitability(
|
||||
&self,
|
||||
spot_price: Cents,
|
||||
) -> [(u64, u128); PROFITABILITY_RANGE_COUNT] {
|
||||
let mut result = [(0u64, 0u128); PROFITABILITY_RANGE_COUNT];
|
||||
) -> [ProfitabilityRangeResult; PROFITABILITY_RANGE_COUNT] {
|
||||
let mut result = [ProfitabilityRangeResult::ZERO; PROFITABILITY_RANGE_COUNT];
|
||||
|
||||
if self.totals.all_sats <= 0 {
|
||||
return result;
|
||||
@@ -327,34 +327,54 @@ impl CostBasisFenwick {
|
||||
|
||||
let boundaries = compute_profitability_boundaries(spot_price);
|
||||
|
||||
let mut prev_sats: i64 = 0;
|
||||
let mut prev_usd: i128 = 0;
|
||||
let mut prev = CostBasisNode::default();
|
||||
|
||||
for (i, &boundary) in boundaries.iter().enumerate() {
|
||||
let boundary_bucket = cents_to_bucket(boundary);
|
||||
// prefix_sum through the bucket BEFORE the boundary
|
||||
let cum = if boundary_bucket > 0 {
|
||||
self.tree.prefix_sum(boundary_bucket - 1)
|
||||
} else {
|
||||
CostBasisNode::default()
|
||||
};
|
||||
let range_sats = cum.all_sats - prev_sats;
|
||||
let range_usd = cum.all_usd - prev_usd;
|
||||
result[i] = (range_sats.max(0) as u64, range_usd.max(0) as u128);
|
||||
prev_sats = cum.all_sats;
|
||||
prev_usd = cum.all_usd;
|
||||
result[i] = ProfitabilityRangeResult {
|
||||
all_sats: (cum.all_sats - prev.all_sats).max(0) as u64,
|
||||
all_usd: (cum.all_usd - prev.all_usd).max(0) as u128,
|
||||
sth_sats: (cum.sth_sats - prev.sth_sats).max(0) as u64,
|
||||
sth_usd: (cum.sth_usd - prev.sth_usd).max(0) as u128,
|
||||
};
|
||||
prev = cum;
|
||||
}
|
||||
|
||||
// Last range: everything >= last boundary
|
||||
let remaining_sats = self.totals.all_sats - prev_sats;
|
||||
let remaining_usd = self.totals.all_usd - prev_usd;
|
||||
result[PROFITABILITY_RANGE_COUNT - 1] =
|
||||
(remaining_sats.max(0) as u64, remaining_usd.max(0) as u128);
|
||||
result[PROFITABILITY_RANGE_COUNT - 1] = ProfitabilityRangeResult {
|
||||
all_sats: (self.totals.all_sats - prev.all_sats).max(0) as u64,
|
||||
all_usd: (self.totals.all_usd - prev.all_usd).max(0) as u128,
|
||||
sth_sats: (self.totals.sth_sats - prev.sth_sats).max(0) as u64,
|
||||
sth_usd: (self.totals.sth_usd - prev.sth_usd).max(0) as u128,
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-range profitability result with all/sth split.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct ProfitabilityRangeResult {
|
||||
pub all_sats: u64,
|
||||
pub all_usd: u128,
|
||||
pub sth_sats: u64,
|
||||
pub sth_usd: u128,
|
||||
}
|
||||
|
||||
impl ProfitabilityRangeResult {
|
||||
const ZERO: Self = Self {
|
||||
all_sats: 0,
|
||||
all_usd: 0,
|
||||
sth_sats: 0,
|
||||
sth_usd: 0,
|
||||
};
|
||||
}
|
||||
|
||||
/// Result of a percentile computation for one cohort.
|
||||
#[derive(Default)]
|
||||
pub(super) struct PercentileResult {
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::{
|
||||
state::UTXOCohortState,
|
||||
},
|
||||
indexes,
|
||||
internal::{AmountPerBlock, CachedWindowStarts},
|
||||
internal::{AmountPerBlockCumulativeWithSums, CachedWindowStarts},
|
||||
prices,
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ pub struct UTXOCohorts<M: StorageMode = Rw> {
|
||||
#[traversable(rename = "type")]
|
||||
pub type_: SpendableType<UTXOCohortVecs<TypeCohortMetrics<M>>>,
|
||||
pub profitability: ProfitabilityMetrics<M>,
|
||||
pub matured: AgeRange<AmountPerBlock<M>>,
|
||||
pub matured: AgeRange<AmountPerBlockCumulativeWithSums<M>>,
|
||||
#[traversable(skip)]
|
||||
pub(super) fenwick: CostBasisFenwick,
|
||||
/// Cached partition_point positions for tick_tock boundary searches.
|
||||
@@ -178,7 +178,7 @@ impl UTXOCohorts<Rw> {
|
||||
);
|
||||
|
||||
// Phase 3b: Import profitability metrics (derived from "all" during k-way merge).
|
||||
let profitability = ProfitabilityMetrics::forced_import(db, v, indexes)?;
|
||||
let profitability = ProfitabilityMetrics::forced_import(db, v, indexes, cached_starts)?;
|
||||
|
||||
// Phase 4: Import aggregate cohorts.
|
||||
|
||||
@@ -256,10 +256,17 @@ impl UTXOCohorts<Rw> {
|
||||
let under_amount = UnderAmount::try_new(&minimal_no_state)?;
|
||||
let over_amount = OverAmount::try_new(&minimal_no_state)?;
|
||||
|
||||
let prefix = CohortContext::Utxo.prefix();
|
||||
let matured = AgeRange::try_new(&|_f: Filter,
|
||||
name: &'static str|
|
||||
-> Result<AmountPerBlock> {
|
||||
AmountPerBlock::forced_import(db, &format!("utxo_{name}_matured"), v, indexes)
|
||||
-> Result<AmountPerBlockCumulativeWithSums> {
|
||||
AmountPerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
&format!("{prefix}_{name}_matured_supply"),
|
||||
v,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
@@ -338,7 +345,7 @@ impl UTXOCohorts<Rw> {
|
||||
matured: &AgeRange<Sats>,
|
||||
) -> Result<()> {
|
||||
for (v, &sats) in self.matured.iter_mut().zip(matured.iter()) {
|
||||
v.sats.height.truncate_push(height, sats)?;
|
||||
v.base.sats.height.truncate_push(height, sats)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -509,10 +516,13 @@ impl UTXOCohorts<Rw> {
|
||||
.try_for_each(|v| v.compute_rest_part1(prices, starting_indexes, exit))?;
|
||||
}
|
||||
|
||||
// Compute matured cents from sats × price
|
||||
// Compute matured cumulative + cents from sats × price
|
||||
self.matured
|
||||
.par_iter_mut()
|
||||
.try_for_each(|v| v.compute(prices, starting_indexes.height, exit))?;
|
||||
.try_for_each(|v| v.compute_rest(starting_indexes.height, prices, exit))?;
|
||||
|
||||
// Compute profitability supply cents and realized price
|
||||
self.profitability.compute(prices, starting_indexes, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -709,8 +719,10 @@ impl UTXOCohorts<Rw> {
|
||||
}
|
||||
vecs.extend(self.profitability.collect_all_vecs_mut());
|
||||
for v in self.matured.iter_mut() {
|
||||
vecs.push(&mut v.sats.height);
|
||||
vecs.push(&mut v.cents.height);
|
||||
vecs.push(&mut v.base.sats.height);
|
||||
vecs.push(&mut v.base.cents.height);
|
||||
vecs.push(&mut v.cumulative.sats.height);
|
||||
vecs.push(&mut v.cumulative.cents.height);
|
||||
}
|
||||
vecs.into_par_iter()
|
||||
}
|
||||
@@ -727,7 +739,7 @@ impl UTXOCohorts<Rw> {
|
||||
.chain(
|
||||
self.matured
|
||||
.iter()
|
||||
.map(|v| Height::from(v.min_stateful_len())),
|
||||
.map(|v| Height::from(v.base.min_stateful_len())),
|
||||
)
|
||||
.min()
|
||||
.unwrap_or_default()
|
||||
|
||||
@@ -6,7 +6,7 @@ use brk_types::{BasisPoints16, Cents, CentsCompact, CostBasisDistribution, Date,
|
||||
|
||||
use crate::distribution::metrics::{CostBasis, ProfitabilityMetrics};
|
||||
|
||||
use super::fenwick::PercentileResult;
|
||||
use super::fenwick::{PercentileResult, ProfitabilityRangeResult};
|
||||
use super::groups::UTXOCohorts;
|
||||
|
||||
use super::COST_BASIS_PRICE_DIGITS;
|
||||
@@ -104,7 +104,7 @@ fn push_cost_basis(
|
||||
}
|
||||
|
||||
/// Convert raw (cents × sats) accumulator to Dollars (÷ 100 for cents→dollars, ÷ 1e8 for sats).
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
fn raw_usd_to_dollars(raw: u128) -> Dollars {
|
||||
Dollars::from(raw as f64 / 1e10)
|
||||
}
|
||||
@@ -112,25 +112,41 @@ fn raw_usd_to_dollars(raw: u128) -> Dollars {
|
||||
/// Push profitability range + profit/loss aggregate values to vecs.
|
||||
fn push_profitability(
|
||||
height: Height,
|
||||
buckets: &[(u64, u128); PROFITABILITY_RANGE_COUNT],
|
||||
buckets: &[ProfitabilityRangeResult; PROFITABILITY_RANGE_COUNT],
|
||||
metrics: &mut ProfitabilityMetrics,
|
||||
) -> Result<()> {
|
||||
// Truncate all buckets once upfront to avoid per-push checks
|
||||
metrics.truncate(height)?;
|
||||
|
||||
// Push 25 range buckets
|
||||
for (i, bucket) in metrics.range.as_array_mut().into_iter().enumerate() {
|
||||
let (sats, usd_raw) = buckets[i];
|
||||
bucket.truncate_push(height, Sats::from(sats), raw_usd_to_dollars(usd_raw))?;
|
||||
let r = &buckets[i];
|
||||
bucket.push(
|
||||
Sats::from(r.all_sats),
|
||||
Sats::from(r.sth_sats),
|
||||
raw_usd_to_dollars(r.all_usd),
|
||||
raw_usd_to_dollars(r.sth_usd),
|
||||
);
|
||||
}
|
||||
|
||||
// Profit: forward cumulative sum over ranges[0..15], pushed in reverse.
|
||||
// profit[0] (breakeven) = sum(0..=13), ..., profit[13] (_500pct) = ranges[0]
|
||||
let profit_arr = metrics.profit.as_array_mut();
|
||||
let mut cum_sats = 0u64;
|
||||
let mut cum_sth_sats = 0u64;
|
||||
let mut cum_usd = 0u128;
|
||||
let mut cum_sth_usd = 0u128;
|
||||
for i in 0..PROFIT_COUNT {
|
||||
cum_sats += buckets[i].0;
|
||||
cum_usd += buckets[i].1;
|
||||
profit_arr[PROFIT_COUNT - 1 - i]
|
||||
.truncate_push(height, Sats::from(cum_sats), raw_usd_to_dollars(cum_usd))?;
|
||||
cum_sats += buckets[i].all_sats;
|
||||
cum_sth_sats += buckets[i].sth_sats;
|
||||
cum_usd += buckets[i].all_usd;
|
||||
cum_sth_usd += buckets[i].sth_usd;
|
||||
profit_arr[PROFIT_COUNT - 1 - i].push(
|
||||
Sats::from(cum_sats),
|
||||
Sats::from(cum_sth_sats),
|
||||
raw_usd_to_dollars(cum_usd),
|
||||
raw_usd_to_dollars(cum_sth_usd),
|
||||
);
|
||||
}
|
||||
|
||||
// Loss: backward cumulative sum over ranges[15..25], pushed in reverse.
|
||||
@@ -138,12 +154,21 @@ fn push_profitability(
|
||||
let loss_arr = metrics.loss.as_array_mut();
|
||||
let loss_count = loss_arr.len();
|
||||
cum_sats = 0;
|
||||
cum_sth_sats = 0;
|
||||
cum_usd = 0;
|
||||
cum_sth_usd = 0;
|
||||
for i in 0..loss_count {
|
||||
cum_sats += buckets[PROFITABILITY_RANGE_COUNT - 1 - i].0;
|
||||
cum_usd += buckets[PROFITABILITY_RANGE_COUNT - 1 - i].1;
|
||||
loss_arr[loss_count - 1 - i]
|
||||
.truncate_push(height, Sats::from(cum_sats), raw_usd_to_dollars(cum_usd))?;
|
||||
let r = &buckets[PROFITABILITY_RANGE_COUNT - 1 - i];
|
||||
cum_sats += r.all_sats;
|
||||
cum_sth_sats += r.sth_sats;
|
||||
cum_usd += r.all_usd;
|
||||
cum_sth_usd += r.sth_usd;
|
||||
loss_arr[loss_count - 1 - i].push(
|
||||
Sats::from(cum_sats),
|
||||
Sats::from(cum_sth_sats),
|
||||
raw_usd_to_dollars(cum_usd),
|
||||
raw_usd_to_dollars(cum_sth_usd),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -16,8 +16,8 @@ impl UTXOCohorts<Rw> {
|
||||
/// Since timestamps are monotonic, positions only advance forward.
|
||||
/// Complexity: O(k * c) where k = 20 boundaries, c = ~1 (forward scan steps).
|
||||
///
|
||||
/// Returns how many sats matured INTO each cohort from the younger adjacent one.
|
||||
/// `under_1h` is always zero since nothing ages into the youngest cohort.
|
||||
/// Returns how many sats matured OUT OF each cohort into the older adjacent one.
|
||||
/// `over_15y` is always zero since nothing ages out of the oldest cohort.
|
||||
pub(crate) fn tick_tock_next_block(
|
||||
&mut self,
|
||||
chain_state: &[BlockState],
|
||||
@@ -92,7 +92,7 @@ impl UTXOCohorts<Rw> {
|
||||
if let Some(state) = age_cohorts[boundary_idx + 1].as_mut() {
|
||||
state.increment_snapshot(&snapshot);
|
||||
}
|
||||
matured[boundary_idx + 1] += block_state.supply.value;
|
||||
matured[boundary_idx] += block_state.supply.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,43 @@
|
||||
use brk_cohort::{Loss, Profit, ProfitabilityRange};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, Sats, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Rw, StorageMode, WritableVec};
|
||||
use brk_types::{
|
||||
BasisPoints32, BasisPointsSigned32, Cents, Dollars, Height, Indexes, Sats, StoredF32, Version,
|
||||
};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{indexes, internal::PerBlock};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
AmountPerBlock, AmountPerBlockWithDeltas, CachedWindowStarts, Identity, LazyPerBlock,
|
||||
PerBlock, PriceWithRatioPerBlock, RatioPerBlock,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct WithSth<All, Sth = All> {
|
||||
pub all: All,
|
||||
pub sth: Sth,
|
||||
}
|
||||
|
||||
/// Supply + realized cap for a single profitability bucket.
|
||||
#[derive(Traversable)]
|
||||
pub struct ProfitabilityBucket<M: StorageMode = Rw> {
|
||||
pub supply: PerBlock<Sats, M>,
|
||||
pub realized_cap: PerBlock<Dollars, M>,
|
||||
pub supply: WithSth<AmountPerBlockWithDeltas<M>, AmountPerBlock<M>>,
|
||||
pub realized_cap: WithSth<PerBlock<Dollars, M>>,
|
||||
pub realized_price: PriceWithRatioPerBlock<M>,
|
||||
pub mvrv: LazyPerBlock<StoredF32>,
|
||||
pub nupl: RatioPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
impl<M: StorageMode> ProfitabilityBucket<M> {
|
||||
fn min_len(&self) -> usize {
|
||||
self.supply.height.len().min(self.realized_cap.height.len())
|
||||
self.supply
|
||||
.all
|
||||
.sats
|
||||
.height
|
||||
.len()
|
||||
.min(self.realized_cap.all.height.len())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,45 +47,154 @@ impl ProfitabilityBucket {
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let realized_price = PriceWithRatioPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_realized_price"),
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
let mvrv = LazyPerBlock::from_lazy::<Identity<StoredF32>, BasisPoints32>(
|
||||
&format!("{name}_mvrv"),
|
||||
version,
|
||||
&realized_price.ratio,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
supply: PerBlock::forced_import(
|
||||
supply: WithSth {
|
||||
all: AmountPerBlockWithDeltas::forced_import(
|
||||
db,
|
||||
&format!("{name}_supply"),
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
sth: AmountPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_sth_supply"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
},
|
||||
realized_cap: WithSth {
|
||||
all: PerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_realized_cap"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
sth: PerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_sth_realized_cap"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
},
|
||||
realized_price,
|
||||
mvrv,
|
||||
nupl: RatioPerBlock::forced_import_raw(
|
||||
db,
|
||||
&format!("{name}_supply"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
realized_cap: PerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_realized_cap"),
|
||||
version,
|
||||
&format!("{name}_nupl"),
|
||||
version + Version::ONE,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
#[inline(always)]
|
||||
pub(crate) fn truncate(&mut self, height: Height) -> Result<()> {
|
||||
self.supply.all.sats.height.truncate_if_needed(height)?;
|
||||
self.supply.sth.sats.height.truncate_if_needed(height)?;
|
||||
self.realized_cap.all.height.truncate_if_needed(height)?;
|
||||
self.realized_cap.sth.height.truncate_if_needed(height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
supply: Sats,
|
||||
sth_supply: Sats,
|
||||
realized_cap: Dollars,
|
||||
sth_realized_cap: Dollars,
|
||||
) {
|
||||
self.supply.all.sats.height.push(supply);
|
||||
self.supply.sth.sats.height.push(sth_supply);
|
||||
self.realized_cap.all.height.push(realized_cap);
|
||||
self.realized_cap.sth.height.push(sth_realized_cap);
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply.height.truncate_push(height, supply)?;
|
||||
self.realized_cap
|
||||
.height
|
||||
.truncate_push(height, realized_cap)?;
|
||||
let max_from = starting_indexes.height;
|
||||
|
||||
self.supply.all.compute(prices, max_from, exit)?;
|
||||
self.supply.sth.compute(prices, max_from, exit)?;
|
||||
|
||||
// Realized price cents = realized_cap_cents × ONE_BTC / supply_sats
|
||||
self.realized_price.cents.height.compute_transform2(
|
||||
max_from,
|
||||
&self.realized_cap.all.height,
|
||||
&self.supply.all.sats.height,
|
||||
|(i, cap_dollars, supply_sats, ..)| {
|
||||
let cap_cents = Cents::from(cap_dollars).as_u128();
|
||||
let supply = supply_sats.as_u128();
|
||||
if supply == 0 {
|
||||
(i, Cents::ZERO)
|
||||
} else {
|
||||
(i, Cents::from(cap_cents * Sats::ONE_BTC_U128 / supply))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Ratio (spot / realized_price) → feeds MVRV lazily
|
||||
self.realized_price
|
||||
.compute_ratio(starting_indexes, &prices.spot.cents.height, exit)?;
|
||||
|
||||
// NUPL = (spot - realized_price) / spot
|
||||
self.nupl.bps.height.compute_transform2(
|
||||
max_from,
|
||||
&prices.spot.cents.height,
|
||||
&self.realized_price.cents.height,
|
||||
|(i, spot, realized, ..)| {
|
||||
let p = spot.as_u128();
|
||||
if p == 0 {
|
||||
(i, BasisPointsSigned32::ZERO)
|
||||
} else {
|
||||
let rp = realized.as_u128();
|
||||
let bps = ((p as i128 - rp as i128) * 10000) / p as i128;
|
||||
(i, BasisPointsSigned32::from(bps as i32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.supply.height as &mut dyn AnyStoredVec,
|
||||
&mut self.realized_cap.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply.all.inner.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply.all.inner.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply.sth.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply.sth.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.realized_cap.all.height as &mut dyn AnyStoredVec,
|
||||
&mut self.realized_cap.sth.height as &mut dyn AnyStoredVec,
|
||||
&mut self.realized_price.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.realized_price.bps.height as &mut dyn AnyStoredVec,
|
||||
&mut self.nupl.bps.height as &mut dyn AnyStoredVec,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// All profitability metrics: 25 ranges + 15 profit thresholds + 10 loss thresholds.
|
||||
/// All profitability metrics: 25 ranges + 14 profit thresholds + 9 loss thresholds.
|
||||
#[derive(Traversable)]
|
||||
pub struct ProfitabilityMetrics<M: StorageMode = Rw> {
|
||||
pub range: ProfitabilityRange<ProfitabilityBucket<M>>,
|
||||
@@ -72,32 +203,46 @@ pub struct ProfitabilityMetrics<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
impl<M: StorageMode> ProfitabilityMetrics<M> {
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.range.iter()
|
||||
pub fn iter(&self) -> impl Iterator<Item = &ProfitabilityBucket<M>> {
|
||||
self.range
|
||||
.iter()
|
||||
.chain(self.profit.iter())
|
||||
.chain(self.loss.iter())
|
||||
.map(|b| b.min_len())
|
||||
.min()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ProfitabilityBucket<M>> {
|
||||
self.range
|
||||
.iter_mut()
|
||||
.chain(self.profit.iter_mut())
|
||||
.chain(self.loss.iter_mut())
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.iter().map(|b| b.min_len()).min().unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ProfitabilityMetrics {
|
||||
pub(crate) fn truncate(&mut self, height: Height) -> Result<()> {
|
||||
self.iter_mut().try_for_each(|b| b.truncate(height))
|
||||
}
|
||||
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let range = ProfitabilityRange::try_new(|name| {
|
||||
ProfitabilityBucket::forced_import(db, name, version, indexes)
|
||||
ProfitabilityBucket::forced_import(db, name, version, indexes, cached_starts)
|
||||
})?;
|
||||
|
||||
let profit = Profit::try_new(|name| {
|
||||
ProfitabilityBucket::forced_import(db, name, version, indexes)
|
||||
ProfitabilityBucket::forced_import(db, name, version, indexes, cached_starts)
|
||||
})?;
|
||||
|
||||
let loss = Loss::try_new(|name| {
|
||||
ProfitabilityBucket::forced_import(db, name, version, indexes)
|
||||
ProfitabilityBucket::forced_import(db, name, version, indexes, cached_starts)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
@@ -107,18 +252,21 @@ impl ProfitabilityMetrics {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.iter_mut()
|
||||
.try_for_each(|b| b.compute(prices, starting_indexes, exit))
|
||||
}
|
||||
|
||||
pub(crate) fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = Vec::new();
|
||||
for bucket in self.range.iter_mut() {
|
||||
vecs.extend(bucket.collect_all_vecs_mut());
|
||||
}
|
||||
for bucket in self.profit.iter_mut() {
|
||||
vecs.extend(bucket.collect_all_vecs_mut());
|
||||
}
|
||||
for bucket in self.loss.iter_mut() {
|
||||
for bucket in self.iter_mut() {
|
||||
vecs.extend(bucket.collect_all_vecs_mut());
|
||||
}
|
||||
vecs
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use brk_types::{Cents, Dollars, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableCloneableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
distribution::{metrics::ImportConfig, state::UnrealizedState},
|
||||
internal::FiatPerBlockCumulativeWithSums,
|
||||
internal::{FiatPerBlockCumulativeWithSums, LazyPerBlock, NegCentsUnsignedToDollars},
|
||||
};
|
||||
|
||||
use super::UnrealizedMinimal;
|
||||
@@ -19,16 +19,28 @@ pub struct UnrealizedBasic<M: StorageMode = Rw> {
|
||||
pub minimal: UnrealizedMinimal<M>,
|
||||
pub profit: FiatPerBlockCumulativeWithSums<Cents, M>,
|
||||
pub loss: FiatPerBlockCumulativeWithSums<Cents, M>,
|
||||
#[traversable(wrap = "loss", rename = "negative")]
|
||||
pub neg_loss: LazyPerBlock<Dollars, Cents>,
|
||||
}
|
||||
|
||||
impl UnrealizedBasic {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v1 = Version::ONE;
|
||||
|
||||
let loss: FiatPerBlockCumulativeWithSums<Cents> = cfg.import("unrealized_loss", v1)?;
|
||||
|
||||
let neg_loss = LazyPerBlock::from_computed::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version,
|
||||
loss.base.cents.height.read_only_boxed_clone(),
|
||||
&loss.base.cents,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
minimal: UnrealizedMinimal::forced_import(cfg)?,
|
||||
profit: cfg.import("unrealized_profit", v1)?,
|
||||
loss: cfg.import("unrealized_loss", v1)?,
|
||||
loss,
|
||||
neg_loss,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,16 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, CentsSigned, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::{
|
||||
metrics::ImportConfig,
|
||||
state::UnrealizedState,
|
||||
},
|
||||
internal::{CentsSubtractToCentsSigned, FiatPerBlock, LazyPerBlock, NegCentsUnsignedToDollars},
|
||||
internal::{CentsSubtractToCentsSigned, FiatPerBlock},
|
||||
};
|
||||
|
||||
use brk_types::Dollars;
|
||||
|
||||
use super::UnrealizedBasic;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
@@ -23,27 +21,16 @@ pub struct UnrealizedCore<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub basic: UnrealizedBasic<M>,
|
||||
|
||||
#[traversable(wrap = "loss", rename = "negative")]
|
||||
pub neg_loss: LazyPerBlock<Dollars, Cents>,
|
||||
pub net_pnl: FiatPerBlock<CentsSigned, M>,
|
||||
}
|
||||
|
||||
impl UnrealizedCore {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let basic = UnrealizedBasic::forced_import(cfg)?;
|
||||
|
||||
let neg_unrealized_loss = LazyPerBlock::from_computed::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version,
|
||||
basic.loss.base.cents.height.read_only_boxed_clone(),
|
||||
&basic.loss.base.cents,
|
||||
);
|
||||
|
||||
let net_unrealized_pnl = cfg.import("net_unrealized_pnl", Version::ZERO)?;
|
||||
|
||||
Ok(Self {
|
||||
basic,
|
||||
neg_loss: neg_unrealized_loss,
|
||||
net_pnl: net_unrealized_pnl,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_types::{Bitcoin, Dollars, Indexes, Sats, StoredF32};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
use brk_types::{Bitcoin, Dollars, Indexes, StoredF32};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::{gini, Vecs};
|
||||
use crate::{distribution, internal::RatioDollarsBp32, market, mining, transactions};
|
||||
@@ -180,14 +180,12 @@ impl Vecs {
|
||||
// Seller Exhaustion Constant: % supply_in_profit × 30d_volatility
|
||||
self.seller_exhaustion_constant
|
||||
.height
|
||||
.compute_transform2(
|
||||
.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&all_metrics.supply.in_profit.sats.height,
|
||||
&market.volatility._1m.height,
|
||||
|(i, profit_sats, volatility, ..)| {
|
||||
let total_sats: Sats = supply_total_sats
|
||||
.collect_one(i)
|
||||
.unwrap_or_default();
|
||||
supply_total_sats,
|
||||
|(i, profit_sats, volatility, total_sats, ..)| {
|
||||
let total = total_sats.as_u128() as f64;
|
||||
if total == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Indexes, Sats, TxInIndex, TxIndex, TxOutIndex, Vout};
|
||||
use brk_types::{Indexes, Sats, TxIndex, TxOutIndex, Vout};
|
||||
use tracing::info;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, ReadableVec, VecIndex, WritableVec};
|
||||
|
||||
@@ -98,11 +98,11 @@ impl Vecs {
|
||||
out_value[entry.original_idx] = entry.value;
|
||||
}
|
||||
|
||||
self.txout_index.truncate_if_needed_at(batch_start)?;
|
||||
self.value.truncate_if_needed_at(batch_start)?;
|
||||
for i in 0..batch_len {
|
||||
let txin_index = TxInIndex::from(batch_start + i);
|
||||
self.txout_index
|
||||
.truncate_push(txin_index, out_txout_index[i])?;
|
||||
self.value.truncate_push(txin_index, out_value[i])?;
|
||||
self.txout_index.push(out_txout_index[i]);
|
||||
self.value.push(out_value[i]);
|
||||
}
|
||||
|
||||
if batch_end < target {
|
||||
|
||||
@@ -71,18 +71,23 @@ impl ExpandingPercentiles {
|
||||
self.tree.add(Self::to_bucket(value), &1);
|
||||
}
|
||||
|
||||
/// Compute 6 percentiles in one call. O(6 × log N).
|
||||
/// Quantiles q must be in (0, 1). Output is in BPS.
|
||||
/// Compute 6 percentiles in one call via kth. O(6 × log N) but with
|
||||
/// shared tree traversal across all 6 targets for better cache locality.
|
||||
/// Quantiles q must be sorted ascending in (0, 1). Output is in BPS.
|
||||
pub fn quantiles(&self, qs: &[f64; 6], out: &mut [u32; 6]) {
|
||||
if self.count == 0 {
|
||||
out.iter_mut().for_each(|o| *o = 0);
|
||||
return;
|
||||
}
|
||||
let mut targets = [0u32; 6];
|
||||
for (i, &q) in qs.iter().enumerate() {
|
||||
let k = ((q * self.count as f64).ceil() as u32).clamp(1, self.count);
|
||||
// kth with 0-indexed k: k-1; result is 0-indexed bucket
|
||||
let bucket = self.tree.kth(k - 1, |n| *n);
|
||||
out[i] = bucket as u32 * BUCKET_BPS as u32;
|
||||
targets[i] = k - 1; // 0-indexed
|
||||
}
|
||||
let mut buckets = [0usize; 6];
|
||||
self.tree.kth(&targets, &|n: &u32| *n, &mut buckets);
|
||||
for (i, bucket) in buckets.iter().enumerate() {
|
||||
out[i] = *bucket as u32 * BUCKET_BPS as u32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,42 +54,15 @@ impl<N: FenwickNode> FenwickTree<N> {
|
||||
result
|
||||
}
|
||||
|
||||
/// Find the 0-indexed bucket containing the k-th element (0-indexed k).
|
||||
/// Find the 0-indexed bucket containing the k-th element for each target.
|
||||
///
|
||||
/// `field_fn` extracts the relevant count field from a node.
|
||||
/// The value type `V` must support comparison and subtraction
|
||||
/// (works with `u32`, `i64`, `i128`).
|
||||
#[inline]
|
||||
pub fn kth<V, F>(&self, k: V, field_fn: F) -> usize
|
||||
where
|
||||
V: Copy + PartialOrd + std::ops::SubAssign,
|
||||
F: Fn(&N) -> V,
|
||||
{
|
||||
debug_assert!(self.size > 0);
|
||||
let mut pos = 0usize;
|
||||
let mut remaining = k;
|
||||
let mut bit = 1usize << (usize::BITS - 1 - self.size.leading_zeros());
|
||||
while bit > 0 {
|
||||
let next = pos + bit;
|
||||
if next <= self.size {
|
||||
let val = field_fn(&self.tree[next]);
|
||||
if remaining >= val {
|
||||
remaining -= val;
|
||||
pos = next;
|
||||
}
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
pos // 0-indexed bucket
|
||||
}
|
||||
|
||||
/// Batch kth for sorted targets. Processes all targets at each tree level
|
||||
/// for better cache locality vs individual kth() calls.
|
||||
/// `sorted_targets` must be sorted ascending. `out` receives the 0-indexed
|
||||
/// bucket for each target. Both slices must have the same length.
|
||||
///
|
||||
/// `sorted_targets` must be sorted ascending. `out` receives the 0-indexed bucket
|
||||
/// for each target. Both slices must have the same length.
|
||||
/// Processes all targets at each tree level for better cache locality.
|
||||
#[inline]
|
||||
pub fn batch_kth<V, F>(&self, sorted_targets: &[V], field_fn: &F, out: &mut [usize])
|
||||
pub fn kth<V, F>(&self, sorted_targets: &[V], field_fn: &F, out: &mut [usize])
|
||||
where
|
||||
V: Copy + PartialOrd + std::ops::SubAssign,
|
||||
F: Fn(&N) -> V,
|
||||
@@ -162,18 +135,14 @@ mod tests {
|
||||
tree.add(3, &5);
|
||||
tree.add(4, &1);
|
||||
|
||||
// kth(0) = first element → bucket 0
|
||||
assert_eq!(tree.kth(0u32, |n| *n), 0);
|
||||
// kth(2) = 3rd element → bucket 0 (last of bucket 0)
|
||||
assert_eq!(tree.kth(2u32, |n| *n), 0);
|
||||
// kth(3) = 4th element → bucket 1
|
||||
assert_eq!(tree.kth(3u32, |n| *n), 1);
|
||||
// kth(4) = 5th element → bucket 1
|
||||
assert_eq!(tree.kth(4u32, |n| *n), 1);
|
||||
// kth(5) = 6th element → bucket 3 (bucket 2 is empty)
|
||||
assert_eq!(tree.kth(5u32, |n| *n), 3);
|
||||
// kth(10) = 11th element → bucket 4
|
||||
assert_eq!(tree.kth(10u32, |n| *n), 4);
|
||||
let mut out = [0usize; 6];
|
||||
tree.kth(&[0u32, 2, 3, 4, 5, 10], &|n: &u32| *n, &mut out);
|
||||
assert_eq!(out[0], 0); // kth(0) → bucket 0
|
||||
assert_eq!(out[1], 0); // kth(2) → bucket 0 (last of bucket 0)
|
||||
assert_eq!(out[2], 1); // kth(3) → bucket 1
|
||||
assert_eq!(out[3], 1); // kth(4) → bucket 1
|
||||
assert_eq!(out[4], 3); // kth(5) → bucket 3 (bucket 2 is empty)
|
||||
assert_eq!(out[5], 4); // kth(10) → bucket 4
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -8,6 +8,11 @@ use super::sliding_window::SlidingWindowSorted;
|
||||
|
||||
/// Compute all 8 rolling distribution stats (avg, min, max, p10, p25, median, p75, p90)
|
||||
/// in a single sorted-vec pass per window.
|
||||
///
|
||||
/// When computing multiple windows from the same source, pass the same
|
||||
/// `&mut Option<(usize, Vec<f64>)>` cache to each call — the first call reads
|
||||
/// and caches, subsequent calls reuse if their range is covered.
|
||||
/// Process the largest window first (1y) so its cache covers all smaller windows.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rolling_distribution_from_starts<I, T, A>(
|
||||
max_from: I,
|
||||
@@ -22,6 +27,7 @@ pub fn compute_rolling_distribution_from_starts<I, T, A>(
|
||||
p75_out: &mut EagerVec<PcoVec<I, T>>,
|
||||
p90_out: &mut EagerVec<PcoVec<I, T>>,
|
||||
exit: &Exit,
|
||||
values_cache: &mut Option<(usize, Vec<f64>)>,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: VecIndex,
|
||||
@@ -68,8 +74,21 @@ where
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let mut partial_values: Vec<f64> = Vec::with_capacity(end - range_start);
|
||||
values.for_each_range_at(range_start, end, |a: A| partial_values.push(f64::from(a)));
|
||||
|
||||
// Reuse cached values if the cache covers our range, otherwise read and cache.
|
||||
let need_read = match values_cache.as_ref() {
|
||||
Some((cached_start, cached)) => {
|
||||
range_start < *cached_start || end > *cached_start + cached.len()
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
if need_read {
|
||||
let mut v = Vec::with_capacity(end - range_start);
|
||||
values.for_each_range_at(range_start, end, |a: A| v.push(f64::from(a)));
|
||||
*values_cache = Some((range_start, v));
|
||||
}
|
||||
let (cached_start, cached) = values_cache.as_ref().unwrap();
|
||||
let partial_values = &cached[(range_start - cached_start)..(end - cached_start)];
|
||||
|
||||
let capacity = if skip > 0 && skip < end {
|
||||
let first_start = window_starts.collect_one_at(skip).unwrap().to_usize();
|
||||
@@ -83,7 +102,7 @@ where
|
||||
let mut window = SlidingWindowSorted::with_capacity(capacity);
|
||||
|
||||
if skip > 0 {
|
||||
window.reconstruct(&partial_values, range_start, skip);
|
||||
window.reconstruct(partial_values, range_start, skip);
|
||||
}
|
||||
|
||||
let starts_batch = window_starts.collect_range_at(skip, end);
|
||||
@@ -92,7 +111,7 @@ where
|
||||
let i = skip + j;
|
||||
let v = partial_values[i - range_start];
|
||||
let start_usize = start.to_usize();
|
||||
window.advance(v, start_usize, &partial_values, range_start);
|
||||
window.advance(v, start_usize, partial_values, range_start);
|
||||
|
||||
if window.is_empty() {
|
||||
let zero = T::from(0.0);
|
||||
|
||||
@@ -26,10 +26,20 @@ impl<A> Windows<A> {
|
||||
[&self._24h, &self._1w, &self._1m, &self._1y]
|
||||
}
|
||||
|
||||
/// Largest window first (1y, 1m, 1w, 24h).
|
||||
pub fn as_array_largest_first(&self) -> [&A; 4] {
|
||||
[&self._1y, &self._1m, &self._1w, &self._24h]
|
||||
}
|
||||
|
||||
pub fn as_mut_array(&mut self) -> [&mut A; 4] {
|
||||
[&mut self._24h, &mut self._1w, &mut self._1m, &mut self._1y]
|
||||
}
|
||||
|
||||
/// Largest window first (1y, 1m, 1w, 24h).
|
||||
pub fn as_mut_array_largest_first(&mut self) -> [&mut A; 4] {
|
||||
[&mut self._1y, &mut self._1m, &mut self._1w, &mut self._24h]
|
||||
}
|
||||
|
||||
pub fn as_mut_array_from_1w(&mut self) -> [&mut A; 3] {
|
||||
[&mut self._1w, &mut self._1m, &mut self._1y]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
@@ -37,17 +37,6 @@ impl AmountPerBlockCumulative {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_with(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute_sats(&mut self.base.sats.height)?;
|
||||
self.compute(prices, max_from, exit)
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
|
||||
@@ -6,6 +6,7 @@ mod lazy;
|
||||
mod lazy_derived_resolutions;
|
||||
mod lazy_rolling_sum;
|
||||
mod rolling_distribution;
|
||||
mod with_deltas;
|
||||
|
||||
pub use base::*;
|
||||
pub use cumulative::*;
|
||||
@@ -15,3 +16,4 @@ pub use lazy::*;
|
||||
pub use lazy_derived_resolutions::*;
|
||||
pub use lazy_rolling_sum::*;
|
||||
pub use rolling_distribution::*;
|
||||
pub use with_deltas::*;
|
||||
|
||||
@@ -42,11 +42,13 @@ impl RollingDistributionSlot {
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
sats_cache: &mut Option<(usize, Vec<f64>)>,
|
||||
cents_cache: &mut Option<(usize, Vec<f64>)>,
|
||||
) -> Result<()> {
|
||||
let d = &mut self.distribution;
|
||||
|
||||
macro_rules! compute_unit {
|
||||
($unit:ident, $source:expr) => {
|
||||
($unit:ident, $source:expr, $cache:expr) => {
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from,
|
||||
starts,
|
||||
@@ -60,11 +62,12 @@ impl RollingDistributionSlot {
|
||||
&mut d.pct75.$unit.height,
|
||||
&mut d.pct90.$unit.height,
|
||||
exit,
|
||||
$cache,
|
||||
)?
|
||||
};
|
||||
}
|
||||
compute_unit!(sats, sats_source);
|
||||
compute_unit!(cents, cents_source);
|
||||
compute_unit!(sats, sats_source, sats_cache);
|
||||
compute_unit!(cents, cents_source, cents_cache);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -104,8 +107,23 @@ impl RollingDistributionAmountPerBlock {
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
for (slot, starts) in self.0.as_mut_array().into_iter().zip(windows.as_array()) {
|
||||
slot.compute(max_from, *starts, sats_source, cents_source, exit)?;
|
||||
let mut sats_cache = None;
|
||||
let mut cents_cache = None;
|
||||
for (slot, starts) in self
|
||||
.0
|
||||
.as_mut_array_largest_first()
|
||||
.into_iter()
|
||||
.zip(windows.as_array_largest_first())
|
||||
{
|
||||
slot.compute(
|
||||
max_from,
|
||||
*starts,
|
||||
sats_source,
|
||||
cents_source,
|
||||
exit,
|
||||
&mut sats_cache,
|
||||
&mut cents_cache,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPointsSigned32, Sats, SatsSigned, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{AmountPerBlock, CachedWindowStarts, LazyRollingDeltasFromHeight},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct AmountPerBlockWithDeltas<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: AmountPerBlock<M>,
|
||||
pub delta: LazyRollingDeltasFromHeight<Sats, SatsSigned, BasisPointsSigned32>,
|
||||
}
|
||||
|
||||
impl AmountPerBlockWithDeltas {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let inner = AmountPerBlock::forced_import(db, name, version, indexes)?;
|
||||
|
||||
let delta = LazyRollingDeltasFromHeight::new(
|
||||
&format!("{name}_delta"),
|
||||
version + Version::ONE,
|
||||
&inner.sats.height,
|
||||
cached_starts,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { inner, delta })
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
let mut values_cache = None;
|
||||
macro_rules! compute_window {
|
||||
($w:ident) => {
|
||||
compute_rolling_distribution_from_starts(
|
||||
@@ -61,13 +62,15 @@ where
|
||||
&mut self.0.pct75.$w.height,
|
||||
&mut self.0.pct90.$w.height,
|
||||
exit,
|
||||
&mut values_cache,
|
||||
)?
|
||||
};
|
||||
}
|
||||
compute_window!(_24h);
|
||||
compute_window!(_1w);
|
||||
compute_window!(_1m);
|
||||
// Largest window first: its cache covers all smaller windows.
|
||||
compute_window!(_1y);
|
||||
compute_window!(_1m);
|
||||
compute_window!(_1w);
|
||||
compute_window!(_24h);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -325,25 +325,33 @@ impl Computer {
|
||||
.compute(indexer, &self.indexes, &starting_indexes, exit)
|
||||
})?;
|
||||
|
||||
timed("Computed inputs", || {
|
||||
self.inputs.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&self.blocks,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)
|
||||
})?;
|
||||
|
||||
timed("Computed scripts", || {
|
||||
self.scripts.compute(
|
||||
indexer,
|
||||
&self.outputs,
|
||||
&self.prices,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)
|
||||
})?;
|
||||
// inputs and scripts are independent — parallelize
|
||||
let (inputs_result, scripts_result) = rayon::join(
|
||||
|| {
|
||||
timed("Computed inputs", || {
|
||||
self.inputs.compute(
|
||||
indexer,
|
||||
&self.indexes,
|
||||
&self.blocks,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
},
|
||||
|| {
|
||||
timed("Computed scripts", || {
|
||||
self.scripts.compute(
|
||||
indexer,
|
||||
&self.outputs,
|
||||
&self.prices,
|
||||
&starting_indexes,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
},
|
||||
);
|
||||
inputs_result?;
|
||||
scripts_result?;
|
||||
|
||||
timed("Computed outputs", || {
|
||||
self.outputs.compute(
|
||||
|
||||
@@ -18,62 +18,68 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.coinbase.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|vec| {
|
||||
// Cursors avoid per-height PcoVec page decompression for the
|
||||
// tx-indexed lookups. Coinbase tx_index values are strictly
|
||||
// increasing, so the cursors only advance forward.
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txout_index.cursor();
|
||||
let mut count_cursor = indexes.tx_index.output_count.cursor();
|
||||
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
|(height, tx_index, ..)| {
|
||||
let ti = tx_index.to_usize();
|
||||
|
||||
txout_cursor.advance(ti - txout_cursor.position());
|
||||
let first_txout_index = txout_cursor.next().unwrap().to_usize();
|
||||
|
||||
count_cursor.advance(ti - count_cursor.position());
|
||||
let output_count: usize = count_cursor.next().unwrap().into();
|
||||
|
||||
let sats = indexer.vecs.outputs.value.fold_range_at(
|
||||
first_txout_index,
|
||||
first_txout_index + output_count,
|
||||
Sats::ZERO,
|
||||
|acc, v| acc + v,
|
||||
);
|
||||
(height, sats)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
// Coinbase fee is 0, so including it in the sum doesn't affect the result
|
||||
// coinbase and fees are independent — parallelize
|
||||
let window_starts = lookback.window_starts();
|
||||
|
||||
self.fees.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
prices,
|
||||
exit,
|
||||
|vec| {
|
||||
vec.compute_sum_from_indexes(
|
||||
let (r_coinbase, r_fees) = rayon::join(
|
||||
|| {
|
||||
self.coinbase.compute(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&transactions_fees.fee.tx_index,
|
||||
prices,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|vec| {
|
||||
let mut txout_cursor =
|
||||
indexer.vecs.transactions.first_txout_index.cursor();
|
||||
let mut count_cursor = indexes.tx_index.output_count.cursor();
|
||||
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
|(height, tx_index, ..)| {
|
||||
let ti = tx_index.to_usize();
|
||||
|
||||
txout_cursor.advance(ti - txout_cursor.position());
|
||||
let first_txout_index =
|
||||
txout_cursor.next().unwrap().to_usize();
|
||||
|
||||
count_cursor.advance(ti - count_cursor.position());
|
||||
let output_count: usize =
|
||||
count_cursor.next().unwrap().into();
|
||||
|
||||
let sats = indexer.vecs.outputs.value.fold_range_at(
|
||||
first_txout_index,
|
||||
first_txout_index + output_count,
|
||||
Sats::ZERO,
|
||||
|acc, v| acc + v,
|
||||
);
|
||||
(height, sats)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|| {
|
||||
self.fees.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
prices,
|
||||
exit,
|
||||
|vec| {
|
||||
vec.compute_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&transactions_fees.fee.tx_index,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
r_coinbase?;
|
||||
r_fees?;
|
||||
|
||||
self.subsidy.base.sats.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
@@ -110,7 +116,6 @@ impl Vecs {
|
||||
},
|
||||
)?;
|
||||
|
||||
// All-time cumulative fee dominance
|
||||
self.fee_dominance
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
starting_indexes.height,
|
||||
@@ -119,7 +124,6 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Rolling fee dominance = sum(fees) / sum(coinbase)
|
||||
self.fee_dominance_rolling
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16, _, _>(
|
||||
starting_indexes.height,
|
||||
@@ -128,7 +132,6 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// All-time cumulative subsidy dominance
|
||||
self.subsidy_dominance
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
starting_indexes.height,
|
||||
@@ -144,7 +147,6 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Fee Ratio Multiple: sum(coinbase) / sum(fees) per rolling window
|
||||
self.fee_ratio_multiple
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32, _, _>(
|
||||
starting_indexes.height,
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Vecs {
|
||||
let version = parent_version;
|
||||
|
||||
let count = CountVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||
let value = ValueVecs::forced_import(&db, version, indexes)?;
|
||||
let value = ValueVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||
let adoption = AdoptionVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
let this = Self {
|
||||
|
||||
@@ -14,7 +14,7 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.op_return.compute_with(
|
||||
self.op_return.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|
||||
@@ -3,20 +3,22 @@ use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{indexes, internal::AmountPerBlockCumulative};
|
||||
use crate::{indexes, internal::{AmountPerBlockCumulativeWithSums, CachedWindowStarts}};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
op_return: AmountPerBlockCumulative::forced_import(
|
||||
op_return: AmountPerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"op_return_value",
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use brk_traversable::Traversable;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::AmountPerBlockCumulative;
|
||||
use crate::internal::AmountPerBlockCumulativeWithSums;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub op_return: AmountPerBlockCumulative<M>,
|
||||
pub op_return: AmountPerBlockCumulativeWithSums<M>,
|
||||
}
|
||||
|
||||
@@ -14,47 +14,7 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.op_return.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|height_vec| {
|
||||
// Validate computed versions against dependencies
|
||||
|
||||
let op_return_dep_version = scripts.value.op_return.base.sats.height.version();
|
||||
height_vec.validate_computed_version_or_reset(op_return_dep_version)?;
|
||||
|
||||
// Copy per-block op_return values from scripts
|
||||
let scripts_target = scripts.value.op_return.base.sats.height.len();
|
||||
if scripts_target > 0 {
|
||||
let target_height = Height::from(scripts_target - 1);
|
||||
let current_len = height_vec.len();
|
||||
let starting_height =
|
||||
Height::from(current_len.min(starting_indexes.height.to_usize()));
|
||||
|
||||
if starting_height <= target_height {
|
||||
let start = starting_height.to_usize();
|
||||
let end = target_height.to_usize() + 1;
|
||||
scripts.value.op_return.base.sats.height.fold_range_at(
|
||||
start,
|
||||
end,
|
||||
start,
|
||||
|idx, value| {
|
||||
height_vec.truncate_push(Height::from(idx), value).unwrap();
|
||||
idx + 1
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
height_vec.write()?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
// 2. Compute unspendable supply = op_return + unclaimed_rewards + genesis (at height 0)
|
||||
// Get reference to op_return height vec for computing unspendable
|
||||
let op_return_height = &self.op_return.base.sats.height;
|
||||
let op_return_height = &scripts.value.op_return.base.sats.height;
|
||||
let unclaimed_height = &mining.rewards.unclaimed.base.sats.height;
|
||||
|
||||
self.unspendable.compute(
|
||||
|
||||
@@ -13,13 +13,6 @@ impl Vecs {
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
op_return: AmountPerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"op_return_supply",
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
unspendable: AmountPerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"unspendable_supply",
|
||||
|
||||
@@ -5,6 +5,5 @@ use crate::internal::AmountPerBlockCumulativeWithSums;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub op_return: AmountPerBlockCumulativeWithSums<M>,
|
||||
pub unspendable: AmountPerBlockCumulativeWithSums<M>,
|
||||
}
|
||||
|
||||
@@ -20,17 +20,19 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Count computes first
|
||||
self.count
|
||||
.compute(indexer, &blocks.lookback, starting_indexes, exit)?;
|
||||
|
||||
// Versions depends on count
|
||||
self.versions
|
||||
.compute(indexer, starting_indexes, exit)?;
|
||||
|
||||
// Size computes next (uses 6-block rolling window)
|
||||
self.size
|
||||
.compute(indexer, indexes, starting_indexes, exit)?;
|
||||
// count, versions, size are independent — parallelize
|
||||
let (r1, (r2, r3)) = rayon::join(
|
||||
|| self.count.compute(indexer, &blocks.lookback, starting_indexes, exit),
|
||||
|| {
|
||||
rayon::join(
|
||||
|| self.versions.compute(indexer, starting_indexes, exit),
|
||||
|| self.size.compute(indexer, indexes, starting_indexes, exit),
|
||||
)
|
||||
},
|
||||
);
|
||||
r1?;
|
||||
r2?;
|
||||
r3?;
|
||||
|
||||
// Fees depends on size
|
||||
self.fees
|
||||
|
||||
@@ -18,21 +18,29 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.input_value.compute_sum_from_indexes(
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txin_index,
|
||||
&indexes.tx_index.input_count,
|
||||
&txins.spent.value,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.output_value.compute_sum_from_indexes(
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txout_index,
|
||||
&indexes.tx_index.output_count,
|
||||
&indexer.vecs.outputs.value,
|
||||
exit,
|
||||
)?;
|
||||
// input_value and output_value are independent — parallelize
|
||||
let (r1, r2) = rayon::join(
|
||||
|| {
|
||||
self.input_value.compute_sum_from_indexes(
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txin_index,
|
||||
&indexes.tx_index.input_count,
|
||||
&txins.spent.value,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
|| {
|
||||
self.output_value.compute_sum_from_indexes(
|
||||
starting_indexes.tx_index,
|
||||
&indexer.vecs.transactions.first_txout_index,
|
||||
&indexes.tx_index.output_count,
|
||||
&indexer.vecs.outputs.value,
|
||||
exit,
|
||||
)
|
||||
},
|
||||
);
|
||||
r1?;
|
||||
r2?;
|
||||
|
||||
self.fee.tx_index.compute_transform2(
|
||||
starting_indexes.tx_index,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Indexes, StoredU64, TxVersion};
|
||||
use vecdb::{Exit, ReadableVec, VecIndex};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::internal::PerBlockCumulativeWithSums;
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn compute(
|
||||
@@ -13,30 +12,86 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let tx_vany = |tx_vany: &mut PerBlockCumulativeWithSums<StoredU64, StoredU64>,
|
||||
tx_version: TxVersion| {
|
||||
let tx_version_vec = &indexer.vecs.transactions.tx_version;
|
||||
// Cursor avoids per-transaction PcoVec page decompression.
|
||||
// Txindex values are sequential, so the cursor only advances forward.
|
||||
let mut cursor = tx_version_vec.cursor();
|
||||
tx_vany.compute(starting_indexes.height, exit, |vec| {
|
||||
vec.compute_filtered_count_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexer.vecs.transactions.txid,
|
||||
|tx_index| {
|
||||
let ti = tx_index.to_usize();
|
||||
cursor.advance(ti - cursor.position());
|
||||
cursor.next().unwrap() == tx_version
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
tx_vany(&mut self.v1, TxVersion::ONE)?;
|
||||
tx_vany(&mut self.v2, TxVersion::TWO)?;
|
||||
tx_vany(&mut self.v3, TxVersion::THREE)?;
|
||||
let dep_version = indexer.vecs.transactions.tx_version.version()
|
||||
+ indexer.vecs.transactions.first_tx_index.version()
|
||||
+ indexer.vecs.transactions.txid.version();
|
||||
|
||||
for vec in [
|
||||
&mut self.v1.base.height,
|
||||
&mut self.v2.base.height,
|
||||
&mut self.v3.base.height,
|
||||
] {
|
||||
vec.validate_and_truncate(dep_version, starting_indexes.height)?;
|
||||
}
|
||||
|
||||
let skip = self
|
||||
.v1
|
||||
.base
|
||||
.height
|
||||
.len()
|
||||
.min(self.v2.base.height.len())
|
||||
.min(self.v3.base.height.len());
|
||||
|
||||
let first_tx_index = &indexer.vecs.transactions.first_tx_index;
|
||||
let end = first_tx_index.len();
|
||||
if skip >= end {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Truncate all 3 to skip, then push (no per-element bounds checks).
|
||||
self.v1.base.height.truncate_if_needed_at(skip)?;
|
||||
self.v2.base.height.truncate_if_needed_at(skip)?;
|
||||
self.v3.base.height.truncate_if_needed_at(skip)?;
|
||||
|
||||
// Single cursor over tx_version — scanned once for all 3 version counts.
|
||||
let mut cursor = indexer.vecs.transactions.tx_version.cursor();
|
||||
let fi_batch = first_tx_index.collect_range_at(skip, end);
|
||||
let txid_len = indexer.vecs.transactions.txid.len();
|
||||
|
||||
for (j, first_index) in fi_batch.iter().enumerate() {
|
||||
let next_first = fi_batch
|
||||
.get(j + 1)
|
||||
.map(|fi| fi.to_usize())
|
||||
.unwrap_or(txid_len);
|
||||
|
||||
let mut c1: usize = 0;
|
||||
let mut c2: usize = 0;
|
||||
let mut c3: usize = 0;
|
||||
|
||||
let fi = first_index.to_usize();
|
||||
cursor.advance(fi - cursor.position());
|
||||
for _ in fi..next_first {
|
||||
match cursor.next().unwrap() {
|
||||
TxVersion::ONE => c1 += 1,
|
||||
TxVersion::TWO => c2 += 1,
|
||||
TxVersion::THREE => c3 += 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.v1.base.height.push(StoredU64::from(c1 as u64));
|
||||
self.v2.base.height.push(StoredU64::from(c2 as u64));
|
||||
self.v3.base.height.push(StoredU64::from(c3 as u64));
|
||||
|
||||
if self.v1.base.height.batch_limit_reached() {
|
||||
let _lock = exit.lock();
|
||||
self.v1.base.height.write()?;
|
||||
self.v2.base.height.write()?;
|
||||
self.v3.base.height.write()?;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let _lock = exit.lock();
|
||||
self.v1.base.height.write()?;
|
||||
self.v2.base.height.write()?;
|
||||
self.v3.base.height.write()?;
|
||||
}
|
||||
|
||||
// Derive cumulative + sums from base
|
||||
self.v1.compute_rest(starting_indexes.height, exit)?;
|
||||
self.v2.compute_rest(starting_indexes.height, exit)?;
|
||||
self.v3.compute_rest(starting_indexes.height, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -22,36 +22,44 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sent_sum.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_filtered_sum_from_indexes(
|
||||
// sent_sum and received_sum are independent — parallelize
|
||||
let (r1, r2) = rayon::join(
|
||||
|| {
|
||||
self.sent_sum.compute(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
prices,
|
||||
exit,
|
||||
)?)
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_filtered_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
exit,
|
||||
)?)
|
||||
},
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
self.received_sum.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_sum_from_indexes(
|
||||
|| {
|
||||
self.received_sum.compute(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.output_value,
|
||||
prices,
|
||||
exit,
|
||||
)?)
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.output_value,
|
||||
exit,
|
||||
)?)
|
||||
},
|
||||
)
|
||||
},
|
||||
)?;
|
||||
);
|
||||
r1?;
|
||||
r2?;
|
||||
|
||||
self.tx_per_sec
|
||||
.height
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Direct-mapped cache size. Power of 2 for fast masking.
|
||||
const CACHE_SIZE: usize = 128;
|
||||
/// 1024 entries × ~32 bytes = 32 KB (fits in L1 cache).
|
||||
const CACHE_SIZE: usize = 1024;
|
||||
const CACHE_MASK: usize = CACHE_SIZE - 1;
|
||||
|
||||
/// Cache entry: (range_low, range_high, value, occupied).
|
||||
|
||||
@@ -2633,6 +2633,31 @@ function createBpsCentsRatioSatsUsdPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BtcCentsDeltaSatsUsdPattern
|
||||
* @property {MetricPattern1<Bitcoin>} btc
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {MetricPattern1<Sats>} sats
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a BtcCentsDeltaSatsUsdPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {BtcCentsDeltaSatsUsdPattern}
|
||||
*/
|
||||
function createBtcCentsDeltaSatsUsdPattern(client, acc) {
|
||||
return {
|
||||
btc: createMetricPattern1(client, acc),
|
||||
cents: createMetricPattern1(client, _m(acc, 'cents')),
|
||||
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
|
||||
sats: createMetricPattern1(client, _m(acc, 'sats')),
|
||||
usd: createMetricPattern1(client, _m(acc, 'usd')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BtcCentsRelSatsUsdPattern
|
||||
* @property {MetricPattern1<Bitcoin>} btc
|
||||
@@ -2758,6 +2783,31 @@ function createInvestedMaxMinPercentilesSupplyPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} MvrvNuplRealizedSupplyPattern
|
||||
* @property {MetricPattern1<StoredF32>} mvrv
|
||||
* @property {BpsRatioPattern} nupl
|
||||
* @property {AllSthPattern} realizedCap
|
||||
* @property {BpsCentsRatioSatsUsdPattern} realizedPrice
|
||||
* @property {AllSthPattern2} supply
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a MvrvNuplRealizedSupplyPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {MvrvNuplRealizedSupplyPattern}
|
||||
*/
|
||||
function createMvrvNuplRealizedSupplyPattern(client, acc) {
|
||||
return {
|
||||
mvrv: createMetricPattern1(client, _m(acc, 'mvrv')),
|
||||
nupl: createBpsRatioPattern(client, _m(acc, 'nupl')),
|
||||
realizedCap: createAllSthPattern(client, acc),
|
||||
realizedPrice: createBpsCentsRatioSatsUsdPattern(client, _m(acc, 'realized_price')),
|
||||
supply: createAllSthPattern2(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PhsReboundThsPattern
|
||||
* @property {MetricPattern1<StoredF32>} phs
|
||||
@@ -3566,7 +3616,7 @@ function createGreedNetPainPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} LossNuplProfitPattern
|
||||
* @property {BaseCumulativeSumPattern3} loss
|
||||
* @property {BaseCumulativeNegativeSumPattern} loss
|
||||
* @property {BpsRatioPattern} nupl
|
||||
* @property {BaseCumulativeSumPattern3} profit
|
||||
*/
|
||||
@@ -3579,7 +3629,7 @@ function createGreedNetPainPattern(client, acc) {
|
||||
*/
|
||||
function createLossNuplProfitPattern(client, acc) {
|
||||
return {
|
||||
loss: createBaseCumulativeSumPattern3(client, _m(acc, 'unrealized_loss')),
|
||||
loss: createBaseCumulativeNegativeSumPattern(client, acc),
|
||||
nupl: createBpsRatioPattern(client, _m(acc, 'nupl')),
|
||||
profit: createBaseCumulativeSumPattern3(client, _m(acc, 'unrealized_profit')),
|
||||
};
|
||||
@@ -3732,6 +3782,44 @@ function createAbsoluteRatePattern2(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AllSthPattern2
|
||||
* @property {BtcCentsDeltaSatsUsdPattern} all
|
||||
* @property {BtcCentsSatsUsdPattern} sth
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a AllSthPattern2 pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {AllSthPattern2}
|
||||
*/
|
||||
function createAllSthPattern2(client, acc) {
|
||||
return {
|
||||
all: createBtcCentsDeltaSatsUsdPattern(client, _m(acc, 'supply')),
|
||||
sth: createBtcCentsSatsUsdPattern(client, _m(acc, 'sth_supply')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AllSthPattern
|
||||
* @property {MetricPattern1<Dollars>} all
|
||||
* @property {MetricPattern1<Dollars>} sth
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a AllSthPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {AllSthPattern}
|
||||
*/
|
||||
function createAllSthPattern(client, acc) {
|
||||
return {
|
||||
all: createMetricPattern1(client, _m(acc, 'realized_cap')),
|
||||
sth: createMetricPattern1(client, _m(acc, 'sth_realized_cap')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BlocksDominancePattern
|
||||
* @property {BaseCumulativeSumPattern2} blocksMined
|
||||
@@ -3903,25 +3991,6 @@ function createPriceValuePattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} RealizedSupplyPattern
|
||||
* @property {MetricPattern1<Dollars>} realizedCap
|
||||
* @property {MetricPattern1<Sats>} supply
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a RealizedSupplyPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {RealizedSupplyPattern}
|
||||
*/
|
||||
function createRealizedSupplyPattern(client, acc) {
|
||||
return {
|
||||
realizedCap: createMetricPattern1(client, _m(acc, 'realized_cap')),
|
||||
supply: createMetricPattern1(client, _m(acc, 'supply')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} RelPattern
|
||||
* @property {BpsPercentRatioPattern} relToMcap
|
||||
@@ -4481,13 +4550,7 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Scripts_Value
|
||||
* @property {MetricsTree_Scripts_Value_OpReturn} opReturn
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Scripts_Value_OpReturn
|
||||
* @property {BtcCentsSatsUsdPattern} base
|
||||
* @property {BtcCentsSatsUsdPattern} cumulative
|
||||
* @property {BaseCumulativeSumPattern4} opReturn
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -5509,7 +5572,6 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Supply_Burned
|
||||
* @property {BaseCumulativeSumPattern4} opReturn
|
||||
* @property {BaseCumulativeSumPattern4} unspendable
|
||||
*/
|
||||
|
||||
@@ -5819,87 +5881,87 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cohorts_Utxo_Profitability_Range
|
||||
* @property {RealizedSupplyPattern} over1000pctInProfit
|
||||
* @property {RealizedSupplyPattern} _500pctTo1000pctInProfit
|
||||
* @property {RealizedSupplyPattern} _300pctTo500pctInProfit
|
||||
* @property {RealizedSupplyPattern} _200pctTo300pctInProfit
|
||||
* @property {RealizedSupplyPattern} _100pctTo200pctInProfit
|
||||
* @property {RealizedSupplyPattern} _90pctTo100pctInProfit
|
||||
* @property {RealizedSupplyPattern} _80pctTo90pctInProfit
|
||||
* @property {RealizedSupplyPattern} _70pctTo80pctInProfit
|
||||
* @property {RealizedSupplyPattern} _60pctTo70pctInProfit
|
||||
* @property {RealizedSupplyPattern} _50pctTo60pctInProfit
|
||||
* @property {RealizedSupplyPattern} _40pctTo50pctInProfit
|
||||
* @property {RealizedSupplyPattern} _30pctTo40pctInProfit
|
||||
* @property {RealizedSupplyPattern} _20pctTo30pctInProfit
|
||||
* @property {RealizedSupplyPattern} _10pctTo20pctInProfit
|
||||
* @property {RealizedSupplyPattern} _0pctTo10pctInProfit
|
||||
* @property {RealizedSupplyPattern} _0pctTo10pctInLoss
|
||||
* @property {RealizedSupplyPattern} _10pctTo20pctInLoss
|
||||
* @property {RealizedSupplyPattern} _20pctTo30pctInLoss
|
||||
* @property {RealizedSupplyPattern} _30pctTo40pctInLoss
|
||||
* @property {RealizedSupplyPattern} _40pctTo50pctInLoss
|
||||
* @property {RealizedSupplyPattern} _50pctTo60pctInLoss
|
||||
* @property {RealizedSupplyPattern} _60pctTo70pctInLoss
|
||||
* @property {RealizedSupplyPattern} _70pctTo80pctInLoss
|
||||
* @property {RealizedSupplyPattern} _80pctTo90pctInLoss
|
||||
* @property {RealizedSupplyPattern} _90pctTo100pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} over1000pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _500pctTo1000pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _300pctTo500pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _200pctTo300pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _100pctTo200pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _90pctTo100pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _80pctTo90pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _70pctTo80pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _60pctTo70pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _50pctTo60pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _40pctTo50pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _30pctTo40pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _20pctTo30pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _10pctTo20pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _0pctTo10pctInProfit
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _0pctTo10pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _10pctTo20pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _20pctTo30pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _30pctTo40pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _40pctTo50pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _50pctTo60pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _60pctTo70pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _70pctTo80pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _80pctTo90pctInLoss
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _90pctTo100pctInLoss
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cohorts_Utxo_Profitability_Profit
|
||||
* @property {RealizedSupplyPattern} breakeven
|
||||
* @property {RealizedSupplyPattern} _10pct
|
||||
* @property {RealizedSupplyPattern} _20pct
|
||||
* @property {RealizedSupplyPattern} _30pct
|
||||
* @property {RealizedSupplyPattern} _40pct
|
||||
* @property {RealizedSupplyPattern} _50pct
|
||||
* @property {RealizedSupplyPattern} _60pct
|
||||
* @property {RealizedSupplyPattern} _70pct
|
||||
* @property {RealizedSupplyPattern} _80pct
|
||||
* @property {RealizedSupplyPattern} _90pct
|
||||
* @property {RealizedSupplyPattern} _100pct
|
||||
* @property {RealizedSupplyPattern} _200pct
|
||||
* @property {RealizedSupplyPattern} _300pct
|
||||
* @property {RealizedSupplyPattern} _500pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} breakeven
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _10pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _20pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _30pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _40pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _50pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _60pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _70pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _80pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _90pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _100pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _200pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _300pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _500pct
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cohorts_Utxo_Profitability_Loss
|
||||
* @property {RealizedSupplyPattern} breakeven
|
||||
* @property {RealizedSupplyPattern} _10pct
|
||||
* @property {RealizedSupplyPattern} _20pct
|
||||
* @property {RealizedSupplyPattern} _30pct
|
||||
* @property {RealizedSupplyPattern} _40pct
|
||||
* @property {RealizedSupplyPattern} _50pct
|
||||
* @property {RealizedSupplyPattern} _60pct
|
||||
* @property {RealizedSupplyPattern} _70pct
|
||||
* @property {RealizedSupplyPattern} _80pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} breakeven
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _10pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _20pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _30pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _40pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _50pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _60pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _70pct
|
||||
* @property {MvrvNuplRealizedSupplyPattern} _80pct
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cohorts_Utxo_Matured
|
||||
* @property {BtcCentsSatsUsdPattern} under1h
|
||||
* @property {BtcCentsSatsUsdPattern} _1hTo1d
|
||||
* @property {BtcCentsSatsUsdPattern} _1dTo1w
|
||||
* @property {BtcCentsSatsUsdPattern} _1wTo1m
|
||||
* @property {BtcCentsSatsUsdPattern} _1mTo2m
|
||||
* @property {BtcCentsSatsUsdPattern} _2mTo3m
|
||||
* @property {BtcCentsSatsUsdPattern} _3mTo4m
|
||||
* @property {BtcCentsSatsUsdPattern} _4mTo5m
|
||||
* @property {BtcCentsSatsUsdPattern} _5mTo6m
|
||||
* @property {BtcCentsSatsUsdPattern} _6mTo1y
|
||||
* @property {BtcCentsSatsUsdPattern} _1yTo2y
|
||||
* @property {BtcCentsSatsUsdPattern} _2yTo3y
|
||||
* @property {BtcCentsSatsUsdPattern} _3yTo4y
|
||||
* @property {BtcCentsSatsUsdPattern} _4yTo5y
|
||||
* @property {BtcCentsSatsUsdPattern} _5yTo6y
|
||||
* @property {BtcCentsSatsUsdPattern} _6yTo7y
|
||||
* @property {BtcCentsSatsUsdPattern} _7yTo8y
|
||||
* @property {BtcCentsSatsUsdPattern} _8yTo10y
|
||||
* @property {BtcCentsSatsUsdPattern} _10yTo12y
|
||||
* @property {BtcCentsSatsUsdPattern} _12yTo15y
|
||||
* @property {BtcCentsSatsUsdPattern} over15y
|
||||
* @property {BaseCumulativeSumPattern4} under1h
|
||||
* @property {BaseCumulativeSumPattern4} _1hTo1d
|
||||
* @property {BaseCumulativeSumPattern4} _1dTo1w
|
||||
* @property {BaseCumulativeSumPattern4} _1wTo1m
|
||||
* @property {BaseCumulativeSumPattern4} _1mTo2m
|
||||
* @property {BaseCumulativeSumPattern4} _2mTo3m
|
||||
* @property {BaseCumulativeSumPattern4} _3mTo4m
|
||||
* @property {BaseCumulativeSumPattern4} _4mTo5m
|
||||
* @property {BaseCumulativeSumPattern4} _5mTo6m
|
||||
* @property {BaseCumulativeSumPattern4} _6mTo1y
|
||||
* @property {BaseCumulativeSumPattern4} _1yTo2y
|
||||
* @property {BaseCumulativeSumPattern4} _2yTo3y
|
||||
* @property {BaseCumulativeSumPattern4} _3yTo4y
|
||||
* @property {BaseCumulativeSumPattern4} _4yTo5y
|
||||
* @property {BaseCumulativeSumPattern4} _5yTo6y
|
||||
* @property {BaseCumulativeSumPattern4} _6yTo7y
|
||||
* @property {BaseCumulativeSumPattern4} _7yTo8y
|
||||
* @property {BaseCumulativeSumPattern4} _8yTo10y
|
||||
* @property {BaseCumulativeSumPattern4} _10yTo12y
|
||||
* @property {BaseCumulativeSumPattern4} _12yTo15y
|
||||
* @property {BaseCumulativeSumPattern4} over15y
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -6870,6 +6932,255 @@ class BrkClient extends BrkClientBase {
|
||||
}
|
||||
});
|
||||
|
||||
PROFITABILITY_RANGE_NAMES = /** @type {const} */ ({
|
||||
"over1000pctInProfit": {
|
||||
"id": "utxos_over_1000pct_in_profit",
|
||||
"short": ">1000%",
|
||||
"long": "Over 1000% Profit"
|
||||
},
|
||||
"_500pctTo1000pctInProfit": {
|
||||
"id": "utxos_500pct_to_1000pct_in_profit",
|
||||
"short": "500-1000%",
|
||||
"long": "500-1000% Profit"
|
||||
},
|
||||
"_300pctTo500pctInProfit": {
|
||||
"id": "utxos_300pct_to_500pct_in_profit",
|
||||
"short": "300-500%",
|
||||
"long": "300-500% Profit"
|
||||
},
|
||||
"_200pctTo300pctInProfit": {
|
||||
"id": "utxos_200pct_to_300pct_in_profit",
|
||||
"short": "200-300%",
|
||||
"long": "200-300% Profit"
|
||||
},
|
||||
"_100pctTo200pctInProfit": {
|
||||
"id": "utxos_100pct_to_200pct_in_profit",
|
||||
"short": "100-200%",
|
||||
"long": "100-200% Profit"
|
||||
},
|
||||
"_90pctTo100pctInProfit": {
|
||||
"id": "utxos_90pct_to_100pct_in_profit",
|
||||
"short": "90-100%",
|
||||
"long": "90-100% Profit"
|
||||
},
|
||||
"_80pctTo90pctInProfit": {
|
||||
"id": "utxos_80pct_to_90pct_in_profit",
|
||||
"short": "80-90%",
|
||||
"long": "80-90% Profit"
|
||||
},
|
||||
"_70pctTo80pctInProfit": {
|
||||
"id": "utxos_70pct_to_80pct_in_profit",
|
||||
"short": "70-80%",
|
||||
"long": "70-80% Profit"
|
||||
},
|
||||
"_60pctTo70pctInProfit": {
|
||||
"id": "utxos_60pct_to_70pct_in_profit",
|
||||
"short": "60-70%",
|
||||
"long": "60-70% Profit"
|
||||
},
|
||||
"_50pctTo60pctInProfit": {
|
||||
"id": "utxos_50pct_to_60pct_in_profit",
|
||||
"short": "50-60%",
|
||||
"long": "50-60% Profit"
|
||||
},
|
||||
"_40pctTo50pctInProfit": {
|
||||
"id": "utxos_40pct_to_50pct_in_profit",
|
||||
"short": "40-50%",
|
||||
"long": "40-50% Profit"
|
||||
},
|
||||
"_30pctTo40pctInProfit": {
|
||||
"id": "utxos_30pct_to_40pct_in_profit",
|
||||
"short": "30-40%",
|
||||
"long": "30-40% Profit"
|
||||
},
|
||||
"_20pctTo30pctInProfit": {
|
||||
"id": "utxos_20pct_to_30pct_in_profit",
|
||||
"short": "20-30%",
|
||||
"long": "20-30% Profit"
|
||||
},
|
||||
"_10pctTo20pctInProfit": {
|
||||
"id": "utxos_10pct_to_20pct_in_profit",
|
||||
"short": "10-20%",
|
||||
"long": "10-20% Profit"
|
||||
},
|
||||
"_0pctTo10pctInProfit": {
|
||||
"id": "utxos_0pct_to_10pct_in_profit",
|
||||
"short": "0-10%",
|
||||
"long": "0-10% Profit"
|
||||
},
|
||||
"_0pctTo10pctInLoss": {
|
||||
"id": "utxos_0pct_to_10pct_in_loss",
|
||||
"short": "0-10%L",
|
||||
"long": "0-10% Loss"
|
||||
},
|
||||
"_10pctTo20pctInLoss": {
|
||||
"id": "utxos_10pct_to_20pct_in_loss",
|
||||
"short": "10-20%L",
|
||||
"long": "10-20% Loss"
|
||||
},
|
||||
"_20pctTo30pctInLoss": {
|
||||
"id": "utxos_20pct_to_30pct_in_loss",
|
||||
"short": "20-30%L",
|
||||
"long": "20-30% Loss"
|
||||
},
|
||||
"_30pctTo40pctInLoss": {
|
||||
"id": "utxos_30pct_to_40pct_in_loss",
|
||||
"short": "30-40%L",
|
||||
"long": "30-40% Loss"
|
||||
},
|
||||
"_40pctTo50pctInLoss": {
|
||||
"id": "utxos_40pct_to_50pct_in_loss",
|
||||
"short": "40-50%L",
|
||||
"long": "40-50% Loss"
|
||||
},
|
||||
"_50pctTo60pctInLoss": {
|
||||
"id": "utxos_50pct_to_60pct_in_loss",
|
||||
"short": "50-60%L",
|
||||
"long": "50-60% Loss"
|
||||
},
|
||||
"_60pctTo70pctInLoss": {
|
||||
"id": "utxos_60pct_to_70pct_in_loss",
|
||||
"short": "60-70%L",
|
||||
"long": "60-70% Loss"
|
||||
},
|
||||
"_70pctTo80pctInLoss": {
|
||||
"id": "utxos_70pct_to_80pct_in_loss",
|
||||
"short": "70-80%L",
|
||||
"long": "70-80% Loss"
|
||||
},
|
||||
"_80pctTo90pctInLoss": {
|
||||
"id": "utxos_80pct_to_90pct_in_loss",
|
||||
"short": "80-90%L",
|
||||
"long": "80-90% Loss"
|
||||
},
|
||||
"_90pctTo100pctInLoss": {
|
||||
"id": "utxos_90pct_to_100pct_in_loss",
|
||||
"short": "90-100%L",
|
||||
"long": "90-100% Loss"
|
||||
}
|
||||
});
|
||||
|
||||
PROFIT_NAMES = /** @type {const} */ ({
|
||||
"breakeven": {
|
||||
"id": "utxos_in_profit",
|
||||
"short": "≥0%",
|
||||
"long": "In Profit (Breakeven+)"
|
||||
},
|
||||
"_10pct": {
|
||||
"id": "utxos_over_10pct_in_profit",
|
||||
"short": "≥10%",
|
||||
"long": "10%+ Profit"
|
||||
},
|
||||
"_20pct": {
|
||||
"id": "utxos_over_20pct_in_profit",
|
||||
"short": "≥20%",
|
||||
"long": "20%+ Profit"
|
||||
},
|
||||
"_30pct": {
|
||||
"id": "utxos_over_30pct_in_profit",
|
||||
"short": "≥30%",
|
||||
"long": "30%+ Profit"
|
||||
},
|
||||
"_40pct": {
|
||||
"id": "utxos_over_40pct_in_profit",
|
||||
"short": "≥40%",
|
||||
"long": "40%+ Profit"
|
||||
},
|
||||
"_50pct": {
|
||||
"id": "utxos_over_50pct_in_profit",
|
||||
"short": "≥50%",
|
||||
"long": "50%+ Profit"
|
||||
},
|
||||
"_60pct": {
|
||||
"id": "utxos_over_60pct_in_profit",
|
||||
"short": "≥60%",
|
||||
"long": "60%+ Profit"
|
||||
},
|
||||
"_70pct": {
|
||||
"id": "utxos_over_70pct_in_profit",
|
||||
"short": "≥70%",
|
||||
"long": "70%+ Profit"
|
||||
},
|
||||
"_80pct": {
|
||||
"id": "utxos_over_80pct_in_profit",
|
||||
"short": "≥80%",
|
||||
"long": "80%+ Profit"
|
||||
},
|
||||
"_90pct": {
|
||||
"id": "utxos_over_90pct_in_profit",
|
||||
"short": "≥90%",
|
||||
"long": "90%+ Profit"
|
||||
},
|
||||
"_100pct": {
|
||||
"id": "utxos_over_100pct_in_profit",
|
||||
"short": "≥100%",
|
||||
"long": "100%+ Profit"
|
||||
},
|
||||
"_200pct": {
|
||||
"id": "utxos_over_200pct_in_profit",
|
||||
"short": "≥200%",
|
||||
"long": "200%+ Profit"
|
||||
},
|
||||
"_300pct": {
|
||||
"id": "utxos_over_300pct_in_profit",
|
||||
"short": "≥300%",
|
||||
"long": "300%+ Profit"
|
||||
},
|
||||
"_500pct": {
|
||||
"id": "utxos_over_500pct_in_profit",
|
||||
"short": "≥500%",
|
||||
"long": "500%+ Profit"
|
||||
}
|
||||
});
|
||||
|
||||
LOSS_NAMES = /** @type {const} */ ({
|
||||
"breakeven": {
|
||||
"id": "utxos_in_loss",
|
||||
"short": "<0%",
|
||||
"long": "In Loss (Below Breakeven)"
|
||||
},
|
||||
"_10pct": {
|
||||
"id": "utxos_over_10pct_in_loss",
|
||||
"short": "≥10%L",
|
||||
"long": "10%+ Loss"
|
||||
},
|
||||
"_20pct": {
|
||||
"id": "utxos_over_20pct_in_loss",
|
||||
"short": "≥20%L",
|
||||
"long": "20%+ Loss"
|
||||
},
|
||||
"_30pct": {
|
||||
"id": "utxos_over_30pct_in_loss",
|
||||
"short": "≥30%L",
|
||||
"long": "30%+ Loss"
|
||||
},
|
||||
"_40pct": {
|
||||
"id": "utxos_over_40pct_in_loss",
|
||||
"short": "≥40%L",
|
||||
"long": "40%+ Loss"
|
||||
},
|
||||
"_50pct": {
|
||||
"id": "utxos_over_50pct_in_loss",
|
||||
"short": "≥50%L",
|
||||
"long": "50%+ Loss"
|
||||
},
|
||||
"_60pct": {
|
||||
"id": "utxos_over_60pct_in_loss",
|
||||
"short": "≥60%L",
|
||||
"long": "60%+ Loss"
|
||||
},
|
||||
"_70pct": {
|
||||
"id": "utxos_over_70pct_in_loss",
|
||||
"short": "≥70%L",
|
||||
"long": "70%+ Loss"
|
||||
},
|
||||
"_80pct": {
|
||||
"id": "utxos_over_80pct_in_loss",
|
||||
"short": "≥80%L",
|
||||
"long": "80%+ Loss"
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert an index value to a Date for date-based indexes.
|
||||
* @param {Index} index - The index type
|
||||
@@ -7203,10 +7514,7 @@ class BrkClient extends BrkClientBase {
|
||||
segwit: createBaseCumulativeSumPattern(this, 'segwit_count'),
|
||||
},
|
||||
value: {
|
||||
opReturn: {
|
||||
base: createBtcCentsSatsUsdPattern(this, 'op_return_value'),
|
||||
cumulative: createBtcCentsSatsUsdPattern(this, 'op_return_value_cumulative'),
|
||||
},
|
||||
opReturn: createBaseCumulativeSumPattern4(this, 'op_return_value'),
|
||||
},
|
||||
adoption: {
|
||||
taproot: createBpsPercentRatioPattern3(this, 'taproot_adoption'),
|
||||
@@ -7945,7 +8253,6 @@ class BrkClient extends BrkClientBase {
|
||||
state: createMetricPattern18(this, 'supply_state'),
|
||||
circulating: createBtcCentsSatsUsdPattern(this, 'circulating_supply'),
|
||||
burned: {
|
||||
opReturn: createBaseCumulativeSumPattern4(this, 'op_return_supply'),
|
||||
unspendable: createBaseCumulativeSumPattern4(this, 'unspendable_supply'),
|
||||
},
|
||||
inflationRate: createBpsPercentRatioPattern(this, 'inflation_rate'),
|
||||
@@ -8183,82 +8490,82 @@ class BrkClient extends BrkClientBase {
|
||||
},
|
||||
profitability: {
|
||||
range: {
|
||||
over1000pctInProfit: createRealizedSupplyPattern(this, 'utxos_over_1000pct_in_profit'),
|
||||
_500pctTo1000pctInProfit: createRealizedSupplyPattern(this, 'utxos_500pct_to_1000pct_in_profit'),
|
||||
_300pctTo500pctInProfit: createRealizedSupplyPattern(this, 'utxos_300pct_to_500pct_in_profit'),
|
||||
_200pctTo300pctInProfit: createRealizedSupplyPattern(this, 'utxos_200pct_to_300pct_in_profit'),
|
||||
_100pctTo200pctInProfit: createRealizedSupplyPattern(this, 'utxos_100pct_to_200pct_in_profit'),
|
||||
_90pctTo100pctInProfit: createRealizedSupplyPattern(this, 'utxos_90pct_to_100pct_in_profit'),
|
||||
_80pctTo90pctInProfit: createRealizedSupplyPattern(this, 'utxos_80pct_to_90pct_in_profit'),
|
||||
_70pctTo80pctInProfit: createRealizedSupplyPattern(this, 'utxos_70pct_to_80pct_in_profit'),
|
||||
_60pctTo70pctInProfit: createRealizedSupplyPattern(this, 'utxos_60pct_to_70pct_in_profit'),
|
||||
_50pctTo60pctInProfit: createRealizedSupplyPattern(this, 'utxos_50pct_to_60pct_in_profit'),
|
||||
_40pctTo50pctInProfit: createRealizedSupplyPattern(this, 'utxos_40pct_to_50pct_in_profit'),
|
||||
_30pctTo40pctInProfit: createRealizedSupplyPattern(this, 'utxos_30pct_to_40pct_in_profit'),
|
||||
_20pctTo30pctInProfit: createRealizedSupplyPattern(this, 'utxos_20pct_to_30pct_in_profit'),
|
||||
_10pctTo20pctInProfit: createRealizedSupplyPattern(this, 'utxos_10pct_to_20pct_in_profit'),
|
||||
_0pctTo10pctInProfit: createRealizedSupplyPattern(this, 'utxos_0pct_to_10pct_in_profit'),
|
||||
_0pctTo10pctInLoss: createRealizedSupplyPattern(this, 'utxos_0pct_to_10pct_in_loss'),
|
||||
_10pctTo20pctInLoss: createRealizedSupplyPattern(this, 'utxos_10pct_to_20pct_in_loss'),
|
||||
_20pctTo30pctInLoss: createRealizedSupplyPattern(this, 'utxos_20pct_to_30pct_in_loss'),
|
||||
_30pctTo40pctInLoss: createRealizedSupplyPattern(this, 'utxos_30pct_to_40pct_in_loss'),
|
||||
_40pctTo50pctInLoss: createRealizedSupplyPattern(this, 'utxos_40pct_to_50pct_in_loss'),
|
||||
_50pctTo60pctInLoss: createRealizedSupplyPattern(this, 'utxos_50pct_to_60pct_in_loss'),
|
||||
_60pctTo70pctInLoss: createRealizedSupplyPattern(this, 'utxos_60pct_to_70pct_in_loss'),
|
||||
_70pctTo80pctInLoss: createRealizedSupplyPattern(this, 'utxos_70pct_to_80pct_in_loss'),
|
||||
_80pctTo90pctInLoss: createRealizedSupplyPattern(this, 'utxos_80pct_to_90pct_in_loss'),
|
||||
_90pctTo100pctInLoss: createRealizedSupplyPattern(this, 'utxos_90pct_to_100pct_in_loss'),
|
||||
over1000pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_1000pct_in_profit'),
|
||||
_500pctTo1000pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_500pct_to_1000pct_in_profit'),
|
||||
_300pctTo500pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_300pct_to_500pct_in_profit'),
|
||||
_200pctTo300pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_200pct_to_300pct_in_profit'),
|
||||
_100pctTo200pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_100pct_to_200pct_in_profit'),
|
||||
_90pctTo100pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_90pct_to_100pct_in_profit'),
|
||||
_80pctTo90pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_80pct_to_90pct_in_profit'),
|
||||
_70pctTo80pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_70pct_to_80pct_in_profit'),
|
||||
_60pctTo70pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_60pct_to_70pct_in_profit'),
|
||||
_50pctTo60pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_50pct_to_60pct_in_profit'),
|
||||
_40pctTo50pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_40pct_to_50pct_in_profit'),
|
||||
_30pctTo40pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_30pct_to_40pct_in_profit'),
|
||||
_20pctTo30pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_20pct_to_30pct_in_profit'),
|
||||
_10pctTo20pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_10pct_to_20pct_in_profit'),
|
||||
_0pctTo10pctInProfit: createMvrvNuplRealizedSupplyPattern(this, 'utxos_0pct_to_10pct_in_profit'),
|
||||
_0pctTo10pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_0pct_to_10pct_in_loss'),
|
||||
_10pctTo20pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_10pct_to_20pct_in_loss'),
|
||||
_20pctTo30pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_20pct_to_30pct_in_loss'),
|
||||
_30pctTo40pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_30pct_to_40pct_in_loss'),
|
||||
_40pctTo50pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_40pct_to_50pct_in_loss'),
|
||||
_50pctTo60pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_50pct_to_60pct_in_loss'),
|
||||
_60pctTo70pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_60pct_to_70pct_in_loss'),
|
||||
_70pctTo80pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_70pct_to_80pct_in_loss'),
|
||||
_80pctTo90pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_80pct_to_90pct_in_loss'),
|
||||
_90pctTo100pctInLoss: createMvrvNuplRealizedSupplyPattern(this, 'utxos_90pct_to_100pct_in_loss'),
|
||||
},
|
||||
profit: {
|
||||
breakeven: createRealizedSupplyPattern(this, 'utxos_in_profit'),
|
||||
_10pct: createRealizedSupplyPattern(this, 'utxos_over_10pct_in_profit'),
|
||||
_20pct: createRealizedSupplyPattern(this, 'utxos_over_20pct_in_profit'),
|
||||
_30pct: createRealizedSupplyPattern(this, 'utxos_over_30pct_in_profit'),
|
||||
_40pct: createRealizedSupplyPattern(this, 'utxos_over_40pct_in_profit'),
|
||||
_50pct: createRealizedSupplyPattern(this, 'utxos_over_50pct_in_profit'),
|
||||
_60pct: createRealizedSupplyPattern(this, 'utxos_over_60pct_in_profit'),
|
||||
_70pct: createRealizedSupplyPattern(this, 'utxos_over_70pct_in_profit'),
|
||||
_80pct: createRealizedSupplyPattern(this, 'utxos_over_80pct_in_profit'),
|
||||
_90pct: createRealizedSupplyPattern(this, 'utxos_over_90pct_in_profit'),
|
||||
_100pct: createRealizedSupplyPattern(this, 'utxos_over_100pct_in_profit'),
|
||||
_200pct: createRealizedSupplyPattern(this, 'utxos_over_200pct_in_profit'),
|
||||
_300pct: createRealizedSupplyPattern(this, 'utxos_over_300pct_in_profit'),
|
||||
_500pct: createRealizedSupplyPattern(this, 'utxos_over_500pct_in_profit'),
|
||||
breakeven: createMvrvNuplRealizedSupplyPattern(this, 'utxos_in_profit'),
|
||||
_10pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_10pct_in_profit'),
|
||||
_20pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_20pct_in_profit'),
|
||||
_30pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_30pct_in_profit'),
|
||||
_40pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_40pct_in_profit'),
|
||||
_50pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_50pct_in_profit'),
|
||||
_60pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_60pct_in_profit'),
|
||||
_70pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_70pct_in_profit'),
|
||||
_80pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_80pct_in_profit'),
|
||||
_90pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_90pct_in_profit'),
|
||||
_100pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_100pct_in_profit'),
|
||||
_200pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_200pct_in_profit'),
|
||||
_300pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_300pct_in_profit'),
|
||||
_500pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_500pct_in_profit'),
|
||||
},
|
||||
loss: {
|
||||
breakeven: createRealizedSupplyPattern(this, 'utxos_in_loss'),
|
||||
_10pct: createRealizedSupplyPattern(this, 'utxos_over_10pct_in_loss'),
|
||||
_20pct: createRealizedSupplyPattern(this, 'utxos_over_20pct_in_loss'),
|
||||
_30pct: createRealizedSupplyPattern(this, 'utxos_over_30pct_in_loss'),
|
||||
_40pct: createRealizedSupplyPattern(this, 'utxos_over_40pct_in_loss'),
|
||||
_50pct: createRealizedSupplyPattern(this, 'utxos_over_50pct_in_loss'),
|
||||
_60pct: createRealizedSupplyPattern(this, 'utxos_over_60pct_in_loss'),
|
||||
_70pct: createRealizedSupplyPattern(this, 'utxos_over_70pct_in_loss'),
|
||||
_80pct: createRealizedSupplyPattern(this, 'utxos_over_80pct_in_loss'),
|
||||
breakeven: createMvrvNuplRealizedSupplyPattern(this, 'utxos_in_loss'),
|
||||
_10pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_10pct_in_loss'),
|
||||
_20pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_20pct_in_loss'),
|
||||
_30pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_30pct_in_loss'),
|
||||
_40pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_40pct_in_loss'),
|
||||
_50pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_50pct_in_loss'),
|
||||
_60pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_60pct_in_loss'),
|
||||
_70pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_70pct_in_loss'),
|
||||
_80pct: createMvrvNuplRealizedSupplyPattern(this, 'utxos_over_80pct_in_loss'),
|
||||
},
|
||||
},
|
||||
matured: {
|
||||
under1h: createBtcCentsSatsUsdPattern(this, 'utxo_under_1h_old_matured'),
|
||||
_1hTo1d: createBtcCentsSatsUsdPattern(this, 'utxo_1h_to_1d_old_matured'),
|
||||
_1dTo1w: createBtcCentsSatsUsdPattern(this, 'utxo_1d_to_1w_old_matured'),
|
||||
_1wTo1m: createBtcCentsSatsUsdPattern(this, 'utxo_1w_to_1m_old_matured'),
|
||||
_1mTo2m: createBtcCentsSatsUsdPattern(this, 'utxo_1m_to_2m_old_matured'),
|
||||
_2mTo3m: createBtcCentsSatsUsdPattern(this, 'utxo_2m_to_3m_old_matured'),
|
||||
_3mTo4m: createBtcCentsSatsUsdPattern(this, 'utxo_3m_to_4m_old_matured'),
|
||||
_4mTo5m: createBtcCentsSatsUsdPattern(this, 'utxo_4m_to_5m_old_matured'),
|
||||
_5mTo6m: createBtcCentsSatsUsdPattern(this, 'utxo_5m_to_6m_old_matured'),
|
||||
_6mTo1y: createBtcCentsSatsUsdPattern(this, 'utxo_6m_to_1y_old_matured'),
|
||||
_1yTo2y: createBtcCentsSatsUsdPattern(this, 'utxo_1y_to_2y_old_matured'),
|
||||
_2yTo3y: createBtcCentsSatsUsdPattern(this, 'utxo_2y_to_3y_old_matured'),
|
||||
_3yTo4y: createBtcCentsSatsUsdPattern(this, 'utxo_3y_to_4y_old_matured'),
|
||||
_4yTo5y: createBtcCentsSatsUsdPattern(this, 'utxo_4y_to_5y_old_matured'),
|
||||
_5yTo6y: createBtcCentsSatsUsdPattern(this, 'utxo_5y_to_6y_old_matured'),
|
||||
_6yTo7y: createBtcCentsSatsUsdPattern(this, 'utxo_6y_to_7y_old_matured'),
|
||||
_7yTo8y: createBtcCentsSatsUsdPattern(this, 'utxo_7y_to_8y_old_matured'),
|
||||
_8yTo10y: createBtcCentsSatsUsdPattern(this, 'utxo_8y_to_10y_old_matured'),
|
||||
_10yTo12y: createBtcCentsSatsUsdPattern(this, 'utxo_10y_to_12y_old_matured'),
|
||||
_12yTo15y: createBtcCentsSatsUsdPattern(this, 'utxo_12y_to_15y_old_matured'),
|
||||
over15y: createBtcCentsSatsUsdPattern(this, 'utxo_over_15y_old_matured'),
|
||||
under1h: createBaseCumulativeSumPattern4(this, 'utxos_under_1h_old_matured_supply'),
|
||||
_1hTo1d: createBaseCumulativeSumPattern4(this, 'utxos_1h_to_1d_old_matured_supply'),
|
||||
_1dTo1w: createBaseCumulativeSumPattern4(this, 'utxos_1d_to_1w_old_matured_supply'),
|
||||
_1wTo1m: createBaseCumulativeSumPattern4(this, 'utxos_1w_to_1m_old_matured_supply'),
|
||||
_1mTo2m: createBaseCumulativeSumPattern4(this, 'utxos_1m_to_2m_old_matured_supply'),
|
||||
_2mTo3m: createBaseCumulativeSumPattern4(this, 'utxos_2m_to_3m_old_matured_supply'),
|
||||
_3mTo4m: createBaseCumulativeSumPattern4(this, 'utxos_3m_to_4m_old_matured_supply'),
|
||||
_4mTo5m: createBaseCumulativeSumPattern4(this, 'utxos_4m_to_5m_old_matured_supply'),
|
||||
_5mTo6m: createBaseCumulativeSumPattern4(this, 'utxos_5m_to_6m_old_matured_supply'),
|
||||
_6mTo1y: createBaseCumulativeSumPattern4(this, 'utxos_6m_to_1y_old_matured_supply'),
|
||||
_1yTo2y: createBaseCumulativeSumPattern4(this, 'utxos_1y_to_2y_old_matured_supply'),
|
||||
_2yTo3y: createBaseCumulativeSumPattern4(this, 'utxos_2y_to_3y_old_matured_supply'),
|
||||
_3yTo4y: createBaseCumulativeSumPattern4(this, 'utxos_3y_to_4y_old_matured_supply'),
|
||||
_4yTo5y: createBaseCumulativeSumPattern4(this, 'utxos_4y_to_5y_old_matured_supply'),
|
||||
_5yTo6y: createBaseCumulativeSumPattern4(this, 'utxos_5y_to_6y_old_matured_supply'),
|
||||
_6yTo7y: createBaseCumulativeSumPattern4(this, 'utxos_6y_to_7y_old_matured_supply'),
|
||||
_7yTo8y: createBaseCumulativeSumPattern4(this, 'utxos_7y_to_8y_old_matured_supply'),
|
||||
_8yTo10y: createBaseCumulativeSumPattern4(this, 'utxos_8y_to_10y_old_matured_supply'),
|
||||
_10yTo12y: createBaseCumulativeSumPattern4(this, 'utxos_10y_to_12y_old_matured_supply'),
|
||||
_12yTo15y: createBaseCumulativeSumPattern4(this, 'utxos_12y_to_15y_old_matured_supply'),
|
||||
over15y: createBaseCumulativeSumPattern4(this, 'utxos_over_15y_old_matured_supply'),
|
||||
},
|
||||
},
|
||||
address: {
|
||||
|
||||
@@ -2585,6 +2585,17 @@ class BpsCentsRatioSatsUsdPattern:
|
||||
self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, _m(acc, 'sats'))
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, acc)
|
||||
|
||||
class BtcCentsDeltaSatsUsdPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.btc: MetricPattern1[Bitcoin] = MetricPattern1(client, acc)
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, _m(acc, 'cents'))
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
|
||||
self.sats: MetricPattern1[Sats] = MetricPattern1(client, _m(acc, 'sats'))
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
|
||||
|
||||
class BtcCentsRelSatsUsdPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2640,6 +2651,17 @@ class InvestedMaxMinPercentilesSupplyPattern:
|
||||
self.percentiles: Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern = Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern(client, _m(acc, 'cost_basis'))
|
||||
self.supply_density: BpsPercentRatioPattern3 = BpsPercentRatioPattern3(client, _m(acc, 'supply_density'))
|
||||
|
||||
class MvrvNuplRealizedSupplyPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.mvrv: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'mvrv'))
|
||||
self.nupl: BpsRatioPattern = BpsRatioPattern(client, _m(acc, 'nupl'))
|
||||
self.realized_cap: AllSthPattern = AllSthPattern(client, acc)
|
||||
self.realized_price: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, _m(acc, 'realized_price'))
|
||||
self.supply: AllSthPattern2 = AllSthPattern2(client, acc)
|
||||
|
||||
class PhsReboundThsPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2992,7 +3014,7 @@ class LossNuplProfitPattern:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.loss: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, _m(acc, 'unrealized_loss'))
|
||||
self.loss: BaseCumulativeNegativeSumPattern = BaseCumulativeNegativeSumPattern(client, acc)
|
||||
self.nupl: BpsRatioPattern = BpsRatioPattern(client, _m(acc, 'nupl'))
|
||||
self.profit: BaseCumulativeSumPattern3 = BaseCumulativeSumPattern3(client, _m(acc, 'unrealized_profit'))
|
||||
|
||||
@@ -3057,6 +3079,22 @@ class AbsoluteRatePattern2:
|
||||
self.absolute: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, acc)
|
||||
self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc)
|
||||
|
||||
class AllSthPattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.all: BtcCentsDeltaSatsUsdPattern = BtcCentsDeltaSatsUsdPattern(client, _m(acc, 'supply'))
|
||||
self.sth: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'sth_supply'))
|
||||
|
||||
class AllSthPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.all: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_cap'))
|
||||
self.sth: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'sth_realized_cap'))
|
||||
|
||||
class BlocksDominancePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -3129,14 +3167,6 @@ class PriceValuePattern:
|
||||
self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'p3sd_4y'))
|
||||
self.value: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'ratio_p3sd_4y'))
|
||||
|
||||
class RealizedSupplyPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.realized_cap: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_cap'))
|
||||
self.supply: MetricPattern1[Sats] = MetricPattern1(client, _m(acc, 'supply'))
|
||||
|
||||
class RelPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -3647,18 +3677,11 @@ class MetricsTree_Scripts_Count:
|
||||
self.unknown_output: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'unknown_output_count')
|
||||
self.segwit: BaseCumulativeSumPattern[StoredU64] = BaseCumulativeSumPattern(client, 'segwit_count')
|
||||
|
||||
class MetricsTree_Scripts_Value_OpReturn:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.base: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'op_return_value')
|
||||
self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'op_return_value_cumulative')
|
||||
|
||||
class MetricsTree_Scripts_Value:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.op_return: MetricsTree_Scripts_Value_OpReturn = MetricsTree_Scripts_Value_OpReturn(client)
|
||||
self.op_return: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'op_return_value')
|
||||
|
||||
class MetricsTree_Scripts_Adoption:
|
||||
"""Metrics tree node."""
|
||||
@@ -4772,7 +4795,6 @@ class MetricsTree_Supply_Burned:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.op_return: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'op_return_supply')
|
||||
self.unspendable: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'unspendable_supply')
|
||||
|
||||
class MetricsTree_Supply_Velocity:
|
||||
@@ -5085,64 +5107,64 @@ class MetricsTree_Cohorts_Utxo_Profitability_Range:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.over_1000pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_1000pct_in_profit')
|
||||
self._500pct_to_1000pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_500pct_to_1000pct_in_profit')
|
||||
self._300pct_to_500pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_300pct_to_500pct_in_profit')
|
||||
self._200pct_to_300pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_200pct_to_300pct_in_profit')
|
||||
self._100pct_to_200pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_100pct_to_200pct_in_profit')
|
||||
self._90pct_to_100pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_90pct_to_100pct_in_profit')
|
||||
self._80pct_to_90pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_80pct_to_90pct_in_profit')
|
||||
self._70pct_to_80pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_70pct_to_80pct_in_profit')
|
||||
self._60pct_to_70pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_60pct_to_70pct_in_profit')
|
||||
self._50pct_to_60pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_50pct_to_60pct_in_profit')
|
||||
self._40pct_to_50pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_40pct_to_50pct_in_profit')
|
||||
self._30pct_to_40pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_30pct_to_40pct_in_profit')
|
||||
self._20pct_to_30pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_20pct_to_30pct_in_profit')
|
||||
self._10pct_to_20pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_10pct_to_20pct_in_profit')
|
||||
self._0pct_to_10pct_in_profit: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_0pct_to_10pct_in_profit')
|
||||
self._0pct_to_10pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_0pct_to_10pct_in_loss')
|
||||
self._10pct_to_20pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_10pct_to_20pct_in_loss')
|
||||
self._20pct_to_30pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_20pct_to_30pct_in_loss')
|
||||
self._30pct_to_40pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_30pct_to_40pct_in_loss')
|
||||
self._40pct_to_50pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_40pct_to_50pct_in_loss')
|
||||
self._50pct_to_60pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_50pct_to_60pct_in_loss')
|
||||
self._60pct_to_70pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_60pct_to_70pct_in_loss')
|
||||
self._70pct_to_80pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_70pct_to_80pct_in_loss')
|
||||
self._80pct_to_90pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_80pct_to_90pct_in_loss')
|
||||
self._90pct_to_100pct_in_loss: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_90pct_to_100pct_in_loss')
|
||||
self.over_1000pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_1000pct_in_profit')
|
||||
self._500pct_to_1000pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_500pct_to_1000pct_in_profit')
|
||||
self._300pct_to_500pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_300pct_to_500pct_in_profit')
|
||||
self._200pct_to_300pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_200pct_to_300pct_in_profit')
|
||||
self._100pct_to_200pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_100pct_to_200pct_in_profit')
|
||||
self._90pct_to_100pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_90pct_to_100pct_in_profit')
|
||||
self._80pct_to_90pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_80pct_to_90pct_in_profit')
|
||||
self._70pct_to_80pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_70pct_to_80pct_in_profit')
|
||||
self._60pct_to_70pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_60pct_to_70pct_in_profit')
|
||||
self._50pct_to_60pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_50pct_to_60pct_in_profit')
|
||||
self._40pct_to_50pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_40pct_to_50pct_in_profit')
|
||||
self._30pct_to_40pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_30pct_to_40pct_in_profit')
|
||||
self._20pct_to_30pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_20pct_to_30pct_in_profit')
|
||||
self._10pct_to_20pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_10pct_to_20pct_in_profit')
|
||||
self._0pct_to_10pct_in_profit: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_0pct_to_10pct_in_profit')
|
||||
self._0pct_to_10pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_0pct_to_10pct_in_loss')
|
||||
self._10pct_to_20pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_10pct_to_20pct_in_loss')
|
||||
self._20pct_to_30pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_20pct_to_30pct_in_loss')
|
||||
self._30pct_to_40pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_30pct_to_40pct_in_loss')
|
||||
self._40pct_to_50pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_40pct_to_50pct_in_loss')
|
||||
self._50pct_to_60pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_50pct_to_60pct_in_loss')
|
||||
self._60pct_to_70pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_60pct_to_70pct_in_loss')
|
||||
self._70pct_to_80pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_70pct_to_80pct_in_loss')
|
||||
self._80pct_to_90pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_80pct_to_90pct_in_loss')
|
||||
self._90pct_to_100pct_in_loss: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_90pct_to_100pct_in_loss')
|
||||
|
||||
class MetricsTree_Cohorts_Utxo_Profitability_Profit:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.breakeven: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_in_profit')
|
||||
self._10pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_10pct_in_profit')
|
||||
self._20pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_20pct_in_profit')
|
||||
self._30pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_30pct_in_profit')
|
||||
self._40pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_40pct_in_profit')
|
||||
self._50pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_50pct_in_profit')
|
||||
self._60pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_60pct_in_profit')
|
||||
self._70pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_70pct_in_profit')
|
||||
self._80pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_80pct_in_profit')
|
||||
self._90pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_90pct_in_profit')
|
||||
self._100pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_100pct_in_profit')
|
||||
self._200pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_200pct_in_profit')
|
||||
self._300pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_300pct_in_profit')
|
||||
self._500pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_500pct_in_profit')
|
||||
self.breakeven: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_in_profit')
|
||||
self._10pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_10pct_in_profit')
|
||||
self._20pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_20pct_in_profit')
|
||||
self._30pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_30pct_in_profit')
|
||||
self._40pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_40pct_in_profit')
|
||||
self._50pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_50pct_in_profit')
|
||||
self._60pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_60pct_in_profit')
|
||||
self._70pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_70pct_in_profit')
|
||||
self._80pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_80pct_in_profit')
|
||||
self._90pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_90pct_in_profit')
|
||||
self._100pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_100pct_in_profit')
|
||||
self._200pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_200pct_in_profit')
|
||||
self._300pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_300pct_in_profit')
|
||||
self._500pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_500pct_in_profit')
|
||||
|
||||
class MetricsTree_Cohorts_Utxo_Profitability_Loss:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.breakeven: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_in_loss')
|
||||
self._10pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_10pct_in_loss')
|
||||
self._20pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_20pct_in_loss')
|
||||
self._30pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_30pct_in_loss')
|
||||
self._40pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_40pct_in_loss')
|
||||
self._50pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_50pct_in_loss')
|
||||
self._60pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_60pct_in_loss')
|
||||
self._70pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_70pct_in_loss')
|
||||
self._80pct: RealizedSupplyPattern = RealizedSupplyPattern(client, 'utxos_over_80pct_in_loss')
|
||||
self.breakeven: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_in_loss')
|
||||
self._10pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_10pct_in_loss')
|
||||
self._20pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_20pct_in_loss')
|
||||
self._30pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_30pct_in_loss')
|
||||
self._40pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_40pct_in_loss')
|
||||
self._50pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_50pct_in_loss')
|
||||
self._60pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_60pct_in_loss')
|
||||
self._70pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_70pct_in_loss')
|
||||
self._80pct: MvrvNuplRealizedSupplyPattern = MvrvNuplRealizedSupplyPattern(client, 'utxos_over_80pct_in_loss')
|
||||
|
||||
class MetricsTree_Cohorts_Utxo_Profitability:
|
||||
"""Metrics tree node."""
|
||||
@@ -5156,27 +5178,27 @@ class MetricsTree_Cohorts_Utxo_Matured:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.under_1h: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_under_1h_old_matured')
|
||||
self._1h_to_1d: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_1h_to_1d_old_matured')
|
||||
self._1d_to_1w: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_1d_to_1w_old_matured')
|
||||
self._1w_to_1m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_1w_to_1m_old_matured')
|
||||
self._1m_to_2m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_1m_to_2m_old_matured')
|
||||
self._2m_to_3m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_2m_to_3m_old_matured')
|
||||
self._3m_to_4m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_3m_to_4m_old_matured')
|
||||
self._4m_to_5m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_4m_to_5m_old_matured')
|
||||
self._5m_to_6m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_5m_to_6m_old_matured')
|
||||
self._6m_to_1y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_6m_to_1y_old_matured')
|
||||
self._1y_to_2y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_1y_to_2y_old_matured')
|
||||
self._2y_to_3y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_2y_to_3y_old_matured')
|
||||
self._3y_to_4y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_3y_to_4y_old_matured')
|
||||
self._4y_to_5y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_4y_to_5y_old_matured')
|
||||
self._5y_to_6y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_5y_to_6y_old_matured')
|
||||
self._6y_to_7y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_6y_to_7y_old_matured')
|
||||
self._7y_to_8y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_7y_to_8y_old_matured')
|
||||
self._8y_to_10y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_8y_to_10y_old_matured')
|
||||
self._10y_to_12y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_10y_to_12y_old_matured')
|
||||
self._12y_to_15y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_12y_to_15y_old_matured')
|
||||
self.over_15y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'utxo_over_15y_old_matured')
|
||||
self.under_1h: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_under_1h_old_matured_supply')
|
||||
self._1h_to_1d: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_1h_to_1d_old_matured_supply')
|
||||
self._1d_to_1w: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_1d_to_1w_old_matured_supply')
|
||||
self._1w_to_1m: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_1w_to_1m_old_matured_supply')
|
||||
self._1m_to_2m: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_1m_to_2m_old_matured_supply')
|
||||
self._2m_to_3m: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_2m_to_3m_old_matured_supply')
|
||||
self._3m_to_4m: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_3m_to_4m_old_matured_supply')
|
||||
self._4m_to_5m: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_4m_to_5m_old_matured_supply')
|
||||
self._5m_to_6m: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_5m_to_6m_old_matured_supply')
|
||||
self._6m_to_1y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_6m_to_1y_old_matured_supply')
|
||||
self._1y_to_2y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_1y_to_2y_old_matured_supply')
|
||||
self._2y_to_3y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_2y_to_3y_old_matured_supply')
|
||||
self._3y_to_4y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_3y_to_4y_old_matured_supply')
|
||||
self._4y_to_5y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_4y_to_5y_old_matured_supply')
|
||||
self._5y_to_6y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_5y_to_6y_old_matured_supply')
|
||||
self._6y_to_7y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_6y_to_7y_old_matured_supply')
|
||||
self._7y_to_8y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_7y_to_8y_old_matured_supply')
|
||||
self._8y_to_10y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_8y_to_10y_old_matured_supply')
|
||||
self._10y_to_12y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_10y_to_12y_old_matured_supply')
|
||||
self._12y_to_15y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_12y_to_15y_old_matured_supply')
|
||||
self.over_15y: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'utxos_over_15y_old_matured_supply')
|
||||
|
||||
class MetricsTree_Cohorts_Utxo:
|
||||
"""Metrics tree node."""
|
||||
@@ -6195,6 +6217,255 @@ class BrkClient(BrkClientBase):
|
||||
}
|
||||
}
|
||||
|
||||
PROFITABILITY_RANGE_NAMES = {
|
||||
"over_1000pct_in_profit": {
|
||||
"id": "utxos_over_1000pct_in_profit",
|
||||
"short": ">1000%",
|
||||
"long": "Over 1000% Profit"
|
||||
},
|
||||
"_500pct_to_1000pct_in_profit": {
|
||||
"id": "utxos_500pct_to_1000pct_in_profit",
|
||||
"short": "500-1000%",
|
||||
"long": "500-1000% Profit"
|
||||
},
|
||||
"_300pct_to_500pct_in_profit": {
|
||||
"id": "utxos_300pct_to_500pct_in_profit",
|
||||
"short": "300-500%",
|
||||
"long": "300-500% Profit"
|
||||
},
|
||||
"_200pct_to_300pct_in_profit": {
|
||||
"id": "utxos_200pct_to_300pct_in_profit",
|
||||
"short": "200-300%",
|
||||
"long": "200-300% Profit"
|
||||
},
|
||||
"_100pct_to_200pct_in_profit": {
|
||||
"id": "utxos_100pct_to_200pct_in_profit",
|
||||
"short": "100-200%",
|
||||
"long": "100-200% Profit"
|
||||
},
|
||||
"_90pct_to_100pct_in_profit": {
|
||||
"id": "utxos_90pct_to_100pct_in_profit",
|
||||
"short": "90-100%",
|
||||
"long": "90-100% Profit"
|
||||
},
|
||||
"_80pct_to_90pct_in_profit": {
|
||||
"id": "utxos_80pct_to_90pct_in_profit",
|
||||
"short": "80-90%",
|
||||
"long": "80-90% Profit"
|
||||
},
|
||||
"_70pct_to_80pct_in_profit": {
|
||||
"id": "utxos_70pct_to_80pct_in_profit",
|
||||
"short": "70-80%",
|
||||
"long": "70-80% Profit"
|
||||
},
|
||||
"_60pct_to_70pct_in_profit": {
|
||||
"id": "utxos_60pct_to_70pct_in_profit",
|
||||
"short": "60-70%",
|
||||
"long": "60-70% Profit"
|
||||
},
|
||||
"_50pct_to_60pct_in_profit": {
|
||||
"id": "utxos_50pct_to_60pct_in_profit",
|
||||
"short": "50-60%",
|
||||
"long": "50-60% Profit"
|
||||
},
|
||||
"_40pct_to_50pct_in_profit": {
|
||||
"id": "utxos_40pct_to_50pct_in_profit",
|
||||
"short": "40-50%",
|
||||
"long": "40-50% Profit"
|
||||
},
|
||||
"_30pct_to_40pct_in_profit": {
|
||||
"id": "utxos_30pct_to_40pct_in_profit",
|
||||
"short": "30-40%",
|
||||
"long": "30-40% Profit"
|
||||
},
|
||||
"_20pct_to_30pct_in_profit": {
|
||||
"id": "utxos_20pct_to_30pct_in_profit",
|
||||
"short": "20-30%",
|
||||
"long": "20-30% Profit"
|
||||
},
|
||||
"_10pct_to_20pct_in_profit": {
|
||||
"id": "utxos_10pct_to_20pct_in_profit",
|
||||
"short": "10-20%",
|
||||
"long": "10-20% Profit"
|
||||
},
|
||||
"_0pct_to_10pct_in_profit": {
|
||||
"id": "utxos_0pct_to_10pct_in_profit",
|
||||
"short": "0-10%",
|
||||
"long": "0-10% Profit"
|
||||
},
|
||||
"_0pct_to_10pct_in_loss": {
|
||||
"id": "utxos_0pct_to_10pct_in_loss",
|
||||
"short": "0-10%L",
|
||||
"long": "0-10% Loss"
|
||||
},
|
||||
"_10pct_to_20pct_in_loss": {
|
||||
"id": "utxos_10pct_to_20pct_in_loss",
|
||||
"short": "10-20%L",
|
||||
"long": "10-20% Loss"
|
||||
},
|
||||
"_20pct_to_30pct_in_loss": {
|
||||
"id": "utxos_20pct_to_30pct_in_loss",
|
||||
"short": "20-30%L",
|
||||
"long": "20-30% Loss"
|
||||
},
|
||||
"_30pct_to_40pct_in_loss": {
|
||||
"id": "utxos_30pct_to_40pct_in_loss",
|
||||
"short": "30-40%L",
|
||||
"long": "30-40% Loss"
|
||||
},
|
||||
"_40pct_to_50pct_in_loss": {
|
||||
"id": "utxos_40pct_to_50pct_in_loss",
|
||||
"short": "40-50%L",
|
||||
"long": "40-50% Loss"
|
||||
},
|
||||
"_50pct_to_60pct_in_loss": {
|
||||
"id": "utxos_50pct_to_60pct_in_loss",
|
||||
"short": "50-60%L",
|
||||
"long": "50-60% Loss"
|
||||
},
|
||||
"_60pct_to_70pct_in_loss": {
|
||||
"id": "utxos_60pct_to_70pct_in_loss",
|
||||
"short": "60-70%L",
|
||||
"long": "60-70% Loss"
|
||||
},
|
||||
"_70pct_to_80pct_in_loss": {
|
||||
"id": "utxos_70pct_to_80pct_in_loss",
|
||||
"short": "70-80%L",
|
||||
"long": "70-80% Loss"
|
||||
},
|
||||
"_80pct_to_90pct_in_loss": {
|
||||
"id": "utxos_80pct_to_90pct_in_loss",
|
||||
"short": "80-90%L",
|
||||
"long": "80-90% Loss"
|
||||
},
|
||||
"_90pct_to_100pct_in_loss": {
|
||||
"id": "utxos_90pct_to_100pct_in_loss",
|
||||
"short": "90-100%L",
|
||||
"long": "90-100% Loss"
|
||||
}
|
||||
}
|
||||
|
||||
PROFIT_NAMES = {
|
||||
"breakeven": {
|
||||
"id": "utxos_in_profit",
|
||||
"short": "≥0%",
|
||||
"long": "In Profit (Breakeven+)"
|
||||
},
|
||||
"_10pct": {
|
||||
"id": "utxos_over_10pct_in_profit",
|
||||
"short": "≥10%",
|
||||
"long": "10%+ Profit"
|
||||
},
|
||||
"_20pct": {
|
||||
"id": "utxos_over_20pct_in_profit",
|
||||
"short": "≥20%",
|
||||
"long": "20%+ Profit"
|
||||
},
|
||||
"_30pct": {
|
||||
"id": "utxos_over_30pct_in_profit",
|
||||
"short": "≥30%",
|
||||
"long": "30%+ Profit"
|
||||
},
|
||||
"_40pct": {
|
||||
"id": "utxos_over_40pct_in_profit",
|
||||
"short": "≥40%",
|
||||
"long": "40%+ Profit"
|
||||
},
|
||||
"_50pct": {
|
||||
"id": "utxos_over_50pct_in_profit",
|
||||
"short": "≥50%",
|
||||
"long": "50%+ Profit"
|
||||
},
|
||||
"_60pct": {
|
||||
"id": "utxos_over_60pct_in_profit",
|
||||
"short": "≥60%",
|
||||
"long": "60%+ Profit"
|
||||
},
|
||||
"_70pct": {
|
||||
"id": "utxos_over_70pct_in_profit",
|
||||
"short": "≥70%",
|
||||
"long": "70%+ Profit"
|
||||
},
|
||||
"_80pct": {
|
||||
"id": "utxos_over_80pct_in_profit",
|
||||
"short": "≥80%",
|
||||
"long": "80%+ Profit"
|
||||
},
|
||||
"_90pct": {
|
||||
"id": "utxos_over_90pct_in_profit",
|
||||
"short": "≥90%",
|
||||
"long": "90%+ Profit"
|
||||
},
|
||||
"_100pct": {
|
||||
"id": "utxos_over_100pct_in_profit",
|
||||
"short": "≥100%",
|
||||
"long": "100%+ Profit"
|
||||
},
|
||||
"_200pct": {
|
||||
"id": "utxos_over_200pct_in_profit",
|
||||
"short": "≥200%",
|
||||
"long": "200%+ Profit"
|
||||
},
|
||||
"_300pct": {
|
||||
"id": "utxos_over_300pct_in_profit",
|
||||
"short": "≥300%",
|
||||
"long": "300%+ Profit"
|
||||
},
|
||||
"_500pct": {
|
||||
"id": "utxos_over_500pct_in_profit",
|
||||
"short": "≥500%",
|
||||
"long": "500%+ Profit"
|
||||
}
|
||||
}
|
||||
|
||||
LOSS_NAMES = {
|
||||
"breakeven": {
|
||||
"id": "utxos_in_loss",
|
||||
"short": "<0%",
|
||||
"long": "In Loss (Below Breakeven)"
|
||||
},
|
||||
"_10pct": {
|
||||
"id": "utxos_over_10pct_in_loss",
|
||||
"short": "≥10%L",
|
||||
"long": "10%+ Loss"
|
||||
},
|
||||
"_20pct": {
|
||||
"id": "utxos_over_20pct_in_loss",
|
||||
"short": "≥20%L",
|
||||
"long": "20%+ Loss"
|
||||
},
|
||||
"_30pct": {
|
||||
"id": "utxos_over_30pct_in_loss",
|
||||
"short": "≥30%L",
|
||||
"long": "30%+ Loss"
|
||||
},
|
||||
"_40pct": {
|
||||
"id": "utxos_over_40pct_in_loss",
|
||||
"short": "≥40%L",
|
||||
"long": "40%+ Loss"
|
||||
},
|
||||
"_50pct": {
|
||||
"id": "utxos_over_50pct_in_loss",
|
||||
"short": "≥50%L",
|
||||
"long": "50%+ Loss"
|
||||
},
|
||||
"_60pct": {
|
||||
"id": "utxos_over_60pct_in_loss",
|
||||
"short": "≥60%L",
|
||||
"long": "60%+ Loss"
|
||||
},
|
||||
"_70pct": {
|
||||
"id": "utxos_over_70pct_in_loss",
|
||||
"short": "≥70%L",
|
||||
"long": "70%+ Loss"
|
||||
},
|
||||
"_80pct": {
|
||||
"id": "utxos_over_80pct_in_loss",
|
||||
"short": "≥80%L",
|
||||
"long": "80%+ Loss"
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, base_url: str = 'http://localhost:3000', timeout: float = 30.0):
|
||||
super().__init__(base_url, timeout)
|
||||
self.metrics = MetricsTree(self)
|
||||
|
||||
@@ -77,6 +77,127 @@ function volumeAndCoinsTree(activity, color, title) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent in profit/loss breakdown tree (shared by full and mid-level activity)
|
||||
* @param {Brk.BaseCumulativeInSumPattern} sent
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function sentProfitLossTree(sent, title) {
|
||||
return [
|
||||
{
|
||||
name: "Sent In Profit",
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Sent Volume In Profit"),
|
||||
bottom: [
|
||||
line({ metric: sent.inProfit.base.usd, name: "Base", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: sent.inProfit.sum._24h.usd, name: "24h", color: colors.time._24h, unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1w.usd, name: "1w", color: colors.time._1w, unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1m.usd, name: "1m", color: colors.time._1m, unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1y.usd, name: "1y", color: colors.time._1y, unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "BTC",
|
||||
title: title("Sent Volume In Profit (BTC)"),
|
||||
bottom: [
|
||||
line({ metric: sent.inProfit.base.btc, name: "Base", color: colors.profit, unit: Unit.btc }),
|
||||
line({ metric: sent.inProfit.sum._24h.btc, name: "24h", color: colors.time._24h, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1w.btc, name: "1w", color: colors.time._1w, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1m.btc, name: "1m", color: colors.time._1m, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1y.btc, name: "1y", color: colors.time._1y, unit: Unit.btc, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sats",
|
||||
title: title("Sent Volume In Profit (Sats)"),
|
||||
bottom: [
|
||||
line({ metric: sent.inProfit.base.sats, name: "Base", color: colors.profit, unit: Unit.sats }),
|
||||
line({ metric: sent.inProfit.sum._24h.sats, name: "24h", color: colors.time._24h, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1w.sats, name: "1w", color: colors.time._1w, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1m.sats, name: "1m", color: colors.time._1m, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.sum._1y.sats, name: "1y", color: colors.time._1y, unit: Unit.sats, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{ name: "Cumulative", title: title("Cumulative Sent In Profit"), bottom: [
|
||||
line({ metric: sent.inProfit.cumulative.usd, name: "USD", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: sent.inProfit.cumulative.btc, name: "BTC", color: colors.profit, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inProfit.cumulative.sats, name: "Sats", color: colors.profit, unit: Unit.sats, defaultActive: false }),
|
||||
]},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sent In Loss",
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Sent Volume In Loss"),
|
||||
bottom: [
|
||||
line({ metric: sent.inLoss.base.usd, name: "Base", color: colors.loss, unit: Unit.usd }),
|
||||
line({ metric: sent.inLoss.sum._24h.usd, name: "24h", color: colors.time._24h, unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1w.usd, name: "1w", color: colors.time._1w, unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1m.usd, name: "1m", color: colors.time._1m, unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1y.usd, name: "1y", color: colors.time._1y, unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "BTC",
|
||||
title: title("Sent Volume In Loss (BTC)"),
|
||||
bottom: [
|
||||
line({ metric: sent.inLoss.base.btc, name: "Base", color: colors.loss, unit: Unit.btc }),
|
||||
line({ metric: sent.inLoss.sum._24h.btc, name: "24h", color: colors.time._24h, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1w.btc, name: "1w", color: colors.time._1w, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1m.btc, name: "1m", color: colors.time._1m, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1y.btc, name: "1y", color: colors.time._1y, unit: Unit.btc, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sats",
|
||||
title: title("Sent Volume In Loss (Sats)"),
|
||||
bottom: [
|
||||
line({ metric: sent.inLoss.base.sats, name: "Base", color: colors.loss, unit: Unit.sats }),
|
||||
line({ metric: sent.inLoss.sum._24h.sats, name: "24h", color: colors.time._24h, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1w.sats, name: "1w", color: colors.time._1w, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1m.sats, name: "1m", color: colors.time._1m, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.sum._1y.sats, name: "1y", color: colors.time._1y, unit: Unit.sats, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{ name: "Cumulative", title: title("Cumulative Sent In Loss"), bottom: [
|
||||
line({ metric: sent.inLoss.cumulative.usd, name: "USD", color: colors.loss, unit: Unit.usd }),
|
||||
line({ metric: sent.inLoss.cumulative.btc, name: "BTC", color: colors.loss, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: sent.inLoss.cumulative.sats, name: "Sats", color: colors.loss, unit: Unit.sats, defaultActive: false }),
|
||||
]},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Volume and coins tree with coinyears, dormancy, and sent in profit/loss (All/STH/LTH)
|
||||
* @param {Brk.CoindaysCoinyearsDormancySentPattern} activity
|
||||
* @param {Color} color
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function fullVolumeTree(activity, color, title) {
|
||||
return [
|
||||
...volumeAndCoinsTree(activity, color, title),
|
||||
...sentProfitLossTree(activity.sent, title),
|
||||
{
|
||||
name: "Coinyears Destroyed",
|
||||
title: title("Coinyears Destroyed"),
|
||||
bottom: [line({ metric: activity.coinyearsDestroyed, name: "CYD", color, unit: Unit.years })],
|
||||
},
|
||||
{
|
||||
name: "Dormancy",
|
||||
title: title("Dormancy"),
|
||||
bottom: [line({ metric: activity.dormancy, name: "Dormancy", color, unit: Unit.days })],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Shared SOPR Helpers
|
||||
// ============================================================================
|
||||
@@ -349,7 +470,7 @@ export function createActivitySectionWithAdjusted({ cohort, title }) {
|
||||
return {
|
||||
name: "Activity",
|
||||
tree: [
|
||||
...volumeAndCoinsTree(tree.activity, color, title),
|
||||
...fullVolumeTree(tree.activity, color, title),
|
||||
{
|
||||
name: "SOPR",
|
||||
tree: [
|
||||
@@ -400,7 +521,7 @@ export function createActivitySection({ cohort, title }) {
|
||||
return {
|
||||
name: "Activity",
|
||||
tree: [
|
||||
...volumeAndCoinsTree(tree.activity, color, title),
|
||||
...fullVolumeTree(tree.activity, color, title),
|
||||
{
|
||||
name: "SOPR",
|
||||
tree: singleRollingSoprTree(sopr.ratio, title),
|
||||
@@ -430,6 +551,7 @@ export function createActivitySectionWithActivity({ cohort, title }) {
|
||||
name: "Activity",
|
||||
tree: [
|
||||
...volumeAndCoinsTree(tree.activity, color, title),
|
||||
...sentProfitLossTree(tree.activity.sent, title),
|
||||
{
|
||||
name: "SOPR",
|
||||
title: title("SOPR (24h)"),
|
||||
|
||||
@@ -83,6 +83,7 @@ export function buildCohortData() {
|
||||
title: `UTXOs ${names.long}`,
|
||||
color: colors.at(i, arr.length),
|
||||
tree: utxoCohorts.ageRange[key],
|
||||
matured: utxoCohorts.matured[key],
|
||||
}));
|
||||
|
||||
const epoch = entries(EPOCH_NAMES).map(([key, names], i, arr) => ({
|
||||
|
||||
@@ -115,6 +115,34 @@ function circulatingSupplyPctSeries(supply) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Ratio of Circulating Supply series (total, profit, loss)
|
||||
* @param {{ relToCirculating: { ratio: AnyMetricPattern }, inProfit: { relToCirculating: { ratio: AnyMetricPattern } }, inLoss: { relToCirculating: { ratio: AnyMetricPattern } } }} supply
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function circulatingSupplyRatioSeries(supply) {
|
||||
return [
|
||||
line({
|
||||
metric: supply.relToCirculating.ratio,
|
||||
name: "Total",
|
||||
color: colors.default,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: supply.inProfit.relToCirculating.ratio,
|
||||
name: "In Profit",
|
||||
color: colors.profit,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: supply.inLoss.relToCirculating.ratio,
|
||||
name: "In Loss",
|
||||
color: colors.loss,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly (UtxoCohortObject | CohortWithoutRelative)[]} list
|
||||
* @param {CohortAll} all
|
||||
@@ -157,7 +185,7 @@ function singleDeltaTree(delta, unit, title, name) {
|
||||
* @template {{ name: string, color: Color }} A
|
||||
* @param {readonly T[]} list
|
||||
* @param {A} all
|
||||
* @param {(c: T | A) => ChangeRatePattern | ChangeRatePattern2} getDelta
|
||||
* @param {(c: T | A) => DeltaPattern} getDelta
|
||||
* @param {Unit} unit
|
||||
* @param {(metric: string) => string} title
|
||||
* @param {string} name
|
||||
@@ -309,11 +337,21 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: [
|
||||
...fullSupplySeries(supply),
|
||||
...circulatingSupplyPctSeries(supply),
|
||||
...ownSupplyPctSeries(supply),
|
||||
tree: [
|
||||
{
|
||||
name: "Overview",
|
||||
title: title("Supply"),
|
||||
bottom: [
|
||||
...fullSupplySeries(supply),
|
||||
...circulatingSupplyPctSeries(supply),
|
||||
...ownSupplyPctSeries(supply),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Supply (% of Circulating)"),
|
||||
bottom: circulatingSupplyRatioSeries(supply),
|
||||
},
|
||||
],
|
||||
},
|
||||
singleUtxoCountChart(cohort, title),
|
||||
@@ -341,10 +379,20 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: [
|
||||
...fullSupplySeries(supply),
|
||||
...circulatingSupplyPctSeries(supply),
|
||||
tree: [
|
||||
{
|
||||
name: "Overview",
|
||||
title: title("Supply"),
|
||||
bottom: [
|
||||
...fullSupplySeries(supply),
|
||||
...circulatingSupplyPctSeries(supply),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Supply (% of Circulating)"),
|
||||
bottom: circulatingSupplyRatioSeries(supply),
|
||||
},
|
||||
],
|
||||
},
|
||||
singleUtxoCountChart(cohort, title),
|
||||
@@ -359,6 +407,33 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Holdings with inProfit/inLoss (no rel, no address count)
|
||||
* For: CohortWithoutRelative (p2ms, unknown, empty)
|
||||
* @param {{ cohort: CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply"),
|
||||
bottom: fullSupplySeries(cohort.tree.supply),
|
||||
},
|
||||
singleUtxoCountChart(cohort, title),
|
||||
{
|
||||
name: "Change",
|
||||
tree: [
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Holdings for CohortAddress (has inProfit/inLoss but no rel, plus address count)
|
||||
* @param {{ cohort: CohortAddress, title: (metric: string) => string }} args
|
||||
@@ -559,6 +634,66 @@ export function createGroupedHoldingsSection({ list, all, title }) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped holdings with inProfit/inLoss (no rel, no address count)
|
||||
* For: CohortWithoutRelative (p2ms, unknown, empty)
|
||||
* @param {{ list: readonly CohortWithoutRelative[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedHoldingsSectionWithProfitLoss({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
}) {
|
||||
return {
|
||||
name: "Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
tree: [
|
||||
{
|
||||
name: "Total",
|
||||
title: title("Supply"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.supply.total, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "In Profit",
|
||||
title: title("Supply In Profit"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({
|
||||
pattern: tree.supply.inProfit,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
title: title("Supply In Loss"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({
|
||||
pattern: tree.supply.inLoss,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
groupedUtxoCountChart(list, all, title),
|
||||
{
|
||||
name: "Change",
|
||||
tree: [
|
||||
groupedDeltaTree(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped holdings with inProfit/inLoss + relToCirculating (no relToOwn)
|
||||
* For: CohortWithAdjusted, CohortAgeRange
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* - activity.js: SOPR, Volume, Lifespan
|
||||
*/
|
||||
|
||||
import { formatCohortTitle } from "../shared.js";
|
||||
import { formatCohortTitle, satsBtcUsd } from "../shared.js";
|
||||
|
||||
// Section builders
|
||||
import {
|
||||
@@ -18,9 +18,11 @@ import {
|
||||
createHoldingsSectionAll,
|
||||
createHoldingsSectionAddress,
|
||||
createHoldingsSectionAddressAmount,
|
||||
createHoldingsSectionWithProfitLoss,
|
||||
createHoldingsSectionWithRelative,
|
||||
createHoldingsSectionWithOwnSupply,
|
||||
createGroupedHoldingsSection,
|
||||
createGroupedHoldingsSectionWithProfitLoss,
|
||||
createGroupedHoldingsSectionAddress,
|
||||
createGroupedHoldingsSectionAddressAmount,
|
||||
createGroupedHoldingsSectionWithRelative,
|
||||
@@ -42,14 +44,16 @@ import {
|
||||
createGroupedCostBasisSectionWithPercentiles,
|
||||
} from "./cost-basis.js";
|
||||
import {
|
||||
createProfitabilitySection,
|
||||
createProfitabilitySectionAll,
|
||||
createProfitabilitySectionFull,
|
||||
createProfitabilitySectionWithNupl,
|
||||
createProfitabilitySectionWithInvestedCapitalPct,
|
||||
createProfitabilitySectionBasicWithInvestedCapitalPct,
|
||||
createProfitabilitySectionAddress,
|
||||
createProfitabilitySectionWithProfitLoss,
|
||||
createProfitabilitySectionLongTerm,
|
||||
createGroupedProfitabilitySection,
|
||||
createGroupedProfitabilitySectionWithProfitLoss,
|
||||
createGroupedProfitabilitySectionWithNupl,
|
||||
createGroupedProfitabilitySectionWithInvestedCapitalPct,
|
||||
createGroupedProfitabilitySectionBasicWithInvestedCapitalPct,
|
||||
@@ -126,7 +130,7 @@ export function createCohortFolderWithAdjusted(cohort) {
|
||||
createHoldingsSectionWithOwnSupply({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createProfitabilitySectionWithNupl({ cohort, title }),
|
||||
createProfitabilitySectionWithInvestedCapitalPct({ cohort, title }),
|
||||
createActivitySectionWithActivity({ cohort, title }),
|
||||
],
|
||||
};
|
||||
@@ -191,6 +195,22 @@ export function createCohortFolderAgeRange(cohort) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Age range folder with matured supply
|
||||
* @param {CohortAgeRangeWithMatured} cohort
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderAgeRangeWithMatured(cohort) {
|
||||
const folder = createCohortFolderAgeRange(cohort);
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
folder.tree.push({
|
||||
name: "Matured",
|
||||
title: title("Matured Supply"),
|
||||
bottom: satsBtcUsd({ pattern: cohort.matured, name: cohort.name }),
|
||||
});
|
||||
return folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic folder WITH RelToMarketCap
|
||||
* @param {CohortBasicWithMarketCap} cohort
|
||||
@@ -242,7 +262,7 @@ export function createCohortFolderAddress(cohort) {
|
||||
createHoldingsSectionAddress({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createProfitabilitySectionBasicWithInvestedCapitalPct({ cohort, title }),
|
||||
createProfitabilitySectionAddress({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
@@ -258,10 +278,10 @@ export function createCohortFolderWithoutRelative(cohort) {
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
createHoldingsSection({ cohort, title }),
|
||||
createHoldingsSectionWithProfitLoss({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createProfitabilitySection({ cohort, title }),
|
||||
createProfitabilitySectionWithProfitLoss({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
@@ -331,7 +351,11 @@ export function createGroupedCohortFolderWithAdjusted({
|
||||
createGroupedHoldingsSectionWithOwnSupply({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithInvestedCapitalPct({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithInvestedCapitalPct({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
}),
|
||||
createGroupedActivitySectionWithActivity({ list, all, title }),
|
||||
],
|
||||
};
|
||||
@@ -412,6 +436,28 @@ export function createGroupedCohortFolderAgeRange({
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ name: string, title: string, list: readonly CohortAgeRangeWithMatured[], all: CohortAll }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedCohortFolderAgeRangeWithMatured({
|
||||
name,
|
||||
title: groupTitle,
|
||||
list,
|
||||
all,
|
||||
}) {
|
||||
const folder = createGroupedCohortFolderAgeRange({ name, title: groupTitle, list, all });
|
||||
const title = formatCohortTitle(groupTitle);
|
||||
folder.tree.push({
|
||||
name: "Matured",
|
||||
title: title("Matured Supply"),
|
||||
bottom: list.flatMap((cohort) =>
|
||||
satsBtcUsd({ pattern: cohort.matured, name: cohort.name, color: cohort.color }),
|
||||
),
|
||||
});
|
||||
return folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortGroupBasicWithMarketCap} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
@@ -503,10 +549,10 @@ export function createGroupedCohortFolderWithoutRelative({
|
||||
return {
|
||||
name: name || "all",
|
||||
tree: [
|
||||
createGroupedHoldingsSection({ list, all, title }),
|
||||
createGroupedHoldingsSectionWithProfitLoss({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedProfitabilitySection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithProfitLoss({ list, all, title }),
|
||||
createGroupedActivitySectionMinimal({ list, all, title }),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -98,8 +98,8 @@ export function createPricesSectionBasic({ cohort, title }) {
|
||||
top: [price({ metric: tree.realized.price, name: "Realized", color })],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Realized Price Ratio"),
|
||||
name: "MVRV",
|
||||
title: title("MVRV"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.mvrv,
|
||||
@@ -109,6 +109,18 @@ export function createPricesSectionBasic({ cohort, title }) {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Price Ratio",
|
||||
title: title("Realized Price Ratio"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.price.ratio,
|
||||
name: "Price Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -105,6 +105,7 @@ function unrealizedPnlTreeAll(u, title) {
|
||||
{ name: "USD", title: title("Unrealized P&L"), bottom: unrealizedUsdSeries(u) },
|
||||
relPnlChart(u.profit.relToMcap, u.loss.relToMcap, "% of Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnGross, u.loss.relToOwnGross, "% of Own P&L", title),
|
||||
...unrealizedCumulativeRollingTree(u.profit, u.loss, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -120,6 +121,7 @@ function unrealizedPnlTreeFull(u, title) {
|
||||
relPnlChart(u.profit.relToMcap, u.loss.relToMcap, "% of Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnMcap, u.loss.relToOwnMcap, "% of Own Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnGross, u.loss.relToOwnGross, "% of Own P&L", title),
|
||||
...unrealizedCumulativeRollingTree(u.profit, u.loss, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -139,21 +141,89 @@ function unrealizedPnlTreeLongTerm(u, title) {
|
||||
},
|
||||
relPnlChart(u.profit.relToOwnMcap, u.loss.relToOwnMcap, "% of Own Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnGross, u.loss.relToOwnGross, "% of Own P&L", title),
|
||||
...unrealizedCumulativeRollingTree(u.profit, u.loss, title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrealized P&L (USD only) for mid-tier cohorts (AgeRange/MaxAge)
|
||||
* Unrealized P&L tree for mid-tier cohorts (AgeRange/MaxAge)
|
||||
* @param {Brk.LossNetNuplProfitPattern} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function unrealizedMid(u) {
|
||||
function unrealizedPnlTreeMid(u, title) {
|
||||
return [
|
||||
...pnlLines(
|
||||
{ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative },
|
||||
Unit.usd,
|
||||
),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized P&L"),
|
||||
bottom: [
|
||||
...pnlLines(
|
||||
{ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative },
|
||||
Unit.usd,
|
||||
),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
...unrealizedCumulativeRollingTree(u.profit, u.loss, title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrealized cumulative + rolling P&L tree (profit and loss have cumulative.usd + sum[w].usd)
|
||||
* @param {{ cumulative: { usd: AnyMetricPattern }, sum: { _24h: { usd: AnyMetricPattern }, _1w: { usd: AnyMetricPattern }, _1m: { usd: AnyMetricPattern }, _1y: { usd: AnyMetricPattern } } }} profit
|
||||
* @param {{ cumulative: { usd: AnyMetricPattern }, sum: { _24h: { usd: AnyMetricPattern }, _1w: { usd: AnyMetricPattern }, _1m: { usd: AnyMetricPattern }, _1y: { usd: AnyMetricPattern } } }} loss
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function unrealizedCumulativeRollingTree(profit, loss, title) {
|
||||
return [
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Unrealized P&L"),
|
||||
bottom: [
|
||||
line({ metric: profit.cumulative.usd, name: "Profit", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: loss.cumulative.usd, name: "Loss", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Profit",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Unrealized Profit"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ metric: profit.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Unrealized Profit (${w.name})`),
|
||||
bottom: [line({ metric: profit.sum[w.key].usd, name: "Profit", color: colors.profit, unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Unrealized Loss"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ metric: loss.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Unrealized Loss (${w.name})`),
|
||||
bottom: [line({ metric: loss.sum[w.key].usd, name: "Loss", color: colors.loss, unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -320,24 +390,103 @@ function realizedPnlCumulativeTreeFull(r, title) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Net realized P&L delta tree (absolute + rate across all rolling windows)
|
||||
* @param {Brk.BaseChangeCumulativeDeltaRelSumPattern | Brk.BaseCumulativeDeltaSumPattern} netPnl
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function realizedNetPnlDeltaTree(netPnl, title) {
|
||||
return {
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Absolute",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Net Realized P&L Change"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({ metric: netPnl.delta.absolute[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L Change (${w.name})`),
|
||||
bottom: [baseline({ metric: netPnl.delta.absolute[w.key].usd, name: "Change", unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rate",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Net Realized P&L Rate"),
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
percentRatio({ pattern: netPnl.delta.rate[w.key], name: w.name, color: w.color }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L Rate (${w.name})`),
|
||||
bottom: percentRatioBaseline({ pattern: netPnl.delta.rate[w.key], name: "Rate" }),
|
||||
})),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Full realized delta tree (absolute + rate + rel to mcap/rcap)
|
||||
* @param {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern | Brk.MetricsTree_Cohorts_Utxo_Lth_Realized} r
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function realized30dChangeTreeFull(r, title) {
|
||||
return [
|
||||
{ name: "USD", title: title("Realized P&L 30d Change"), bottom: [baseline({ metric: r.netPnl.delta.absolute._1m.usd, name: "30d Change", unit: Unit.usd })] },
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Realized 30d Change (% of Mcap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.change1m.relToMcap, name: "30d Change" }),
|
||||
},
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Realized 30d Change (% of Realized Cap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.change1m.relToRcap, name: "30d Change" }),
|
||||
},
|
||||
];
|
||||
function realizedNetPnlDeltaTreeFull(r, title) {
|
||||
const base = realizedNetPnlDeltaTree(r.netPnl, title);
|
||||
return {
|
||||
...base,
|
||||
tree: [
|
||||
...base.tree,
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Net Realized P&L Change (% of Mcap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.change1m.relToMcap, name: "30d Change" }),
|
||||
},
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Net Realized P&L Change (% of Rcap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.change1m.relToRcap, name: "30d Change" }),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolling net realized P&L tree (reusable by full and mid realized)
|
||||
* @param {{ sum: { _24h: { usd: AnyMetricPattern }, _1w: { usd: AnyMetricPattern }, _1m: { usd: AnyMetricPattern }, _1y: { usd: AnyMetricPattern } } }} netPnl
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function rollingNetRealizedTree(netPnl, title) {
|
||||
return {
|
||||
name: "Net",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Net Realized P&L"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({ metric: netPnl.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L (${w.name})`),
|
||||
bottom: [baseline({ metric: netPnl.sum[w.key].usd, name: "Net", unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,23 +531,7 @@ function singleRollingRealizedTreeFull(r, title) {
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Net",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Net Realized P&L"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({ metric: r.netPnl.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L (${w.name})`),
|
||||
bottom: [baseline({ metric: r.netPnl.sum[w.key].usd, name: "Net", unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
rollingNetRealizedTree(r.netPnl, title),
|
||||
{
|
||||
name: "P/L Ratio",
|
||||
tree: [
|
||||
@@ -451,6 +584,96 @@ function singleRollingRealizedTreeBasic(profit, loss, title) {
|
||||
// Realized Subfolder Builders
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Value Created/Destroyed tree for a single P&L side (profit or loss)
|
||||
* @param {CountPattern<number>} valueCreated
|
||||
* @param {CountPattern<number>} valueDestroyed
|
||||
* @param {string} label - "Profit" or "Loss"
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function realizedValueTree(valueCreated, valueDestroyed, label, title) {
|
||||
return {
|
||||
name: label,
|
||||
tree: [
|
||||
{
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title(`${label} Value Created vs Destroyed`),
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) => [
|
||||
line({ metric: valueCreated.sum[w.key], name: `Created (${w.name})`, color: w.color, unit: Unit.usd }),
|
||||
line({ metric: valueDestroyed.sum[w.key], name: `Destroyed (${w.name})`, color: w.color, unit: Unit.usd, style: 2 }),
|
||||
]),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`${label} Value (${w.name})`),
|
||||
bottom: [
|
||||
line({ metric: valueCreated.sum[w.key], name: "Created", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: valueDestroyed.sum[w.key], name: "Destroyed", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title(`Cumulative ${label} Value`),
|
||||
bottom: [
|
||||
line({ metric: valueCreated.cumulative, name: "Created", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: valueDestroyed.cumulative, name: "Destroyed", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Investor price percentiles tree (pct1/2/5/95/98/99)
|
||||
* @param {InvestorPercentilesPattern} percentiles
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function investorPricePercentilesTree(percentiles, title) {
|
||||
/** @type {readonly [InvestorPercentileEntry, string, Color][]} */
|
||||
const pcts = [
|
||||
[percentiles.pct99, "p99", colors.stat.max],
|
||||
[percentiles.pct98, "p98", colors.stat.pct90],
|
||||
[percentiles.pct95, "p95", colors.stat.pct75],
|
||||
[percentiles.pct5, "p5", colors.stat.pct25],
|
||||
[percentiles.pct2, "p2", colors.stat.pct10],
|
||||
[percentiles.pct1, "p1", colors.stat.min],
|
||||
];
|
||||
|
||||
return {
|
||||
name: "Percentiles",
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Investor Price Percentiles"),
|
||||
bottom: pcts.map(([p, name, color]) =>
|
||||
line({ metric: p.price.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Sats",
|
||||
title: title("Investor Price Percentiles (Sats)"),
|
||||
bottom: pcts.map(([p, name, color]) =>
|
||||
line({ metric: p.price.sats, name, color, unit: Unit.sats }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Investor Price Percentile Ratios"),
|
||||
bottom: pcts.map(([p, name, color]) =>
|
||||
baseline({ metric: p.ratio, name, color, unit: Unit.ratio }),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Full realized subfolder (All/STH/LTH)
|
||||
* @param {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern | Brk.MetricsTree_Cohorts_Utxo_Lth_Realized} r
|
||||
@@ -463,7 +686,7 @@ function realizedSubfolderFull(r, title) {
|
||||
tree: [
|
||||
{ name: "P&L", tree: realizedPnlSumTreeFull(r, title) },
|
||||
{ name: "Net", tree: realizedNetPnlSumTreeFull(r, title) },
|
||||
{ name: "30d Change", tree: realized30dChangeTreeFull(r, title) },
|
||||
realizedNetPnlDeltaTreeFull(r, title),
|
||||
{
|
||||
name: "Gross P&L",
|
||||
tree: [
|
||||
@@ -488,6 +711,13 @@ function realizedSubfolderFull(r, title) {
|
||||
{ name: "Cumulative", title: title("Total Realized P&L"), bottom: [line({ metric: r.grossPnl.cumulative.usd, name: "Total", unit: Unit.usd, color: colors.bitcoin })] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Value",
|
||||
tree: [
|
||||
realizedValueTree(r.profit.valueCreated, r.profit.valueDestroyed, "Profit", title),
|
||||
realizedValueTree(r.loss.valueCreated, r.loss.valueDestroyed, "Loss", title),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "P/L Ratio",
|
||||
title: title("Realized Profit/Loss Ratio"),
|
||||
@@ -498,6 +728,12 @@ function realizedSubfolderFull(r, title) {
|
||||
title: title("Realized Peak Regret"),
|
||||
bottom: [line({ metric: r.peakRegret.base, name: "Peak Regret", unit: Unit.usd })],
|
||||
},
|
||||
{
|
||||
name: "Investor Price",
|
||||
tree: [
|
||||
investorPricePercentilesTree(r.investor.price.percentiles, title),
|
||||
],
|
||||
},
|
||||
{ name: "Rolling", tree: singleRollingRealizedTreeFull(r, title) },
|
||||
{
|
||||
name: "Cumulative",
|
||||
@@ -555,12 +791,14 @@ function realizedSubfolderMid(r, title) {
|
||||
title: title("Net Realized P&L"),
|
||||
bottom: [dotsBaseline({ metric: r.netPnl.base.usd, name: "Net", unit: Unit.usd })],
|
||||
},
|
||||
realizedNetPnlDeltaTree(r.netPnl, title),
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized P&L 30d Change"),
|
||||
bottom: [baseline({ metric: r.netPnl.delta.absolute._1m.usd, name: "30d Change", unit: Unit.usd })],
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
...singleRollingRealizedTreeBasic(r.profit, r.loss, title),
|
||||
rollingNetRealizedTree(r.netPnl, title),
|
||||
],
|
||||
},
|
||||
{ name: "Rolling", tree: singleRollingRealizedTreeBasic(r.profit, r.loss, title) },
|
||||
{
|
||||
name: "Cumulative",
|
||||
tree: [
|
||||
@@ -620,7 +858,7 @@ function realizedSubfolderBasic(r, title) {
|
||||
|
||||
/**
|
||||
* Basic profitability section (NUPL only unrealized, basic realized)
|
||||
* @param {{ cohort: UtxoCohortObject | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @param {{ cohort: UtxoCohortObject, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createProfitabilitySection({ cohort, title }) {
|
||||
@@ -639,6 +877,42 @@ export function createProfitabilitySection({ cohort, title }) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Profitability section with unrealized P&L + NUPL (no netPnl, no rel)
|
||||
* For: CohortWithoutRelative (p2ms, unknown, empty)
|
||||
* @param {{ cohort: CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createProfitabilitySectionWithProfitLoss({ cohort, title }) {
|
||||
const u = cohort.tree.unrealized;
|
||||
return {
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{
|
||||
name: "P&L",
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized P&L"),
|
||||
bottom: [
|
||||
...pnlLines({ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative }, Unit.usd),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
...unrealizedCumulativeRollingTree(u.profit, u.loss, title),
|
||||
],
|
||||
},
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
],
|
||||
},
|
||||
realizedSubfolderBasic(cohort.tree.realized, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Section for All cohort
|
||||
* @param {{ cohort: CohortAll, title: (metric: string) => string }} args
|
||||
@@ -764,7 +1038,7 @@ export function createProfitabilitySectionWithInvestedCapitalPct({ cohort, title
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{ name: "P&L", title: title("Unrealized P&L"), bottom: unrealizedMid(u) },
|
||||
{ name: "P&L", tree: unrealizedPnlTreeMid(u, title) },
|
||||
{ name: "Net P&L", title: title("Net Unrealized P&L"), bottom: netUnrealizedMid(u) },
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
],
|
||||
@@ -795,6 +1069,41 @@ export function createProfitabilitySectionBasicWithInvestedCapitalPct({ cohort,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Section for CohortAddress (has unrealized profit/loss + NUPL, basic realized)
|
||||
* @param {{ cohort: CohortAddress, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createProfitabilitySectionAddress({ cohort, title }) {
|
||||
const u = cohort.tree.unrealized;
|
||||
return {
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{
|
||||
name: "P&L",
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized P&L"),
|
||||
bottom: [
|
||||
...pnlLines({ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative }, Unit.usd),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
...unrealizedCumulativeRollingTree(u.profit, u.loss, title),
|
||||
],
|
||||
},
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
],
|
||||
},
|
||||
realizedSubfolderBasic(cohort.tree.realized, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Grouped Cohort Helpers
|
||||
// ============================================================================
|
||||
@@ -945,6 +1254,63 @@ function groupedRealizedSubfolder(list, all, title) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped net realized P&L delta (Absolute + Rate with all rolling windows)
|
||||
* @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} list
|
||||
* @param {CohortAll} all
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function groupedRealizedNetPnlDeltaTree(list, all, title) {
|
||||
return {
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Absolute",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Net Realized P&L Change"),
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
mapCohortsWithAll(list, all, ({ name, tree }) =>
|
||||
baseline({ metric: tree.realized.netPnl.delta.absolute[w.key].usd, name: `${name} (${w.name})`, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L Change (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.realized.netPnl.delta.absolute[w.key].usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rate",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Net Realized P&L Rate"),
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
flatMapCohortsWithAll(list, all, ({ name, tree }) =>
|
||||
percentRatio({ pattern: tree.realized.netPnl.delta.rate[w.key], name: `${name} (${w.name})`, color: w.color }),
|
||||
),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L Rate (${w.name})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.realized.netPnl.delta.rate[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped realized subfolder for full cohorts
|
||||
* @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} list
|
||||
@@ -964,13 +1330,7 @@ function groupedRealizedSubfolderFull(list, all, title) {
|
||||
baseline({ metric: tree.realized.netPnl.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized P&L 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.realized.netPnl.delta.absolute._1m.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
groupedRealizedNetPnlDeltaTree(list, all, title),
|
||||
{ name: "Rolling", tree: groupedRollingRealizedChartsFull(list, all, title) },
|
||||
{
|
||||
name: "Cumulative",
|
||||
@@ -1265,6 +1625,41 @@ export function createGroupedProfitabilitySection({ list, all, title }) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped profitability with unrealized profit/loss + NUPL
|
||||
* For: CohortWithoutRelative (p2ms, unknown, empty)
|
||||
* @param {{ list: readonly CohortWithoutRelative[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedProfitabilitySectionWithProfitLoss({ list, all, title }) {
|
||||
return {
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{
|
||||
name: "Profit",
|
||||
title: title("Unrealized Profit"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
title: title("Unrealized Loss"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...groupedNuplCharts(list, all, title),
|
||||
],
|
||||
},
|
||||
groupedRealizedSubfolder(list, all, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped section with invested capital % (basic cohorts — uses NUPL only)
|
||||
* @param {{ list: readonly CohortBasicWithoutMarketCap[], all: CohortAll, title: (metric: string) => string }} args
|
||||
|
||||
89
website/scripts/options/distribution/utxo-profitability.js
Normal file
89
website/scripts/options/distribution/utxo-profitability.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/** UTXO Profitability section — range bands, cumulative profit/loss thresholds */
|
||||
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { entries } from "../../utils/array.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, price } from "../series.js";
|
||||
import { brk } from "../../client.js";
|
||||
import { satsBtcUsd } from "../shared.js";
|
||||
|
||||
/**
|
||||
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }[]} list
|
||||
* @param {string} titlePrefix
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function bucketCharts(list, titlePrefix) {
|
||||
return [
|
||||
{
|
||||
name: "Supply",
|
||||
title: `${titlePrefix}: Supply`,
|
||||
bottom: list.flatMap(({ name, color, pattern }) =>
|
||||
satsBtcUsd({ pattern: pattern.supply, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Realized Cap",
|
||||
title: `${titlePrefix}: Realized Cap`,
|
||||
bottom: list.map(({ name, color, pattern }) =>
|
||||
line({ metric: pattern.realizedCap, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Realized Price",
|
||||
title: `${titlePrefix}: Realized Price`,
|
||||
top: list.map(({ name, color, pattern }) =>
|
||||
price({ metric: pattern.realizedPrice, name, color }),
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createUtxoProfitabilitySection() {
|
||||
const { range, profit, loss } = brk.metrics.cohorts.utxo.profitability;
|
||||
const {
|
||||
PROFITABILITY_RANGE_NAMES,
|
||||
PROFIT_NAMES,
|
||||
LOSS_NAMES,
|
||||
} = brk;
|
||||
|
||||
const rangeList = entries(PROFITABILITY_RANGE_NAMES).map(
|
||||
([key, names], i, arr) => ({
|
||||
name: names.short,
|
||||
color: colors.at(i, arr.length),
|
||||
pattern: range[key],
|
||||
}),
|
||||
);
|
||||
|
||||
const profitList = entries(PROFIT_NAMES).map(([key, names], i, arr) => ({
|
||||
name: names.short,
|
||||
color: colors.at(i, arr.length),
|
||||
pattern: profit[key],
|
||||
}));
|
||||
|
||||
const lossList = entries(LOSS_NAMES).map(([key, names], i, arr) => ({
|
||||
name: names.short,
|
||||
color: colors.at(i, arr.length),
|
||||
pattern: loss[key],
|
||||
}));
|
||||
|
||||
return {
|
||||
name: "UTXO Profitability",
|
||||
tree: [
|
||||
{
|
||||
name: "Range",
|
||||
tree: bucketCharts(rangeList, "Profitability Range"),
|
||||
},
|
||||
{
|
||||
name: "In Profit",
|
||||
tree: bucketCharts(profitList, "In Profit"),
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
tree: bucketCharts(lossList, "In Loss"),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -36,6 +36,18 @@ import { periodIdToName } from "./utils.js";
|
||||
* @property {Brk.BpsCentsRatioSatsUsdPattern} ratio
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create index (percent) + ratio line pair from a BpsPercentRatioPattern
|
||||
* @param {{ pattern: { percent: AnyMetricPattern, ratio: AnyMetricPattern }, name: string, color?: Color, defaultActive?: boolean }} args
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function indexRatio({ pattern, name, color, defaultActive }) {
|
||||
return [
|
||||
line({ metric: pattern.percent, name, color, defaultActive, unit: Unit.index }),
|
||||
line({ metric: pattern.ratio, name, color, defaultActive, unit: Unit.ratio }),
|
||||
];
|
||||
}
|
||||
|
||||
const commonMaIds = /** @type {const} */ ([
|
||||
"1w",
|
||||
"1m",
|
||||
@@ -671,166 +683,43 @@ export function createMarketSection() {
|
||||
name: "Compare",
|
||||
title: "RSI Comparison",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._24h.rsi.percent,
|
||||
name: "1d",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1w.rsi.percent,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1m.rsi.percent,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1y.rsi.percent,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...ROLLING_WINDOWS.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 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Day",
|
||||
title: "RSI (1d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._24h.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._24h.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._24h.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 70 }),
|
||||
priceLine({
|
||||
unit: Unit.index,
|
||||
number: 50,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 30 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Week",
|
||||
title: "RSI (1w)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._1w.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1w.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1w.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 70 }),
|
||||
priceLine({
|
||||
unit: Unit.index,
|
||||
number: 50,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 30 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Month",
|
||||
title: "RSI (1m)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._1m.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1m.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1m.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 70 }),
|
||||
priceLine({
|
||||
unit: Unit.index,
|
||||
number: 50,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 30 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Year",
|
||||
title: "RSI (1y)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._1y.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1y.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1y.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 70 }),
|
||||
priceLine({
|
||||
unit: Unit.index,
|
||||
number: 50,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ unit: Unit.index, number: 30 }),
|
||||
],
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => {
|
||||
const rsi = technical.rsi[w.key];
|
||||
return {
|
||||
name: w.name,
|
||||
tree: [
|
||||
{
|
||||
name: "Value",
|
||||
title: `RSI (${w.name})`,
|
||||
bottom: [
|
||||
...indexRatio({ pattern: rsi.rsi, name: "RSI", color: colors.indicator.main }),
|
||||
...indexRatio({ pattern: rsi.rsiMax, name: "Max", color: colors.stat.max, defaultActive: false }),
|
||||
...indexRatio({ pattern: rsi.rsiMin, name: "Min", color: colors.stat.min, defaultActive: false }),
|
||||
priceLine({ unit: Unit.index, number: 70 }),
|
||||
priceLine({ unit: Unit.index, number: 50, defaultActive: false }),
|
||||
priceLine({ unit: Unit.index, number: 30 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Components",
|
||||
title: `RSI Components (${w.name})`,
|
||||
bottom: [
|
||||
line({ metric: rsi.averageGain, name: "Avg Gain", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: rsi.averageLoss, name: "Avg Loss", color: colors.loss, unit: Unit.usd }),
|
||||
line({ metric: rsi.gains, name: "Gains", color: colors.profit, defaultActive: false, unit: Unit.usd }),
|
||||
line({ metric: rsi.losses, name: "Losses", color: colors.loss, defaultActive: false, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -840,127 +729,33 @@ export function createMarketSection() {
|
||||
name: "Compare",
|
||||
title: "Stochastic RSI Comparison",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._24h.stochRsiK.percent,
|
||||
name: "1d K",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1w.stochRsiK.percent,
|
||||
name: "1w K",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1m.stochRsiK.percent,
|
||||
name: "1m K",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1y.stochRsiK.percent,
|
||||
name: "1y K",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Day",
|
||||
title: "Stochastic RSI (1d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._24h.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._24h.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Week",
|
||||
title: "Stochastic RSI (1w)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._1w.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1w.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Month",
|
||||
title: "Stochastic RSI (1m)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._1m.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1m.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "1 Year",
|
||||
title: "Stochastic RSI (1y)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.rsi._1y.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.rsi._1y.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...ROLLING_WINDOWS.flatMap((w) =>
|
||||
indexRatio({ pattern: technical.rsi[w.key].stochRsiK, name: `${w.name} K`, color: w.color }),
|
||||
),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
],
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => {
|
||||
const rsi = technical.rsi[w.key];
|
||||
return {
|
||||
name: w.name,
|
||||
title: `Stochastic RSI (${w.name})`,
|
||||
bottom: [
|
||||
...indexRatio({ pattern: rsi.stochRsi, name: "Raw", color: colors.indicator.main, defaultActive: false }),
|
||||
...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: "Stochastic",
|
||||
title: "Stochastic Oscillator",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.stochK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: technical.stochD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
...indexRatio({ pattern: technical.stochK, name: "K", color: colors.indicator.fast }),
|
||||
...indexRatio({ pattern: technical.stochD, name: "D", color: colors.indicator.slow }),
|
||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
||||
],
|
||||
},
|
||||
@@ -970,32 +765,9 @@ export function createMarketSection() {
|
||||
{
|
||||
name: "Compare",
|
||||
title: "MACD Comparison",
|
||||
bottom: [
|
||||
line({
|
||||
metric: technical.macd._24h.line,
|
||||
name: "1d",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: technical.macd._1w.line,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: technical.macd._1m.line,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: technical.macd._1y.line,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ metric: technical.macd[w.key].line, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
|
||||
@@ -118,7 +118,6 @@ export function createNetworkSection() {
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
const countTypes = /** @type {const} */ ([
|
||||
{
|
||||
name: "Funded",
|
||||
@@ -568,7 +567,7 @@ export function createNetworkSection() {
|
||||
name: "Base",
|
||||
title: "OP_RETURN Burned",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: supply.burned.opReturn.base,
|
||||
pattern: scripts.value.opReturn.base,
|
||||
name: "sum",
|
||||
}),
|
||||
},
|
||||
@@ -580,7 +579,7 @@ export function createNetworkSection() {
|
||||
title: "OP_RETURN Burned Rolling",
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
satsBtcUsd({
|
||||
pattern: supply.burned.opReturn.sum[w.key],
|
||||
pattern: scripts.value.opReturn.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
@@ -590,7 +589,7 @@ export function createNetworkSection() {
|
||||
name: w.name,
|
||||
title: `OP_RETURN Burned ${w.name}`,
|
||||
bottom: satsBtcUsd({
|
||||
pattern: supply.burned.opReturn.sum[w.key],
|
||||
pattern: scripts.value.opReturn.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
@@ -601,7 +600,7 @@ export function createNetworkSection() {
|
||||
name: "Cumulative",
|
||||
title: "OP_RETURN Burned (Total)",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: supply.burned.opReturn.cumulative,
|
||||
pattern: scripts.value.opReturn.cumulative,
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
@@ -1074,7 +1073,8 @@ export function createNetworkSection() {
|
||||
title: "UTXO Count 30d Change",
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: cohorts.utxo.all.outputs.unspentCount.delta.absolute._1m,
|
||||
metric:
|
||||
cohorts.utxo.all.outputs.unspentCount.delta.absolute._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
createCohortFolderFull,
|
||||
createCohortFolderWithAdjusted,
|
||||
createCohortFolderLongTerm,
|
||||
createCohortFolderAgeRange,
|
||||
createCohortFolderAgeRangeWithMatured,
|
||||
createCohortFolderBasicWithMarketCap,
|
||||
createCohortFolderBasicWithoutMarketCap,
|
||||
createCohortFolderWithoutRelative,
|
||||
@@ -14,12 +14,13 @@ import {
|
||||
createAddressCohortFolder,
|
||||
createGroupedCohortFolderWithAdjusted,
|
||||
createGroupedCohortFolderWithNupl,
|
||||
createGroupedCohortFolderAgeRange,
|
||||
createGroupedCohortFolderAgeRangeWithMatured,
|
||||
createGroupedCohortFolderBasicWithMarketCap,
|
||||
createGroupedCohortFolderBasicWithoutMarketCap,
|
||||
createGroupedCohortFolderAddress,
|
||||
createGroupedAddressCohortFolder,
|
||||
} from "./distribution/index.js";
|
||||
import { createUtxoProfitabilitySection } from "./distribution/utxo-profitability.js";
|
||||
import { createMarketSection } from "./market.js";
|
||||
import { createNetworkSection } from "./network.js";
|
||||
import { createMiningSection } from "./mining.js";
|
||||
@@ -110,26 +111,26 @@ export function createPartialOptions() {
|
||||
{
|
||||
name: "Older Than",
|
||||
tree: [
|
||||
createGroupedCohortFolderBasicWithMarketCap({
|
||||
createGroupedCohortFolderWithAdjusted({
|
||||
name: "Compare",
|
||||
title: "Over Age",
|
||||
list: overAge,
|
||||
all: cohortAll,
|
||||
}),
|
||||
...overAge.map(createCohortFolderBasicWithMarketCap),
|
||||
...overAge.map(createCohortFolderWithAdjusted),
|
||||
],
|
||||
},
|
||||
// Range
|
||||
{
|
||||
name: "Range",
|
||||
tree: [
|
||||
createGroupedCohortFolderAgeRange({
|
||||
createGroupedCohortFolderAgeRangeWithMatured({
|
||||
name: "Compare",
|
||||
title: "Age Ranges",
|
||||
list: ageRange,
|
||||
all: cohortAll,
|
||||
}),
|
||||
...ageRange.map(createCohortFolderAgeRange),
|
||||
...ageRange.map(createCohortFolderAgeRangeWithMatured),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -246,13 +247,13 @@ export function createPartialOptions() {
|
||||
{
|
||||
name: "Epochs",
|
||||
tree: [
|
||||
createGroupedCohortFolderBasicWithoutMarketCap({
|
||||
createGroupedCohortFolderWithAdjusted({
|
||||
name: "Compare",
|
||||
title: "Epochs",
|
||||
list: epoch,
|
||||
all: cohortAll,
|
||||
}),
|
||||
...epoch.map(createCohortFolderBasicWithoutMarketCap),
|
||||
...epoch.map(createCohortFolderWithAdjusted),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -260,15 +261,18 @@ export function createPartialOptions() {
|
||||
{
|
||||
name: "Years",
|
||||
tree: [
|
||||
createGroupedCohortFolderBasicWithoutMarketCap({
|
||||
createGroupedCohortFolderWithAdjusted({
|
||||
name: "Compare",
|
||||
title: "Years",
|
||||
list: class_,
|
||||
all: cohortAll,
|
||||
}),
|
||||
...class_.map(createCohortFolderBasicWithoutMarketCap),
|
||||
...class_.map(createCohortFolderWithAdjusted),
|
||||
],
|
||||
},
|
||||
|
||||
// UTXO Profitability bands
|
||||
createUtxoProfitabilitySection(),
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
@@ -233,6 +233,9 @@
|
||||
* @property {Color} color
|
||||
* @property {AgeRangePattern} tree
|
||||
*
|
||||
* Age range cohort with matured supply
|
||||
* @typedef {CohortAgeRange & { matured: AnyValuePattern }} CohortAgeRangeWithMatured
|
||||
*
|
||||
* Basic cohort WITH RelToMarketCap (geAmount.*, ltAmount.*)
|
||||
* @typedef {Object} CohortBasicWithMarketCap
|
||||
* @property {string} name
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*
|
||||
* @import { Color } from "./utils/colors.js"
|
||||
*
|
||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, FetchedDotsBaselineSeriesBlueprint, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, PatternWithoutRelative, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortWithoutRelative, CohortAddress, CohortLongTerm, CohortAgeRange, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, CohortGroupWithoutRelative, CohortGroupAddress, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint, FetchedPriceSeriesBlueprint, AnyPricePattern, AnyValuePattern } from "./options/partial.js"
|
||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, FetchedDotsBaselineSeriesBlueprint, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, PatternWithoutRelative, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortWithoutRelative, CohortAddress, CohortLongTerm, CohortAgeRange, CohortAgeRangeWithMatured, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, CohortGroupWithoutRelative, CohortGroupAddress, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint, FetchedPriceSeriesBlueprint, AnyPricePattern, AnyValuePattern } from "./options/partial.js"
|
||||
*
|
||||
*
|
||||
* @import { UnitObject as Unit } from "./utils/units.js"
|
||||
@@ -49,7 +49,7 @@
|
||||
* @typedef {Brk.AddressOutputsRealizedSupplyUnrealizedPattern} AddressAmountPattern
|
||||
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} BasicUtxoPattern
|
||||
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} EpochPattern
|
||||
* @typedef {Brk.OutputsRealizedSupplyUnrealizedPattern} EmptyPattern
|
||||
* @typedef {Brk.OutputsRealizedSupplyUnrealizedPattern2} EmptyPattern
|
||||
* @typedef {Brk._0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} Ratio1ySdPattern
|
||||
* @typedef {Brk.Dollars} Dollars
|
||||
* CoinbasePattern: base + cumulative + rolling windows (flattened)
|
||||
@@ -82,6 +82,9 @@
|
||||
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} FullRelativePattern
|
||||
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} UnrealizedPattern
|
||||
*
|
||||
* Profitability bucket pattern
|
||||
* @typedef {Brk.RealizedSupplyPattern} RealizedSupplyPattern
|
||||
*
|
||||
* Realized patterns
|
||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
|
||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern2
|
||||
@@ -204,6 +207,14 @@
|
||||
* All cohorts with circulating supply relative metrics
|
||||
* @typedef {UtxoCohortWithCirculatingSupplyRelative | AddressCohortWithCirculatingSupplyRelative} CohortWithCirculatingSupplyRelative
|
||||
*
|
||||
* Delta patterns with absolute + rate rolling windows
|
||||
* @typedef {Brk.AbsoluteRatePattern} DeltaPattern
|
||||
* @typedef {Brk.AbsoluteRatePattern2} FiatDeltaPattern
|
||||
*
|
||||
* Investor price percentiles (pct1/2/5/95/98/99)
|
||||
* @typedef {Brk.Pct1Pct2Pct5Pct95Pct98Pct99Pattern} InvestorPercentilesPattern
|
||||
* @typedef {Brk.BpsPriceRatioPattern} InvestorPercentileEntry
|
||||
*
|
||||
* Generic tree node type for walking
|
||||
* @typedef {AnyMetricPattern | Record<string, unknown>} TreeNode
|
||||
*
|
||||
|
||||
@@ -19,11 +19,6 @@ export const Unit = /** @type {const} */ ({
|
||||
// Relative percentages
|
||||
pctSupply: { id: "pct-supply", name: "% of circulating" },
|
||||
pctOwn: { id: "pct-own", name: "% of Own" },
|
||||
pctMcap: { id: "pct-mcap", name: "% of Market Cap" },
|
||||
pctRcap: { id: "pct-rcap", name: "% of Realized Cap" },
|
||||
pctOwnRcap: { id: "pct-own-rcap", name: "% of Own Realized Cap" },
|
||||
pctOwnMcap: { id: "pct-own-mcap", name: "% of Own Market Cap" },
|
||||
pctOwnPnl: { id: "pct-own-pnl", name: "% of Own P&L" },
|
||||
|
||||
// Time
|
||||
days: { id: "days", name: "Days" },
|
||||
|
||||
Reference in New Issue
Block a user