next: ai part 11

This commit is contained in:
nym21
2026-07-29 10:50:14 +02:00
parent eccbda34ee
commit 1cdee36f2e
56 changed files with 2269 additions and 1037 deletions
+214 -214
View File
@@ -1276,7 +1276,7 @@ pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern {
pub mvrv: SeriesPattern1<StoredF32>,
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
pub peak_regret: BlockCumulativeSumPattern,
pub price: CentsPercentilesRatioRawSatsSmaStdUsdPattern,
pub price: CentsPercentilesPpmRatioSatsSmaStdUsdPattern,
pub profit: BlockCumulativeSumPattern,
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
pub sell_side_risk_ratio: _1m1w1y24hPattern8,
@@ -1292,7 +1292,7 @@ pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern2 {
pub mvrv: SeriesPattern1<StoredF32>,
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
pub peak_regret: BlockCumulativeSumPattern,
pub price: CentsPercentilesRatioRawSatsSmaStdUsdPattern,
pub price: CentsPercentilesPpmRatioSatsSmaStdUsdPattern,
pub profit: BlockCumulativeSumPattern,
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
pub sell_side_risk_ratio: _1m1w1y24hPattern8,
@@ -1641,7 +1641,7 @@ pub struct CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 {
pub invested_capital: InPattern2,
pub loss: CentsNegativeToUsdPattern2,
pub net_pnl: CentsToUsdPattern3,
pub nupl: RatioRawPattern,
pub nupl: PpmRatioPattern,
pub profit: CentsToUsdPattern4,
pub sentiment: GreedNetPainPattern,
}
@@ -1656,7 +1656,7 @@ impl CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 {
invested_capital: InPattern2::new(client.clone(), _m(&acc, "invested_capital_in")),
loss: CentsNegativeToUsdPattern2::new(client.clone(), _m(&acc, "unrealized_loss")),
net_pnl: CentsToUsdPattern3::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
nupl: RatioRawPattern::new(client.clone(), _m(&acc, "nupl")),
nupl: PpmRatioPattern::new(client.clone(), _m(&acc, "nupl")),
profit: CentsToUsdPattern4::new(client.clone(), _m(&acc, "unrealized_profit")),
sentiment: GreedNetPainPattern::new(client.clone(), acc.clone()),
}
@@ -1694,11 +1694,11 @@ impl Pct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern {
}
/// Pattern struct for repeated tree structure.
pub struct CentsPercentilesRatioRawSatsSmaStdUsdPattern {
pub struct CentsPercentilesPpmRatioSatsSmaStdUsdPattern {
pub cents: SeriesPattern1<Cents>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub sats: SeriesPattern1<SatsFract>,
pub sma: _1m1w1y2y4yAllPattern,
pub std_dev: _1y2y4yAllPattern,
@@ -1707,28 +1707,28 @@ pub struct CentsPercentilesRatioRawSatsSmaStdUsdPattern {
/// Pattern struct for repeated tree structure.
pub struct Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern {
pub pct0_5: PriceRatioRawPattern,
pub pct1: PriceRatioRawPattern,
pub pct2: PriceRatioRawPattern,
pub pct5: PriceRatioRawPattern,
pub pct95: PriceRatioRawPattern,
pub pct98: PriceRatioRawPattern,
pub pct99: PriceRatioRawPattern,
pub pct99_5: PriceRatioRawPattern,
pub pct0_5: PpmPriceRatioPattern,
pub pct1: PpmPriceRatioPattern,
pub pct2: PpmPriceRatioPattern,
pub pct5: PpmPriceRatioPattern,
pub pct95: PpmPriceRatioPattern,
pub pct98: PpmPriceRatioPattern,
pub pct99: PpmPriceRatioPattern,
pub pct99_5: PpmPriceRatioPattern,
}
impl Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
pct0_5: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct0_5".to_string()),
pct1: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct1".to_string()),
pct2: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct2".to_string()),
pct5: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct5".to_string()),
pct95: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct95".to_string()),
pct98: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct98".to_string()),
pct99: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct99".to_string()),
pct99_5: PriceRatioRawPattern::new(client.clone(), acc.clone(), "pct99_5".to_string()),
pct0_5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct0_5".to_string()),
pct1: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct1".to_string()),
pct2: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct2".to_string()),
pct5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct5".to_string()),
pct95: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct95".to_string()),
pct98: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct98".to_string()),
pct99: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99".to_string()),
pct99_5: PpmPriceRatioPattern::new(client.clone(), acc.clone(), "pct99_5".to_string()),
}
}
}
@@ -1813,7 +1813,7 @@ pub struct CapLossMvrvNetPriceProfitSoprPattern {
pub loss: BlockCumulativeNegativeSumPattern,
pub mvrv: SeriesPattern1<StoredF32>,
pub net_pnl: BlockCumulativeDeltaSumPattern,
pub price: CentsRatioRawSatsUsdPattern,
pub price: CentsPpmRatioSatsUsdPattern,
pub profit: BlockCumulativeSumPattern,
pub sopr: RatioValuePattern,
}
@@ -1826,7 +1826,7 @@ impl CapLossMvrvNetPriceProfitSoprPattern {
loss: BlockCumulativeNegativeSumPattern::new(client.clone(), _m(&acc, "realized_loss")),
mvrv: SeriesPattern1::new(client.clone(), _m(&acc, "mvrv")),
net_pnl: BlockCumulativeDeltaSumPattern::new(client.clone(), _m(&acc, "net_realized_pnl")),
price: CentsRatioRawSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")),
price: CentsPpmRatioSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")),
profit: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_profit")),
sopr: RatioValuePattern::new(client.clone(), acc.clone()),
}
@@ -1939,24 +1939,24 @@ impl<T: DeserializeOwned> MaxMedianMinPct10Pct25Pct75Pct90Pattern<T> {
/// Pattern struct for repeated tree structure.
pub struct _1m1w1y2y4yAllPattern {
pub _1m: RatioRawPattern2,
pub _1w: RatioRawPattern2,
pub _1y: RatioRawPattern2,
pub _2y: RatioRawPattern2,
pub _4y: RatioRawPattern2,
pub all: RatioRawPattern2,
pub _1m: PpmRatioPattern2,
pub _1w: PpmRatioPattern2,
pub _1y: PpmRatioPattern2,
pub _2y: PpmRatioPattern2,
pub _4y: PpmRatioPattern2,
pub all: PpmRatioPattern2,
}
impl _1m1w1y2y4yAllPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
_1m: RatioRawPattern2::new(client.clone(), _m(&acc, "1m")),
_1w: RatioRawPattern2::new(client.clone(), _m(&acc, "1w")),
_1y: RatioRawPattern2::new(client.clone(), _m(&acc, "1y")),
_2y: RatioRawPattern2::new(client.clone(), _m(&acc, "2y")),
_4y: RatioRawPattern2::new(client.clone(), _m(&acc, "4y")),
all: RatioRawPattern2::new(client.clone(), _m(&acc, "all")),
_1m: PpmRatioPattern2::new(client.clone(), _m(&acc, "1m")),
_1w: PpmRatioPattern2::new(client.clone(), _m(&acc, "1w")),
_1y: PpmRatioPattern2::new(client.clone(), _m(&acc, "1y")),
_2y: PpmRatioPattern2::new(client.clone(), _m(&acc, "2y")),
_4y: PpmRatioPattern2::new(client.clone(), _m(&acc, "4y")),
all: PpmRatioPattern2::new(client.clone(), _m(&acc, "all")),
}
}
}
@@ -2034,23 +2034,23 @@ impl CentsNegativeToUsdPattern2 {
}
/// Pattern struct for repeated tree structure.
pub struct CentsPercentilesRatioRawSatsUsdPattern {
pub struct CentsPercentilesPpmRatioSatsUsdPattern {
pub cents: SeriesPattern1<Cents>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub sats: SeriesPattern1<SatsFract>,
pub usd: SeriesPattern1<Dollars>,
}
impl CentsPercentilesRatioRawSatsUsdPattern {
impl CentsPercentilesPpmRatioSatsUsdPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), acc.clone()),
ppm: SeriesPattern1::new(client.clone(), _m(&acc, "ratio_ppm")),
ratio: SeriesPattern1::new(client.clone(), _m(&acc, "ratio")),
raw: SeriesPattern1::new(client.clone(), _m(&acc, "ratio_ppm")),
sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")),
usd: SeriesPattern1::new(client.clone(), acc.clone()),
}
@@ -2341,7 +2341,7 @@ pub struct CapLossMvrvPriceProfitPattern {
pub cap: CentsDeltaUsdPattern,
pub loss: BlockCumulativeSumPattern,
pub mvrv: SeriesPattern1<StoredF32>,
pub price: CentsRatioRawSatsUsdPattern,
pub price: CentsPpmRatioSatsUsdPattern,
pub profit: BlockCumulativeSumPattern,
}
@@ -2352,28 +2352,28 @@ impl CapLossMvrvPriceProfitPattern {
cap: CentsDeltaUsdPattern::new(client.clone(), _m(&acc, "realized_cap")),
loss: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_loss")),
mvrv: SeriesPattern1::new(client.clone(), _m(&acc, "mvrv")),
price: CentsRatioRawSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")),
price: CentsPpmRatioSatsUsdPattern::new(client.clone(), _m(&acc, "realized_price")),
profit: BlockCumulativeSumPattern::new(client.clone(), _m(&acc, "realized_profit")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CentsRatioRawSatsUsdPattern {
pub struct CentsPpmRatioSatsUsdPattern {
pub cents: SeriesPattern1<Cents>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub sats: SeriesPattern1<SatsFract>,
pub usd: SeriesPattern1<Dollars>,
}
impl CentsRatioRawSatsUsdPattern {
impl CentsPpmRatioSatsUsdPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")),
ppm: SeriesPattern1::new(client.clone(), _m(&acc, "ratio_ppm")),
ratio: SeriesPattern1::new(client.clone(), _m(&acc, "ratio")),
raw: SeriesPattern1::new(client.clone(), _m(&acc, "ratio_ppm")),
sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")),
usd: SeriesPattern1::new(client.clone(), acc.clone()),
}
@@ -2827,7 +2827,7 @@ impl CoindaysCoinyearsDormancyTransferPattern {
pub struct LossNetNuplProfitPattern {
pub loss: CentsNegativeUsdPattern,
pub net_pnl: CentsUsdPattern,
pub nupl: RatioRawPattern,
pub nupl: PpmRatioPattern,
pub profit: CentsUsdPattern3,
}
@@ -2837,7 +2837,7 @@ impl LossNetNuplProfitPattern {
Self {
loss: CentsNegativeUsdPattern::new(client.clone(), _m(&acc, "unrealized_loss")),
net_pnl: CentsUsdPattern::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
nupl: RatioRawPattern::new(client.clone(), _m(&acc, "nupl")),
nupl: PpmRatioPattern::new(client.clone(), _m(&acc, "nupl")),
profit: CentsUsdPattern3::new(client.clone(), _m(&acc, "unrealized_profit")),
}
}
@@ -2865,7 +2865,7 @@ impl MobilitySpendingSupplyPattern {
/// Pattern struct for repeated tree structure.
pub struct NuplRealizedSupplyUnrealizedPattern {
pub nupl: RatioRawPattern,
pub nupl: PpmRatioPattern,
pub realized_cap: AllSthPattern,
pub supply: AllSthPattern2,
pub unrealized_pnl: AllSthPattern,
@@ -2875,7 +2875,7 @@ impl NuplRealizedSupplyUnrealizedPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
nupl: RatioRawPattern::new(client.clone(), _m(&acc, "nupl")),
nupl: PpmRatioPattern::new(client.clone(), _m(&acc, "nupl")),
realized_cap: AllSthPattern::new(client.clone(), acc.clone(), "realized_cap".to_string()),
supply: AllSthPattern2::new(client.clone(), acc.clone()),
unrealized_pnl: AllSthPattern::new(client.clone(), acc.clone(), "unrealized_pnl".to_string()),
@@ -3131,7 +3131,7 @@ impl GreedNetPainPattern {
/// Pattern struct for repeated tree structure.
pub struct LossNuplProfitPattern {
pub loss: CentsNegativeUsdPattern,
pub nupl: RatioRawPattern,
pub nupl: PpmRatioPattern,
pub profit: CentsUsdPattern3,
}
@@ -3140,7 +3140,7 @@ impl LossNuplProfitPattern {
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
loss: CentsNegativeUsdPattern::new(client.clone(), _m(&acc, "unrealized_loss")),
nupl: RatioRawPattern::new(client.clone(), _m(&acc, "nupl")),
nupl: PpmRatioPattern::new(client.clone(), _m(&acc, "nupl")),
profit: CentsUsdPattern3::new(client.clone(), _m(&acc, "unrealized_profit")),
}
}
@@ -3201,19 +3201,19 @@ impl PercentPpmRatioPattern {
}
/// Pattern struct for repeated tree structure.
pub struct PriceRatioRawPattern {
pub struct PpmPriceRatioPattern {
pub ppm: SeriesPattern1<PartsPerMillion32>,
pub price: CentsSatsUsdPattern,
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillion32>,
}
impl PriceRatioRawPattern {
impl PpmPriceRatioPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String, disc: String) -> Self {
Self {
ppm: SeriesPattern1::new(client.clone(), _m(&acc, &format!("ratio_{disc}_ppm", disc=disc))),
price: CentsSatsUsdPattern::new(client.clone(), _m(&acc, &disc)),
ratio: SeriesPattern1::new(client.clone(), _m(&acc, &format!("ratio_{disc}", disc=disc))),
raw: SeriesPattern1::new(client.clone(), _m(&acc, &format!("ratio_{disc}_ppm", disc=disc))),
}
}
}
@@ -3642,6 +3642,54 @@ impl PerPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct PpmRatioPattern2 {
pub ppm: SeriesPattern1<PartsPerMillion32>,
pub ratio: SeriesPattern1<StoredF32>,
}
impl PpmRatioPattern2 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ppm: SeriesPattern1::new(client.clone(), _m(&acc, "ppm")),
ratio: SeriesPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct PpmRatioPattern3 {
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
}
impl PpmRatioPattern3 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ppm: SeriesPattern1::new(client.clone(), _m(&acc, "ppm")),
ratio: SeriesPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct PpmRatioPattern {
pub ppm: SeriesPattern1<PartsPerMillionSigned32>,
pub ratio: SeriesPattern1<StoredF32>,
}
impl PpmRatioPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ppm: SeriesPattern1::new(client.clone(), _m(&acc, "ppm")),
ratio: SeriesPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct PriceRatioPattern {
pub price: CentsSatsUsdPattern,
@@ -3658,54 +3706,6 @@ impl PriceRatioPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct RatioRawPattern2 {
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillion32>,
}
impl RatioRawPattern2 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ratio: SeriesPattern1::new(client.clone(), acc.clone()),
raw: SeriesPattern1::new(client.clone(), _m(&acc, "ppm")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct RatioRawPattern3 {
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillion64>,
}
impl RatioRawPattern3 {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ratio: SeriesPattern1::new(client.clone(), acc.clone()),
raw: SeriesPattern1::new(client.clone(), _m(&acc, "ppm")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct RatioRawPattern {
pub ratio: SeriesPattern1<StoredF32>,
pub raw: SeriesPattern1<PartsPerMillionSigned32>,
}
impl RatioRawPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ratio: SeriesPattern1::new(client.clone(), acc.clone()),
raw: SeriesPattern1::new(client.clone(), _m(&acc, "ppm")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct RatioValuePattern2 {
pub ratio: _1m1w1y24hPattern<StoredF64>,
@@ -3790,28 +3790,28 @@ impl InPattern3 {
/// Pattern struct for repeated tree structure.
pub struct NuplPattern {
pub nupl: RatioRawPattern,
pub nupl: PpmRatioPattern,
}
impl NuplPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
nupl: RatioRawPattern::new(client.clone(), acc.clone()),
nupl: PpmRatioPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct PricePattern {
pub price: CentsPercentilesRatioRawSatsUsdPattern,
pub price: CentsPercentilesPpmRatioSatsUsdPattern,
}
impl PricePattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
price: CentsPercentilesRatioRawSatsUsdPattern::new(client.clone(), acc.clone()),
price: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), acc.clone()),
}
}
}
@@ -5779,19 +5779,19 @@ impl SeriesTree_Mining_Rewards_Fees {
/// Series tree node.
pub struct SeriesTree_Mining_Rewards_Fees_ToSubsidyRatio {
pub _24h: RatioRawPattern3,
pub _1w: RatioRawPattern3,
pub _1m: RatioRawPattern3,
pub _1y: RatioRawPattern3,
pub _24h: PpmRatioPattern3,
pub _1w: PpmRatioPattern3,
pub _1m: PpmRatioPattern3,
pub _1y: PpmRatioPattern3,
}
impl SeriesTree_Mining_Rewards_Fees_ToSubsidyRatio {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
_24h: RatioRawPattern3::new(client.clone(), "fee_to_subsidy_ratio_24h".to_string()),
_1w: RatioRawPattern3::new(client.clone(), "fee_to_subsidy_ratio_1w".to_string()),
_1m: RatioRawPattern3::new(client.clone(), "fee_to_subsidy_ratio_1m".to_string()),
_1y: RatioRawPattern3::new(client.clone(), "fee_to_subsidy_ratio_1y".to_string()),
_24h: PpmRatioPattern3::new(client.clone(), "fee_to_subsidy_ratio_24h".to_string()),
_1w: PpmRatioPattern3::new(client.clone(), "fee_to_subsidy_ratio_1w".to_string()),
_1m: PpmRatioPattern3::new(client.clone(), "fee_to_subsidy_ratio_1m".to_string()),
_1y: PpmRatioPattern3::new(client.clone(), "fee_to_subsidy_ratio_1y".to_string()),
}
}
}
@@ -6016,7 +6016,7 @@ pub struct SeriesTree_Cointime_Cap {
pub vaulted: CentsUsdPattern3,
pub active: CentsUsdPattern3,
pub cointime: CentsUsdPattern3,
pub aviv: RatioRawPattern2,
pub aviv: PpmRatioPattern2,
}
impl SeriesTree_Cointime_Cap {
@@ -6027,26 +6027,26 @@ impl SeriesTree_Cointime_Cap {
vaulted: CentsUsdPattern3::new(client.clone(), "vaulted_cap".to_string()),
active: CentsUsdPattern3::new(client.clone(), "active_cap".to_string()),
cointime: CentsUsdPattern3::new(client.clone(), "cointime_cap".to_string()),
aviv: RatioRawPattern2::new(client.clone(), "aviv_ratio".to_string()),
aviv: PpmRatioPattern2::new(client.clone(), "aviv_ratio".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Cointime_Prices {
pub vaulted: CentsPercentilesRatioRawSatsUsdPattern,
pub active: CentsPercentilesRatioRawSatsUsdPattern,
pub true_market_mean: CentsPercentilesRatioRawSatsUsdPattern,
pub cointime: CentsPercentilesRatioRawSatsUsdPattern,
pub vaulted: CentsPercentilesPpmRatioSatsUsdPattern,
pub active: CentsPercentilesPpmRatioSatsUsdPattern,
pub true_market_mean: CentsPercentilesPpmRatioSatsUsdPattern,
pub cointime: CentsPercentilesPpmRatioSatsUsdPattern,
}
impl SeriesTree_Cointime_Prices {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
vaulted: CentsPercentilesRatioRawSatsUsdPattern::new(client.clone(), "vaulted_price".to_string()),
active: CentsPercentilesRatioRawSatsUsdPattern::new(client.clone(), "active_price".to_string()),
true_market_mean: CentsPercentilesRatioRawSatsUsdPattern::new(client.clone(), "true_market_mean".to_string()),
cointime: CentsPercentilesRatioRawSatsUsdPattern::new(client.clone(), "cointime_price".to_string()),
vaulted: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "vaulted_price".to_string()),
active: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "active_price".to_string()),
true_market_mean: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "true_market_mean".to_string()),
cointime: CentsPercentilesPpmRatioSatsUsdPattern::new(client.clone(), "cointime_price".to_string()),
}
}
}
@@ -6091,7 +6091,7 @@ pub struct SeriesTree_Coinflow {
pub supply: SeriesTree_Coinflow_Supply,
pub horizon: SeriesTree_Coinflow_Horizon,
pub cap: CentsUsdPattern3,
pub price: CentsRatioRawSatsUsdPattern,
pub price: CentsPpmRatioSatsUsdPattern,
}
impl SeriesTree_Coinflow {
@@ -6101,7 +6101,7 @@ impl SeriesTree_Coinflow {
supply: SeriesTree_Coinflow_Supply::new(client.clone(), format!("{base_path}_supply")),
horizon: SeriesTree_Coinflow_Horizon::new(client.clone(), format!("{base_path}_horizon")),
cap: CentsUsdPattern3::new(client.clone(), "coinflow_cap".to_string()),
price: CentsRatioRawSatsUsdPattern::new(client.clone(), "coinflow_price".to_string()),
price: CentsPpmRatioSatsUsdPattern::new(client.clone(), "coinflow_price".to_string()),
}
}
}
@@ -6872,11 +6872,11 @@ impl SeriesTree_Indexes_Timestamp {
/// Series tree node.
pub struct SeriesTree_Indicators {
pub puell_multiple: RatioRawPattern3,
pub nvt: RatioRawPattern3,
pub puell_multiple: PpmRatioPattern3,
pub nvt: PpmRatioPattern3,
pub gini: PercentPpmRatioPattern2,
pub rhodl_ratio: RatioRawPattern3,
pub thermo_cap_multiple: RatioRawPattern3,
pub rhodl_ratio: PpmRatioPattern3,
pub thermo_cap_multiple: PpmRatioPattern3,
pub coindays_destroyed_supply_adj: SeriesPattern1<StoredF32>,
pub coinyears_destroyed_supply_adj: SeriesPattern1<StoredF32>,
pub dormancy: SeriesTree_Indicators_Dormancy,
@@ -6888,11 +6888,11 @@ pub struct SeriesTree_Indicators {
impl SeriesTree_Indicators {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
puell_multiple: RatioRawPattern3::new(client.clone(), "puell_multiple".to_string()),
nvt: RatioRawPattern3::new(client.clone(), "nvt".to_string()),
puell_multiple: PpmRatioPattern3::new(client.clone(), "puell_multiple".to_string()),
nvt: PpmRatioPattern3::new(client.clone(), "nvt".to_string()),
gini: PercentPpmRatioPattern2::new(client.clone(), "gini".to_string()),
rhodl_ratio: RatioRawPattern3::new(client.clone(), "rhodl_ratio".to_string()),
thermo_cap_multiple: RatioRawPattern3::new(client.clone(), "thermo_cap_multiple".to_string()),
rhodl_ratio: PpmRatioPattern3::new(client.clone(), "rhodl_ratio".to_string()),
thermo_cap_multiple: PpmRatioPattern3::new(client.clone(), "thermo_cap_multiple".to_string()),
coindays_destroyed_supply_adj: SeriesPattern1::new(client.clone(), "coindays_destroyed_supply_adj".to_string()),
coinyears_destroyed_supply_adj: SeriesPattern1::new(client.clone(), "coinyears_destroyed_supply_adj".to_string()),
dormancy: SeriesTree_Indicators_Dormancy::new(client.clone(), format!("{base_path}_dormancy")),
@@ -7388,43 +7388,43 @@ impl SeriesTree_Market_MovingAverage {
/// Series tree node.
pub struct SeriesTree_Market_MovingAverage_Sma {
pub _1w: CentsRatioRawSatsUsdPattern,
pub _8d: CentsRatioRawSatsUsdPattern,
pub _13d: CentsRatioRawSatsUsdPattern,
pub _21d: CentsRatioRawSatsUsdPattern,
pub _1m: CentsRatioRawSatsUsdPattern,
pub _34d: CentsRatioRawSatsUsdPattern,
pub _55d: CentsRatioRawSatsUsdPattern,
pub _89d: CentsRatioRawSatsUsdPattern,
pub _111d: CentsRatioRawSatsUsdPattern,
pub _144d: CentsRatioRawSatsUsdPattern,
pub _1w: CentsPpmRatioSatsUsdPattern,
pub _8d: CentsPpmRatioSatsUsdPattern,
pub _13d: CentsPpmRatioSatsUsdPattern,
pub _21d: CentsPpmRatioSatsUsdPattern,
pub _1m: CentsPpmRatioSatsUsdPattern,
pub _34d: CentsPpmRatioSatsUsdPattern,
pub _55d: CentsPpmRatioSatsUsdPattern,
pub _89d: CentsPpmRatioSatsUsdPattern,
pub _111d: CentsPpmRatioSatsUsdPattern,
pub _144d: CentsPpmRatioSatsUsdPattern,
pub _200d: SeriesTree_Market_MovingAverage_Sma_200d,
pub _350d: SeriesTree_Market_MovingAverage_Sma_350d,
pub _1y: CentsRatioRawSatsUsdPattern,
pub _2y: CentsRatioRawSatsUsdPattern,
pub _200w: CentsRatioRawSatsUsdPattern,
pub _4y: CentsRatioRawSatsUsdPattern,
pub _1y: CentsPpmRatioSatsUsdPattern,
pub _2y: CentsPpmRatioSatsUsdPattern,
pub _200w: CentsPpmRatioSatsUsdPattern,
pub _4y: CentsPpmRatioSatsUsdPattern,
}
impl SeriesTree_Market_MovingAverage_Sma {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
_1w: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_1w".to_string()),
_8d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_8d".to_string()),
_13d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_13d".to_string()),
_21d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_21d".to_string()),
_1m: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_1m".to_string()),
_34d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_34d".to_string()),
_55d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_55d".to_string()),
_89d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_89d".to_string()),
_111d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_111d".to_string()),
_144d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_144d".to_string()),
_1w: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_1w".to_string()),
_8d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_8d".to_string()),
_13d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_13d".to_string()),
_21d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_21d".to_string()),
_1m: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_1m".to_string()),
_34d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_34d".to_string()),
_55d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_55d".to_string()),
_89d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_89d".to_string()),
_111d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_111d".to_string()),
_144d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_144d".to_string()),
_200d: SeriesTree_Market_MovingAverage_Sma_200d::new(client.clone(), format!("{base_path}_200d")),
_350d: SeriesTree_Market_MovingAverage_Sma_350d::new(client.clone(), format!("{base_path}_350d")),
_1y: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_1y".to_string()),
_2y: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_2y".to_string()),
_200w: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_200w".to_string()),
_4y: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_sma_4y".to_string()),
_1y: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_1y".to_string()),
_2y: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_2y".to_string()),
_200w: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_200w".to_string()),
_4y: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_sma_4y".to_string()),
}
}
}
@@ -7434,7 +7434,7 @@ pub struct SeriesTree_Market_MovingAverage_Sma_200d {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub x2_4: CentsSatsUsdPattern,
pub x0_8: CentsSatsUsdPattern,
@@ -7446,7 +7446,7 @@ impl SeriesTree_Market_MovingAverage_Sma_200d {
usd: SeriesPattern1::new(client.clone(), "price_sma_200d".to_string()),
cents: SeriesPattern1::new(client.clone(), "price_sma_200d_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "price_sma_200d_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "price_sma_200d_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "price_sma_200d_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "price_sma_200d_ratio".to_string()),
x2_4: CentsSatsUsdPattern::new(client.clone(), "price_sma_200d_x2_4".to_string()),
x0_8: CentsSatsUsdPattern::new(client.clone(), "price_sma_200d_x0_8".to_string()),
@@ -7459,7 +7459,7 @@ pub struct SeriesTree_Market_MovingAverage_Sma_350d {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub x2: CentsSatsUsdPattern,
}
@@ -7470,7 +7470,7 @@ impl SeriesTree_Market_MovingAverage_Sma_350d {
usd: SeriesPattern1::new(client.clone(), "price_sma_350d".to_string()),
cents: SeriesPattern1::new(client.clone(), "price_sma_350d_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "price_sma_350d_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "price_sma_350d_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "price_sma_350d_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "price_sma_350d_ratio".to_string()),
x2: CentsSatsUsdPattern::new(client.clone(), "price_sma_350d_x2".to_string()),
}
@@ -7479,43 +7479,43 @@ impl SeriesTree_Market_MovingAverage_Sma_350d {
/// Series tree node.
pub struct SeriesTree_Market_MovingAverage_Ema {
pub _1w: CentsRatioRawSatsUsdPattern,
pub _8d: CentsRatioRawSatsUsdPattern,
pub _12d: CentsRatioRawSatsUsdPattern,
pub _13d: CentsRatioRawSatsUsdPattern,
pub _21d: CentsRatioRawSatsUsdPattern,
pub _26d: CentsRatioRawSatsUsdPattern,
pub _1m: CentsRatioRawSatsUsdPattern,
pub _34d: CentsRatioRawSatsUsdPattern,
pub _55d: CentsRatioRawSatsUsdPattern,
pub _89d: CentsRatioRawSatsUsdPattern,
pub _144d: CentsRatioRawSatsUsdPattern,
pub _200d: CentsRatioRawSatsUsdPattern,
pub _1y: CentsRatioRawSatsUsdPattern,
pub _2y: CentsRatioRawSatsUsdPattern,
pub _200w: CentsRatioRawSatsUsdPattern,
pub _4y: CentsRatioRawSatsUsdPattern,
pub _1w: CentsPpmRatioSatsUsdPattern,
pub _8d: CentsPpmRatioSatsUsdPattern,
pub _12d: CentsPpmRatioSatsUsdPattern,
pub _13d: CentsPpmRatioSatsUsdPattern,
pub _21d: CentsPpmRatioSatsUsdPattern,
pub _26d: CentsPpmRatioSatsUsdPattern,
pub _1m: CentsPpmRatioSatsUsdPattern,
pub _34d: CentsPpmRatioSatsUsdPattern,
pub _55d: CentsPpmRatioSatsUsdPattern,
pub _89d: CentsPpmRatioSatsUsdPattern,
pub _144d: CentsPpmRatioSatsUsdPattern,
pub _200d: CentsPpmRatioSatsUsdPattern,
pub _1y: CentsPpmRatioSatsUsdPattern,
pub _2y: CentsPpmRatioSatsUsdPattern,
pub _200w: CentsPpmRatioSatsUsdPattern,
pub _4y: CentsPpmRatioSatsUsdPattern,
}
impl SeriesTree_Market_MovingAverage_Ema {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
_1w: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_1w".to_string()),
_8d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_8d".to_string()),
_12d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_12d".to_string()),
_13d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_13d".to_string()),
_21d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_21d".to_string()),
_26d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_26d".to_string()),
_1m: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_1m".to_string()),
_34d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_34d".to_string()),
_55d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_55d".to_string()),
_89d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_89d".to_string()),
_144d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_144d".to_string()),
_200d: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_200d".to_string()),
_1y: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_1y".to_string()),
_2y: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_2y".to_string()),
_200w: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_200w".to_string()),
_4y: CentsRatioRawSatsUsdPattern::new(client.clone(), "price_ema_4y".to_string()),
_1w: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_1w".to_string()),
_8d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_8d".to_string()),
_12d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_12d".to_string()),
_13d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_13d".to_string()),
_21d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_21d".to_string()),
_26d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_26d".to_string()),
_1m: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_1m".to_string()),
_34d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_34d".to_string()),
_55d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_55d".to_string()),
_89d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_89d".to_string()),
_144d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_144d".to_string()),
_200d: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_200d".to_string()),
_1y: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_1y".to_string()),
_2y: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_2y".to_string()),
_200w: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_200w".to_string()),
_4y: CentsPpmRatioSatsUsdPattern::new(client.clone(), "price_ema_4y".to_string()),
}
}
}
@@ -7523,7 +7523,7 @@ impl SeriesTree_Market_MovingAverage_Ema {
/// Series tree node.
pub struct SeriesTree_Market_Technical {
pub rsi: SeriesTree_Market_Technical_Rsi,
pub pi_cycle: RatioRawPattern2,
pub pi_cycle: PpmRatioPattern2,
pub macd: SeriesTree_Market_Technical_Macd,
}
@@ -7531,7 +7531,7 @@ impl SeriesTree_Market_Technical {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
rsi: SeriesTree_Market_Technical_Rsi::new(client.clone(), format!("{base_path}_rsi")),
pi_cycle: RatioRawPattern2::new(client.clone(), "pi_cycle".to_string()),
pi_cycle: PpmRatioPattern2::new(client.clone(), "pi_cycle".to_string()),
macd: SeriesTree_Market_Technical_Macd::new(client.clone(), format!("{base_path}_macd")),
}
}
@@ -8272,7 +8272,7 @@ pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
@@ -8285,7 +8285,7 @@ impl SeriesTree_Cohorts_Utxo_All_Realized_Price {
usd: SeriesPattern1::new(client.clone(), "realized_price".to_string()),
cents: SeriesPattern1::new(client.clone(), "realized_price_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "realized_price_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "realized_price_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "realized_price_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "realized_price_ratio".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "realized_price_ratio_sma".to_string()),
@@ -8538,7 +8538,7 @@ impl SeriesTree_Cohorts_Utxo_All_CostBasis {
/// Series tree node.
pub struct SeriesTree_Cohorts_Utxo_All_Unrealized {
pub nupl: RatioRawPattern,
pub nupl: PpmRatioPattern,
pub profit: SeriesTree_Cohorts_Utxo_All_Unrealized_Profit,
pub loss: SeriesTree_Cohorts_Utxo_All_Unrealized_Loss,
pub net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl,
@@ -8552,7 +8552,7 @@ pub struct SeriesTree_Cohorts_Utxo_All_Unrealized {
impl SeriesTree_Cohorts_Utxo_All_Unrealized {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
nupl: RatioRawPattern::new(client.clone(), "nupl".to_string()),
nupl: PpmRatioPattern::new(client.clone(), "nupl".to_string()),
profit: SeriesTree_Cohorts_Utxo_All_Unrealized_Profit::new(client.clone(), format!("{base_path}_profit")),
loss: SeriesTree_Cohorts_Utxo_All_Unrealized_Loss::new(client.clone(), format!("{base_path}_loss")),
net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl::new(client.clone(), format!("{base_path}_net_pnl")),
@@ -8704,7 +8704,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
@@ -8717,7 +8717,7 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized_Price {
usd: SeriesPattern1::new(client.clone(), "sth_realized_price".to_string()),
cents: SeriesPattern1::new(client.clone(), "sth_realized_price_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "sth_realized_price_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "sth_realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "sth_realized_price_ratio_sma".to_string()),
@@ -8974,7 +8974,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
@@ -8987,7 +8987,7 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized_Price {
usd: SeriesPattern1::new(client.clone(), "lth_realized_price".to_string()),
cents: SeriesPattern1::new(client.clone(), "lth_realized_price_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "lth_realized_price_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "lth_realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "lth_realized_price_ratio_sma".to_string()),
@@ -9474,7 +9474,7 @@ pub struct SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
@@ -9487,7 +9487,7 @@ impl SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price {
usd: SeriesPattern1::new(client.clone(), "veteran_realized_price".to_string()),
cents: SeriesPattern1::new(client.clone(), "veteran_realized_price_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "veteran_realized_price_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "veteran_realized_price_ratio".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "veteran_realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "veteran_realized_price_ratio_sma".to_string()),
@@ -9744,7 +9744,7 @@ pub struct SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price {
pub usd: SeriesPattern1<Dollars>,
pub cents: SeriesPattern1<Cents>,
pub sats: SeriesPattern1<SatsFract>,
pub raw: SeriesPattern1<PartsPerMillion64>,
pub ppm: SeriesPattern1<PartsPerMillion64>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
@@ -9757,7 +9757,7 @@ impl SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price {
usd: SeriesPattern1::new(client.clone(), "rookie_realized_price".to_string()),
cents: SeriesPattern1::new(client.clone(), "rookie_realized_price_cents".to_string()),
sats: SeriesPattern1::new(client.clone(), "rookie_realized_price_sats".to_string()),
raw: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_ppm".to_string()),
ppm: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio_ppm".to_string()),
ratio: SeriesPattern1::new(client.clone(), "rookie_realized_price_ratio".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "rookie_realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "rookie_realized_price_ratio_sma".to_string()),
@@ -90,7 +90,7 @@ impl ProfitabilityBucket {
indexes,
)?,
},
nupl: RatioPerBlock::forced_import_raw(
nupl: RatioPerBlock::forced_import_ppm(
db,
&format!("{name}_nupl"),
version + Version::ONE,
@@ -152,7 +152,7 @@ impl ProfitabilityBucket {
exit,
)?;
self.nupl.raw.height.compute_transform3(
self.nupl.ppm.height.compute_transform3(
max_from,
&prices.spot.cents.height,
&self.realized_cap.all.height,
@@ -230,7 +230,7 @@ impl ProfitabilityBucket {
&mut self.realized_cap.sth.height,
&mut self.unrealized_pnl.all.height,
&mut self.unrealized_pnl.sth.height,
&mut self.nupl.raw.height,
&mut self.nupl.ppm.height,
]
}
}
@@ -15,7 +15,7 @@ pub struct UnrealizedMinimal<M: StorageMode = Rw> {
impl UnrealizedMinimal {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
nupl: RatioPerBlock::forced_import_raw(
nupl: RatioPerBlock::forced_import_ppm(
cfg.db,
&cfg.name("nupl"),
cfg.version + Version::ONE,
@@ -31,7 +31,7 @@ impl UnrealizedMinimal {
realized_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.nupl.raw.height.compute_transform2(
self.nupl.ppm.height.compute_transform2(
max_from,
spot_price,
realized_price,
@@ -23,7 +23,7 @@ impl Vecs {
// Puell Multiple: daily_subsidy_usd / sma_365d_subsidy_usd
self.puell_multiple
.raw
.ppm
.compute_binary::<Dollars, Dollars, RatioDollars<PartsPerMillion64>>(
starting_lengths.height,
&mining.rewards.subsidy.block.usd,
@@ -36,7 +36,7 @@ impl Vecs {
// RHODL Ratio: 1d-1w realized cap / 1y-2y realized cap
self.rhodl_ratio
.raw
.ppm
.compute_binary::<Dollars, Dollars, RatioDollars<PartsPerMillion64>>(
starting_lengths.height,
&distribution
@@ -70,7 +70,7 @@ impl Vecs {
.usd
.height;
self.nvt
.raw
.ppm
.compute_binary::<Dollars, Dollars, RatioDollars<PartsPerMillion64>>(
starting_lengths.height,
market_cap,
@@ -80,7 +80,7 @@ impl Vecs {
// Thermocap Multiple: market_cap / thermo_cap
self.thermo_cap_multiple
.raw
.ppm
.compute_binary::<Dollars, Dollars, RatioDollars<PartsPerMillion64>>(
starting_lengths.height,
market_cap,
+4 -4
View File
@@ -23,12 +23,12 @@ impl Vecs {
let db = open_db(parent_path, super::DB_NAME, 100_000)?;
let v = parent_version + VERSION;
let puell_multiple = RatioPerBlock::forced_import_raw(&db, "puell_multiple", v, indexes)?;
let nvt = RatioPerBlock::forced_import_raw(&db, "nvt", v, indexes)?;
let puell_multiple = RatioPerBlock::forced_import_ppm(&db, "puell_multiple", v, indexes)?;
let nvt = RatioPerBlock::forced_import_ppm(&db, "nvt", v, indexes)?;
let gini = PercentPerBlock::forced_import(&db, "gini", v, indexes)?;
let rhodl_ratio = RatioPerBlock::forced_import_raw(&db, "rhodl_ratio", v, indexes)?;
let rhodl_ratio = RatioPerBlock::forced_import_ppm(&db, "rhodl_ratio", v, indexes)?;
let thermo_cap_multiple =
RatioPerBlock::forced_import_raw(&db, "thermo_cap_multiple", v, indexes)?;
RatioPerBlock::forced_import_ppm(&db, "thermo_cap_multiple", v, indexes)?;
let coindays_destroyed_supply_adj =
PerBlock::forced_import(&db, "coindays_destroyed_supply_adj", v, indexes)?;
let coinyears_destroyed_supply_adj =
@@ -11,7 +11,7 @@ use crate::{
#[derive(Traversable)]
pub struct RatioPerBlock<R: FixedRatio, M: StorageMode = Rw> {
pub raw: PerBlock<R, M>,
pub ppm: PerBlock<R, M>,
pub ratio: LazyPerBlock<StoredF32, R>,
}
@@ -24,10 +24,10 @@ impl<R: FixedRatio> RatioPerBlock<R> {
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Self::forced_import_raw(db, &format!("{name}_ratio"), version, indexes)
Self::forced_import_ppm(db, &format!("{name}_ratio"), version, indexes)
}
pub(crate) fn forced_import_raw(
pub(crate) fn forced_import_ppm(
db: &Database,
name: &str,
version: Version,
@@ -35,16 +35,16 @@ impl<R: FixedRatio> RatioPerBlock<R> {
) -> Result<Self> {
let v = version + VERSION;
let raw = PerBlock::forced_import(db, &format!("{name}_{}", R::SUFFIX), v, indexes)?;
let ppm = PerBlock::forced_import(db, &format!("{name}_{}", R::SUFFIX), v, indexes)?;
let ratio = LazyPerBlock::from_computed::<R::ToRatio>(
name,
v,
raw.height.read_only_boxed_clone(),
&raw,
ppm.height.read_only_boxed_clone(),
&ppm,
);
Ok(Self { raw, ratio })
Ok(Self { ppm, ratio })
}
pub(crate) fn compute_ratio(
@@ -54,7 +54,7 @@ impl<R: FixedRatio> RatioPerBlock<R> {
series_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.raw.height.compute_transform2(
self.ppm.height.compute_transform2(
starting_lengths.height,
close_price,
series_price,
@@ -53,7 +53,7 @@ impl RatioPerBlockPercentiles {
macro_rules! import_ratio {
($suffix:expr) => {
RatioPerBlock::forced_import_raw(db, &format!("{name}_{}", $suffix), v, indexes)?
RatioPerBlock::forced_import_ppm(db, &format!("{name}_{}", $suffix), v, indexes)?
};
}
@@ -120,14 +120,14 @@ impl RatioPerBlockPercentiles {
let new_ratios = ratio_source.collect_range_at(start, ratio_len);
let mut pct_vecs: [&mut EagerVec<PcoVec<Height, PartsPerMillion32>>; 8] = [
&mut self.pct0_5.ratio.raw.height,
&mut self.pct1.ratio.raw.height,
&mut self.pct2.ratio.raw.height,
&mut self.pct5.ratio.raw.height,
&mut self.pct95.ratio.raw.height,
&mut self.pct98.ratio.raw.height,
&mut self.pct99.ratio.raw.height,
&mut self.pct99_5.ratio.raw.height,
&mut self.pct0_5.ratio.ppm.height,
&mut self.pct1.ratio.ppm.height,
&mut self.pct2.ratio.ppm.height,
&mut self.pct5.ratio.ppm.height,
&mut self.pct95.ratio.ppm.height,
&mut self.pct98.ratio.ppm.height,
&mut self.pct99.ratio.ppm.height,
&mut self.pct99_5.ratio.ppm.height,
];
const PCTS: [f64; 8] = [0.005, 0.01, 0.02, 0.05, 0.95, 0.98, 0.99, 0.995];
let mut out = [0.0; 8];
@@ -162,7 +162,7 @@ impl RatioPerBlockPercentiles {
.compute_binary::<Cents, PartsPerMillion32, PriceTimesRatio<PartsPerMillion32>>(
starting_lengths.height,
series_price,
&self.$band.ratio.raw.height,
&self.$band.ratio.ppm.height,
exit,
)?;
};
@@ -184,14 +184,14 @@ impl RatioPerBlockPercentiles {
&mut self,
) -> impl Iterator<Item = &mut EagerVec<PcoVec<Height, PartsPerMillion32>>> {
[
&mut self.pct0_5.ratio.raw.height,
&mut self.pct1.ratio.raw.height,
&mut self.pct2.ratio.raw.height,
&mut self.pct5.ratio.raw.height,
&mut self.pct95.ratio.raw.height,
&mut self.pct98.ratio.raw.height,
&mut self.pct99.ratio.raw.height,
&mut self.pct99_5.ratio.raw.height,
&mut self.pct0_5.ratio.ppm.height,
&mut self.pct1.ratio.ppm.height,
&mut self.pct2.ratio.ppm.height,
&mut self.pct5.ratio.ppm.height,
&mut self.pct95.ratio.ppm.height,
&mut self.pct98.ratio.ppm.height,
&mut self.pct99.ratio.ppm.height,
&mut self.pct99_5.ratio.ppm.height,
]
.into_iter()
}
@@ -15,7 +15,7 @@ pub struct PriceWithRatioPerBlock<M: StorageMode = Rw> {
pub usd: LazyPerBlock<Dollars, Cents>,
pub cents: PerBlock<Cents, M>,
pub sats: LazyPerBlock<SatsFract, Dollars>,
pub raw: PerBlock<PartsPerMillion64, M>,
pub ppm: PerBlock<PartsPerMillion64, M>,
pub ratio: LazyPerBlock<StoredF32, PartsPerMillion64>,
}
@@ -32,7 +32,7 @@ impl PriceWithRatioPerBlock {
usd: price.usd,
cents: price.cents,
sats: price.sats,
raw: ratio.raw,
ppm: ratio.ppm,
ratio: ratio.ratio,
})
}
@@ -44,7 +44,7 @@ impl PriceWithRatioPerBlock {
close_price: &impl ReadableVec<Height, Cents>,
exit: &Exit,
) -> Result<()> {
self.raw.height.compute_transform2(
self.ppm.height.compute_transform2(
starting_lengths.height,
close_price,
&self.cents.height,
@@ -31,7 +31,7 @@ impl RatioSma {
macro_rules! import {
($suffix:expr) => {
RatioPerBlock::forced_import_raw(
RatioPerBlock::forced_import_ppm(
db,
&format!("{name}_ratio_sma_{}", $suffix),
v,
@@ -58,7 +58,7 @@ impl RatioSma {
ratio_source: &impl ReadableVec<Height, StoredF32>,
) -> Result<()> {
// Expanding SMA (all history)
self.all.raw.height.compute_sma_(
self.all.ppm.height.compute_sma_(
starting_lengths.height,
ratio_source,
usize::MAX,
@@ -74,7 +74,7 @@ impl RatioSma {
(&mut self._2y, &blocks.lookback._2y),
(&mut self._4y, &blocks.lookback._4y),
] {
sma.raw.height.compute_rolling_average(
sma.ppm.height.compute_rolling_average(
starting_lengths.height,
lookback,
ratio_source,
@@ -24,7 +24,7 @@ impl<B: FixedRatio> RatioRollingWindows<B> {
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self(Windows::try_from_fn(|suffix| {
RatioPerBlock::forced_import_raw(db, &format!("{name}_{suffix}"), version, indexes)
RatioPerBlock::forced_import_ppm(db, &format!("{name}_{suffix}"), version, indexes)
})?))
}
@@ -50,7 +50,7 @@ impl<B: FixedRatio> RatioRollingWindows<B> {
.zip(sources2)
{
target
.raw
.ppm
.compute_binary::<S1T, S2T, F>(max_from, s1, s2, exit)?;
}
Ok(())
@@ -62,7 +62,7 @@ impl Vecs {
}
self.pi_cycle
.raw
.ppm
.compute_binary::<Dollars, Dollars, RatioDollars<PartsPerMillion32>>(
starting_height,
&moving_average.sma._111d.usd.height,
@@ -100,7 +100,7 @@ impl Vecs {
})?;
let macd = WindowsTo1m::try_from_fn(|tf| MacdChain::forced_import(db, tf, v, indexes))?;
let pi_cycle = RatioPerBlock::forced_import_raw(db, "pi_cycle", v, indexes)?;
let pi_cycle = RatioPerBlock::forced_import_ppm(db, "pi_cycle", v, indexes)?;
Ok(Self {
rsi,
@@ -3,7 +3,7 @@ use std::{cmp::Ordering, collections::BTreeMap, fs, path::Path};
use brk_cohort::{AGE_RANGE_NAMES, CohortContext};
use brk_error::Result;
use brk_indexer::Indexer;
use brk_types::{CentsCompact, Date, Day1, Dollars, StoredF64, UrpdRaw, Version};
use brk_types::{CentsCompact, Date, Day1, Dollars, Sats, StoredF64, UrpdRaw, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecValue, WritableVec};
use super::vecs::{Levels, MODE_COUNT, ModeVecs, Percentiles, Vecs};
@@ -189,6 +189,12 @@ impl Vecs {
.iter()
.map(|cohort| &cohort.liveliness.day1)
.collect();
let age_supplies: Vec<_> = distribution
.utxo_cohorts
.age_range
.iter()
.map(|cohort| &cohort.metrics.supply.total.sats.day1)
.collect();
let coinflow_mobility: Vec<_> = coinflow
.age_range
.iter()
@@ -225,6 +231,7 @@ impl Vecs {
.chain(std::iter::once(distribution.supply_state.version()))
.chain(std::iter::once(raw_loss_share.version()))
.chain(weighted_loss_shares.iter().map(|vec| vec.version()))
.chain(age_supplies.iter().map(|vec| vec.version()))
.chain(cointime_liveliness.iter().map(|vec| vec.version()))
.chain(coinflow_mobility.iter().map(|vec| vec.version()))
.chain(coinflow_spending_rate.iter().map(|vec| vec.version()))
@@ -237,6 +244,7 @@ impl Vecs {
let source_end = std::iter::once(indexes.day1.date.len())
.chain(std::iter::once(raw_loss_share.len()))
.chain(weighted_loss_shares.iter().map(|vec| vec.len()))
.chain(age_supplies.iter().map(|vec| vec.len()))
.chain(cointime_liveliness.iter().map(|vec| vec.len()))
.chain(coinflow_mobility.iter().map(|vec| vec.len()))
.chain(coinflow_spending_rate.iter().map(|vec| vec.len()))
@@ -266,6 +274,7 @@ impl Vecs {
{
let weights = mode_weights(
day,
&age_supplies,
&cointime_liveliness,
&coinflow_mobility,
&coinflow_spending_rate,
@@ -381,6 +390,7 @@ fn recompute_day(indexer: &Indexer, indexes: &indexes::Vecs) -> Option<Day1> {
fn mode_weights(
day: Day1,
age_supplies: &[&impl ReadableVec<Day1, Option<Sats>>],
cointime_liveliness: &[&impl ReadableVec<Day1, Option<StoredF64>>],
coinflow_mobility: &[&impl ReadableVec<Day1, Option<StoredF64>>],
coinflow_spending_rate: &[&impl ReadableVec<Day1, Option<StoredF64>>],
@@ -389,12 +399,12 @@ fn mode_weights(
debug_assert_eq!(COINFLOW_HORIZON_START + HORIZON_COUNT, MODE_COUNT);
let mut weights = [None; MODE_COUNT];
weights[RAW_MODE] = Some([1.0; AGE_COHORT_COUNT]);
weights[COINTIME_MODE] = collect_age_values(cointime_liveliness, day)
weights[COINTIME_MODE] = collect_age_values(cointime_liveliness, age_supplies, day)
.map(|values| values.map(|v| v.clamp(0.0, 1.0)));
weights[COINFLOW_MODE] = collect_age_values(coinflow_mobility, age_supplies, day)
.map(|values| values.map(|v| v.clamp(0.0, 1.0)));
weights[COINFLOW_MODE] =
collect_age_values(coinflow_mobility, day).map(|values| values.map(|v| v.clamp(0.0, 1.0)));
if let Some(hazards) = collect_age_values(coinflow_spending_rate, day) {
if let Some(hazards) = collect_age_values(coinflow_spending_rate, age_supplies, day) {
let hazards = hazards.map(|value| value.max(0.0));
for (offset, horizon) in HORIZON_DAYS.iter().copied().enumerate() {
weights[COINFLOW_HORIZON_START + offset] = Some(std::array::from_fn(|age| {
@@ -405,25 +415,38 @@ fn mode_weights(
weights
}
fn collect_age_values(
sources: &[&impl ReadableVec<Day1, Option<StoredF64>>],
fn collect_age_values<T>(
sources: &[&impl ReadableVec<Day1, Option<T>>],
supplies: &[&impl ReadableVec<Day1, Option<Sats>>],
day: Day1,
) -> Option<[f64; AGE_COHORT_COUNT]> {
if sources.len() != AGE_COHORT_COUNT {
) -> Option<[f64; AGE_COHORT_COUNT]>
where
T: VecValue,
f64: From<T>,
{
if sources.len() != AGE_COHORT_COUNT || supplies.len() != AGE_COHORT_COUNT {
return None;
}
let mut values = [0.0; AGE_COHORT_COUNT];
for (value, source) in values.iter_mut().zip(sources) {
let collected = f64::from(source.collect_one(day).flatten()?);
if !collected.is_finite() {
return None;
}
*value = collected;
for ((value, source), supply) in values.iter_mut().zip(sources).zip(supplies) {
let supply = supply.collect_one(day).flatten()?;
*value = resolve_age_value(source.collect_one(day).flatten(), supply)?;
}
Some(values)
}
fn resolve_age_value<T>(value: Option<T>, supply: Sats) -> Option<f64>
where
f64: From<T>,
{
match value.map(f64::from) {
Some(value) if value.is_finite() => Some(value),
_ if supply == Sats::ZERO => Some(0.0),
_ => None,
}
}
fn read_weighted_urpd(
states_path: &Path,
date: Date,
@@ -568,6 +591,26 @@ mod tests {
assert_eq!(quantile(&[], 0.95), None);
}
#[test]
fn empty_age_cohort_uses_zero_weight() {
assert_eq!(resolve_age_value::<StoredF64>(None, Sats::ZERO), Some(0.0));
assert_eq!(
resolve_age_value(Some(StoredF64::NAN), Sats::ZERO),
Some(0.0)
);
}
#[test]
fn non_empty_age_cohort_requires_finite_weight() {
let supply = Sats::from(1_u64);
assert_eq!(resolve_age_value::<StoredF64>(None, supply), None);
assert_eq!(resolve_age_value(Some(StoredF64::NAN), supply), None);
assert_eq!(
resolve_age_value(Some(StoredF64::from(0.25)), supply),
Some(0.25)
);
}
#[test]
fn daily_loss_share_calibrates_the_floor() {
let weighted = BTreeMap::from([
@@ -14,7 +14,7 @@ use crate::{
internal::db_utils::{finalize_db, open_db},
};
const VERSION: Version = Version::TWO;
const VERSION: Version = Version::new(3);
fn import_percentiles<T>(mut import: impl FnMut(&str) -> Result<T>) -> Result<Percentiles<T>> {
Ok(Percentiles {
+1 -1
View File
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
///
#[derive(Debug, Default, Clone, Hash, Serialize, Deserialize, JsonSchema)]
pub struct AddrMempoolStats {
/// Net unconfirmed balance change in satoshis; negative when pending spends exceed receipts
/// Net pending (unconfirmed) balance change in satoshis; negative when pending spends exceed receipts
pub balance_delta: SatsSigned,
/// Number of unconfirmed transaction outputs funding this address
+1 -1
View File
@@ -21,6 +21,6 @@ pub struct AddrStats {
/// Statistics for unconfirmed transactions in the mempool
pub mempool_stats: AddrMempoolStats,
/// Current balance in satoshis, including unconfirmed mempool changes
/// Total current balance in satoshis, including pending (unconfirmed) mempool changes
pub balance: Sats,
}
+231 -231
View File
@@ -48,7 +48,7 @@
* Based on mempool.space's format.
*
* @typedef {Object} AddrMempoolStats
* @property {SatsSigned} balanceDelta - Net unconfirmed balance change in satoshis; negative when pending spends exceed receipts
* @property {SatsSigned} balanceDelta - Net pending (unconfirmed) balance change in satoshis; negative when pending spends exceed receipts
* @property {number} fundedTxoCount - Number of unconfirmed transaction outputs funding this address
* @property {Sats} fundedTxoSum - Total amount in satoshis being received in unconfirmed transactions
* @property {number} spentTxoCount - Number of unconfirmed transaction inputs spending from this address
@@ -69,7 +69,7 @@
* @property {OutputType} addrType - Address type (p2pkh, p2sh, v0_p2wpkh, v0_p2wsh, v1_p2tr, etc.)
* @property {AddrChainStats} chainStats - Statistics for confirmed transactions on the blockchain
* @property {AddrMempoolStats} mempoolStats - Statistics for unconfirmed transactions in the mempool
* @property {Sats} balance - Current balance in satoshis, including unconfirmed mempool changes
* @property {Sats} balance - Total current balance in satoshis, including pending (unconfirmed) mempool changes
*/
/**
* Address validation result
@@ -2696,7 +2696,7 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, acc) {
* @property {SeriesPattern1<StoredF32>} mvrv
* @property {BlockChangeCumulativeDeltaSumPattern} netPnl
* @property {BlockCumulativeSumPattern} peakRegret
* @property {CentsPercentilesRatioRawSatsSmaStdUsdPattern} price
* @property {CentsPercentilesPpmRatioSatsSmaStdUsdPattern} price
* @property {BlockCumulativeSumPattern} profit
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
* @property {_1m1w1y24hPattern8} sellSideRiskRatio
@@ -2712,7 +2712,7 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, acc) {
* @property {SeriesPattern1<StoredF32>} mvrv
* @property {BlockChangeCumulativeDeltaSumPattern} netPnl
* @property {BlockCumulativeSumPattern} peakRegret
* @property {CentsPercentilesRatioRawSatsSmaStdUsdPattern} price
* @property {CentsPercentilesPpmRatioSatsSmaStdUsdPattern} price
* @property {BlockCumulativeSumPattern} profit
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
* @property {_1m1w1y24hPattern8} sellSideRiskRatio
@@ -3093,7 +3093,7 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) {
* @property {InPattern2} investedCapital
* @property {CentsNegativeToUsdPattern2} loss
* @property {CentsToUsdPattern3} netPnl
* @property {RatioRawPattern} nupl
* @property {PpmRatioPattern} nupl
* @property {CentsToUsdPattern4} profit
* @property {GreedNetPainPattern} sentiment
*/
@@ -3112,7 +3112,7 @@ function createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client
investedCapital: createInPattern2(client, _m(acc, 'invested_capital_in')),
loss: createCentsNegativeToUsdPattern2(client, _m(acc, 'unrealized_loss')),
netPnl: createCentsToUsdPattern3(client, _m(acc, 'net_unrealized_pnl')),
nupl: createRatioRawPattern(client, _m(acc, 'nupl')),
nupl: createPpmRatioPattern(client, _m(acc, 'nupl')),
profit: createCentsToUsdPattern4(client, _m(acc, 'unrealized_profit')),
sentiment: createGreedNetPainPattern(client, acc),
};
@@ -3152,11 +3152,11 @@ function createPct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern(client, acc)
}
/**
* @typedef {Object} CentsPercentilesRatioRawSatsSmaStdUsdPattern
* @typedef {Object} CentsPercentilesPpmRatioSatsSmaStdUsdPattern
* @property {SeriesPattern1<Cents>} cents
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<SatsFract>} sats
* @property {_1m1w1y2y4yAllPattern} sma
* @property {_1y2y4yAllPattern} stdDev
@@ -3165,14 +3165,14 @@ function createPct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern(client, acc)
/**
* @typedef {Object} Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern
* @property {PriceRatioRawPattern} pct05
* @property {PriceRatioRawPattern} pct1
* @property {PriceRatioRawPattern} pct2
* @property {PriceRatioRawPattern} pct5
* @property {PriceRatioRawPattern} pct95
* @property {PriceRatioRawPattern} pct98
* @property {PriceRatioRawPattern} pct99
* @property {PriceRatioRawPattern} pct995
* @property {PpmPriceRatioPattern} pct05
* @property {PpmPriceRatioPattern} pct1
* @property {PpmPriceRatioPattern} pct2
* @property {PpmPriceRatioPattern} pct5
* @property {PpmPriceRatioPattern} pct95
* @property {PpmPriceRatioPattern} pct98
* @property {PpmPriceRatioPattern} pct99
* @property {PpmPriceRatioPattern} pct995
*/
/**
@@ -3183,14 +3183,14 @@ function createPct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern(client, acc)
*/
function createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc) {
return {
pct05: createPriceRatioRawPattern(client, acc, 'pct0_5'),
pct1: createPriceRatioRawPattern(client, acc, 'pct1'),
pct2: createPriceRatioRawPattern(client, acc, 'pct2'),
pct5: createPriceRatioRawPattern(client, acc, 'pct5'),
pct95: createPriceRatioRawPattern(client, acc, 'pct95'),
pct98: createPriceRatioRawPattern(client, acc, 'pct98'),
pct99: createPriceRatioRawPattern(client, acc, 'pct99'),
pct995: createPriceRatioRawPattern(client, acc, 'pct99_5'),
pct05: createPpmPriceRatioPattern(client, acc, 'pct0_5'),
pct1: createPpmPriceRatioPattern(client, acc, 'pct1'),
pct2: createPpmPriceRatioPattern(client, acc, 'pct2'),
pct5: createPpmPriceRatioPattern(client, acc, 'pct5'),
pct95: createPpmPriceRatioPattern(client, acc, 'pct95'),
pct98: createPpmPriceRatioPattern(client, acc, 'pct98'),
pct99: createPpmPriceRatioPattern(client, acc, 'pct99'),
pct995: createPpmPriceRatioPattern(client, acc, 'pct99_5'),
};
}
@@ -3280,7 +3280,7 @@ function create_1m1w1y24hPercentPpmRatioPattern(client, acc) {
* @property {BlockCumulativeNegativeSumPattern} loss
* @property {SeriesPattern1<StoredF32>} mvrv
* @property {BlockCumulativeDeltaSumPattern} netPnl
* @property {CentsRatioRawSatsUsdPattern} price
* @property {CentsPpmRatioSatsUsdPattern} price
* @property {BlockCumulativeSumPattern} profit
* @property {RatioValuePattern} sopr
*/
@@ -3297,7 +3297,7 @@ function createCapLossMvrvNetPriceProfitSoprPattern(client, acc) {
loss: createBlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss')),
mvrv: createSeriesPattern1(client, _m(acc, 'mvrv')),
netPnl: createBlockCumulativeDeltaSumPattern(client, _m(acc, 'net_realized_pnl')),
price: createCentsRatioRawSatsUsdPattern(client, _m(acc, 'realized_price')),
price: createCentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price')),
profit: createBlockCumulativeSumPattern(client, _m(acc, 'realized_profit')),
sopr: createRatioValuePattern(client, acc),
};
@@ -3423,12 +3423,12 @@ function createMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) {
/**
* @typedef {Object} _1m1w1y2y4yAllPattern
* @property {RatioRawPattern2} _1m
* @property {RatioRawPattern2} _1w
* @property {RatioRawPattern2} _1y
* @property {RatioRawPattern2} _2y
* @property {RatioRawPattern2} _4y
* @property {RatioRawPattern2} all
* @property {PpmRatioPattern2} _1m
* @property {PpmRatioPattern2} _1w
* @property {PpmRatioPattern2} _1y
* @property {PpmRatioPattern2} _2y
* @property {PpmRatioPattern2} _4y
* @property {PpmRatioPattern2} all
*/
/**
@@ -3439,12 +3439,12 @@ function createMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) {
*/
function create_1m1w1y2y4yAllPattern(client, acc) {
return {
_1m: createRatioRawPattern2(client, _m(acc, '1m')),
_1w: createRatioRawPattern2(client, _m(acc, '1w')),
_1y: createRatioRawPattern2(client, _m(acc, '1y')),
_2y: createRatioRawPattern2(client, _m(acc, '2y')),
_4y: createRatioRawPattern2(client, _m(acc, '4y')),
all: createRatioRawPattern2(client, _m(acc, 'all')),
_1m: createPpmRatioPattern2(client, _m(acc, '1m')),
_1w: createPpmRatioPattern2(client, _m(acc, '1w')),
_1y: createPpmRatioPattern2(client, _m(acc, '1y')),
_2y: createPpmRatioPattern2(client, _m(acc, '2y')),
_4y: createPpmRatioPattern2(client, _m(acc, '4y')),
all: createPpmRatioPattern2(client, _m(acc, 'all')),
};
}
@@ -3530,27 +3530,27 @@ function createCentsNegativeToUsdPattern2(client, acc) {
}
/**
* @typedef {Object} CentsPercentilesRatioRawSatsUsdPattern
* @typedef {Object} CentsPercentilesPpmRatioSatsUsdPattern
* @property {SeriesPattern1<Cents>} cents
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<Dollars>} usd
*/
/**
* Create a CentsPercentilesRatioRawSatsUsdPattern pattern node
* Create a CentsPercentilesPpmRatioSatsUsdPattern pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {CentsPercentilesRatioRawSatsUsdPattern}
* @returns {CentsPercentilesPpmRatioSatsUsdPattern}
*/
function createCentsPercentilesRatioRawSatsUsdPattern(client, acc) {
function createCentsPercentilesPpmRatioSatsUsdPattern(client, acc) {
return {
cents: createSeriesPattern1(client, _m(acc, 'cents')),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc),
ppm: createSeriesPattern1(client, _m(acc, 'ratio_ppm')),
ratio: createSeriesPattern1(client, _m(acc, 'ratio')),
raw: createSeriesPattern1(client, _m(acc, 'ratio_ppm')),
sats: createSeriesPattern1(client, _m(acc, 'sats')),
usd: createSeriesPattern1(client, acc),
};
@@ -3876,7 +3876,7 @@ function createBtcCentsSatsShareUsdPattern(client, acc) {
* @property {CentsDeltaUsdPattern} cap
* @property {BlockCumulativeSumPattern} loss
* @property {SeriesPattern1<StoredF32>} mvrv
* @property {CentsRatioRawSatsUsdPattern} price
* @property {CentsPpmRatioSatsUsdPattern} price
* @property {BlockCumulativeSumPattern} profit
*/
@@ -3891,31 +3891,31 @@ function createCapLossMvrvPriceProfitPattern(client, acc) {
cap: createCentsDeltaUsdPattern(client, _m(acc, 'realized_cap')),
loss: createBlockCumulativeSumPattern(client, _m(acc, 'realized_loss')),
mvrv: createSeriesPattern1(client, _m(acc, 'mvrv')),
price: createCentsRatioRawSatsUsdPattern(client, _m(acc, 'realized_price')),
price: createCentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price')),
profit: createBlockCumulativeSumPattern(client, _m(acc, 'realized_profit')),
};
}
/**
* @typedef {Object} CentsRatioRawSatsUsdPattern
* @typedef {Object} CentsPpmRatioSatsUsdPattern
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<Dollars>} usd
*/
/**
* Create a CentsRatioRawSatsUsdPattern pattern node
* Create a CentsPpmRatioSatsUsdPattern pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {CentsRatioRawSatsUsdPattern}
* @returns {CentsPpmRatioSatsUsdPattern}
*/
function createCentsRatioRawSatsUsdPattern(client, acc) {
function createCentsPpmRatioSatsUsdPattern(client, acc) {
return {
cents: createSeriesPattern1(client, _m(acc, 'cents')),
ppm: createSeriesPattern1(client, _m(acc, 'ratio_ppm')),
ratio: createSeriesPattern1(client, _m(acc, 'ratio')),
raw: createSeriesPattern1(client, _m(acc, 'ratio_ppm')),
sats: createSeriesPattern1(client, _m(acc, 'sats')),
usd: createSeriesPattern1(client, acc),
};
@@ -4433,7 +4433,7 @@ function createCoindaysCoinyearsDormancyTransferPattern(client, acc) {
* @typedef {Object} LossNetNuplProfitPattern
* @property {CentsNegativeUsdPattern} loss
* @property {CentsUsdPattern} netPnl
* @property {RatioRawPattern} nupl
* @property {PpmRatioPattern} nupl
* @property {CentsUsdPattern3} profit
*/
@@ -4447,7 +4447,7 @@ function createLossNetNuplProfitPattern(client, acc) {
return {
loss: createCentsNegativeUsdPattern(client, _m(acc, 'unrealized_loss')),
netPnl: createCentsUsdPattern(client, _m(acc, 'net_unrealized_pnl')),
nupl: createRatioRawPattern(client, _m(acc, 'nupl')),
nupl: createPpmRatioPattern(client, _m(acc, 'nupl')),
profit: createCentsUsdPattern3(client, _m(acc, 'unrealized_profit')),
};
}
@@ -4477,7 +4477,7 @@ function createMobilitySpendingSupplyPattern(client, acc) {
/**
* @typedef {Object} NuplRealizedSupplyUnrealizedPattern
* @property {RatioRawPattern} nupl
* @property {PpmRatioPattern} nupl
* @property {AllSthPattern} realizedCap
* @property {AllSthPattern2} supply
* @property {AllSthPattern} unrealizedPnl
@@ -4491,7 +4491,7 @@ function createMobilitySpendingSupplyPattern(client, acc) {
*/
function createNuplRealizedSupplyUnrealizedPattern(client, acc) {
return {
nupl: createRatioRawPattern(client, _m(acc, 'nupl')),
nupl: createPpmRatioPattern(client, _m(acc, 'nupl')),
realizedCap: createAllSthPattern(client, acc, 'realized_cap'),
supply: createAllSthPattern2(client, acc),
unrealizedPnl: createAllSthPattern(client, acc, 'unrealized_pnl'),
@@ -4789,7 +4789,7 @@ function createGreedNetPainPattern(client, acc) {
/**
* @typedef {Object} LossNuplProfitPattern
* @property {CentsNegativeUsdPattern} loss
* @property {RatioRawPattern} nupl
* @property {PpmRatioPattern} nupl
* @property {CentsUsdPattern3} profit
*/
@@ -4802,7 +4802,7 @@ function createGreedNetPainPattern(client, acc) {
function createLossNuplProfitPattern(client, acc) {
return {
loss: createCentsNegativeUsdPattern(client, _m(acc, 'unrealized_loss')),
nupl: createRatioRawPattern(client, _m(acc, 'nupl')),
nupl: createPpmRatioPattern(client, _m(acc, 'nupl')),
profit: createCentsUsdPattern3(client, _m(acc, 'unrealized_profit')),
};
}
@@ -4871,24 +4871,24 @@ function createPercentPpmRatioPattern(client, acc) {
}
/**
* @typedef {Object} PriceRatioRawPattern
* @typedef {Object} PpmPriceRatioPattern
* @property {SeriesPattern1<PartsPerMillion32>} ppm
* @property {CentsSatsUsdPattern} price
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillion32>} raw
*/
/**
* Create a PriceRatioRawPattern pattern node
* Create a PpmPriceRatioPattern pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @param {string} disc - Discriminator suffix
* @returns {PriceRatioRawPattern}
* @returns {PpmPriceRatioPattern}
*/
function createPriceRatioRawPattern(client, acc, disc) {
function createPpmPriceRatioPattern(client, acc, disc) {
return {
ppm: createSeriesPattern1(client, _m(acc, `ratio_${disc}_ppm`)),
price: createCentsSatsUsdPattern(client, _m(acc, disc)),
ratio: createSeriesPattern1(client, _m(_m(acc, 'ratio'), disc)),
raw: createSeriesPattern1(client, _m(acc, `ratio_${disc}_ppm`)),
};
}
@@ -5398,6 +5398,63 @@ function createPerPattern(client, acc) {
};
}
/**
* @typedef {Object} PpmRatioPattern2
* @property {SeriesPattern1<PartsPerMillion32>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
*/
/**
* Create a PpmRatioPattern2 pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {PpmRatioPattern2}
*/
function createPpmRatioPattern2(client, acc) {
return {
ppm: createSeriesPattern1(client, _m(acc, 'ppm')),
ratio: createSeriesPattern1(client, acc),
};
}
/**
* @typedef {Object} PpmRatioPattern3
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
*/
/**
* Create a PpmRatioPattern3 pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {PpmRatioPattern3}
*/
function createPpmRatioPattern3(client, acc) {
return {
ppm: createSeriesPattern1(client, _m(acc, 'ppm')),
ratio: createSeriesPattern1(client, acc),
};
}
/**
* @typedef {Object} PpmRatioPattern
* @property {SeriesPattern1<PartsPerMillionSigned32>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
*/
/**
* Create a PpmRatioPattern pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {PpmRatioPattern}
*/
function createPpmRatioPattern(client, acc) {
return {
ppm: createSeriesPattern1(client, _m(acc, 'ppm')),
ratio: createSeriesPattern1(client, acc),
};
}
/**
* @typedef {Object} PriceRatioPattern
* @property {CentsSatsUsdPattern} price
@@ -5418,63 +5475,6 @@ function createPriceRatioPattern(client, acc, disc) {
};
}
/**
* @typedef {Object} RatioRawPattern2
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillion32>} raw
*/
/**
* Create a RatioRawPattern2 pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {RatioRawPattern2}
*/
function createRatioRawPattern2(client, acc) {
return {
ratio: createSeriesPattern1(client, acc),
raw: createSeriesPattern1(client, _m(acc, 'ppm')),
};
}
/**
* @typedef {Object} RatioRawPattern3
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillion64>} raw
*/
/**
* Create a RatioRawPattern3 pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {RatioRawPattern3}
*/
function createRatioRawPattern3(client, acc) {
return {
ratio: createSeriesPattern1(client, acc),
raw: createSeriesPattern1(client, _m(acc, 'ppm')),
};
}
/**
* @typedef {Object} RatioRawPattern
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<PartsPerMillionSigned32>} raw
*/
/**
* Create a RatioRawPattern pattern node
* @param {BrkClient} client
* @param {string} acc - Accumulated series name
* @returns {RatioRawPattern}
*/
function createRatioRawPattern(client, acc) {
return {
ratio: createSeriesPattern1(client, acc),
raw: createSeriesPattern1(client, _m(acc, 'ppm')),
};
}
/**
* @typedef {Object} RatioValuePattern2
* @property {_1m1w1y24hPattern<StoredF64>} ratio
@@ -5574,7 +5574,7 @@ function createInPattern3(client, acc) {
/**
* @typedef {Object} NuplPattern
* @property {RatioRawPattern} nupl
* @property {PpmRatioPattern} nupl
*/
/**
@@ -5585,13 +5585,13 @@ function createInPattern3(client, acc) {
*/
function createNuplPattern(client, acc) {
return {
nupl: createRatioRawPattern(client, acc),
nupl: createPpmRatioPattern(client, acc),
};
}
/**
* @typedef {Object} PricePattern
* @property {CentsPercentilesRatioRawSatsUsdPattern} price
* @property {CentsPercentilesPpmRatioSatsUsdPattern} price
*/
/**
@@ -5602,7 +5602,7 @@ function createNuplPattern(client, acc) {
*/
function createPricePattern(client, acc) {
return {
price: createCentsPercentilesRatioRawSatsUsdPattern(client, acc),
price: createCentsPercentilesPpmRatioSatsUsdPattern(client, acc),
};
}
@@ -6511,10 +6511,10 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Mining_Rewards_Fees_ToSubsidyRatio
* @property {RatioRawPattern3} _24h
* @property {RatioRawPattern3} _1w
* @property {RatioRawPattern3} _1m
* @property {RatioRawPattern3} _1y
* @property {PpmRatioPattern3} _24h
* @property {PpmRatioPattern3} _1w
* @property {PpmRatioPattern3} _1m
* @property {PpmRatioPattern3} _1y
*/
/**
@@ -6617,15 +6617,15 @@ function createTransferPattern(client, acc) {
* @property {CentsUsdPattern3} vaulted
* @property {CentsUsdPattern3} active
* @property {CentsUsdPattern3} cointime
* @property {RatioRawPattern2} aviv
* @property {PpmRatioPattern2} aviv
*/
/**
* @typedef {Object} SeriesTree_Cointime_Prices
* @property {CentsPercentilesRatioRawSatsUsdPattern} vaulted
* @property {CentsPercentilesRatioRawSatsUsdPattern} active
* @property {CentsPercentilesRatioRawSatsUsdPattern} trueMarketMean
* @property {CentsPercentilesRatioRawSatsUsdPattern} cointime
* @property {CentsPercentilesPpmRatioSatsUsdPattern} vaulted
* @property {CentsPercentilesPpmRatioSatsUsdPattern} active
* @property {CentsPercentilesPpmRatioSatsUsdPattern} trueMarketMean
* @property {CentsPercentilesPpmRatioSatsUsdPattern} cointime
*/
/**
@@ -6648,7 +6648,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesTree_Coinflow_Supply} supply
* @property {SeriesTree_Coinflow_Horizon} horizon
* @property {CentsUsdPattern3} cap
* @property {CentsRatioRawSatsUsdPattern} price
* @property {CentsPpmRatioSatsUsdPattern} price
*/
/**
@@ -6975,11 +6975,11 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Indicators
* @property {RatioRawPattern3} puellMultiple
* @property {RatioRawPattern3} nvt
* @property {PpmRatioPattern3} puellMultiple
* @property {PpmRatioPattern3} nvt
* @property {PercentPpmRatioPattern2} gini
* @property {RatioRawPattern3} rhodlRatio
* @property {RatioRawPattern3} thermoCapMultiple
* @property {PpmRatioPattern3} rhodlRatio
* @property {PpmRatioPattern3} thermoCapMultiple
* @property {SeriesPattern1<StoredF32>} coindaysDestroyedSupplyAdj
* @property {SeriesPattern1<StoredF32>} coinyearsDestroyedSupplyAdj
* @property {SeriesTree_Indicators_Dormancy} dormancy
@@ -7200,22 +7200,22 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Market_MovingAverage_Sma
* @property {CentsRatioRawSatsUsdPattern} _1w
* @property {CentsRatioRawSatsUsdPattern} _8d
* @property {CentsRatioRawSatsUsdPattern} _13d
* @property {CentsRatioRawSatsUsdPattern} _21d
* @property {CentsRatioRawSatsUsdPattern} _1m
* @property {CentsRatioRawSatsUsdPattern} _34d
* @property {CentsRatioRawSatsUsdPattern} _55d
* @property {CentsRatioRawSatsUsdPattern} _89d
* @property {CentsRatioRawSatsUsdPattern} _111d
* @property {CentsRatioRawSatsUsdPattern} _144d
* @property {CentsPpmRatioSatsUsdPattern} _1w
* @property {CentsPpmRatioSatsUsdPattern} _8d
* @property {CentsPpmRatioSatsUsdPattern} _13d
* @property {CentsPpmRatioSatsUsdPattern} _21d
* @property {CentsPpmRatioSatsUsdPattern} _1m
* @property {CentsPpmRatioSatsUsdPattern} _34d
* @property {CentsPpmRatioSatsUsdPattern} _55d
* @property {CentsPpmRatioSatsUsdPattern} _89d
* @property {CentsPpmRatioSatsUsdPattern} _111d
* @property {CentsPpmRatioSatsUsdPattern} _144d
* @property {SeriesTree_Market_MovingAverage_Sma_200d} _200d
* @property {SeriesTree_Market_MovingAverage_Sma_350d} _350d
* @property {CentsRatioRawSatsUsdPattern} _1y
* @property {CentsRatioRawSatsUsdPattern} _2y
* @property {CentsRatioRawSatsUsdPattern} _200w
* @property {CentsRatioRawSatsUsdPattern} _4y
* @property {CentsPpmRatioSatsUsdPattern} _1y
* @property {CentsPpmRatioSatsUsdPattern} _2y
* @property {CentsPpmRatioSatsUsdPattern} _200w
* @property {CentsPpmRatioSatsUsdPattern} _4y
*/
/**
@@ -7223,7 +7223,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {CentsSatsUsdPattern} x24
* @property {CentsSatsUsdPattern} x08
@@ -7234,35 +7234,35 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {CentsSatsUsdPattern} x2
*/
/**
* @typedef {Object} SeriesTree_Market_MovingAverage_Ema
* @property {CentsRatioRawSatsUsdPattern} _1w
* @property {CentsRatioRawSatsUsdPattern} _8d
* @property {CentsRatioRawSatsUsdPattern} _12d
* @property {CentsRatioRawSatsUsdPattern} _13d
* @property {CentsRatioRawSatsUsdPattern} _21d
* @property {CentsRatioRawSatsUsdPattern} _26d
* @property {CentsRatioRawSatsUsdPattern} _1m
* @property {CentsRatioRawSatsUsdPattern} _34d
* @property {CentsRatioRawSatsUsdPattern} _55d
* @property {CentsRatioRawSatsUsdPattern} _89d
* @property {CentsRatioRawSatsUsdPattern} _144d
* @property {CentsRatioRawSatsUsdPattern} _200d
* @property {CentsRatioRawSatsUsdPattern} _1y
* @property {CentsRatioRawSatsUsdPattern} _2y
* @property {CentsRatioRawSatsUsdPattern} _200w
* @property {CentsRatioRawSatsUsdPattern} _4y
* @property {CentsPpmRatioSatsUsdPattern} _1w
* @property {CentsPpmRatioSatsUsdPattern} _8d
* @property {CentsPpmRatioSatsUsdPattern} _12d
* @property {CentsPpmRatioSatsUsdPattern} _13d
* @property {CentsPpmRatioSatsUsdPattern} _21d
* @property {CentsPpmRatioSatsUsdPattern} _26d
* @property {CentsPpmRatioSatsUsdPattern} _1m
* @property {CentsPpmRatioSatsUsdPattern} _34d
* @property {CentsPpmRatioSatsUsdPattern} _55d
* @property {CentsPpmRatioSatsUsdPattern} _89d
* @property {CentsPpmRatioSatsUsdPattern} _144d
* @property {CentsPpmRatioSatsUsdPattern} _200d
* @property {CentsPpmRatioSatsUsdPattern} _1y
* @property {CentsPpmRatioSatsUsdPattern} _2y
* @property {CentsPpmRatioSatsUsdPattern} _200w
* @property {CentsPpmRatioSatsUsdPattern} _4y
*/
/**
* @typedef {Object} SeriesTree_Market_Technical
* @property {SeriesTree_Market_Technical_Rsi} rsi
* @property {RatioRawPattern2} piCycle
* @property {PpmRatioPattern2} piCycle
* @property {SeriesTree_Market_Technical_Macd} macd
*/
@@ -7606,7 +7606,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
@@ -7724,7 +7724,7 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Cohorts_Utxo_All_Unrealized
* @property {RatioRawPattern} nupl
* @property {PpmRatioPattern} nupl
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_Profit} profit
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_Loss} loss
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl} netPnl
@@ -7798,7 +7798,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
@@ -7921,7 +7921,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
@@ -8150,7 +8150,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
@@ -8273,7 +8273,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<Dollars>} usd
* @property {SeriesPattern1<Cents>} cents
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<PartsPerMillion64>} raw
* @property {SeriesPattern1<PartsPerMillion64>} ppm
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
@@ -10394,10 +10394,10 @@ class BrkClient extends BrkClientBase {
pct90: create_1m1w1y24hPattern4(this, 'fees_pct90'),
dominance: create_1m1w1y24hPercentPpmRatioPattern(this, 'fee_dominance'),
toSubsidyRatio: {
_24h: createRatioRawPattern3(this, 'fee_to_subsidy_ratio_24h'),
_1w: createRatioRawPattern3(this, 'fee_to_subsidy_ratio_1w'),
_1m: createRatioRawPattern3(this, 'fee_to_subsidy_ratio_1m'),
_1y: createRatioRawPattern3(this, 'fee_to_subsidy_ratio_1y'),
_24h: createPpmRatioPattern3(this, 'fee_to_subsidy_ratio_24h'),
_1w: createPpmRatioPattern3(this, 'fee_to_subsidy_ratio_1w'),
_1m: createPpmRatioPattern3(this, 'fee_to_subsidy_ratio_1m'),
_1y: createPpmRatioPattern3(this, 'fee_to_subsidy_ratio_1y'),
},
},
outputVolume: createSeriesPattern18(this, 'output_volume'),
@@ -10473,13 +10473,13 @@ class BrkClient extends BrkClientBase {
vaulted: createCentsUsdPattern3(this, 'vaulted_cap'),
active: createCentsUsdPattern3(this, 'active_cap'),
cointime: createCentsUsdPattern3(this, 'cointime_cap'),
aviv: createRatioRawPattern2(this, 'aviv_ratio'),
aviv: createPpmRatioPattern2(this, 'aviv_ratio'),
},
prices: {
vaulted: createCentsPercentilesRatioRawSatsUsdPattern(this, 'vaulted_price'),
active: createCentsPercentilesRatioRawSatsUsdPattern(this, 'active_price'),
trueMarketMean: createCentsPercentilesRatioRawSatsUsdPattern(this, 'true_market_mean'),
cointime: createCentsPercentilesRatioRawSatsUsdPattern(this, 'cointime_price'),
vaulted: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'vaulted_price'),
active: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'active_price'),
trueMarketMean: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'true_market_mean'),
cointime: createCentsPercentilesPpmRatioSatsUsdPattern(this, 'cointime_price'),
},
adjusted: {
inflationRate: createPercentPpmRatioPattern(this, 'cointime_adj_inflation_rate'),
@@ -10536,7 +10536,7 @@ class BrkClient extends BrkClientBase {
_1m: createSupplyPattern(this, 'coinflow_1m_supply_in_loss_share'),
},
cap: createCentsUsdPattern3(this, 'coinflow_cap'),
price: createCentsRatioRawSatsUsdPattern(this, 'coinflow_price'),
price: createCentsPpmRatioSatsUsdPattern(this, 'coinflow_price'),
},
bedrock: {
raw: createFloorLevelLossPattern(this, 'bedrock_raw'),
@@ -10705,11 +10705,11 @@ class BrkClient extends BrkClientBase {
},
},
indicators: {
puellMultiple: createRatioRawPattern3(this, 'puell_multiple'),
nvt: createRatioRawPattern3(this, 'nvt'),
puellMultiple: createPpmRatioPattern3(this, 'puell_multiple'),
nvt: createPpmRatioPattern3(this, 'nvt'),
gini: createPercentPpmRatioPattern2(this, 'gini'),
rhodlRatio: createRatioRawPattern3(this, 'rhodl_ratio'),
thermoCapMultiple: createRatioRawPattern3(this, 'thermo_cap_multiple'),
rhodlRatio: createPpmRatioPattern3(this, 'rhodl_ratio'),
thermoCapMultiple: createPpmRatioPattern3(this, 'thermo_cap_multiple'),
coindaysDestroyedSupplyAdj: createSeriesPattern1(this, 'coindays_destroyed_supply_adj'),
coinyearsDestroyedSupplyAdj: createSeriesPattern1(this, 'coinyears_destroyed_supply_adj'),
dormancy: {
@@ -10862,21 +10862,21 @@ class BrkClient extends BrkClientBase {
},
movingAverage: {
sma: {
_1w: createCentsRatioRawSatsUsdPattern(this, 'price_sma_1w'),
_8d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_8d'),
_13d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_13d'),
_21d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_21d'),
_1m: createCentsRatioRawSatsUsdPattern(this, 'price_sma_1m'),
_34d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_34d'),
_55d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_55d'),
_89d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_89d'),
_111d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_111d'),
_144d: createCentsRatioRawSatsUsdPattern(this, 'price_sma_144d'),
_1w: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_1w'),
_8d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_8d'),
_13d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_13d'),
_21d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_21d'),
_1m: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_1m'),
_34d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_34d'),
_55d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_55d'),
_89d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_89d'),
_111d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_111d'),
_144d: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_144d'),
_200d: {
usd: createSeriesPattern1(this, 'price_sma_200d'),
cents: createSeriesPattern1(this, 'price_sma_200d_cents'),
sats: createSeriesPattern1(this, 'price_sma_200d_sats'),
raw: createSeriesPattern1(this, 'price_sma_200d_ratio_ppm'),
ppm: createSeriesPattern1(this, 'price_sma_200d_ratio_ppm'),
ratio: createSeriesPattern1(this, 'price_sma_200d_ratio'),
x24: createCentsSatsUsdPattern(this, 'price_sma_200d_x2_4'),
x08: createCentsSatsUsdPattern(this, 'price_sma_200d_x0_8'),
@@ -10885,32 +10885,32 @@ class BrkClient extends BrkClientBase {
usd: createSeriesPattern1(this, 'price_sma_350d'),
cents: createSeriesPattern1(this, 'price_sma_350d_cents'),
sats: createSeriesPattern1(this, 'price_sma_350d_sats'),
raw: createSeriesPattern1(this, 'price_sma_350d_ratio_ppm'),
ppm: createSeriesPattern1(this, 'price_sma_350d_ratio_ppm'),
ratio: createSeriesPattern1(this, 'price_sma_350d_ratio'),
x2: createCentsSatsUsdPattern(this, 'price_sma_350d_x2'),
},
_1y: createCentsRatioRawSatsUsdPattern(this, 'price_sma_1y'),
_2y: createCentsRatioRawSatsUsdPattern(this, 'price_sma_2y'),
_200w: createCentsRatioRawSatsUsdPattern(this, 'price_sma_200w'),
_4y: createCentsRatioRawSatsUsdPattern(this, 'price_sma_4y'),
_1y: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_1y'),
_2y: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_2y'),
_200w: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_200w'),
_4y: createCentsPpmRatioSatsUsdPattern(this, 'price_sma_4y'),
},
ema: {
_1w: createCentsRatioRawSatsUsdPattern(this, 'price_ema_1w'),
_8d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_8d'),
_12d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_12d'),
_13d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_13d'),
_21d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_21d'),
_26d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_26d'),
_1m: createCentsRatioRawSatsUsdPattern(this, 'price_ema_1m'),
_34d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_34d'),
_55d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_55d'),
_89d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_89d'),
_144d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_144d'),
_200d: createCentsRatioRawSatsUsdPattern(this, 'price_ema_200d'),
_1y: createCentsRatioRawSatsUsdPattern(this, 'price_ema_1y'),
_2y: createCentsRatioRawSatsUsdPattern(this, 'price_ema_2y'),
_200w: createCentsRatioRawSatsUsdPattern(this, 'price_ema_200w'),
_4y: createCentsRatioRawSatsUsdPattern(this, 'price_ema_4y'),
_1w: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_1w'),
_8d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_8d'),
_12d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_12d'),
_13d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_13d'),
_21d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_21d'),
_26d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_26d'),
_1m: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_1m'),
_34d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_34d'),
_55d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_55d'),
_89d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_89d'),
_144d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_144d'),
_200d: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_200d'),
_1y: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_1y'),
_2y: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_2y'),
_200w: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_200w'),
_4y: createCentsPpmRatioSatsUsdPattern(this, 'price_ema_4y'),
},
},
technical: {
@@ -10919,7 +10919,7 @@ class BrkClient extends BrkClientBase {
_1w: createRsiStochPattern(this, 'rsi', '1w'),
_1m: createRsiStochPattern(this, 'rsi', '1m'),
},
piCycle: createRatioRawPattern2(this, 'pi_cycle'),
piCycle: createPpmRatioPattern2(this, 'pi_cycle'),
macd: {
_24h: {
emaFast: createSeriesPattern1(this, 'macd_ema_fast_24h'),
@@ -11171,7 +11171,7 @@ class BrkClient extends BrkClientBase {
usd: createSeriesPattern1(this, 'realized_price'),
cents: createSeriesPattern1(this, 'realized_price_cents'),
sats: createSeriesPattern1(this, 'realized_price_sats'),
raw: createSeriesPattern1(this, 'realized_price_ratio_ppm'),
ppm: createSeriesPattern1(this, 'realized_price_ratio_ppm'),
ratio: createSeriesPattern1(this, 'realized_price_ratio'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'realized_price_ratio_sma'),
@@ -11273,7 +11273,7 @@ class BrkClient extends BrkClientBase {
supplyDensity: createPercentPpmRatioPattern2(this, 'supply_density'),
},
unrealized: {
nupl: createRatioRawPattern(this, 'nupl'),
nupl: createPpmRatioPattern(this, 'nupl'),
profit: {
usd: createSeriesPattern1(this, 'unrealized_profit'),
cents: createSeriesPattern1(this, 'unrealized_profit_cents'),
@@ -11316,7 +11316,7 @@ class BrkClient extends BrkClientBase {
usd: createSeriesPattern1(this, 'sth_realized_price'),
cents: createSeriesPattern1(this, 'sth_realized_price_cents'),
sats: createSeriesPattern1(this, 'sth_realized_price_sats'),
raw: createSeriesPattern1(this, 'sth_realized_price_ratio_ppm'),
ppm: createSeriesPattern1(this, 'sth_realized_price_ratio_ppm'),
ratio: createSeriesPattern1(this, 'sth_realized_price_ratio'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'sth_realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'sth_realized_price_ratio_sma'),
@@ -11416,7 +11416,7 @@ class BrkClient extends BrkClientBase {
usd: createSeriesPattern1(this, 'lth_realized_price'),
cents: createSeriesPattern1(this, 'lth_realized_price_cents'),
sats: createSeriesPattern1(this, 'lth_realized_price_sats'),
raw: createSeriesPattern1(this, 'lth_realized_price_ratio_ppm'),
ppm: createSeriesPattern1(this, 'lth_realized_price_ratio_ppm'),
ratio: createSeriesPattern1(this, 'lth_realized_price_ratio'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'lth_realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'lth_realized_price_ratio_sma'),
@@ -11607,7 +11607,7 @@ class BrkClient extends BrkClientBase {
usd: createSeriesPattern1(this, 'veteran_realized_price'),
cents: createSeriesPattern1(this, 'veteran_realized_price_cents'),
sats: createSeriesPattern1(this, 'veteran_realized_price_sats'),
raw: createSeriesPattern1(this, 'veteran_realized_price_ratio_ppm'),
ppm: createSeriesPattern1(this, 'veteran_realized_price_ratio_ppm'),
ratio: createSeriesPattern1(this, 'veteran_realized_price_ratio'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'veteran_realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'veteran_realized_price_ratio_sma'),
@@ -11707,7 +11707,7 @@ class BrkClient extends BrkClientBase {
usd: createSeriesPattern1(this, 'rookie_realized_price'),
cents: createSeriesPattern1(this, 'rookie_realized_price_cents'),
sats: createSeriesPattern1(this, 'rookie_realized_price_sats'),
raw: createSeriesPattern1(this, 'rookie_realized_price_ratio_ppm'),
ppm: createSeriesPattern1(this, 'rookie_realized_price_ratio_ppm'),
ratio: createSeriesPattern1(this, 'rookie_realized_price_ratio'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'rookie_realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'rookie_realized_price_ratio_sma'),
+108 -108
View File
@@ -318,7 +318,7 @@ class AddrMempoolStats(TypedDict):
Based on mempool.space's format.
Attributes:
balance_delta: Net unconfirmed balance change in satoshis; negative when pending spends exceed receipts
balance_delta: Net pending (unconfirmed) balance change in satoshis; negative when pending spends exceed receipts
funded_txo_count: Number of unconfirmed transaction outputs funding this address
funded_txo_sum: Total amount in satoshis being received in unconfirmed transactions
spent_txo_count: Number of unconfirmed transaction inputs spending from this address
@@ -347,7 +347,7 @@ class AddrStats(TypedDict):
addr_type: Address type (p2pkh, p2sh, v0_p2wpkh, v0_p2wsh, v1_p2tr, etc.)
chain_stats: Statistics for confirmed transactions on the blockchain
mempool_stats: Statistics for unconfirmed transactions in the mempool
balance: Current balance in satoshis, including unconfirmed mempool changes
balance: Total current balance in satoshis, including pending (unconfirmed) mempool changes
"""
address: Addr
addr_type: OutputType
@@ -3270,7 +3270,7 @@ class CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2:
self.invested_capital: InPattern2 = InPattern2(client, _m(acc, 'invested_capital_in'))
self.loss: CentsNegativeToUsdPattern2 = CentsNegativeToUsdPattern2(client, _m(acc, 'unrealized_loss'))
self.net_pnl: CentsToUsdPattern3 = CentsToUsdPattern3(client, _m(acc, 'net_unrealized_pnl'))
self.nupl: RatioRawPattern = RatioRawPattern(client, _m(acc, 'nupl'))
self.nupl: PpmRatioPattern = PpmRatioPattern(client, _m(acc, 'nupl'))
self.profit: CentsToUsdPattern4 = CentsToUsdPattern4(client, _m(acc, 'unrealized_profit'))
self.sentiment: GreedNetPainPattern = GreedNetPainPattern(client, acc)
@@ -3289,7 +3289,7 @@ class Pct10Pct20Pct30Pct40Pct50Pct60Pct70Pct80Pct90Pattern:
self.pct80: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'pct80'))
self.pct90: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'pct90'))
class CentsPercentilesRatioRawSatsSmaStdUsdPattern:
class CentsPercentilesPpmRatioSatsSmaStdUsdPattern:
"""Pattern struct for repeated tree structure."""
pass
@@ -3298,14 +3298,14 @@ class Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern:
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.pct0_5: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct0_5')
self.pct1: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct1')
self.pct2: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct2')
self.pct5: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct5')
self.pct95: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct95')
self.pct98: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct98')
self.pct99: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct99')
self.pct99_5: PriceRatioRawPattern = PriceRatioRawPattern(client, acc, 'pct99_5')
self.pct0_5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct0_5')
self.pct1: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct1')
self.pct2: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct2')
self.pct5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct5')
self.pct95: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct95')
self.pct98: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct98')
self.pct99: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99')
self.pct99_5: PpmPriceRatioPattern = PpmPriceRatioPattern(client, acc, 'pct99_5')
class _10y2y3y4y5y6y8yPattern:
"""Pattern struct for repeated tree structure."""
@@ -3350,7 +3350,7 @@ class CapLossMvrvNetPriceProfitSoprPattern:
self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss'))
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'mvrv'))
self.net_pnl: BlockCumulativeDeltaSumPattern = BlockCumulativeDeltaSumPattern(client, _m(acc, 'net_realized_pnl'))
self.price: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, _m(acc, 'realized_price'))
self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price'))
self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_profit'))
self.sopr: RatioValuePattern = RatioValuePattern(client, acc)
@@ -3411,12 +3411,12 @@ class _1m1w1y2y4yAllPattern:
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self._1m: RatioRawPattern2 = RatioRawPattern2(client, _m(acc, '1m'))
self._1w: RatioRawPattern2 = RatioRawPattern2(client, _m(acc, '1w'))
self._1y: RatioRawPattern2 = RatioRawPattern2(client, _m(acc, '1y'))
self._2y: RatioRawPattern2 = RatioRawPattern2(client, _m(acc, '2y'))
self._4y: RatioRawPattern2 = RatioRawPattern2(client, _m(acc, '4y'))
self.all: RatioRawPattern2 = RatioRawPattern2(client, _m(acc, 'all'))
self._1m: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '1m'))
self._1w: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '1w'))
self._1y: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '1y'))
self._2y: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '2y'))
self._4y: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, '4y'))
self.all: PpmRatioPattern2 = PpmRatioPattern2(client, _m(acc, 'all'))
class ActivityAddrOutputsRealizedSupplyUnrealizedPattern:
"""Pattern struct for repeated tree structure."""
@@ -3454,15 +3454,15 @@ class CentsNegativeToUsdPattern2:
self.to_own_mcap: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, _m(acc, 'to_own_mcap'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
class CentsPercentilesRatioRawSatsUsdPattern:
class CentsPercentilesPpmRatioSatsUsdPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents'))
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc)
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ratio_ppm'))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'ratio'))
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ratio_ppm'))
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
@@ -3613,17 +3613,17 @@ class CapLossMvrvPriceProfitPattern:
self.cap: CentsDeltaUsdPattern = CentsDeltaUsdPattern(client, _m(acc, 'realized_cap'))
self.loss: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_loss'))
self.mvrv: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'mvrv'))
self.price: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, _m(acc, 'realized_price'))
self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, _m(acc, 'realized_price'))
self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_profit'))
class CentsRatioRawSatsUsdPattern:
class CentsPpmRatioSatsUsdPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents'))
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ratio_ppm'))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'ratio'))
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ratio_ppm'))
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
@@ -3855,7 +3855,7 @@ class LossNetNuplProfitPattern:
"""Create pattern node with accumulated series name."""
self.loss: CentsNegativeUsdPattern = CentsNegativeUsdPattern(client, _m(acc, 'unrealized_loss'))
self.net_pnl: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'net_unrealized_pnl'))
self.nupl: RatioRawPattern = RatioRawPattern(client, _m(acc, 'nupl'))
self.nupl: PpmRatioPattern = PpmRatioPattern(client, _m(acc, 'nupl'))
self.profit: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'unrealized_profit'))
class MobilitySpendingSupplyPattern:
@@ -3873,7 +3873,7 @@ class NuplRealizedSupplyUnrealizedPattern:
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.nupl: RatioRawPattern = RatioRawPattern(client, _m(acc, 'nupl'))
self.nupl: PpmRatioPattern = PpmRatioPattern(client, _m(acc, 'nupl'))
self.realized_cap: AllSthPattern = AllSthPattern(client, acc, 'realized_cap')
self.supply: AllSthPattern2 = AllSthPattern2(client, acc)
self.unrealized_pnl: AllSthPattern = AllSthPattern(client, acc, 'unrealized_pnl')
@@ -4007,7 +4007,7 @@ class LossNuplProfitPattern:
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.loss: CentsNegativeUsdPattern = CentsNegativeUsdPattern(client, _m(acc, 'unrealized_loss'))
self.nupl: RatioRawPattern = RatioRawPattern(client, _m(acc, 'nupl'))
self.nupl: PpmRatioPattern = PpmRatioPattern(client, _m(acc, 'nupl'))
self.profit: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'unrealized_profit'))
class PercentPpmRatioPattern2:
@@ -4037,14 +4037,14 @@ class PercentPpmRatioPattern:
self.ppm: SeriesPattern1[PartsPerMillionSigned64] = SeriesPattern1(client, _m(acc, 'ppm'))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'ratio'))
class PriceRatioRawPattern:
class PpmPriceRatioPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str, disc: str):
"""Create pattern node with accumulated series name."""
self.ppm: SeriesPattern1[PartsPerMillion32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}_ppm'))
self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, disc))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}'))
self.raw: SeriesPattern1[PartsPerMillion32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}_ppm'))
class RatioTransferValuePattern:
"""Pattern struct for repeated tree structure."""
@@ -4258,6 +4258,30 @@ class PerPattern:
self.per_coin: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'coin'))
self.per_dollar: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'dollar'))
class PpmRatioPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.ppm: SeriesPattern1[PartsPerMillion32] = SeriesPattern1(client, _m(acc, 'ppm'))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc)
class PpmRatioPattern3:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ppm'))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc)
class PpmRatioPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.ppm: SeriesPattern1[PartsPerMillionSigned32] = SeriesPattern1(client, _m(acc, 'ppm'))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc)
class PriceRatioPattern:
"""Pattern struct for repeated tree structure."""
@@ -4266,30 +4290,6 @@ class PriceRatioPattern:
self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, disc))
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}'))
class RatioRawPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc)
self.raw: SeriesPattern1[PartsPerMillion32] = SeriesPattern1(client, _m(acc, 'ppm'))
class RatioRawPattern3:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc)
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, _m(acc, 'ppm'))
class RatioRawPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc)
self.raw: SeriesPattern1[PartsPerMillionSigned32] = SeriesPattern1(client, _m(acc, 'ppm'))
class RatioValuePattern2:
"""Pattern struct for repeated tree structure."""
@@ -4337,14 +4337,14 @@ class NuplPattern:
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.nupl: RatioRawPattern = RatioRawPattern(client, acc)
self.nupl: PpmRatioPattern = PpmRatioPattern(client, acc)
class PricePattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClient, acc: str):
"""Create pattern node with accumulated series name."""
self.price: CentsPercentilesRatioRawSatsUsdPattern = CentsPercentilesRatioRawSatsUsdPattern(client, acc)
self.price: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, acc)
class SharePattern:
"""Pattern struct for repeated tree structure."""
@@ -5232,10 +5232,10 @@ class SeriesTree_Mining_Rewards_Fees_ToSubsidyRatio:
"""Series tree node."""
def __init__(self, client: BrkClient, base_path: str = ''):
self._24h: RatioRawPattern3 = RatioRawPattern3(client, 'fee_to_subsidy_ratio_24h')
self._1w: RatioRawPattern3 = RatioRawPattern3(client, 'fee_to_subsidy_ratio_1w')
self._1m: RatioRawPattern3 = RatioRawPattern3(client, 'fee_to_subsidy_ratio_1m')
self._1y: RatioRawPattern3 = RatioRawPattern3(client, 'fee_to_subsidy_ratio_1y')
self._24h: PpmRatioPattern3 = PpmRatioPattern3(client, 'fee_to_subsidy_ratio_24h')
self._1w: PpmRatioPattern3 = PpmRatioPattern3(client, 'fee_to_subsidy_ratio_1w')
self._1m: PpmRatioPattern3 = PpmRatioPattern3(client, 'fee_to_subsidy_ratio_1m')
self._1y: PpmRatioPattern3 = PpmRatioPattern3(client, 'fee_to_subsidy_ratio_1y')
class SeriesTree_Mining_Rewards_Fees:
"""Series tree node."""
@@ -5370,16 +5370,16 @@ class SeriesTree_Cointime_Cap:
self.vaulted: CentsUsdPattern3 = CentsUsdPattern3(client, 'vaulted_cap')
self.active: CentsUsdPattern3 = CentsUsdPattern3(client, 'active_cap')
self.cointime: CentsUsdPattern3 = CentsUsdPattern3(client, 'cointime_cap')
self.aviv: RatioRawPattern2 = RatioRawPattern2(client, 'aviv_ratio')
self.aviv: PpmRatioPattern2 = PpmRatioPattern2(client, 'aviv_ratio')
class SeriesTree_Cointime_Prices:
"""Series tree node."""
def __init__(self, client: BrkClient, base_path: str = ''):
self.vaulted: CentsPercentilesRatioRawSatsUsdPattern = CentsPercentilesRatioRawSatsUsdPattern(client, 'vaulted_price')
self.active: CentsPercentilesRatioRawSatsUsdPattern = CentsPercentilesRatioRawSatsUsdPattern(client, 'active_price')
self.true_market_mean: CentsPercentilesRatioRawSatsUsdPattern = CentsPercentilesRatioRawSatsUsdPattern(client, 'true_market_mean')
self.cointime: CentsPercentilesRatioRawSatsUsdPattern = CentsPercentilesRatioRawSatsUsdPattern(client, 'cointime_price')
self.vaulted: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'vaulted_price')
self.active: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'active_price')
self.true_market_mean: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'true_market_mean')
self.cointime: CentsPercentilesPpmRatioSatsUsdPattern = CentsPercentilesPpmRatioSatsUsdPattern(client, 'cointime_price')
class SeriesTree_Cointime_Adjusted:
"""Series tree node."""
@@ -5473,7 +5473,7 @@ class SeriesTree_Coinflow:
self.supply: SeriesTree_Coinflow_Supply = SeriesTree_Coinflow_Supply(client)
self.horizon: SeriesTree_Coinflow_Horizon = SeriesTree_Coinflow_Horizon(client)
self.cap: CentsUsdPattern3 = CentsUsdPattern3(client, 'coinflow_cap')
self.price: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'coinflow_price')
self.price: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'coinflow_price')
class SeriesTree_Bedrock:
"""Series tree node."""
@@ -5801,11 +5801,11 @@ class SeriesTree_Indicators:
"""Series tree node."""
def __init__(self, client: BrkClient, base_path: str = ''):
self.puell_multiple: RatioRawPattern3 = RatioRawPattern3(client, 'puell_multiple')
self.nvt: RatioRawPattern3 = RatioRawPattern3(client, 'nvt')
self.puell_multiple: PpmRatioPattern3 = PpmRatioPattern3(client, 'puell_multiple')
self.nvt: PpmRatioPattern3 = PpmRatioPattern3(client, 'nvt')
self.gini: PercentPpmRatioPattern2 = PercentPpmRatioPattern2(client, 'gini')
self.rhodl_ratio: RatioRawPattern3 = RatioRawPattern3(client, 'rhodl_ratio')
self.thermo_cap_multiple: RatioRawPattern3 = RatioRawPattern3(client, 'thermo_cap_multiple')
self.rhodl_ratio: PpmRatioPattern3 = PpmRatioPattern3(client, 'rhodl_ratio')
self.thermo_cap_multiple: PpmRatioPattern3 = PpmRatioPattern3(client, 'thermo_cap_multiple')
self.coindays_destroyed_supply_adj: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'coindays_destroyed_supply_adj')
self.coinyears_destroyed_supply_adj: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'coinyears_destroyed_supply_adj')
self.dormancy: SeriesTree_Indicators_Dormancy = SeriesTree_Indicators_Dormancy(client)
@@ -6017,7 +6017,7 @@ class SeriesTree_Market_MovingAverage_Sma_200d:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'price_sma_200d')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'price_sma_200d_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'price_sma_200d_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'price_sma_200d_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'price_sma_200d_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_sma_200d_ratio')
self.x2_4: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_sma_200d_x2_4')
self.x0_8: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_sma_200d_x0_8')
@@ -6029,7 +6029,7 @@ class SeriesTree_Market_MovingAverage_Sma_350d:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'price_sma_350d')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'price_sma_350d_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'price_sma_350d_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'price_sma_350d_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'price_sma_350d_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_sma_350d_ratio')
self.x2: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_sma_350d_x2')
@@ -6037,43 +6037,43 @@ class SeriesTree_Market_MovingAverage_Sma:
"""Series tree node."""
def __init__(self, client: BrkClient, base_path: str = ''):
self._1w: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_1w')
self._8d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_8d')
self._13d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_13d')
self._21d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_21d')
self._1m: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_1m')
self._34d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_34d')
self._55d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_55d')
self._89d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_89d')
self._111d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_111d')
self._144d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_144d')
self._1w: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_1w')
self._8d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_8d')
self._13d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_13d')
self._21d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_21d')
self._1m: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_1m')
self._34d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_34d')
self._55d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_55d')
self._89d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_89d')
self._111d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_111d')
self._144d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_144d')
self._200d: SeriesTree_Market_MovingAverage_Sma_200d = SeriesTree_Market_MovingAverage_Sma_200d(client)
self._350d: SeriesTree_Market_MovingAverage_Sma_350d = SeriesTree_Market_MovingAverage_Sma_350d(client)
self._1y: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_1y')
self._2y: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_2y')
self._200w: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_200w')
self._4y: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_sma_4y')
self._1y: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_1y')
self._2y: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_2y')
self._200w: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_200w')
self._4y: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_sma_4y')
class SeriesTree_Market_MovingAverage_Ema:
"""Series tree node."""
def __init__(self, client: BrkClient, base_path: str = ''):
self._1w: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_1w')
self._8d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_8d')
self._12d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_12d')
self._13d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_13d')
self._21d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_21d')
self._26d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_26d')
self._1m: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_1m')
self._34d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_34d')
self._55d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_55d')
self._89d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_89d')
self._144d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_144d')
self._200d: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_200d')
self._1y: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_1y')
self._2y: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_2y')
self._200w: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_200w')
self._4y: CentsRatioRawSatsUsdPattern = CentsRatioRawSatsUsdPattern(client, 'price_ema_4y')
self._1w: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_1w')
self._8d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_8d')
self._12d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_12d')
self._13d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_13d')
self._21d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_21d')
self._26d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_26d')
self._1m: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_1m')
self._34d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_34d')
self._55d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_55d')
self._89d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_89d')
self._144d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_144d')
self._200d: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_200d')
self._1y: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_1y')
self._2y: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_2y')
self._200w: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_200w')
self._4y: CentsPpmRatioSatsUsdPattern = CentsPpmRatioSatsUsdPattern(client, 'price_ema_4y')
class SeriesTree_Market_MovingAverage:
"""Series tree node."""
@@ -6133,7 +6133,7 @@ class SeriesTree_Market_Technical:
def __init__(self, client: BrkClient, base_path: str = ''):
self.rsi: SeriesTree_Market_Technical_Rsi = SeriesTree_Market_Technical_Rsi(client)
self.pi_cycle: RatioRawPattern2 = RatioRawPattern2(client, 'pi_cycle')
self.pi_cycle: PpmRatioPattern2 = PpmRatioPattern2(client, 'pi_cycle')
self.macd: SeriesTree_Market_Technical_Macd = SeriesTree_Market_Technical_Macd(client)
class SeriesTree_Market:
@@ -6497,7 +6497,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'realized_price')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'realized_price_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'realized_price_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'realized_price_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'realized_price_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'realized_price_ratio_sma')
@@ -6587,7 +6587,7 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized:
"""Series tree node."""
def __init__(self, client: BrkClient, base_path: str = ''):
self.nupl: RatioRawPattern = RatioRawPattern(client, 'nupl')
self.nupl: PpmRatioPattern = PpmRatioPattern(client, 'nupl')
self.profit: SeriesTree_Cohorts_Utxo_All_Unrealized_Profit = SeriesTree_Cohorts_Utxo_All_Unrealized_Profit(client)
self.loss: SeriesTree_Cohorts_Utxo_All_Unrealized_Loss = SeriesTree_Cohorts_Utxo_All_Unrealized_Loss(client)
self.net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl = SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl(client)
@@ -6705,7 +6705,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'sth_realized_price')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'sth_realized_price_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'sth_realized_price_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'sth_realized_price_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'sth_realized_price_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'sth_realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'sth_realized_price_ratio_sma')
@@ -6836,7 +6836,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'lth_realized_price')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'lth_realized_price_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'lth_realized_price_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'lth_realized_price_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'lth_realized_price_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'lth_realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'lth_realized_price_ratio_sma')
@@ -7072,7 +7072,7 @@ class SeriesTree_Cohorts_Utxo_Entry_Discount_Realized_Price:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'veteran_realized_price')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'veteran_realized_price_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'veteran_realized_price_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'veteran_realized_price_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'veteran_realized_price_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'veteran_realized_price_ratio')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'veteran_realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'veteran_realized_price_ratio_sma')
@@ -7203,7 +7203,7 @@ class SeriesTree_Cohorts_Utxo_Entry_Premium_Realized_Price:
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'rookie_realized_price')
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'rookie_realized_price_cents')
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'rookie_realized_price_sats')
self.raw: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'rookie_realized_price_ratio_ppm')
self.ppm: SeriesPattern1[PartsPerMillion64] = SeriesPattern1(client, 'rookie_realized_price_ratio_ppm')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'rookie_realized_price_ratio')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'rookie_realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'rookie_realized_price_ratio_sma')
+2 -2
View File
@@ -1468,7 +1468,7 @@ curl -s "https://bitview.space/version"
### `AddrMempoolStats`
- `balance_delta`: `SatsSigned` (required) — Net unconfirmed balance change in satoshis; negative when pending spends exceed receipts
- `balance_delta`: `SatsSigned` (required) — Net pending (unconfirmed) balance change in satoshis; negative when pending spends exceed receipts
- `funded_txo_count`: `integer` (required) — Number of unconfirmed transaction outputs funding this address
- `funded_txo_sum`: `Sats` (required) — Total amount in satoshis being received in unconfirmed transactions
- `spent_txo_count`: `integer` (required) — Number of unconfirmed transaction inputs spending from this address
@@ -1481,7 +1481,7 @@ curl -s "https://bitview.space/version"
- `addr_type`: `OutputType` (required) — Address type (p2pkh, p2sh, v0_p2wpkh, v0_p2wsh, v1_p2tr, etc.)
- `chain_stats`: `AddrChainStats` (required) — Statistics for confirmed transactions on the blockchain
- `mempool_stats`: `AddrMempoolStats` (required) — Statistics for unconfirmed transactions in the mempool
- `balance`: `Sats` (required) — Current balance in satoshis, including unconfirmed mempool changes
- `balance`: `Sats` (required) — Total current balance in satoshis, including pending (unconfirmed) mempool changes
### `AddrValidation`
+15 -14
View File
@@ -42,7 +42,7 @@
* @typedef {Brk.BtcCentsSatsUsdPattern} SupplyPattern
* @typedef {Brk.AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern} BlockSizePattern
* @typedef {keyof Brk.SeriesTree_Cohorts_Utxo_Type} SpendableType
* @typedef {Brk.SpendingSpentUnspentPattern} OutputsPattern
* @typedef {Brk.SpentUnspentUtxoPattern} OutputsPattern
* @typedef {keyof Brk.SeriesTree_Addrs_Raw} AddressableType
*
* Brk pattern types (using new pattern names)
@@ -69,11 +69,11 @@
* @typedef {Brk.MempoolBlock} MempoolBlock
* @typedef {Brk.NextBlockHash} NextBlockHash
* ActivePriceRatioPattern: ratio pattern with price (extended)
* @typedef {Brk.BpsPriceRatioPattern} ActivePriceRatioPattern
* @typedef {Brk.PriceRatioPattern} ActivePriceRatioPattern
* PriceRatioPercentilesPattern: price pattern with ratio + percentiles (no SMAs/stdDev)
* @typedef {Brk.BpsCentsPercentilesRatioSatsUsdPattern} PriceRatioPercentilesPattern
* @typedef {Brk.CentsPercentilesPpmRatioSatsUsdPattern} PriceRatioPercentilesPattern
* AnyRatioPattern: full ratio pattern with percentiles, SMAs, and std dev bands
* @typedef {Brk.BpsCentsPercentilesRatioSatsSmaStdUsdPattern} AnyRatioPattern
* @typedef {Brk.CentsPercentilesPpmRatioSatsSmaStdUsdPattern} AnyRatioPattern
* FullValuePattern: block + cumulative + sum + average rolling windows (sats/btc/cents/usd)
* @typedef {Brk.AverageBlockCumulativeSumPattern3} FullValuePattern
* RollingWindowSlot: a single rolling window with stats (pct10, pct25, median, pct75, pct90, max, min) per unit
@@ -92,6 +92,7 @@
*
* Realized pattern (full: cap + gross + capitalized + loss + mvrv + net + peak + price + profit + sell + sopr)
* @typedef {Brk.CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
* @typedef {Omit<RealizedPattern, "sopr">} FullRealizedProfitabilityPattern
*
* Transfer volume pattern (block + cumulative + inProfit/inLoss + sum windows)
* @typedef {Brk.AverageBlockCumulativeInSumPattern} TransferVolumePattern
@@ -103,14 +104,14 @@
* @typedef {Brk.CoindaysCoinyearsDormancyTransferPattern} FullActivityPattern
*
*
* BPS + percent + ratio pattern
* @typedef {Brk.BpsPercentRatioPattern2} PercentRatioPattern
* PPM + percent + ratio pattern
* @typedef {Brk.PercentPpmRatioPattern2} PercentRatioPattern
*
* Percent + ratio per window + cumulative (mirrors CountPattern but for percent)
* @typedef {Brk._1m1w1y24hBpsPercentRatioPattern} PercentRatioCumulativePattern
* @typedef {Brk._1m1w1y24hPercentPpmRatioPattern} PercentRatioCumulativePattern
*
* BPS + ratio pattern (for NUPL and similar)
* @typedef {Brk.BpsRatioPattern} NuplPattern
* PPM + ratio pattern (for NUPL and similar)
* @typedef {Brk.PpmRatioPattern} NuplPattern
*
* LTH realized tree
* @typedef {Brk.SeriesTree_Cohorts_Utxo_Lth_Realized} LthRealizedPattern
@@ -127,8 +128,8 @@
* Basic realized pattern (cap + loss + MVRV + price + profit, no net/sopr)
* @typedef {Brk.CapLossMvrvPriceProfitPattern} BasicRealizedPattern
*
* Moving average price ratio pattern (bps + cents + ratio + sats + usd)
* @typedef {Brk.BpsCentsRatioSatsUsdPattern} MaPriceRatioPattern
* Moving average price ratio pattern (ppm + cents + ratio + sats + usd)
* @typedef {Brk.CentsPpmRatioSatsUsdPattern} MaPriceRatioPattern
*
* Address count pattern (base + delta with absolute + rate)
* @typedef {Brk.BaseDeltaPattern} AddrCountPattern
@@ -189,7 +190,7 @@
*/
/**
* Dominance pattern: percent/ratio at top level + per rolling window
* @typedef {Brk._1m1w1y24hBpsPercentRatioPattern} DominancePattern
* @typedef {Brk._1m1w1y24hPercentPpmRatioPattern} DominancePattern
*/
/**
@@ -243,7 +244,7 @@
* @typedef {{ name: string, title: string, color: Color, tree: PatternWithActivity }} CohortWithActivity
* @typedef {{ name: string, title: string, color: Color, tree: PatternWithCostBasisPercentiles }} CohortWithCostBasisPercentiles
*
* Cohorts with nupl + percentiles (CohortFull and CohortLongTerm both have nupl and percentiles)
* Cohorts with full NUPL and cost-basis percentiles.
* @typedef {CohortFull | CohortLongTerm} CohortWithNuplPercentiles
* @typedef {{ name: string, title: string, list: readonly CohortWithNuplPercentiles[], all: CohortAll }} CohortGroupWithNuplPercentiles
*
@@ -255,7 +256,7 @@
*
* Capitalized price percentiles (pct1/2/5/95/98/99)
* @typedef {Brk.Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} CapitalizedPercentilesPattern
* @typedef {Brk.BpsPriceRatioPattern} CapitalizedPercentileEntry
* @typedef {Brk.PriceRatioPattern} CapitalizedPercentileEntry
*
* Generic tree node type for walking
* @typedef {AnySeriesPattern | Record<string, unknown>} TreeNode
+18
View File
@@ -0,0 +1,18 @@
import { entries } from "../utils/array.js";
import { brk } from "../utils/client.js";
import { colors } from "../utils/colors.js";
/**
* Shared display metadata for every UTXO age range.
*
* Consumers attach their own tree because Distribution, Cointime, and
* Coinflow expose different metrics for the same ranges.
*/
export const ageRanges = entries(brk.AGE_RANGE_NAMES).map(
([key, names], index, all) => ({
key,
name: names.short,
title: `UTXOs ${names.long}`,
color: colors.at(index, all.length),
}),
);
+10 -8
View File
@@ -1,6 +1,7 @@
import { colors } from "../../utils/colors.js";
import { entries } from "../../utils/array.js";
import { brk } from "../../utils/client.js";
import { ageRanges } from "../age-ranges.js";
/** @type {readonly AddressableType[]} */
const ADDRESSABLE_TYPES = [
@@ -14,9 +15,13 @@ const ADDRESSABLE_TYPES = [
"p2pk65",
];
/** @type {(key: SpendableType) => key is AddressableType} */
const isAddressable = (key) =>
/** @type {readonly string[]} */ (ADDRESSABLE_TYPES).includes(key);
/**
* @param {SpendableType} key
* @returns {key is AddressableType}
*/
function isAddressable(key) {
return /** @type {readonly string[]} */ (ADDRESSABLE_TYPES).includes(key);
}
export function buildCohortData() {
const utxoCohorts = brk.series.cohorts.utxo;
@@ -27,7 +32,6 @@ export function buildCohortData() {
EPOCH_NAMES,
UNDER_AGE_NAMES,
OVER_AGE_NAMES,
AGE_RANGE_NAMES,
OVER_AMOUNT_NAMES,
UNDER_AMOUNT_NAMES,
AMOUNT_RANGE_NAMES,
@@ -83,10 +87,8 @@ export function buildCohortData() {
tree: utxoCohorts.overAge[key],
}));
const ageRange = entries(AGE_RANGE_NAMES).map(([key, names], i, arr) => ({
name: names.short,
title: `UTXOs ${names.long}`,
color: colors.at(i, arr.length),
const ageRange = ageRanges.map(({ key, ...range }) => ({
...range,
tree: utxoCohorts.ageRange[key],
matured: utxoCohorts.matured[key],
}));
@@ -77,10 +77,15 @@ function groupedOutputsFolder(list, all, title) {
}),
},
{
name: "Spending Rate",
title: title("Spending Rate"),
name: "1Y Turnover",
title: title("1 Year UTXO Turnover"),
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
line({ series: tree.outputs.spendingRate, name, color, unit: Unit.ratio }),
line({
series: tree.outputs.utxoTurnover1y,
name,
color,
unit: Unit.ratio,
}),
),
},
],
@@ -308,10 +313,15 @@ function outputsFolder(outputs, color, title) {
tree: chartsFromCount({ pattern: outputs.spentCount, title, metric: "Spent UTXO Count", unit: Unit.count, color }),
},
{
name: "Spending Rate",
title: title("Spending Rate"),
name: "1Y Turnover",
title: title("1 Year UTXO Turnover"),
bottom: [
line({ series: outputs.spendingRate, name: "Rate", color, unit: Unit.ratio }),
line({
series: outputs.utxoTurnover1y,
name: "Turnover",
color,
unit: Unit.ratio,
}),
],
},
],
@@ -501,7 +501,7 @@ function realizedOverviewFolder({
/**
* Full realized subfolder (All/STH/LTH)
* @param {RealizedPattern | LthRealizedPattern} r
* @param {FullRealizedProfitabilityPattern} r
* @param {(name: string) => string} title
* @returns {PartialOptionsGroup}
*/
@@ -809,7 +809,8 @@ export function createProfitabilitySectionAll({ cohort, title }) {
/**
* Section for cohorts with full realized and unrealized profitability data.
* @param {{ cohort: { tree: { unrealized: FullRelativePattern, realized: RealizedPattern } }, title: (name: string) => string }} args
* This section does not use SOPR, whose shape differs between STH and Entry.
* @param {{ cohort: { tree: { unrealized: FullRelativePattern, realized: FullRealizedProfitabilityPattern } }, title: (name: string) => string }} args
* @returns {PartialOptionsGroup}
*/
export function createProfitabilitySectionFull({ cohort, title }) {
@@ -0,0 +1,186 @@
import { brk } from "../../utils/client.js";
import { colors } from "../../utils/colors.js";
import { Unit } from "../../utils/units.js";
import { ageRanges } from "../age-ranges.js";
import { line, price } from "../series.js";
import { satsBtcUsd } from "../shared.js";
/**
* @typedef {Object} CoinflowAgeRange
* @property {string} name
* @property {Color} color
* @property {{
* mobility: AnySeriesPattern,
* spendingRate: AnySeriesPattern,
* spendingExposure: AnySeriesPattern,
* supply: { mobile: AnyValuePattern, immobile: AnyValuePattern },
* }} tree
*/
/**
* @param {readonly CoinflowAgeRange[]} ranges
* @param {"mobility" | "spendingRate" | "spendingExposure"} key
* @param {string} name
* @returns {PartialChartOption}
*/
function ageRangeRatioChart(ranges, key, name) {
return {
name,
title: `${name} by UTXO Age`,
bottom: ranges.map((range) =>
line({
series: range.tree[key],
name: range.name,
color: range.color,
unit: Unit.ratio,
}),
),
};
}
/**
* @param {readonly CoinflowAgeRange[]} ranges
* @param {"mobile" | "immobile"} key
* @param {string} name
* @returns {PartialChartOption}
*/
function ageRangeSupplyChart(ranges, key, name) {
return {
name,
title: `${name} Supply by UTXO Age`,
bottom: ranges.flatMap((range) =>
satsBtcUsd({
pattern: range.tree.supply[key],
name: range.name,
color: range.color,
}),
),
};
}
/**
* Create Coinflow section.
* @returns {PartialOptionsGroup}
*/
export function createCoinflowSection() {
const { coinflow } = brk.series;
const ranges = ageRanges.map(({ key, ...range }) => ({
...range,
tree: coinflow.ageRange[key],
}));
const horizons = /** @type {const} */ ([
{ key: "_8y", name: "8Y" },
{ key: "_4y", name: "4Y" },
{ key: "_2y", name: "2Y" },
{ key: "_1y", name: "1Y" },
{ key: "_6m", name: "6M" },
{ key: "_3m", name: "3M" },
{ key: "_1m", name: "1M" },
]).map((horizon, index, all) => ({
...horizon,
color: colors.at(index, all.length),
}));
return {
name: "Coinflow",
tree: [
{
name: "Supply",
tree: [
{
name: "Breakdown",
title: "Mobile vs Immobile Supply",
bottom: [
...satsBtcUsd({
pattern: coinflow.supply.mobile,
name: "Mobile",
color: colors.mobile,
}),
...satsBtcUsd({
pattern: coinflow.supply.immobile,
name: "Immobile",
color: colors.immobile,
}),
],
},
{
name: "Mobile in Loss",
title: "Mobile Supply in Loss",
bottom: [
line({
series: coinflow.supply.mobile.inLoss.share,
name: "Lifetime",
color: colors.loss,
unit: Unit.ratio,
}),
],
},
{
name: "In Loss by Horizon",
title: "Mobile Supply in Loss by Horizon",
bottom: horizons.map((horizon) =>
line({
series:
coinflow.horizon[horizon.key].supply.inLoss.share,
name: horizon.name,
color: horizon.color,
unit: Unit.ratio,
}),
),
},
],
},
{
name: "Cap",
title: "Coinflow Cap",
bottom: [
line({
series: coinflow.cap.usd,
name: "Coinflow",
color: colors.coinflow,
unit: Unit.usd,
}),
],
},
{
name: "Price",
title: "Coinflow Price",
top: [
price({
series: coinflow.price,
name: "Coinflow",
color: colors.coinflow,
}),
],
bottom: [
line({
series: coinflow.price.ratio,
name: "Spot / Coinflow",
color: colors.coinflow,
unit: Unit.ratio,
}),
],
},
{
name: "Age Range",
tree: [
ageRangeRatioChart(ranges, "mobility", "Mobility"),
ageRangeRatioChart(ranges, "spendingRate", "Spending Rate"),
ageRangeRatioChart(
ranges,
"spendingExposure",
"Spending Exposure",
),
{
name: "Supply",
tree: [
ageRangeSupplyChart(ranges, "mobile", "Mobile"),
ageRangeSupplyChart(ranges, "immobile", "Immobile"),
],
},
],
},
],
};
}
@@ -0,0 +1,162 @@
import { Unit } from "../../../utils/units.js";
import { line, ROLLING_WINDOWS } from "../../series.js";
import { satsBtcUsd } from "../../shared.js";
/**
* @typedef {{
* average: Record<"_24h" | "_1w" | "_1m" | "_1y", AnySeriesPattern>,
* sum: Record<"_24h" | "_1w" | "_1m" | "_1y", AnySeriesPattern>,
* cumulative: AnySeriesPattern,
* }} CointimeAgeRangeCoindays
*
* @typedef {Object} CointimeAgeRange
* @property {string} name
* @property {Color} color
* @property {{
* coindaysCreated: CointimeAgeRangeCoindays,
* coindaysConsumed: CointimeAgeRangeCoindays,
* coindaysStored: CointimeAgeRangeCoindays,
* liveliness: AnySeriesPattern,
* vaultedness: AnySeriesPattern,
* ratio: AnySeriesPattern,
* supply: { active: AnyValuePattern, vaulted: AnyValuePattern },
* }} tree
*/
/**
* @param {readonly CointimeAgeRange[]} ranges
* @param {"liveliness" | "vaultedness" | "ratio"} key
* @param {string} name
* @param {string} legend
* @returns {PartialChartOption}
*/
function activityChart(ranges, key, name, legend) {
return {
name,
title: `${legend} by UTXO Age`,
bottom: ranges.map((range) =>
line({
series: range.tree[key],
name: range.name,
color: range.color,
unit: Unit.ratio,
}),
),
};
}
/**
* @param {readonly CointimeAgeRange[]} ranges
* @param {"active" | "vaulted"} key
* @param {string} name
* @returns {PartialChartOption}
*/
function supplyChart(ranges, key, name) {
return {
name,
title: `${name} Supply by UTXO Age`,
bottom: ranges.flatMap((range) =>
satsBtcUsd({
pattern: range.tree.supply[key],
name: range.name,
color: range.color,
}),
),
};
}
/**
* @param {readonly CointimeAgeRange[]} ranges
* @param {"coindaysCreated" | "coindaysConsumed" | "coindaysStored"} key
* @param {string} name
* @returns {PartialOptionsGroup}
*/
function coindaysTree(ranges, key, name) {
return {
name,
tree: [
{
name: "Average",
tree: ROLLING_WINDOWS.map((window) => ({
name: window.name,
title: `${window.title} Average ${name} by UTXO Age`,
bottom: ranges.map((range) =>
line({
series: range.tree[key].average[window.key],
name: range.name,
color: range.color,
unit: Unit.coindays,
}),
),
})),
},
{
name: "Sum",
tree: ROLLING_WINDOWS.map((window) => ({
name: window.name,
title: `${window.title} ${name} by UTXO Age`,
bottom: ranges.map((range) =>
line({
series: range.tree[key].sum[window.key],
name: range.name,
color: range.color,
unit: Unit.coindays,
}),
),
})),
},
{
name: "Cumulative",
title: `Cumulative ${name} by UTXO Age`,
bottom: ranges.map((range) =>
line({
series: range.tree[key].cumulative,
name: range.name,
color: range.color,
unit: Unit.coindays,
}),
),
},
],
};
}
/**
* @param {readonly CointimeAgeRange[]} ranges
* @returns {PartialOptionsGroup}
*/
export function createCointimeAgeRangeSection(ranges) {
return {
name: "Age Range",
tree: [
{
name: "Supply",
tree: [
supplyChart(ranges, "active", "Active"),
supplyChart(ranges, "vaulted", "Vaulted"),
],
},
{
name: "Activity",
tree: [
activityChart(ranges, "liveliness", "Liveliness", "Liveliness"),
activityChart(ranges, "vaultedness", "Vaultedness", "Vaultedness"),
activityChart(
ranges,
"ratio",
"Activity Ratio",
"Liveliness / Vaultedness",
),
],
},
{
name: "Coindays",
tree: [
coindaysTree(ranges, "coindaysCreated", "Coindays Created"),
coindaysTree(ranges, "coindaysConsumed", "Coindays Consumed"),
coindaysTree(ranges, "coindaysStored", "Coindays Stored"),
],
},
],
};
}
@@ -1,6 +1,6 @@
import { colors } from "../utils/colors.js";
import { brk } from "../utils/client.js";
import { Unit } from "../utils/units.js";
import { colors } from "../../../utils/colors.js";
import { brk } from "../../../utils/client.js";
import { Unit } from "../../../utils/units.js";
import {
dots,
line,
@@ -8,8 +8,10 @@ import {
multiSeriesTree,
percentRatioDots,
sumsAndAveragesCumulative,
} from "./series.js";
import { satsBtcUsd, priceRatioPercentilesTree } from "./shared.js";
} from "../../series.js";
import { ageRanges } from "../../age-ranges.js";
import { satsBtcUsd, priceRatioPercentilesTree } from "../../shared.js";
import { createCointimeAgeRangeSection } from "./age-range.js";
/**
* Create Cointime section
@@ -27,6 +29,10 @@ export function createCointimeSection() {
value,
} = cointime;
const { all } = cohorts.utxo;
const cointimeAgeRanges = ageRanges.map(({ key, ...range }) => ({
...range,
tree: cointime.ageRange[key],
}));
// Reference lines for cap comparisons
const capReferenceLines = /** @type {const} */ ([
@@ -247,12 +253,31 @@ export function createCointimeSection() {
{
name: "Supply",
title: "Active vs Vaulted Supply",
bottom: supplyBreakdown.flatMap(({ pattern, name, color }) =>
satsBtcUsd({ pattern, name, color }),
),
tree: [
{
name: "Breakdown",
title: "Active vs Vaulted Supply",
bottom: supplyBreakdown.flatMap(({ pattern, name, color }) =>
satsBtcUsd({ pattern, name, color }),
),
},
{
name: "Active in Loss",
title: "Active Supply in Loss",
bottom: [
line({
series: cointimeSupply.active.inLoss.share,
name: "Share",
color: colors.loss,
unit: Unit.ratio,
}),
],
},
],
},
createCointimeAgeRangeSection(cointimeAgeRanges),
{
name: "Activity",
title: "Liveliness & Vaultedness",
+1 -42
View File
@@ -17,7 +17,7 @@ import {
ROLLING_WINDOWS,
ROLLING_WINDOWS_TO_1M,
} from "./series.js";
import { simplePriceRatioTree, percentileBands, priceBands } from "./shared.js";
import { simplePriceRatioTree } from "./shared.js";
/**
* @typedef {Object} Period
@@ -921,47 +921,6 @@ export function createMarketSection() {
{
name: "Indicators",
tree: [
{
name: "Rarity Meter",
tree: /** @type {const} */ ([
{ key: "full", name: "Full", title: "Rarity Meter" },
{ key: "local", name: "Local", title: "Local Rarity Meter" },
{ key: "cycle", name: "Cycle", title: "Cycle Rarity Meter" },
]).map((v) => {
const m = indicators.rarityMeter[v.key];
return {
name: v.name,
title: v.title,
top: priceBands(percentileBands(m), { defaultActive: true }),
bottom: [
histogram({
series: m.index,
name: "Index",
unit: Unit.count,
colorFn: (v) =>
/** @type {const} */ ([
colors.ratioPct._0_5,
colors.ratioPct._1,
colors.ratioPct._2,
colors.ratioPct._5,
colors.transparent,
colors.ratioPct._95,
colors.ratioPct._98,
colors.ratioPct._99,
colors.ratioPct._99_5,
])[v + 4],
}),
baseline({
series: m.score,
name: "Score",
unit: Unit.count,
color: [colors.ratioPct._99, colors.ratioPct._1],
defaultActive: false,
}),
],
};
}),
},
{
name: "NVT",
title: "NVT Ratio",
+136
View File
@@ -0,0 +1,136 @@
import { brk } from "../../utils/client.js";
import { colors } from "../../utils/colors.js";
import { Unit } from "../../utils/units.js";
import { line } from "../series.js";
const FLOOR_PERCENTILES = /** @type {const} */ ([
{ key: "pct95", name: "P95" },
{ key: "pct98", name: "P98" },
{ key: "pct99", name: "P99" },
{ key: "pct995", name: "P99.5" },
{ key: "pct999", name: "P99.9" },
]);
const LEVEL_PERCENTILES = /** @type {const} */ ([
{ key: "pct10", name: "P10" },
{ key: "pct20", name: "P20" },
{ key: "pct30", name: "P30" },
{ key: "pct40", name: "P40" },
{ key: "pct50", name: "P50" },
{ key: "pct60", name: "P60" },
{ key: "pct70", name: "P70" },
{ key: "pct80", name: "P80" },
{ key: "pct90", name: "P90" },
]);
/**
* @typedef {Object} BedrockMode
* @property {string} name
* @property {AnySeriesPattern} inLoss
* @property {{
* floor: Record<string, AnySeriesPattern>,
* level: Record<string, AnySeriesPattern>,
* lossThreshold: Record<string, AnySeriesPattern>,
* }} tree
*/
/**
* @param {BedrockMode} mode
* @param {AnySeriesPattern} ath
* @returns {PartialChartOption}
*/
function modeChart(mode, ath) {
return {
name: mode.name,
title: `Bitcoin Bedrock Model: ${mode.name}`,
top: [
...FLOOR_PERCENTILES.map((percentile, index) =>
line({
series: mode.tree.floor[percentile.key],
name: percentile.name,
color: colors.bedrock.percentiles[index],
unit: Unit.usd,
}),
),
...LEVEL_PERCENTILES.map((percentile, index) =>
line({
series: mode.tree.level[percentile.key],
name: `L${percentile.name.slice(1)}`,
color: colors.bedrock.levels[index],
unit: Unit.usd,
style: 1,
}),
),
line({
series: ath,
name: "L100",
color: colors.bedrock.levels[9],
unit: Unit.usd,
style: 1,
}),
],
bottom: [
line({
series: mode.inLoss,
name: "Loss",
color: colors.default,
defaultActive: false,
unit: Unit.ratio,
}),
...FLOOR_PERCENTILES.map((percentile, index) =>
line({
series: mode.tree.lossThreshold[percentile.key],
name: percentile.name,
color: colors.bedrock.percentiles[index],
defaultActive: false,
unit: Unit.ratio,
}),
),
],
};
}
/**
* Create Bedrock model section.
* @returns {PartialOptionsGroup}
*/
export function createBedrockSection() {
const { bedrock, market, cohorts, cointime, coinflow } = brk.series;
const horizonModes = /** @type {const} */ ([
{ key: "coinflow8y", horizon: "_8y", name: "Coinflow 8Y" },
{ key: "coinflow4y", horizon: "_4y", name: "Coinflow 4Y" },
{ key: "coinflow2y", horizon: "_2y", name: "Coinflow 2Y" },
{ key: "coinflow1y", horizon: "_1y", name: "Coinflow 1Y" },
{ key: "coinflow6m", horizon: "_6m", name: "Coinflow 6M" },
{ key: "coinflow3m", horizon: "_3m", name: "Coinflow 3M" },
{ key: "coinflow1m", horizon: "_1m", name: "Coinflow 1M" },
]).map((mode) => ({
name: mode.name,
tree: bedrock[mode.key],
inLoss: coinflow.horizon[mode.horizon].supply.inLoss.share,
}));
const modes = /** @type {readonly BedrockMode[]} */ ([
{
name: "Raw",
tree: bedrock.raw,
inLoss: cohorts.utxo.all.supply.inLoss.share.ratio,
},
{
name: "Cointime",
tree: bedrock.cointime,
inLoss: cointime.supply.active.inLoss.share,
},
{
name: "Coinflow",
tree: bedrock.coinflow,
inLoss: coinflow.supply.mobile.inLoss.share,
},
...horizonModes,
]);
return {
name: "Bedrock",
tree: modes.map((mode) => modeChart(mode, market.ath.high.usd)),
};
}
@@ -0,0 +1,118 @@
import { brk } from "../../utils/client.js";
import { colors } from "../../utils/colors.js";
import { Unit } from "../../utils/units.js";
import { baseline, histogram } from "../series.js";
import {
percentileBands,
priceBands,
priceRatioPercentilesTree,
} from "../shared.js";
/**
* Create Rarity Meter model section.
* @returns {PartialOptionsGroup}
*/
export function createRarityMeterSection() {
const { rarityMeter } = brk.series.indicators;
const { all, sth, lth } = brk.series.cohorts.utxo;
return {
name: "Rarity Meter",
tree: [
.../** @type {const} */ ([
{ key: "full", name: "Full", title: "Bitcoin Rarity Meter: Full" },
{ key: "local", name: "Local", title: "Bitcoin Rarity Meter: Local" },
{ key: "cycle", name: "Cycle", title: "Bitcoin Rarity Meter: Cycle" },
]).map((variant) => {
const meter = rarityMeter[variant.key];
return {
name: variant.name,
title: variant.title,
top: priceBands(percentileBands(meter), { defaultActive: true }),
bottom: [
histogram({
series: meter.index,
name: "Index",
unit: Unit.count,
colorFn: (value) =>
/** @type {const} */ ([
colors.ratioPct._0_5,
colors.ratioPct._1,
colors.ratioPct._2,
colors.ratioPct._5,
colors.transparent,
colors.ratioPct._95,
colors.ratioPct._98,
colors.ratioPct._99,
colors.ratioPct._99_5,
])[value + 4],
}),
baseline({
series: meter.score,
name: "Score",
unit: Unit.count,
color: [colors.ratioPct._99, colors.ratioPct._1],
defaultActive: false,
}),
],
};
}),
{
name: "Components",
tree: [
{
name: "Realized Price",
title: "Realized Price",
pattern: all.realized.price,
legend: "Realized",
color: colors.realized,
},
{
name: "Capitalized Price",
title: "Capitalized Price",
pattern: all.realized.capitalized.price,
legend: "Capitalized",
color: colors.capitalized,
},
{
name: "STH RP",
title: "STH Realized Price",
pattern: sth.realized.price,
legend: "Realized",
color: colors.realized,
},
{
name: "STH CP",
title: "STH Capitalized Price",
pattern: sth.realized.capitalized.price,
legend: "Capitalized",
color: colors.capitalized,
},
{
name: "LTH RP",
title: "LTH Realized Price",
pattern: lth.realized.price,
legend: "Realized",
color: colors.realized,
},
{
name: "LTH CP",
title: "LTH Capitalized Price",
pattern: lth.realized.capitalized.price,
legend: "Capitalized",
color: colors.capitalized,
},
].map((component) => {
const [, ratioChart] = priceRatioPercentilesTree({
pattern: component.pattern,
title: component.title,
legend: component.legend,
color: component.color,
defaultActivePercentiles: true,
});
return { ...ratioChart, name: component.name };
}),
},
],
};
}
+74 -102
View File
@@ -32,6 +32,7 @@ import {
groupedWindowsCumulative,
avgHoldingsSubtree,
exposedSubtree,
reusedCountTree,
reusedSubtree,
} from "./shared.js";
import { createOpReturnSection } from "./network/op-return.js";
@@ -111,50 +112,6 @@ export function createNetworkSection() {
const respent = addrs.respent;
const key = /** @type {const} */ ("all");
/**
* Windowed sums + cumulative, overlaying reused (primary) and respent (gray).
* @param {CountPattern<number>} reusedPattern
* @param {CountPattern<number>} respentPattern
* @param {string} metric
* @returns {PartialOptionsTree}
*/
const countPair = (reusedPattern, respentPattern, metric) => [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${metric}`),
bottom: [
line({
series: reusedPattern.sum[w.key],
name: "2+ Funded",
unit: Unit.count,
}),
line({
series: respentPattern.sum[w.key],
name: "2+ Spent",
color: colors.gray,
unit: Unit.count,
}),
],
})),
{
name: "Cumulative",
title: title(`Cumulative ${metric}`),
bottom: [
line({
series: reusedPattern.cumulative,
name: "2+ Funded",
unit: Unit.count,
}),
line({
series: respentPattern.cumulative,
name: "2+ Spent",
color: colors.gray,
unit: Unit.count,
}),
],
},
];
return {
name: "Reused",
tree: [
@@ -242,9 +199,10 @@ export function createNetworkSection() {
tree: [
{
name: "Count",
tree: countPair(
tree: reusedCountTree(
reused.events.outputToReusedAddrCount[key],
respent.events.outputToReusedAddrCount[key],
title,
"Transaction Outputs to Reused Addresses",
),
},
@@ -296,9 +254,10 @@ export function createNetworkSection() {
tree: [
{
name: "Count",
tree: countPair(
tree: reusedCountTree(
reused.events.inputFromReusedAddrCount[key],
respent.events.inputFromReusedAddrCount[key],
title,
"Transaction Inputs from Reused Addresses",
),
},
@@ -855,40 +814,75 @@ export function createNetworkSection() {
defaultActive: false,
},
]);
/**
* @param {Object} args
* @param {Readonly<Record<string, CountPattern<number>>>} args.patterns
* @param {(window: (typeof ROLLING_WINDOWS)[number], average: boolean) => string} args.windowTitle
* @param {string} args.cumulativeTitle
* @returns {PartialOptionsTree}
*/
const countComparisonTree = ({
patterns,
windowTitle,
cumulativeTitle,
}) => [
...ROLLING_WINDOWS.map((window) => ({
name: window.name,
title: windowTitle(window, false),
bottom: countTypes.map((type) =>
line({
series: patterns[type.key].sum[window.key],
name: type.name,
color: type.color,
unit: Unit.count,
defaultActive: type.defaultActive,
}),
),
})),
{
name: "Average",
tree: ROLLING_WINDOWS.map((window) => ({
name: window.name,
title: windowTitle(window, true),
bottom: countTypes.map((type) =>
line({
series: patterns[type.key].average[window.key],
name: type.name,
color: type.color,
unit: Unit.count,
defaultActive: type.defaultActive,
}),
),
})),
},
{
name: "Cumulative",
title: cumulativeTitle,
bottom: countTypes.map((type) =>
line({
series: patterns[type.key].cumulative,
name: type.name,
color: type.color,
unit: Unit.count,
defaultActive: type.defaultActive,
}),
),
},
];
return [
{
name: "Compare",
tree: [
{
name: "Count",
tree: [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${w.title} ${label} Count by Type`,
bottom: countTypes.map((t) =>
line({
series: count[t.key].sum[w.key],
name: t.name,
color: t.color,
unit: Unit.count,
defaultActive: t.defaultActive,
}),
),
})),
{
name: "Cumulative",
title: `Cumulative ${label} Count by Type`,
bottom: countTypes.map((t) =>
line({
series: count[t.key].cumulative,
name: t.name,
color: t.color,
unit: Unit.count,
defaultActive: t.defaultActive,
}),
),
},
],
tree: countComparisonTree({
patterns: count,
windowTitle: (window, average) =>
`${window.title}${average ? " Average" : ""} ${label} Count by Type`,
cumulativeTitle: `Cumulative ${label} Count by Type`,
}),
},
{
name: "Share",
@@ -904,34 +898,12 @@ export function createNetworkSection() {
},
{
name: "Transaction Count",
tree: [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${w.title} Transactions by ${label} Type`,
bottom: countTypes.map((t) =>
line({
series: txCount[t.key].sum[w.key],
name: t.name,
color: t.color,
unit: Unit.count,
defaultActive: t.defaultActive,
}),
),
})),
{
name: "Cumulative",
title: `Cumulative Transactions by ${label} Type`,
bottom: countTypes.map((t) =>
line({
series: txCount[t.key].cumulative,
name: t.name,
color: t.color,
unit: Unit.count,
defaultActive: t.defaultActive,
}),
),
},
],
tree: countComparisonTree({
patterns: txCount,
windowTitle: (window, average) =>
`${window.title}${average ? " Average" : ""} Transactions by ${label} Type`,
cumulativeTitle: `Cumulative Transactions by ${label} Type`,
}),
},
{
name: "Transaction Share",
+10 -2
View File
@@ -24,7 +24,10 @@ import {
import { createMarketSection } from "./market.js";
import { createNetworkSection } from "./network.js";
import { createMiningSection } from "./mining.js";
import { createCointimeSection } from "./cointime.js";
import { createCointimeSection } from "./frameworks/cointime/index.js";
import { createCoinflowSection } from "./frameworks/coinflow.js";
import { createBedrockSection } from "./models/bedrock.js";
import { createRarityMeterSection } from "./models/rarity-meter.js";
import { createInvestingSection } from "./investing.js";
import {
oracleOutputsHeatmapOption,
@@ -316,7 +319,12 @@ export function createPartialOptions() {
{
name: "Frameworks",
tree: [createCointimeSection()],
tree: [createCointimeSection(), createCoinflowSection()],
},
{
name: "Models",
tree: [createRarityMeterSection(), createBedrockSection()],
},
],
},
+12 -3
View File
@@ -1256,7 +1256,7 @@ export function chartsFromPercentCumulativeEntries({
}
/**
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
* Windowed sums + optional averages + cumulative for multiple named entries.
* @param {Object} args
* @param {Array<[string, CountPattern<number>]>} args.entries
* @param {(metric: string) => string} [args.title]
@@ -1269,15 +1269,24 @@ export function chartsFromCountEntries({ entries, title = (s) => s, metric, unit
name,
color: colors.at(i, arr.length),
sum: data.sum,
average: data.average,
cumulative: data.cumulative,
}));
return [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${metric}`),
bottom: items.map((e) =>
bottom: items.flatMap((e) => [
line({ series: e.sum[w.key], name: e.name, color: e.color, unit }),
),
line({
series: e.average[w.key],
name: `${e.name} Avg`,
color: e.color,
unit,
defaultActive: false,
style: 1,
}),
]),
})),
{
name: "Cumulative",
+77 -44
View File
@@ -363,6 +363,68 @@ export function exposedSubtree(exposed, key, title) {
};
}
/**
* Windowed reused/respent counts with sums and optional averages.
* @param {CountPattern<number>} reused
* @param {CountPattern<number>} respent
* @param {(name: string) => string} title
* @param {string} metric
* @returns {PartialOptionsTree}
*/
export function reusedCountTree(reused, respent, title, metric) {
return [
...ROLLING_WINDOWS.map((window) => ({
name: window.name,
title: title(`${window.title} ${metric}`),
bottom: [
line({
series: reused.sum[window.key],
name: "2+ Funded",
unit: Unit.count,
}),
line({
series: respent.sum[window.key],
name: "2+ Spent",
color: colors.gray,
unit: Unit.count,
}),
line({
series: reused.average[window.key],
name: "2+ Funded Avg",
unit: Unit.count,
defaultActive: false,
style: 1,
}),
line({
series: respent.average[window.key],
name: "2+ Spent Avg",
color: colors.gray,
unit: Unit.count,
defaultActive: false,
style: 1,
}),
],
})),
{
name: "Cumulative",
title: title(`Cumulative ${metric}`),
bottom: [
line({
series: reused.cumulative,
name: "2+ Funded",
unit: Unit.count,
}),
line({
series: respent.cumulative,
name: "2+ Spent",
color: colors.gray,
unit: Unit.count,
}),
],
},
];
}
/**
* "Reused" subtree (per-type / per-cohort, no "Active" window since that
* data is only tracked globally). Respent (addresses whose outputs have
@@ -376,42 +438,6 @@ export function exposedSubtree(exposed, key, title) {
* @returns {PartialOptionsGroup}
*/
export function reusedSubtree(reused, respent, key, title) {
/**
* Windowed sums + cumulative, overlaying reused (primary) and respent (gray).
* @param {CountPattern<number>} reusedPattern
* @param {CountPattern<number>} respentPattern
* @param {string} metric
* @returns {PartialOptionsTree}
*/
const countPair = (reusedPattern, respentPattern, metric) => [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${metric}`),
bottom: [
line({ series: reusedPattern.sum[w.key], name: "2+ Funded", unit: Unit.count }),
line({
series: respentPattern.sum[w.key],
name: "2+ Spent",
color: colors.gray,
unit: Unit.count,
}),
],
})),
{
name: "Cumulative",
title: title(`Cumulative ${metric}`),
bottom: [
line({ series: reusedPattern.cumulative, name: "2+ Funded", unit: Unit.count }),
line({
series: respentPattern.cumulative,
name: "2+ Spent",
color: colors.gray,
unit: Unit.count,
}),
],
},
];
return {
name: "Reused",
tree: [
@@ -446,9 +472,10 @@ export function reusedSubtree(reused, respent, key, title) {
tree: [
{
name: "Count",
tree: countPair(
tree: reusedCountTree(
reused.events.outputToReusedAddrCount[key],
respent.events.outputToReusedAddrCount[key],
title,
"Transaction Outputs to Reused Addresses",
),
},
@@ -477,9 +504,10 @@ export function reusedSubtree(reused, respent, key, title) {
tree: [
{
name: "Count",
tree: countPair(
tree: reusedCountTree(
reused.events.inputFromReusedAddrCount[key],
respent.events.inputFromReusedAddrCount[key],
title,
"Transaction Inputs from Reused Addresses",
),
},
@@ -645,14 +673,17 @@ export function priceBands(bands, opts) {
);
}
/** @param {{ name: string, prop: AnySeriesPattern, color: Color }[]} bands */
function ratioBands(bands) {
/**
* @param {{ name: string, prop: AnySeriesPattern, color: Color }[]} bands
* @param {{ defaultActive?: boolean }} [opts]
*/
function ratioBands(bands, opts) {
return bands.map(({ name, prop, color }) =>
line({
series: prop,
name,
color,
defaultActive: false,
defaultActive: opts?.defaultActive ?? false,
unit: Unit.ratio,
options: { lineStyle: 1 },
}),
@@ -668,6 +699,7 @@ function ratioBands(bands) {
* @param {Color} [args.color]
* @param {string} [args.ratioTitle]
* @param {FetchedPriceSeriesBlueprint[]} [args.priceReferences]
* @param {boolean} [args.defaultActivePercentiles]
* @returns {PartialOptionsTree}
*/
export function priceRatioPercentilesTree({
@@ -677,6 +709,7 @@ export function priceRatioPercentilesTree({
color,
ratioTitle,
priceReferences,
defaultActivePercentiles,
}) {
const p = pattern.percentiles;
const pctUsd = percentileBandsWith(p, (e) => e.price);
@@ -688,7 +721,7 @@ export function priceRatioPercentilesTree({
top: [
price({ series: pattern, name: legend, color }),
...(priceReferences ?? []),
...priceBands(pctUsd),
...priceBands(pctUsd, { defaultActive: defaultActivePercentiles }),
],
},
{
@@ -696,7 +729,7 @@ export function priceRatioPercentilesTree({
title: ratioTitle ?? `${title} Ratio`,
top: [
price({ series: pattern, name: legend, color }),
...priceBands(pctUsd),
...priceBands(pctUsd, { defaultActive: defaultActivePercentiles }),
],
bottom: [
baseline({
@@ -705,7 +738,7 @@ export function priceRatioPercentilesTree({
unit: Unit.ratio,
base: 1,
}),
...ratioBands(pctRatio),
...ratioBands(pctRatio, { defaultActive: defaultActivePercentiles }),
],
},
];
+1 -1
View File
@@ -93,7 +93,7 @@
* @typedef {Object} PartialChartOptionSpecific
* @property {"chart"} [kind]
* @property {string} title
* @property {FetchedPriceSeriesBlueprint[]} [top]
* @property {(FetchedPriceSeriesBlueprint | AnyFetchedSeriesBlueprint)[]} [top]
* @property {AnyFetchedSeriesBlueprint[]} [bottom]
*
* @typedef {PartialOption & PartialChartOptionSpecific} PartialChartOption
+1 -3
View File
@@ -35,9 +35,7 @@ function walkSeries(node, map, path) {
const joined = newPath.join(".");
if (
joined.endsWith(".count.total.average") ||
joined.endsWith(".versions.v1.average") ||
joined.endsWith(".versions.v2.average") ||
joined.endsWith(".versions.v3.average")
joined === "cohorts.utxo.all.supply.dominance"
)
continue;
walkSeries(/** @type {TreeNode | null | undefined} */ (value), map, newPath);
+6 -6
View File
@@ -64,10 +64,10 @@ import { Unit } from "../units.js";
*
* @typedef {Object} Legend
* @property {HTMLLegendElement} element
* @property {function(HTMLElement): void} setPrefix
* @property {function(): void} clearPrefix
* @property {function({ series: AnySeries, name: string, order: number, colors: Color[] }): void} addOrReplace
* @property {function(number): void} removeFrom
* @property {(element: HTMLElement) => void} setPrefix
* @property {() => void} clearPrefix
* @property {(args: { series: AnySeries, name: string, order: number, colors: Color[] }) => void} addOrReplace
* @property {(index: number) => void} removeFrom
*/
const lineWidth = /** @type {1} */ (/** @type {unknown} */ (1.5));
@@ -234,7 +234,7 @@ export function createChart({ parent, brk, fitContent }) {
const chartEl = document.createElement("div");
root.append(chartEl);
const ichart = /** @type {CreateLCChart} */ (untypedLcCreateChart)(
const ichart = /** @type {typeof CreateLCChart} */ (untypedLcCreateChart)(
chartEl,
/** @satisfies {DeepPartial<ChartOptions>} */ ({
autoSize: true,
@@ -1706,5 +1706,5 @@ export function createChart({ parent, brk, fitContent }) {
/**
* @typedef {typeof createChart} CreateChart
* @typedef {ReturnType<createChart>} Chart
* @typedef {ReturnType<typeof createChart>} Chart
*/
+25
View File
@@ -152,6 +152,9 @@ export const colors = {
active: palette.rose,
activity: palette.purple,
cointime: palette.yellow,
coinflow: palette.blue,
mobile: palette.rose,
immobile: palette.lime,
destroyed: palette.red,
created: palette.orange,
stored: palette.green,
@@ -227,6 +230,28 @@ export const colors = {
_0_5: palette.indigo,
},
bedrock: {
levels: [
palette.purple,
palette.violet,
palette.indigo,
palette.blue,
palette.sky,
palette.cyan,
palette.teal,
palette.emerald,
palette.green,
palette.lime,
],
percentiles: [
palette.yellow,
palette.amber,
palette.orange,
palette.rose,
palette.red,
],
},
// Standard deviation bands (warm = positive, cool = negative)
sd: {
_0: palette.lime,
+2
View File
@@ -328,6 +328,7 @@ export function createAskPage() {
const {
output,
artifacts = [],
capability,
metricPaths,
apiContext,
sourceContext,
@@ -369,6 +370,7 @@ export function createAskPage() {
content: response,
elapsedMs,
steps,
capability,
metricPaths,
...(apiContext ? { apiContext } : {}),
...(sourceContext?.length ? { sourceContext } : {}),
+7
View File
@@ -43,6 +43,7 @@ const CHART_COLORS = new Set([
* @property {number} [elapsedMs]
* @property {StoredResponseStep[]} [steps]
* @property {StoredArtifact[]} [artifacts]
* @property {string} [capability]
* @property {string[]} [metricPaths]
* @property {ApiContext} [apiContext]
* @property {SourceContext[]} [sourceContext]
@@ -276,6 +277,11 @@ function readMessage(value) {
)
: [];
const rawMetricPaths = message.metricPaths;
const capability =
typeof message.capability === "string" &&
/^[a-z][a-z0-9_]{0,63}$/.test(message.capability)
? message.capability
: undefined;
const hasMetricPaths = Array.isArray(rawMetricPaths);
const metricPaths = hasMetricPaths
? [...new Set(/** @type {string[]} */ (rawMetricPaths.filter(
@@ -295,6 +301,7 @@ function readMessage(value) {
...(elapsedMs !== undefined ? { elapsedMs } : {}),
...(steps.length ? { steps } : {}),
...(artifacts.length ? { artifacts } : {}),
...(capability ? { capability } : {}),
...(hasMetricPaths ? { metricPaths } : {}),
...(apiContext ? { apiContext } : {}),
...(sourceContext.length ? { sourceContext } : {}),
+8 -8
View File
@@ -137,10 +137,13 @@ export function createApiAnswerTool(grounding) {
index,
score: relevance(
grounding.question,
`${field.name} ${field.description ?? ""}`,
`${field.name} ${field.ownDescription || field.description || ""}`,
) +
relevance(grounding.question, field.name) +
relevance(grounding.question, field.ownDescription ?? "") -
relevance(
grounding.question,
field.ownDescription || field.description || "",
) -
Math.max(0, field.name.split(".").length - 1) * 2,
}))
.sort((left, right) => {
@@ -155,16 +158,13 @@ export function createApiAnswerTool(grounding) {
name !== previousName &&
!parameterNames.has(name.split(".").at(-1) ?? name)
);
const numericCandidates = answerCandidates.filter(
({ value }) => typeof value === "number",
);
const best = numericCandidates
const best = answerCandidates
.sort((left, right) => right.score - left.score || left.index - right.index)[0];
const runnerUp = numericCandidates
const runnerUp = answerCandidates
.filter(({ name }) => name !== best?.name)
.sort((left, right) => right.score - left.score || left.index - right.index)[0];
const direct = best && best.score >= 6 &&
best.score >= (runnerUp?.score ?? 0) + 2
best.score >= (runnerUp?.score ?? 0) + 0.5
? best
: undefined;
const siblings = best
+11 -6
View File
@@ -1,4 +1,4 @@
/** @param {unknown} value */
/** @param {unknown} value @returns {value is Record<string, unknown>} */
function isObject(value) {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
@@ -17,16 +17,21 @@ function equalValue(left, right) {
* @param {Record<string, unknown>} [arguments_]
*/
export function focusApiData(data, arguments_ = {}) {
if (!Array.isArray(data)) return data;
if (data.length === 1) return data[0];
const page = isObject(data) &&
typeof data.count === "number" &&
Array.isArray(data.sample)
? data.sample
: data;
if (!Array.isArray(page)) return page;
if (page.length === 1) return page[0];
const supplied = Object.entries(arguments_);
if (!supplied.length) return data;
const matches = data.filter((item) =>
if (!supplied.length) return page;
const matches = page.filter((item) =>
isObject(item) &&
supplied.every(([name, value]) =>
Object.hasOwn(item, name) && equalValue(item[name], value)
)
);
return matches.length === 1 ? matches[0] : data;
return matches.length === 1 ? matches[0] : page;
}
+6 -3
View File
@@ -1,5 +1,5 @@
import { QuickMatch, QuickMatchConfig } from "../../../modules/quickmatch-js/0.5.0/src/index.js";
import { normalize } from "../text.js";
import { normalize, tokenAffinity } from "../text.js";
import { operationsFromOpenApi } from "./openapi.js";
const SEARCH_CANDIDATES = 256;
@@ -116,8 +116,11 @@ function searchOne(index, query, limit) {
const frequency = index.documentFrequency.get(word) ?? index.operations.length;
const idf = Math.log((index.operations.length + 1) / (frequency + 1)) + 1;
specificity += idf;
if (titleTokens.has(word)) titleMatched += 1;
score += idf * (titleTokens.has(word) ? 3 : 1);
const titleMatch = [...titleTokens].some((token) =>
tokenAffinity(word, token) >= 0.75
);
if (titleMatch) titleMatched += 1;
score += idf * (titleMatch ? 3 : 1);
}
return { operation, matched, titleMatched, score, specificity };
})
+112 -41
View File
@@ -7,6 +7,7 @@ import { prewarmApiIndex, terminateApiIndex } from "./api/index.js";
import { prewarmMetricIndex, terminateMetricIndex } from "./metrics/index.js";
import { renderEvidence } from "./render.js";
import { AskToolSession } from "./session/index.js";
import { arithmeticAnswer } from "./source/arithmetic.js";
import { AskSource } from "./source/index.js";
import { normalize } from "./text.js";
@@ -77,6 +78,7 @@ function removeUnsupportedQuantitySentences(answer, messages) {
* @property {import("../storage.js").ApiContext} [apiContext]
* @property {import("../storage.js").SourceContext[]} [sourceContext]
* @property {import("../storage.js").KnowledgeContext} [knowledgeContext]
* @property {string} [capability]
* @property {import("../storage.js").StoredChat} chat
*/
@@ -86,16 +88,49 @@ function removeUnsupportedQuantitySentences(answer, messages) {
* @param {(status: string) => void} onStatus
*/
async function answerFromEvidence(model, grounding, onStatus) {
const arithmetic = arithmeticAnswer(grounding);
if (arithmetic) {
return {
output: renderEvidence({
facts: [arithmetic, ...grounding.facts],
sources: grounding.excerpts.slice(0, 2),
excerpts: [],
}),
sourceContext: grounding.excerpts.slice(0, 2),
knowledgeContext: {
title: grounding.metrics[0].name,
description: arithmetic,
},
};
}
onStatus("Answering from source…");
const evidence = [
`Request: ${grounding.question}`,
grounding.metrics.length
? `Verified metrics:\n${grounding.metrics.map(({ name, path, unit }) =>
`- ${name} | ${path}${unit ? ` | unit: ${unit}` : ""}`
).join("\n")}`
: "",
grounding.facts.length
? `Verified facts:\n${grounding.facts.map((fact) => `- ${fact}`).join("\n")}`
: "",
grounding.excerpts.length
? `Verified source excerpts, strongest first:\n${grounding.excerpts.map(
({ path, startLine, endLine, content }, index) =>
`[${index + 1}] ${path}:${startLine}${endLine ? `-${endLine}` : ""}\n${content}`,
).join("\n\n")}`
: "",
].filter(Boolean).join("\n\n");
const result = await model.generate(
[
{
role: "system",
content: "Answer the exact request in at most 45 words and normal sentence casing using only verified facts, metric metadata, and source excerpts. Evidence is strongest first; ignore later excerpts unless needed. A declaration proves its definition and literal return type; a call expression proves its caller. Copy provided metric names, code identifiers, and types exactly; never respell, expand, or abbreviate them. Answer directly, never discuss the request's wording. Do not add background knowledge or guesses.",
content: "Use only the verified evidence. Answer the exact request in at most 45 words. Metric names and units are exact. Never add a fact absent from the evidence. Do not cite, number, name, or quote source files; the renderer appends source links.",
},
{
role: "user",
content: JSON.stringify(grounding),
content: `${evidence}\n\nUse only the verified evidence above. Do not explain what code identifiers mean unless the evidence does.`,
},
],
() => {},
@@ -127,6 +162,34 @@ async function answerFromEvidence(model, grounding, onStatus) {
};
}
/** @param {string} question */
function requestedArithmetic(question) {
const words = new Set(normalize(question).split(" "));
const matches = [
{ action: "add", words: ["add", "plus"] },
{ action: "subtract", words: ["subtract", "minus"] },
{ action: "multiply", words: ["multiply", "times"] },
{ action: "divide", words: ["divide"] },
].filter(({ words: candidates }) =>
candidates.some((word) => words.has(word))
);
return matches.length === 1 ? matches[0].action : undefined;
}
/** @param {string} question @param {string} field */
function fieldPosition(question, field) {
const words = normalize(question).split(" ");
const fieldWords = new Set(
normalize(field.split(".").at(-1)).split(" ").filter((word) =>
word.length > 2
),
);
const positions = words
.map((word, index) => fieldWords.has(word) ? index : -1)
.filter((index) => index >= 0);
return positions.length ? Math.min(...positions) : -1;
}
/**
* @param {import("../model.js").AskModel} model
* @param {NonNullable<ToolOutcome["apiGrounding"]>} grounding
@@ -134,7 +197,9 @@ async function answerFromEvidence(model, grounding, onStatus) {
*/
async function answerFromApi(model, grounding, onStatus) {
const apiAnswer = createApiAnswerTool(grounding);
const question = ` ${normalize(grounding.question)} `;
const normalizedQuestion = normalize(grounding.question);
const question = ` ${normalizedQuestion} `;
const arithmetic = requestedArithmetic(grounding.question);
const parameterNames = new Set(
grounding.operation.parameters.map(({ name }) => normalize(name)),
);
@@ -149,37 +214,52 @@ async function answerFromApi(model, grounding, onStatus) {
const name = normalize(field.name.split(".").at(-1));
return name && question.includes(` ${name} `);
});
const requestTokens = new Set(
normalize(grounding.question).split(" ").filter((token) => token.length > 2),
);
const canSelectDirectly = (/** @type {typeof apiAnswer.fields[number]} */ field) => {
const ownTokens = new Set(normalize(field.name).split(" "));
const qualifiers = [...requestTokens].filter((token) => !ownTokens.has(token));
return !apiAnswer.fields.some((candidate) =>
candidate.name !== field.name &&
qualifiers.some((token) =>
normalize(`${candidate.name} ${candidate.description ?? ""}`)
.split(" ")
.includes(token)
)
);
};
if (directFields.length === 1 && canSelectDirectly(directFields[0])) {
const field = directFields[0];
if (arithmetic && apiAnswer.previous && directFields.length === 1) {
const previous = apiAnswer.previous;
const current = directFields[0];
const previousPosition = fieldPosition(grounding.question, previous.name);
const currentPosition = fieldPosition(grounding.question, current.name);
const fromPosition = normalizedQuestion.split(" ").indexOf("from");
const reverseSubtract = arithmetic === "subtract" &&
fromPosition >= 0 &&
previousPosition >= 0 &&
previousPosition < fromPosition &&
currentPosition > fromPosition;
const [left, right] = reverseSubtract
? [current, previous]
: previousPosition >= 0 &&
currentPosition >= 0 &&
currentPosition < previousPosition
? [current, previous]
: [previous, current];
const label = `${left.name.split(".").at(-1)?.replaceAll("_", " ")} ${
arithmetic === "add"
? "plus"
: arithmetic === "subtract"
? "minus"
: arithmetic === "multiply"
? "times"
: "divided by"
} ${right.name.split(".").at(-1)?.replaceAll("_", " ")}`;
return {
output: finishApiAnswer(
"select_api_field",
"calculate_api_fields",
{
field: field.ref,
label: field.name.split(".").at(-1)?.replaceAll("_", " "),
operator: arithmetic,
left: left.ref,
right: right.ref,
label,
},
apiAnswer.fields,
grounding,
),
fields: [field.name],
fields: [left.name, right.name],
};
}
if (apiAnswer.resolved && canSelectDirectly(apiAnswer.resolved)) {
if (
!arithmetic &&
apiAnswer.resolved
) {
const field = apiAnswer.resolved;
return {
output: finishApiAnswer(
@@ -218,7 +298,7 @@ async function answerFromApi(model, grounding, onStatus) {
) {
return summarizeApiAnswer(grounding);
}
if (apiAnswer.direct) {
if (!arithmetic && apiAnswer.direct) {
const field = apiAnswer.direct;
return {
output: finishApiAnswer(
@@ -283,19 +363,6 @@ async function answerFromApi(model, grounding, onStatus) {
? "answer_api_text"
: "";
let actionName = actionFor(call.arguments);
const selectedField = actionName === "select_api_field"
? apiAnswer.fields.find(({ ref }) => ref === call.arguments.field)
: undefined;
if (selectedField && !canSelectDirectly(selectedField)) {
answer = await generateAnswer(
`Do not select ${selectedField.ref} (${selectedField.name}): its schema scope does not satisfy all request qualifiers. Derive the requested result from matching component fields or choose an exact narrower field.`,
);
call = answer.toolCalls[0];
if (!call || call.name !== "answer_api") {
return summarizeApiAnswer(grounding);
}
actionName = actionFor(call.arguments);
}
if (!actionName) return summarizeApiAnswer(grounding);
const selectedRefs = actionName === "select_api_field"
? [call.arguments.field]
@@ -423,7 +490,7 @@ export function createAskTools() {
signal.throwIfAborted();
await session.prepareAction(action, onStatus);
signal.throwIfAborted();
if (!call || action === "explain_evidence") {
if (!call || action === "explain_metric_calculation") {
call = session.directCall(action) ?? call;
}
if (!call) {
@@ -447,7 +514,7 @@ export function createAskTools() {
},
{
role: "user",
content: "Replace the draft with a direct answer containing no unsupported quantities. Keep established static Bitcoin facts only when the request directly needs them; otherwise use qualitative examples. Return only the replacement answer. Never mention the draft, review, evidence, context, or these instructions.",
content: "Inspect the newest request before replacing the draft. If it requests quantities but the verified context identifies no exact metric, resource, object, or timeframe, return one concise clarification question asking what to measure. Otherwise replace the draft with a direct answer containing no unsupported observations; established static Bitcoin facts are allowed only when directly requested. Return only the replacement answer. Never mention the draft, review, evidence, context, or these instructions.",
},
],
() => {},
@@ -516,6 +583,7 @@ export function createAskTools() {
return {
output: `Which ${subject} should I use?`,
artifacts: [],
capability: action,
chat: prepared.chat,
};
}
@@ -540,6 +608,7 @@ export function createAskTools() {
return {
output: answered.output,
artifacts: [],
capability: action,
apiContext: outcome.apiContext
? {
...outcome.apiContext,
@@ -560,6 +629,7 @@ export function createAskTools() {
return {
output: grounded.output,
artifacts: [],
capability: action,
metricPaths: outcome.metricPaths,
sourceContext: grounded.sourceContext,
knowledgeContext: grounded.knowledgeContext,
@@ -569,6 +639,7 @@ export function createAskTools() {
return {
output: outcome.output ?? "",
artifacts: outcome.artifacts ?? [],
capability: action,
metricPaths: outcome.metricPaths,
apiContext: outcome.apiContext,
sourceContext: outcome.sourceContext,
+1 -1
View File
@@ -93,7 +93,7 @@ export function metricsByPaths(paths, onProgress) {
return index.request("byPaths", { paths }, onProgress);
}
/** @param {{ name: string }} metric @param {string} query @returns {Promise<{ totalSeries: number, groups: { family: string, examples: string[] }[], series: (CatalogMetric & { selector: string, matchedTerms: number })[] } | undefined>} */
/** @param {{ name: string }} metric @param {string} query @returns {Promise<{ totalSeries: number, groups: { family: string, examples: string[] }[], series: (CatalogMetric & { selector: string, matchedTerms: number, specificity: number })[] } | undefined>} */
export function metricVariants(metric, query = "") {
return index.request("variants", {
name: metric.name,
+60 -30
View File
@@ -258,26 +258,6 @@ function variants(index, name, path, query) {
}
if (candidates.length <= 1) return undefined;
const preferredPaths = new Map(
index.matcher.matchesWith(searchable(query), index.config.withLimit(SEARCH_CANDIDATES))
.map((document, rank) => [index.byDocument.get(document)?.path, rank]),
);
const queryTerms = queryVocabulary(query);
const ranked = candidates
.map((candidate) => ({
...publicMetric(candidate),
rank: preferredPaths.get(candidate.path) ?? SEARCH_CANDIDATES,
queryMatches: searchable(candidate.path)
.split(" ")
.filter((token) => queryTerms.has(token)).length,
}))
.sort((left, right) =>
right.queryMatches - left.queryMatches ||
Number(right.name === name) - Number(left.name === name) ||
left.rank - right.rank ||
left.path.localeCompare(right.path)
);
let commonSuffix = candidates[0].path.split(".");
for (const candidate of candidates.slice(1)) {
const path = candidate.path.split(".");
@@ -290,7 +270,7 @@ function variants(index, name, path, query) {
commonSuffix = count ? commonSuffix.slice(-count) : [];
}
const selectors = ranked.map((candidate) => {
const selectors = candidates.map((candidate) => {
const path = candidate.path.split(".");
return commonSuffix.length ? path.slice(0, -commonSuffix.length) : path;
});
@@ -305,8 +285,52 @@ function variants(index, name, path, query) {
commonPrefix = commonPrefix.slice(0, count);
}
const preferredPaths = new Map(
index.matcher.matchesWith(searchable(query), index.config.withLimit(SEARCH_CANDIDATES))
.map((document, rank) => [index.byDocument.get(document)?.path, rank]),
);
const queryTerms = queryVocabulary(query);
const selectorTokens = selectors.map((selector) =>
[...new Set(
searchable(selector.slice(commonPrefix.length).join(" "))
.split(" ")
.filter(Boolean),
)]
);
const selectorFrequency = new Map();
for (const tokens of selectorTokens) {
for (const token of tokens) {
selectorFrequency.set(token, (selectorFrequency.get(token) ?? 0) + 1);
}
}
const ranked = candidates
.map((candidate, candidateIndex) => {
const matches = selectorTokens[candidateIndex].filter((token) =>
queryTerms.has(token)
);
return {
...publicMetric(candidate),
selector: selectors[candidateIndex],
rank: preferredPaths.get(candidate.path) ?? SEARCH_CANDIDATES,
queryMatches: matches.length,
specificity: matches.reduce((sum, token) =>
sum +
Math.log(
(candidates.length + 1) /
((selectorFrequency.get(token) ?? candidates.length) + 1),
) + 1, 0),
};
})
.sort((left, right) =>
right.specificity - left.specificity ||
right.queryMatches - left.queryMatches ||
Number(right.name === name) - Number(left.name === name) ||
left.rank - right.rank ||
left.path.localeCompare(right.path)
);
const groups = new Map();
for (const selector of selectors) {
for (const { selector } of ranked) {
const varying = selector.slice(commonPrefix.length);
const family = varying[0] ?? commonPrefix.at(-1) ?? "root";
const value = varying.slice(1).join(" / ") || varying[0] || "all";
@@ -320,13 +344,20 @@ function variants(index, name, path, query) {
totalSeries: ranked.length,
groups: [...groups.values()].slice(0, 8),
series: ranked.slice(0, 16).map((
{ path, name: metricName, suggestedUnit, indexes, type },
index,
{
path,
name: metricName,
suggestedUnit,
indexes,
type,
selector: selectorParts,
queryMatches,
specificity,
},
) => {
const selector = selectors[index]
const selector = selectorParts
.slice(commonPrefix.length)
.join(" ") || selectors[index].at(-1) || "";
const selectorTokens = new Set(searchable(selector).split(" "));
.join(" ") || selectorParts.at(-1) || "";
return {
path,
name: metricName,
@@ -334,9 +365,8 @@ function variants(index, name, path, query) {
indexes,
type,
selector,
matchedTerms: [...selectorTokens].filter((token) =>
token && queryTerms.has(token)
).length,
matchedTerms: queryMatches,
specificity,
};
}),
};
+34 -15
View File
@@ -87,11 +87,11 @@ export function availableActions(evidence) {
"read_metric_at",
"read_metric_range",
"build_metric_chart",
"list_metric_variants",
"list_metric_cohorts_variants",
);
}
if (evidence.guideOptions.length || evidence.metricOptions.length) {
actions.push("explain_evidence");
actions.push("explain_metric_calculation");
}
if (!evidence.metricOptions.length) actions.push("find_chart_metrics");
actions.push("search_source");
@@ -109,9 +109,9 @@ const ROUTE_DESCRIPTIONS = {
read_metric_at: "Choose when the requested result is a metric value at a stated block height, date, or position.",
read_metric_range: "Choose when the requested result is metric values across a stated range.",
build_metric_chart: "Choose when the requested result is a new metric chart or graph.",
list_metric_variants: "Choose only when the requested result is a list of available cohorts, groupings, or series variants.",
list_metric_cohorts_variants: "Choose only when the requested result is a list of available cohorts, groupings, or series variants.",
select_metric_variant: "Choose when the request selects one matched cohort or series variant without requesting a value or chart yet.",
explain_evidence: "Choose for a Bitview metric definition grounded in matched metric evidence.",
explain_metric_calculation: "Choose for a Bitview metric definition grounded in matched metric evidence.",
find_chart_metrics: "Choose when the user asks which chart metrics exist but no exact metric matched yet.",
search_source: "Choose for a question about BRK repository code, implementation, callers, or source structure.",
call_api: "Choose for a concrete blockchain record or resource when a generated operation can accept the supplied or contextual identifier.",
@@ -152,7 +152,7 @@ export function directAction(evidence, question) {
for (const [actionTerm, actionsForTerm] of owners) {
if (
actionsForTerm.length === 1 &&
tokenAffinity(queryTerm, actionTerm) >= 0.75
tokenAffinity(queryTerm, actionTerm) >= 0.68
) {
matched.add(actionsForTerm[0]);
}
@@ -163,6 +163,23 @@ export function directAction(evidence, question) {
const variants = evidence.metricOptions.filter(
(/** @type {any} */ { origin }) => origin === "variant",
);
const mentioned = evidence.metricOptions.filter(
(/** @type {any} */ { origin }) => origin === "mentioned",
);
if (variants.length === 1 && matched.size > 1) {
const resultActions = [...matched].filter((action) =>
action !== "list_metric_cohorts_variants" &&
action !== "select_metric_variant"
);
if (resultActions.length === 1) return resultActions[0];
}
if (
matched.size === 0 &&
variants.length + mentioned.length === 1 &&
evidence.context.capability === "read_latest_metric"
) {
return evidence.context.capability;
}
return matched.size === 0 && variants.length === 1
? "select_metric_variant"
: undefined;
@@ -192,12 +209,14 @@ export function capabilityMetrics(evidence, action) {
(/** @type {any} */ { origin }) => origin === "variant",
);
const options = mentioned.length
? [...new Map(
[...mentioned, ...contextual].map((option) => [
option.metric.path,
option,
]),
).values()]
? action === "build_metric_chart"
? [...new Map(
[...mentioned, ...contextual].map((option) => [
option.metric.path,
option,
]),
).values()]
: mentioned
: variants.length
? action === "build_metric_chart" && contextual.length
? [...variants, ...contextual]
@@ -380,7 +399,7 @@ export function actionTool(evidence, action) {
["refs", ...(asksContextDecision ? ["includeContext"] : [])],
);
}
if (action === "list_metric_variants") {
if (action === "list_metric_cohorts_variants") {
return tool(
action,
"Select the one metric whose source-derived variants were requested.",
@@ -396,7 +415,7 @@ export function actionTool(evidence, action) {
["refs"],
);
}
if (action === "explain_evidence") {
if (action === "explain_metric_calculation") {
const evidenceOptions = [...sourceOptions, ...guideOptions];
return tool(
action,
@@ -497,7 +516,7 @@ export function apiArgumentTool(operation) {
export const ROUTE_INSTRUCTION = `Choose one capability for the newest request from verified context and matches. Treat context.activeCapability as the active tool mode: continue it for an elliptical follow-up unless the newest request clearly selects a different available output.
The requested output wins: edit an active chart with its edit/style capability; otherwise choose the matching chart, latest-value, historical-value, range, variant-list, or variant-selection capability.
Use call_api for a concrete blockchain resource or its contextual follow-up, explain_evidence for a metric definition, find_chart_metrics to discover real chart series when none matched yet, describe_capabilities only for a request about the assistant itself, and answer_general for ordinary Bitcoin knowledge or conversation.
Use call_api for a concrete blockchain resource or its contextual follow-up, explain_metric_calculation for a metric definition, find_chart_metrics to discover real chart series when none matched yet, describe_capabilities only for a request about the assistant itself, and answer_general for ordinary Bitcoin knowledge or conversation.
Use search_source only when the request explicitly asks about BRK repository code, source location, implementation, or callers. Never choose it merely because source matches exist.
Use clarify when essential information is missing. In particular, a requested quantitative result without a matched metric, API resource, or quantitative context needs one concise clarification instead of a qualitative answer or guessed dataset. With call_api select apiRef. With search_source provide sourceQuery.
Call choose_capability exactly once.`;
@@ -524,7 +543,7 @@ export function actionInstruction(action) {
if (action === "set_chart_view_scale") {
return `${common} Apply only the explicitly requested view or scale.`;
}
if (action === "explain_evidence") {
if (action === "explain_metric_calculation") {
return `${common} Select the one excerpt that directly answers the request. For a metric definition, prefer its computation or formula over UI configuration, imports, aggregation, or downstream usage. Select matching metrics when the request is about a metric.`;
}
if (action === "search_source") {
@@ -50,6 +50,7 @@ export async function loadSessionContext(history, onProgress) {
return {
...(chart ? { chart } : {}),
...(message.capability ? { capability: message.capability } : {}),
metrics: metrics.filter(({ path }) => activePaths.includes(path)),
recentMetrics: metrics.filter(({ path }) => recentPaths.includes(path)),
...(operation
+121 -11
View File
@@ -10,12 +10,15 @@ import { normalize } from "../text.js";
import {
explicitArguments,
hasRequiredArguments,
reusableArguments,
} from "../api/routing.js";
const MAX_METRICS = 5;
const MAX_API = 4;
const MAX_API = 6;
const MAX_API_CANDIDATES = 64;
const MAX_SOURCE = 6;
const MAX_GUIDES = 2;
const MAX_SOURCE_SCHEMA_QUERIES = 2;
/** @param {string} value */
function label(value) {
@@ -33,13 +36,90 @@ function unique(values, key) {
});
}
/** @param {unknown} value */
function searchTerms(value) {
return new Set(
normalize(value).split(" ").filter((term) => term.length >= 3),
);
}
/**
* Turn generated response-schema matches into source-level symbols. A nested
* response field already tells us both its owning Rust type and its field
* name, so source lookup can be precise without maintaining API-to-code maps.
*
* @param {string} question
* @param {import("../api/index.js").ApiOperation[]} operations
*/
export function schemaSourceQueries(question, operations) {
const query = searchTerms(question);
if (!query.size) return [];
const documents = operations.flatMap((operation) =>
operation.response.fields.map((field, index) => ({
operation,
field,
index,
names: searchTerms(field.name),
terms: searchTerms(
`${operation.summary} ${field.name} ${field.ownDescription}`,
),
}))
);
const frequency = new Map();
for (const { terms } of documents) {
for (const term of terms) {
frequency.set(term, (frequency.get(term) ?? 0) + 1);
}
}
const candidates = documents.map(
({ operation, field, index, names, terms }) => {
const matched = [...query].filter((term) => terms.has(term));
const named = matched.filter((term) => names.has(term)).length;
const parts = field.name.split(".");
const parentName = parts.slice(0, -1).join(".");
const parent = parentName
? operation.response.fields.find(({ name }) => name === parentName)
: undefined;
const owner = parent?.type || operation.response.type;
const fieldName = parts.at(-1) ?? field.name;
return {
query: `${owner} ${fieldName}`,
matched: matched.length,
named,
specificity: matched.reduce((sum, term) =>
sum + Math.log(
(documents.length + 1) /
((frequency.get(term) ?? documents.length) + 1),
) + 1, 0),
rank: Number(operation.score ?? 0),
index,
};
},
)
.filter(({ matched, named }) => matched >= 2 && named > 0)
.sort((left, right) =>
right.specificity - left.specificity ||
right.matched - left.matched ||
right.rank - left.rank ||
left.index - right.index
);
const strongest = candidates[0]?.specificity ?? 0;
return unique(
candidates
.filter(({ specificity }) => specificity === strongest)
.map(({ query }) => query),
(query) => normalize(query),
).slice(0, MAX_SOURCE_SCHEMA_QUERIES);
}
/** @param {any} metric */
function acceptsMetric(metric) {
return Number(metric.matchedTerms ?? 0) >= 2;
}
/** @param {any} operation @param {string} question */
function acceptsApi(operation, question) {
/** @param {any} operation @param {string} question @param {any} previous */
function acceptsApi(operation, question, previous) {
const specificity = Number(operation.specificity ?? 0);
const required = operation.parameters.some(
(/** @type {any} */ parameter) => parameter.required,
@@ -48,8 +128,11 @@ function acceptsApi(operation, question) {
return Number(operation.titleMatchedTerms ?? 0) > 0 &&
specificity >= 2.5;
}
return specificity >= 1.5 &&
hasRequiredArguments(operation, explicitArguments(operation, question));
return Number(operation.titleMatchedTerms ?? 0) > 0 &&
(
hasRequiredArguments(operation, explicitArguments(operation, question)) ||
Boolean(reusableArguments(operation, previous))
);
}
/** @param {any} match */
@@ -107,7 +190,7 @@ export async function collectEvidence({
),
searchApi(
[question],
MAX_API,
MAX_API_CANDIDATES,
() => onStatus("Indexing API…"),
),
searchLearn(question, MAX_GUIDES),
@@ -133,13 +216,16 @@ export async function collectEvidence({
origin: explicit ? "mentioned" : "search",
};
});
const variantMetrics = (await Promise.all(
const variantResults = await Promise.all(
context.metrics.map((/** @type {any} */ metric) =>
metricVariants(metric, question)
),
)).flatMap((variants) =>
)
);
const variantMetrics = variantResults.flatMap((variants) =>
variants?.series
.filter((/** @type {any} */ { matchedTerms }) => matchedTerms > 0)
.filter((/** @type {any} */ { specificity }) =>
Number(specificity ?? 0) >= 3
)
.map((/** @type {any} */ metric) => {
const name = label(metric.name);
const selector = label(metric.selector);
@@ -152,6 +238,11 @@ export async function collectEvidence({
};
}) ?? []
);
const variantMiss = context.metrics.length > 0 &&
variantResults.some(Boolean) &&
variantMetrics.length === 0 &&
!linkedMetrics.some(({ origin }) => origin === "mentioned") &&
foundMetrics.some(acceptsMetric);
const metrics = unique(
[
@@ -173,7 +264,16 @@ export async function collectEvidence({
const api = unique(
[
...(context.api ? [context.api.operation] : []),
...foundApi.filter((operation) => acceptsApi(operation, question)),
...foundApi
.filter((operation) => acceptsApi(operation, question, context.api))
.sort((left, right) =>
Number(right.titleMatchedTerms ?? 0) -
Number(left.titleMatchedTerms ?? 0) ||
right.response.fields.length - left.response.fields.length ||
Number(right.matchedTerms ?? 0) -
Number(left.matchedTerms ?? 0) ||
Number(right.score ?? 0) - Number(left.score ?? 0)
),
],
(operation) => operation.key,
).slice(0, MAX_API);
@@ -222,9 +322,11 @@ export async function collectEvidence({
return {
metricOptions,
apiOptions,
apiCandidates: foundApi,
sourceOptions,
guideOptions,
context,
variantMiss,
};
}
@@ -250,6 +352,10 @@ export async function collectSourceOptions({
const metricSubject = metric
? await sourceMetricSubject(metric)
: undefined;
const schemaQueries = schemaSourceQueries(
question,
evidence.apiCandidates ?? [],
);
const queries = [
...(metricSubject
? [{
@@ -260,6 +366,10 @@ export async function collectSourceOptions({
focus: /** @type {const} */ ("implementation"),
}]
: []),
...schemaQueries.map((query) => ({
query,
focus: /** @type {const} */ ("implementation"),
})),
{ query: question, focus: undefined },
].filter((value, index, values) =>
values.findIndex((candidate) =>
+77 -13
View File
@@ -5,6 +5,8 @@ import { resolveChartUnit } from "../chart/units.js";
import { readMetric } from "../data.js";
import { metricVariants, searchMetrics } from "../metrics/index.js";
import { renderData } from "../render.js";
import { normalize } from "../text.js";
import { schemaSourceQueries } from "./evidence.js";
const CHART_VIEWS = new Set(["line", "area", "stacked", "bar", "dots"]);
const CHART_SCALES = new Set(["linear", "log"]);
@@ -31,7 +33,7 @@ function uniqueRefs(value) {
/** @param {string} value */
function label(value) {
return value.replaceAll("_", " ");
return normalize(value);
}
/** @param {string | undefined} value */
@@ -51,6 +53,42 @@ function sourceSubject(value) {
);
}
/** @param {string} content @param {string} field */
function computesField(content, field) {
return content.split("\n").some((line) => {
const normalized = normalize(line);
return normalized.includes(field) &&
["+=", "-=", "*=", "/=", " + ", " - ", " * ", " / "]
.some((operator) => line.includes(operator));
});
}
/** @param {any} result @param {string} query */
function rankedSchemaMatches(result, query) {
const parts = query.trim().split(/\s+/);
const field = normalize(parts.at(-1) ?? "");
const owner = normalize(parts.slice(0, -1).join(" "));
const terms = normalize(query).split(" ").filter(Boolean);
return [...result.matches].sort((left, right) => {
const leftContent = normalize(left.content);
const rightContent = normalize(right.content);
const leftOwner = owner && leftContent.includes(owner) ? 1 : 0;
const rightOwner = owner && rightContent.includes(owner) ? 1 : 0;
const leftComputes = leftOwner && computesField(left.content, field) ? 1 : 0;
const rightComputes = rightOwner && computesField(right.content, field) ? 1 : 0;
const leftPath = terms.filter((term) =>
normalize(left.path).split(" ").includes(term)
).length;
const rightPath = terms.filter((term) =>
normalize(right.path).split(" ").includes(term)
).length;
return rightComputes - leftComputes ||
rightOwner - leftOwner ||
rightPath - leftPath ||
Number(right.score ?? 0) - Number(left.score ?? 0);
});
}
export class CapabilityExecutor {
/**
* @param {Object} options
@@ -149,6 +187,10 @@ export class CapabilityExecutor {
/** @param {Record<string, unknown>} arguments_ @param {(status: string) => void} onStatus */
async searchSource(arguments_, onStatus) {
const query = requiredString(arguments_.query, "a source search query");
const schemaQueries = schemaSourceQueries(
this.question,
this.evidence.apiCandidates ?? [],
);
const subject = sourceSubject(
this.evidence.context.knowledge?.description,
);
@@ -166,32 +208,47 @@ export class CapabilityExecutor {
? `${query} ${subject}`
: query;
const searches = [
{ query, path: undefined },
{ query, path: undefined, focus: undefined },
...schemaQueries.map((schemaQuery) => ({
query: schemaQuery,
path: undefined,
focus: /** @type {const} */ ("implementation"),
})),
...(contextualQuery === query
? []
: [{ query: contextualQuery, path: undefined }]),
...(subject ? [{ query: subject, path: undefined }] : []),
...paths.map((path) => ({ query: contextualQuery, path })),
: [{ query: contextualQuery, path: undefined, focus: undefined }]),
...(subject
? [{ query: subject, path: undefined, focus: undefined }]
: []),
...paths.map((path) => ({
query: contextualQuery,
path,
focus: undefined,
})),
];
onStatus("Searching source…");
const results = await Promise.all(
searches.map(({ query: scopedQuery, path }) =>
searches.map(({ query: scopedQuery, path, focus }) =>
this.source.search(
scopedQuery,
path,
undefined,
focus,
({ loaded, total }) =>
onStatus(`Indexing source · ${loaded} / ${total}`),
)
),
);
const scopedResults = results.filter((_, index) => searches[index].path);
const schemaResults = results.slice(1, 1 + schemaQueries.length);
const contextualIndex = 1 + schemaQueries.length;
const contextualResult = contextualQuery === query
? undefined
: results[1];
: results[contextualIndex];
const subjectResult = subject
? results[contextualQuery === query ? 1 : 2]
? results[
contextualIndex + (contextualQuery === query ? 0 : 1)
]
: undefined;
const rawResult = results[0];
const seeded = this.evidence.sourceOptions.map(
@@ -199,6 +256,13 @@ export class CapabilityExecutor {
);
const excerpts = [...new Map([
...(paths.length ? [] : seeded),
...schemaResults.flatMap((result, index) =>
rankedSchemaMatches(result, schemaQueries[index]).slice(0, 2)
.map((/** @type {any} */ match) => ({
...match,
revision: result.revision,
}))
),
...scopedResults.flatMap((result) =>
result.matches.slice(0, 1).map((/** @type {any} */ match) => ({
...match,
@@ -261,8 +325,8 @@ export class CapabilityExecutor {
const groups = variants.groups
.map((group) =>
group.examples.length === 1 && group.examples[0] === group.family
? group.family
: `${group.family}: ${group.examples.join(", ")}`
? label(group.family)
: `${label(group.family)}: ${group.examples.map(label).join(", ")}`
)
.join("; ");
return {
@@ -491,13 +555,13 @@ export class CapabilityExecutor {
return this.describeCapabilities(call.arguments);
}
if (call.name === "clarify") return this.clarify(call.arguments);
if (call.name === "explain_evidence") {
if (call.name === "explain_metric_calculation") {
return await this.explain(call.arguments);
}
if (call.name === "search_source") {
return await this.searchSource(call.arguments, onStatus);
}
if (call.name === "list_metric_variants") {
if (call.name === "list_metric_cohorts_variants") {
return await this.listVariants(call.arguments);
}
if (call.name === "find_chart_metrics") {
+83 -31
View File
@@ -31,8 +31,8 @@ function schemaTokens(values) {
}
/** @param {Set<string>} query @param {Set<string>} document */
function overlaps(query, document) {
return [...query].some((token) => document.has(token));
function overlapCount(query, document) {
return [...query].filter((token) => document.has(token)).length;
}
export class AskToolSession {
@@ -107,7 +107,8 @@ export class AskToolSession {
? "source"
: context.knowledge
? "general"
: undefined,
: undefined,
previousCapability: context.capability,
...(context.chart
? {
activeChart: {
@@ -186,8 +187,47 @@ export class AskToolSession {
directRoute() {
if (!this.evidence) return undefined;
if (this.evidence.variantMiss) {
const metric = this.evidence.context.metrics[0];
return {
action: "clarify",
call: {
name: "clarify",
arguments: {
question: `I could not find a matching variant of ${
normalize(metric?.name ?? "the active metric").replaceAll("_", " ")
}. Which available cohort or variant should I use?`,
},
},
};
}
const action = directAction(this.evidence, this.question);
if (action === "search_source") {
if (this.evidence.context.source.length) return undefined;
return {
action,
call: {
name: action,
arguments: { query: this.question },
},
};
}
if (action) {
return {
action,
call: this.directCall(action),
};
}
if (
this.evidence.context.metrics.length ||
this.evidence.context.chart
) {
return undefined;
}
const query = schemaTokens([this.question]);
const contextKey = this.evidence.context.api?.operation.key;
const apiMatches = [];
for (const { ref, operation } of this.evidence.apiOptions) {
const required = schemaTokens(
operation.parameters
@@ -203,44 +243,56 @@ export class AskToolSession {
field.description,
]),
);
const fieldMatch = overlaps(query, returned);
const suppliedResource = required.size > 0 && overlaps(query, required);
const fieldMatches = overlapCount(query, returned);
const suppliedResource = required.size > 0 &&
overlapCount(query, required) > 0;
const inheritedResource = reusableArguments(
operation,
this.evidence.context.api,
);
if (
fieldMatch &&
(suppliedResource || operation.key === contextKey)
fieldMatches > 0 &&
(suppliedResource || inheritedResource || operation.key === contextKey)
) {
return {
apiMatches.push({
score: fieldMatches,
action: "call_api",
call: {
name: "call_api",
arguments: { ref },
},
};
});
}
}
const action = directAction(this.evidence, this.question);
if (action === "search_source") {
return {
action,
call: {
name: action,
arguments: { query: this.question },
},
};
apiMatches.sort((left, right) => right.score - left.score);
if (
apiMatches[0] &&
apiMatches[0].score > (apiMatches[1]?.score ?? 0)
) {
return apiMatches[0];
}
if (action) {
return {
action,
call: this.directCall(action),
};
}
return undefined;
const supplied = this.evidence.apiOptions.find(({ operation }) =>
hasRequiredArguments(
operation,
explicitArguments(operation, this.question),
)
);
return supplied
? {
action: "call_api",
call: {
name: "call_api",
arguments: { ref: supplied.ref },
},
}
: undefined;
}
/** @param {string} action @param {(status: string) => void} onStatus */
async prepareAction(action, onStatus) {
if (
action !== "explain_evidence" ||
action !== "explain_metric_calculation" ||
!this.evidence ||
!this.refs
) return;
@@ -318,7 +370,7 @@ export class AskToolSession {
};
}
if (action === "list_metric_variants") {
if (action === "list_metric_cohorts_variants") {
const variants = evidence.metricOptions.filter(
({ origin }) => origin === "variant",
);
@@ -355,7 +407,7 @@ export class AskToolSession {
}
}
if (action !== "explain_evidence") return undefined;
if (action !== "explain_metric_calculation") return undefined;
const { context, metricOptions, sourceOptions, guideOptions } = evidence;
const contextual = context.metrics[0]
? metricOptions.find(({ metric }) =>
@@ -403,9 +455,9 @@ export class AskToolSession {
"add_chart_series",
"remove_chart_series",
"replace_chart_series",
"list_metric_variants",
"list_metric_cohorts_variants",
"select_metric_variant",
"explain_evidence",
"explain_metric_calculation",
].includes(action)) {
evidence.metrics = actionMetrics.map(
(/** @type {any} */ { ref, label, metric, origin }) => ({
@@ -421,7 +473,7 @@ export class AskToolSession {
}),
);
}
if (action === "explain_evidence" || action === "search_source") {
if (action === "explain_metric_calculation" || action === "search_source") {
evidence.source = sourceOptions.map(
(/** @type {any} */ { ref, source }) => ({
ref,
+134
View File
@@ -0,0 +1,134 @@
import { normalize } from "../text.js";
const COMPOUND_ASSIGNMENT = /^(.+?)\s*(\+=|-=|\*=|\/=)\s*(.+?);?\s*$/;
const LOCAL_ASSIGNMENT =
/^\s*(?:let|const|var)\s+([A-Za-z_]\w*)(?:\s*:[^=]+)?\s*=\s*(.+?);?\s*$/;
const COMMENT_FORMULA =
/^\s*(?:\/\/\/?|#|\*)?\s*([A-Za-z_]\w*)\s*=\s*(.+?)\s*$/;
/** @param {string} value */
function metricTokens(value) {
return normalize(value).split(" ").filter((token) => token.length > 2);
}
/** @param {string} line @param {string[]} tokens */
function overlap(line, tokens) {
const words = new Set(normalize(line).split(" "));
return tokens.filter((token) => words.has(token)).length;
}
/** @param {string} value */
function cleanExpression(value) {
let cleaned = value
.replace(/\.as_u\d+\(\)/g, "")
.replace(/\bself\./g, "")
.replace(/\b([A-Za-z_]\w*)_u\d+\b/g, "$1")
.replace(/\s*\*\s*/g, " × ")
.replace(/\s*\/\s*/g, " ÷ ")
.replace(/\s+/g, " ")
.replace(/;$/, "")
.trim();
while (/\(\(([^()]+)\)\)/.test(cleaned)) {
cleaned = cleaned.replace(/\(\(([^()]+)\)\)/g, "($1)");
}
return cleaned;
}
/** @param {string} expression @param {string[]} preceding */
function expandLocals(expression, preceding) {
let expanded = expression;
for (let pass = 0; pass < 2; pass += 1) {
const identifiers = new Set(expanded.match(/\b[A-Za-z_]\w*\b/g) ?? []);
let changed = false;
for (const line of [...preceding].reverse()) {
const match = line.match(LOCAL_ASSIGNMENT);
if (
!match ||
!identifiers.has(match[1]) ||
!/(?:\s[+\-*/]\s)/.test(match[2])
) {
continue;
}
expanded = expanded.replace(
new RegExp(`\\b${match[1]}\\b`, "g"),
`(${match[2]})`,
);
changed = true;
}
if (!changed) break;
}
return cleanExpression(expanded);
}
/** @param {string | undefined} unit */
function displayUnit(unit) {
if (!unit) return "";
return unit.length <= 5 ? unit.toUpperCase() : unit;
}
/**
* Turn a literal source formula into a concise answer without asking the model
* to invent meanings for code identifiers.
*
* @param {{ metrics: { name: string, unit?: string }[], excerpts: { content: string }[] }} grounding
*/
export function arithmeticAnswer(grounding) {
if (grounding.metrics.length !== 1) return undefined;
const metric = grounding.metrics[0];
const tokens = metricTokens(metric.name);
if (!tokens.length) return undefined;
for (const { content } of grounding.excerpts) {
const lines = content.split("\n");
const candidates = lines
.flatMap((line, index) => {
const match = line.match(COMPOUND_ASSIGNMENT);
return match
? [{ index, match, matched: overlap(match[1], tokens) }]
: [];
})
.sort((left, right) =>
right.matched - left.matched || left.index - right.index
);
const candidate = candidates[0];
if (candidate?.matched) {
const [, , operator, right] = candidate.match;
const expression = expandLocals(
right,
lines.slice(0, candidate.index),
);
const action = operator === "+="
? "adds"
: operator === "-="
? "subtracts"
: operator === "*="
? "multiplies its running value by"
: "divides its running value by";
const target = operator === "+=" || operator === "-="
? `${action} \`${expression}\` to its running total`
: `${action} \`${expression}\``;
const unit = displayUnit(metric.unit);
return `**${metric.name}** ${target}.${unit ? ` It is reported in ${unit}.` : ""}`;
}
const formulas = lines
.flatMap((line, index) => {
const match = line.match(COMMENT_FORMULA);
const arithmetic = match &&
/(?:\s[+\-*/]\s|[Σ∑])/.test(match[2]);
return arithmetic
? [{ index, match, matched: overlap(match[1], tokens) }]
: [];
})
.sort((left, right) =>
right.matched - left.matched || left.index - right.index
);
const formula = formulas[0];
if (formula?.matched) {
const expression = cleanExpression(formula.match[2]);
const unit = displayUnit(metric.unit);
return `**${metric.name}** is calculated as \`${expression}\`.${unit ? ` It is reported in ${unit}.` : ""}`;
}
}
return undefined;
}
+4 -2
View File
@@ -16,13 +16,15 @@ export class AskSource {
* @param {string} query
* @param {string | undefined} path
* @param {"definition" | "implementation" | "availability" | undefined} focus
* @param {(progress: { loaded: number, total: number }) => void} onProgress
* @param {((progress: { loaded: number, total: number }) => void) | undefined} [onProgress]
*/
search(query, path, focus, onProgress) {
return this.#client.request(
"search",
{ query, path, focus },
({ loaded, total }) => onProgress({ loaded, total }),
onProgress
? ({ loaded, total }) => onProgress({ loaded, total })
: undefined,
);
}
+34 -10
View File
@@ -29,10 +29,17 @@ function declarations(lines) {
/** @param {string} text @param {number} line @param {number | undefined} declaration */
function excerptAt(text, line, declaration) {
const lines = text.split("\n");
const start = Math.max(1, line - 3);
const end = Math.min(lines.length, line + 12);
const local = lines.slice(start - 1, end).join("\n");
const declarationLine = declaration === undefined ? undefined : declaration + 1;
const nearbyDeclaration = declarationLine !== undefined &&
line - declarationLine < EXCERPT_WINDOW_LINES - 3;
const start = nearbyDeclaration ? declarationLine : Math.max(1, line - 3);
const end = Math.min(
lines.length,
nearbyDeclaration
? start + EXCERPT_WINDOW_LINES - 1
: line + 12,
);
const local = lines.slice(start - 1, end).join("\n");
const content = declarationLine !== undefined && declarationLine < start
? `${lines[declarationLine - 1]}\n...\n${local}`
: local;
@@ -94,6 +101,16 @@ function computesQueryDirectly(content, query) {
});
}
/** @param {string} content @param {string} query */
function containsDirectFormula(content, query) {
return content.split("\n").some((line) => {
const assignment = line.match(/^(.+?)(?:\+=|-=|\*=|\/=|=)(.+)$/);
return assignment &&
normalize(assignment[1]).includes(query) &&
/[+\-*/×÷Σ∑]/.test(assignment[2]);
});
}
/** @param {{ path: string, text: string }[]} files */
export function createSourceSearchIndex(files) {
/** @type {Map<string, number[]>} */
@@ -297,16 +314,22 @@ export function searchSource(index, rawQuery, pathPrefix = "", focus = undefined
? ""
: normalize(file.text.split("\n")[declaration]);
const definitionScore = focus === "definition" &&
declarationText.includes(query)
(
declarationText.includes(query) ||
excerpt.content.split("\n").some((line) =>
DECLARATION.test(line) && normalize(line).includes(query)
)
)
? 60
: 0;
const implementationScore = focus === "implementation"
const computesDirectly = focus === "implementation" &&
computesQueryDirectly(excerpt.content, query);
const implementationScore = computesDirectly
? computationWeight(excerpt.content) * 30
: 0;
const directImplementationScore =
focus === "implementation" &&
computesQueryDirectly(excerpt.content, query)
? 40
const directImplementationScore = computesDirectly ? 40 : 0;
const formulaScore = containsDirectFormula(excerpt.content, query)
? 80
: 0;
return {
...match,
@@ -314,7 +337,8 @@ export function searchSource(index, rawQuery, pathPrefix = "", focus = undefined
localPhraseOccurrences * 10 +
definitionScore +
implementationScore +
directImplementationScore,
directImplementationScore +
formulaScore,
phraseOccurrences: localPhraseOccurrences,
...excerpt,
};
+2
View File
@@ -1,10 +1,12 @@
const CAMEL_BOUNDARY = /([a-z0-9])([A-Z])/g;
const CAMEL_NUMBER_BOUNDARY = /([A-Z][a-z]+)(\d)/g;
const NON_WORD = /[^a-z0-9%]+/g;
/** @param {unknown} value */
export function normalize(value) {
return String(value)
.replace(CAMEL_BOUNDARY, "$1 $2")
.replace(CAMEL_NUMBER_BOUNDARY, "$1 $2")
.toLowerCase()
.replace(NON_WORD, " ")
.trim()
+2 -2
View File
@@ -1468,7 +1468,7 @@ curl -s "https://bitview.space/version"
### `AddrMempoolStats`
- `balance_delta`: `SatsSigned` (required) — Net unconfirmed balance change in satoshis; negative when pending spends exceed receipts
- `balance_delta`: `SatsSigned` (required) — Net pending (unconfirmed) balance change in satoshis; negative when pending spends exceed receipts
- `funded_txo_count`: `integer` (required) — Number of unconfirmed transaction outputs funding this address
- `funded_txo_sum`: `Sats` (required) — Total amount in satoshis being received in unconfirmed transactions
- `spent_txo_count`: `integer` (required) — Number of unconfirmed transaction inputs spending from this address
@@ -1481,7 +1481,7 @@ curl -s "https://bitview.space/version"
- `addr_type`: `OutputType` (required) — Address type (p2pkh, p2sh, v0_p2wpkh, v0_p2wsh, v1_p2tr, etc.)
- `chain_stats`: `AddrChainStats` (required) — Statistics for confirmed transactions on the blockchain
- `mempool_stats`: `AddrMempoolStats` (required) — Statistics for unconfirmed transactions in the mempool
- `balance`: `Sats` (required) — Current balance in satoshis, including unconfirmed mempool changes
- `balance`: `Sats` (required) — Total current balance in satoshis, including pending (unconfirmed) mempool changes
### `AddrValidation`