mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-31 12:43:06 -07:00
global: snap
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
//! This module detects repeating tree structures and analyzes them
|
||||
//! using the bottom-up name deconstruction algorithm.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
};
|
||||
|
||||
use brk_types::{TreeNode, extract_json_type};
|
||||
|
||||
@@ -111,7 +114,7 @@ pub fn detect_structural_patterns(
|
||||
// Also collects node bases for each tree path
|
||||
let node_bases = analyze_pattern_modes(tree, &mut patterns, &pattern_lookup);
|
||||
|
||||
patterns.sort_by(|a, b| b.fields.len().cmp(&a.fields.len()));
|
||||
patterns.sort_by_key(|p| Reverse(p.fields.len()));
|
||||
(patterns, concrete_to_pattern, type_mappings, node_bases)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ impl ClientConstants {
|
||||
|
||||
let pools = pools();
|
||||
let mut sorted_pools: Vec<_> = pools.iter().collect();
|
||||
sorted_pools.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||
sorted_pools.sort_by_key(|p| p.name.to_lowercase());
|
||||
let pool_map: BTreeMap<PoolSlug, &'static str> =
|
||||
sorted_pools.iter().map(|p| (p.slug(), p.name)).collect();
|
||||
|
||||
|
||||
+121
-38
@@ -1310,7 +1310,7 @@ impl IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5 {
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 {
|
||||
pub all: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
pub p2a: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
pub p2pk33: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
@@ -1322,7 +1322,7 @@ pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5 {
|
||||
pub p2wsh: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
}
|
||||
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5 {
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
@@ -1340,7 +1340,7 @@ impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5 {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3 {
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4 {
|
||||
pub all: SeriesPattern1<StoredU64>,
|
||||
pub p2a: SeriesPattern1<StoredU64>,
|
||||
pub p2pk33: SeriesPattern1<StoredU64>,
|
||||
@@ -1352,7 +1352,7 @@ pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3 {
|
||||
pub p2wsh: SeriesPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3 {
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
@@ -1370,7 +1370,7 @@ impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3 {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 {
|
||||
pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7 {
|
||||
pub all: _1m1w1y24hBpsPercentRatioPattern,
|
||||
pub p2a: _1m1w1y24hBpsPercentRatioPattern,
|
||||
pub p2pk33: _1m1w1y24hBpsPercentRatioPattern,
|
||||
@@ -1382,7 +1382,7 @@ pub struct AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 {
|
||||
pub p2wsh: _1m1w1y24hBpsPercentRatioPattern,
|
||||
}
|
||||
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 {
|
||||
impl AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
@@ -1434,7 +1434,7 @@ pub struct CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 {
|
||||
pub capitalized_cap_in_loss_raw: SeriesPattern18<CentsSquaredSats>,
|
||||
pub capitalized_cap_in_profit_raw: SeriesPattern18<CentsSquaredSats>,
|
||||
pub gross_pnl: CentsUsdPattern3,
|
||||
pub invested_capital: InPattern,
|
||||
pub invested_capital: InPattern2,
|
||||
pub loss: CentsNegativeToUsdPattern2,
|
||||
pub net_pnl: CentsToUsdPattern3,
|
||||
pub nupl: BpsRatioPattern,
|
||||
@@ -1449,7 +1449,7 @@ impl CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 {
|
||||
capitalized_cap_in_loss_raw: SeriesPattern18::new(client.clone(), _m(&acc, "capitalized_cap_in_loss_raw")),
|
||||
capitalized_cap_in_profit_raw: SeriesPattern18::new(client.clone(), _m(&acc, "capitalized_cap_in_profit_raw")),
|
||||
gross_pnl: CentsUsdPattern3::new(client.clone(), _m(&acc, "unrealized_gross_pnl")),
|
||||
invested_capital: InPattern::new(client.clone(), _m(&acc, "invested_capital_in")),
|
||||
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: BpsRatioPattern::new(client.clone(), _m(&acc, "nupl")),
|
||||
@@ -1960,7 +1960,7 @@ impl ActivityOutputsRealizedSupplyUnrealizedPattern2 {
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BlockChangeCumulativeDeltaSumPattern {
|
||||
pub block: CentsUsdPattern4,
|
||||
pub change_1m: ToPattern,
|
||||
pub change_1m: ToPattern2,
|
||||
pub cumulative: CentsUsdPattern,
|
||||
pub delta: AbsoluteRatePattern2,
|
||||
pub sum: _1m1w1y24hPattern5,
|
||||
@@ -1971,7 +1971,7 @@ impl BlockChangeCumulativeDeltaSumPattern {
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
block: CentsUsdPattern4::new(client.clone(), _m(&acc, "realized_pnl")),
|
||||
change_1m: ToPattern::new(client.clone(), _m(&acc, "pnl_change_1m_to")),
|
||||
change_1m: ToPattern2::new(client.clone(), _m(&acc, "pnl_change_1m_to")),
|
||||
cumulative: CentsUsdPattern::new(client.clone(), _m(&acc, "realized_pnl_cumulative")),
|
||||
delta: AbsoluteRatePattern2::new(client.clone(), _m(&acc, "realized_pnl_delta")),
|
||||
sum: _1m1w1y24hPattern5::new(client.clone(), _m(&acc, "realized_pnl_sum")),
|
||||
@@ -2966,6 +2966,22 @@ impl AbsoluteRatePattern2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AddrUtxoPattern {
|
||||
pub addr: BtcCentsSatsUsdPattern,
|
||||
pub utxo: BtcCentsSatsUsdPattern,
|
||||
}
|
||||
|
||||
impl AddrUtxoPattern {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
addr: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "addr_amount")),
|
||||
utxo: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "utxo_amount")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AllSthPattern2 {
|
||||
pub all: BtcCentsDeltaSatsUsdPattern,
|
||||
@@ -3192,32 +3208,48 @@ impl DeltaTotalPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct FundedTotalPattern {
|
||||
pub funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3,
|
||||
pub total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3,
|
||||
pub funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4,
|
||||
pub total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4,
|
||||
}
|
||||
|
||||
impl FundedTotalPattern {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3::new(client.clone(), acc.clone()),
|
||||
total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3::new(client.clone(), _p("total", &acc)),
|
||||
funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4::new(client.clone(), acc.clone()),
|
||||
total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4::new(client.clone(), _p("total", &acc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct InPattern2 {
|
||||
pub in_loss: CentsUsdPattern3,
|
||||
pub in_profit: CentsUsdPattern3,
|
||||
}
|
||||
|
||||
impl InPattern2 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
in_loss: CentsUsdPattern3::new(client.clone(), _m(&acc, "loss")),
|
||||
in_profit: CentsUsdPattern3::new(client.clone(), _m(&acc, "profit")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct InPattern {
|
||||
pub in_loss: CentsUsdPattern3,
|
||||
pub in_profit: CentsUsdPattern3,
|
||||
pub in_loss: ToPattern,
|
||||
pub in_profit: ToPattern,
|
||||
}
|
||||
|
||||
impl InPattern {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
in_loss: CentsUsdPattern3::new(client.clone(), _m(&acc, "loss")),
|
||||
in_profit: CentsUsdPattern3::new(client.clone(), _m(&acc, "profit")),
|
||||
in_loss: ToPattern::new(client.clone(), _m(&acc, "loss_to_own")),
|
||||
in_profit: ToPattern::new(client.clone(), _m(&acc, "profit_to_own")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3277,12 +3309,12 @@ pub struct SdSmaPattern {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ToPattern {
|
||||
pub struct ToPattern2 {
|
||||
pub to_mcap: BpsPercentRatioPattern,
|
||||
pub to_rcap: BpsPercentRatioPattern,
|
||||
}
|
||||
|
||||
impl ToPattern {
|
||||
impl ToPattern2 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
@@ -3334,6 +3366,20 @@ impl PricePattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ToPattern {
|
||||
pub to_own: BpsPercentRatioPattern2,
|
||||
}
|
||||
|
||||
impl ToPattern {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
to_own: BpsPercentRatioPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct TransferPattern {
|
||||
pub transfer_volume: AverageBlockCumulativeSumPattern3,
|
||||
@@ -4197,14 +4243,15 @@ pub struct SeriesTree_Addrs {
|
||||
pub raw: SeriesTree_Addrs_Raw,
|
||||
pub indexes: SeriesTree_Addrs_Indexes,
|
||||
pub data: SeriesTree_Addrs_Data,
|
||||
pub funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3,
|
||||
pub empty: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3,
|
||||
pub funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4,
|
||||
pub empty: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4,
|
||||
pub activity: SeriesTree_Addrs_Activity,
|
||||
pub total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3,
|
||||
pub new: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5,
|
||||
pub total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4,
|
||||
pub new: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6,
|
||||
pub reused: SeriesTree_Addrs_Reused,
|
||||
pub exposed: SeriesTree_Addrs_Exposed,
|
||||
pub delta: SeriesTree_Addrs_Delta,
|
||||
pub avg_amount: SeriesTree_Addrs_AvgAmount,
|
||||
}
|
||||
|
||||
impl SeriesTree_Addrs {
|
||||
@@ -4213,14 +4260,15 @@ impl SeriesTree_Addrs {
|
||||
raw: SeriesTree_Addrs_Raw::new(client.clone(), format!("{base_path}_raw")),
|
||||
indexes: SeriesTree_Addrs_Indexes::new(client.clone(), format!("{base_path}_indexes")),
|
||||
data: SeriesTree_Addrs_Data::new(client.clone(), format!("{base_path}_data")),
|
||||
funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3::new(client.clone(), "addr_count".to_string()),
|
||||
empty: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3::new(client.clone(), "empty_addr_count".to_string()),
|
||||
funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4::new(client.clone(), "addr_count".to_string()),
|
||||
empty: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4::new(client.clone(), "empty_addr_count".to_string()),
|
||||
activity: SeriesTree_Addrs_Activity::new(client.clone(), format!("{base_path}_activity")),
|
||||
total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3::new(client.clone(), "total_addr_count".to_string()),
|
||||
new: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5::new(client.clone(), "new_addr_count".to_string()),
|
||||
total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4::new(client.clone(), "total_addr_count".to_string()),
|
||||
new: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6::new(client.clone(), "new_addr_count".to_string()),
|
||||
reused: SeriesTree_Addrs_Reused::new(client.clone(), format!("{base_path}_reused")),
|
||||
exposed: SeriesTree_Addrs_Exposed::new(client.clone(), format!("{base_path}_exposed")),
|
||||
delta: SeriesTree_Addrs_Delta::new(client.clone(), format!("{base_path}_delta")),
|
||||
avg_amount: SeriesTree_Addrs_AvgAmount::new(client.clone(), format!("{base_path}_avg_amount")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4485,11 +4533,11 @@ impl SeriesTree_Addrs_Reused {
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Addrs_Reused_Events {
|
||||
pub output_to_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5,
|
||||
pub output_to_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6,
|
||||
pub output_to_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6,
|
||||
pub output_to_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7,
|
||||
pub spendable_output_to_reused_addr_share: _1m1w1y24hBpsPercentRatioPattern,
|
||||
pub input_from_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5,
|
||||
pub input_from_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6,
|
||||
pub input_from_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6,
|
||||
pub input_from_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7,
|
||||
pub active_reused_addr_count: _1m1w1y24hBlockPattern,
|
||||
pub active_reused_addr_share: SeriesTree_Addrs_Reused_Events_ActiveReusedAddrShare,
|
||||
}
|
||||
@@ -4497,11 +4545,11 @@ pub struct SeriesTree_Addrs_Reused_Events {
|
||||
impl SeriesTree_Addrs_Reused_Events {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
output_to_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5::new(client.clone(), "output_to_reused_addr_count".to_string()),
|
||||
output_to_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6::new(client.clone(), "output_to_reused_addr_share".to_string()),
|
||||
output_to_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6::new(client.clone(), "output_to_reused_addr_count".to_string()),
|
||||
output_to_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7::new(client.clone(), "output_to_reused_addr_share".to_string()),
|
||||
spendable_output_to_reused_addr_share: _1m1w1y24hBpsPercentRatioPattern::new(client.clone(), "spendable_output_to_reused_addr_share".to_string()),
|
||||
input_from_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5::new(client.clone(), "input_from_reused_addr_count".to_string()),
|
||||
input_from_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6::new(client.clone(), "input_from_reused_addr_share".to_string()),
|
||||
input_from_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6::new(client.clone(), "input_from_reused_addr_count".to_string()),
|
||||
input_from_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7::new(client.clone(), "input_from_reused_addr_share".to_string()),
|
||||
active_reused_addr_count: _1m1w1y24hBlockPattern::new(client.clone(), "active_reused_addr_count".to_string()),
|
||||
active_reused_addr_share: SeriesTree_Addrs_Reused_Events_ActiveReusedAddrShare::new(client.clone(), format!("{base_path}_active_reused_addr_share")),
|
||||
}
|
||||
@@ -4633,6 +4681,35 @@ impl SeriesTree_Addrs_Delta {
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Addrs_AvgAmount {
|
||||
pub all: AddrUtxoPattern,
|
||||
pub p2pk65: AddrUtxoPattern,
|
||||
pub p2pk33: AddrUtxoPattern,
|
||||
pub p2pkh: AddrUtxoPattern,
|
||||
pub p2sh: AddrUtxoPattern,
|
||||
pub p2wpkh: AddrUtxoPattern,
|
||||
pub p2wsh: AddrUtxoPattern,
|
||||
pub p2tr: AddrUtxoPattern,
|
||||
pub p2a: AddrUtxoPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Addrs_AvgAmount {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
all: AddrUtxoPattern::new(client.clone(), "avg".to_string()),
|
||||
p2pk65: AddrUtxoPattern::new(client.clone(), "p2pk65_avg".to_string()),
|
||||
p2pk33: AddrUtxoPattern::new(client.clone(), "p2pk33_avg".to_string()),
|
||||
p2pkh: AddrUtxoPattern::new(client.clone(), "p2pkh_avg".to_string()),
|
||||
p2sh: AddrUtxoPattern::new(client.clone(), "p2sh_avg".to_string()),
|
||||
p2wpkh: AddrUtxoPattern::new(client.clone(), "p2wpkh_avg".to_string()),
|
||||
p2wsh: AddrUtxoPattern::new(client.clone(), "p2wsh_avg".to_string()),
|
||||
p2tr: AddrUtxoPattern::new(client.clone(), "p2tr_avg".to_string()),
|
||||
p2a: AddrUtxoPattern::new(client.clone(), "p2a_avg".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Scripts {
|
||||
pub raw: SeriesTree_Scripts_Raw,
|
||||
@@ -6975,6 +7052,7 @@ pub struct SeriesTree_Cohorts_Utxo_All {
|
||||
pub realized: SeriesTree_Cohorts_Utxo_All_Realized,
|
||||
pub cost_basis: SeriesTree_Cohorts_Utxo_All_CostBasis,
|
||||
pub unrealized: SeriesTree_Cohorts_Utxo_All_Unrealized,
|
||||
pub invested_capital: InPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_All {
|
||||
@@ -6986,6 +7064,7 @@ impl SeriesTree_Cohorts_Utxo_All {
|
||||
realized: SeriesTree_Cohorts_Utxo_All_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||
cost_basis: SeriesTree_Cohorts_Utxo_All_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")),
|
||||
unrealized: SeriesTree_Cohorts_Utxo_All_Unrealized::new(client.clone(), format!("{base_path}_unrealized")),
|
||||
invested_capital: InPattern::new(client.clone(), "invested_capital_in".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7358,7 +7437,7 @@ pub struct SeriesTree_Cohorts_Utxo_All_Unrealized {
|
||||
pub loss: SeriesTree_Cohorts_Utxo_All_Unrealized_Loss,
|
||||
pub net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl,
|
||||
pub gross_pnl: CentsUsdPattern3,
|
||||
pub invested_capital: InPattern,
|
||||
pub invested_capital: InPattern2,
|
||||
pub capitalized_cap_in_profit_raw: SeriesPattern18<CentsSquaredSats>,
|
||||
pub capitalized_cap_in_loss_raw: SeriesPattern18<CentsSquaredSats>,
|
||||
pub sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment,
|
||||
@@ -7372,7 +7451,7 @@ impl SeriesTree_Cohorts_Utxo_All_Unrealized {
|
||||
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")),
|
||||
gross_pnl: CentsUsdPattern3::new(client.clone(), "unrealized_gross_pnl".to_string()),
|
||||
invested_capital: InPattern::new(client.clone(), "invested_capital_in".to_string()),
|
||||
invested_capital: InPattern2::new(client.clone(), "invested_capital_in".to_string()),
|
||||
capitalized_cap_in_profit_raw: SeriesPattern18::new(client.clone(), "capitalized_cap_in_profit_raw".to_string()),
|
||||
capitalized_cap_in_loss_raw: SeriesPattern18::new(client.clone(), "capitalized_cap_in_loss_raw".to_string()),
|
||||
sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment::new(client.clone(), format!("{base_path}_sentiment")),
|
||||
@@ -7462,6 +7541,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth {
|
||||
pub realized: SeriesTree_Cohorts_Utxo_Sth_Realized,
|
||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||
pub invested_capital: InPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Sth {
|
||||
@@ -7473,6 +7553,7 @@ impl SeriesTree_Cohorts_Utxo_Sth {
|
||||
realized: SeriesTree_Cohorts_Utxo_Sth_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "sth".to_string()),
|
||||
unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "sth".to_string()),
|
||||
invested_capital: InPattern::new(client.clone(), "sth_invested_capital_in".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7730,6 +7811,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth {
|
||||
pub realized: SeriesTree_Cohorts_Utxo_Lth_Realized,
|
||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||
pub invested_capital: InPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_Cohorts_Utxo_Lth {
|
||||
@@ -7741,6 +7823,7 @@ impl SeriesTree_Cohorts_Utxo_Lth {
|
||||
realized: SeriesTree_Cohorts_Utxo_Lth_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "lth".to_string()),
|
||||
unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "lth".to_string()),
|
||||
invested_capital: InPattern::new(client.clone(), "lth_invested_capital_in".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ pub(crate) fn process_funded_addrs(
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addrs_data.funded.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addrs_data.funded.len();
|
||||
for (addr_type, type_index, data) in pushes_iter {
|
||||
for (next_index, (addr_type, type_index, data)) in
|
||||
(addrs_data.funded.len()..).zip(pushes_iter)
|
||||
{
|
||||
addrs_data.funded.push(data);
|
||||
result.get_mut(addr_type).unwrap().insert(
|
||||
type_index,
|
||||
AnyAddrIndex::from(FundedAddrIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
@@ -138,14 +138,14 @@ pub(crate) fn process_empty_addrs(
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addrs_data.empty.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addrs_data.empty.len();
|
||||
for (addr_type, type_index, data) in pushes_iter {
|
||||
for (next_index, (addr_type, type_index, data)) in
|
||||
(addrs_data.empty.len()..).zip(pushes_iter)
|
||||
{
|
||||
addrs_data.empty.push(data);
|
||||
result.get_mut(addr_type).unwrap().insert(
|
||||
type_index,
|
||||
AnyAddrIndex::from(EmptyAddrIndex::from(next_index)),
|
||||
);
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
|
||||
@@ -542,17 +542,14 @@ impl UTXOCohorts<Rw> {
|
||||
}
|
||||
|
||||
/// Second phase of post-processing: compute relative metrics.
|
||||
pub(crate) fn compute_rest_part2<HM>(
|
||||
pub(crate) fn compute_rest_part2(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_market_cap: &HM,
|
||||
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
HM: ReadableVec<Height, Dollars> + Sync,
|
||||
{
|
||||
) -> Result<()> {
|
||||
// Get under_1h value sources for adjusted computation (cloned to avoid borrow conflicts).
|
||||
let under_1h_value_created = self
|
||||
.age_range
|
||||
|
||||
@@ -258,6 +258,7 @@ pub(crate) fn process_blocks(
|
||||
.chain(vecs.addrs.activity.par_iter_height_mut())
|
||||
.chain(vecs.addrs.reused.par_iter_height_mut())
|
||||
.chain(vecs.addrs.exposed.par_iter_height_mut())
|
||||
.chain(vecs.addrs.avg_amount.par_iter_height_mut())
|
||||
.chain(rayon::iter::once(
|
||||
&mut vecs.coinblocks_destroyed.block as &mut dyn AnyStoredVec,
|
||||
))
|
||||
|
||||
@@ -156,6 +156,7 @@ impl AllCohortMetrics {
|
||||
starting_indexes.height,
|
||||
&self.supply,
|
||||
&self.unrealized,
|
||||
&self.realized,
|
||||
height_to_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -132,6 +132,7 @@ impl ExtendedCohortMetrics {
|
||||
starting_indexes.height,
|
||||
&self.supply,
|
||||
&self.unrealized,
|
||||
&self.realized,
|
||||
height_to_market_cap,
|
||||
all_supply_sats,
|
||||
&self.supply.total.usd.height,
|
||||
|
||||
@@ -133,7 +133,7 @@ pub use realized::{
|
||||
AdjustedSopr, RealizedCore, RealizedFull, RealizedFullAccum, RealizedLike, RealizedMinimal,
|
||||
};
|
||||
pub use relative::{RelativeForAll, RelativeToAll, RelativeWithExtended};
|
||||
pub use supply::{SupplyBase, SupplyCore};
|
||||
pub use supply::{AvgAmountMetrics, SupplyBase, SupplyCore};
|
||||
pub use unrealized::{
|
||||
UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike, UnrealizedMinimal,
|
||||
};
|
||||
|
||||
@@ -118,11 +118,11 @@ impl RealizedMinimal {
|
||||
|(i, cap_cents, supply, ..)| {
|
||||
let cap = cap_cents.as_u128();
|
||||
let supply_sats = Sats::from(supply).as_u128();
|
||||
if supply_sats == 0 {
|
||||
(i, Cents::ZERO)
|
||||
} else {
|
||||
(i, Cents::from(cap * Sats::ONE_BTC_U128 / supply_sats))
|
||||
}
|
||||
let cents = (cap * Sats::ONE_BTC_U128)
|
||||
.checked_div(supply_sats)
|
||||
.map(Cents::from)
|
||||
.unwrap_or(Cents::ZERO);
|
||||
(i, cents)
|
||||
},
|
||||
exit,
|
||||
)?)
|
||||
|
||||
@@ -4,9 +4,9 @@ use brk_types::{Dollars, Height};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedFull, SupplyCore, UnrealizedFull};
|
||||
|
||||
use super::{RelativeExtendedOwnPnl, RelativeFull};
|
||||
use super::{RelativeExtendedOwnPnl, RelativeFull, RelativeInvestedCapital};
|
||||
|
||||
/// Relative metrics for the "all" cohort (base + own_pnl, NO rel_to_all).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
@@ -17,6 +17,8 @@ pub struct RelativeForAll<M: StorageMode = Rw> {
|
||||
pub base: RelativeFull<M>,
|
||||
#[traversable(flatten)]
|
||||
pub extended_own_pnl: RelativeExtendedOwnPnl<M>,
|
||||
#[traversable(flatten)]
|
||||
pub invested_capital: RelativeInvestedCapital<M>,
|
||||
}
|
||||
|
||||
impl RelativeForAll {
|
||||
@@ -24,6 +26,7 @@ impl RelativeForAll {
|
||||
Ok(Self {
|
||||
base: RelativeFull::forced_import(cfg)?,
|
||||
extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg)?,
|
||||
invested_capital: RelativeInvestedCapital::forced_import(cfg)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,6 +35,7 @@ impl RelativeForAll {
|
||||
max_from: Height,
|
||||
supply: &SupplyCore,
|
||||
unrealized: &UnrealizedFull,
|
||||
realized: &RealizedFull,
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
@@ -43,6 +47,8 @@ impl RelativeForAll {
|
||||
&unrealized.gross_pnl.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
self.invested_capital
|
||||
.compute(max_from, unrealized, realized, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, Cents, Height, Version};
|
||||
use vecdb::{Exit, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PercentPerBlock, RatioCentsBp16};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedFull, UnrealizedFull};
|
||||
|
||||
/// Shares of invested capital in profit / in loss relative to own realized cap.
|
||||
/// Present for cohorts with `UnrealizedFull` (all, sth, lth).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeInvestedCapital<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "invested_capital/in_profit", rename = "to_own")]
|
||||
pub in_profit_to_own: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "invested_capital/in_loss", rename = "to_own")]
|
||||
pub in_loss_to_own: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
impl RelativeInvestedCapital {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
Ok(Self {
|
||||
in_profit_to_own: cfg.import("invested_capital_in_profit_to_own", v0)?,
|
||||
in_loss_to_own: cfg.import("invested_capital_in_loss_to_own", v0)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
realized: &RealizedFull,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let realized_cap = &realized.core.minimal.cap.cents.height;
|
||||
self.in_profit_to_own
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp16>(
|
||||
max_from,
|
||||
&unrealized.invested_capital.in_profit.cents.height,
|
||||
realized_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.in_loss_to_own
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp16>(
|
||||
max_from,
|
||||
&unrealized.invested_capital.in_loss.cents.height,
|
||||
realized_cap,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ mod extended_own_market_cap;
|
||||
mod extended_own_pnl;
|
||||
mod for_all;
|
||||
mod full;
|
||||
mod invested_capital;
|
||||
mod to_all;
|
||||
mod with_extended;
|
||||
|
||||
@@ -9,5 +10,6 @@ pub use extended_own_market_cap::RelativeExtendedOwnMarketCap;
|
||||
pub use extended_own_pnl::RelativeExtendedOwnPnl;
|
||||
pub use for_all::RelativeForAll;
|
||||
pub use full::RelativeFull;
|
||||
pub use invested_capital::RelativeInvestedCapital;
|
||||
pub use to_all::RelativeToAll;
|
||||
pub use with_extended::RelativeWithExtended;
|
||||
|
||||
@@ -4,9 +4,12 @@ use brk_types::{Dollars, Height, Sats};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedFull, SupplyCore, UnrealizedFull};
|
||||
|
||||
use super::{RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeFull, RelativeToAll};
|
||||
use super::{
|
||||
RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeFull, RelativeInvestedCapital,
|
||||
RelativeToAll,
|
||||
};
|
||||
|
||||
/// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl).
|
||||
/// Used by: sth, lth cohorts.
|
||||
@@ -22,6 +25,8 @@ pub struct RelativeWithExtended<M: StorageMode = Rw> {
|
||||
pub extended_own_market_cap: RelativeExtendedOwnMarketCap<M>,
|
||||
#[traversable(flatten)]
|
||||
pub extended_own_pnl: RelativeExtendedOwnPnl<M>,
|
||||
#[traversable(flatten)]
|
||||
pub invested_capital: RelativeInvestedCapital<M>,
|
||||
}
|
||||
|
||||
impl RelativeWithExtended {
|
||||
@@ -31,6 +36,7 @@ impl RelativeWithExtended {
|
||||
rel_to_all: RelativeToAll::forced_import(cfg)?,
|
||||
extended_own_market_cap: RelativeExtendedOwnMarketCap::forced_import(cfg)?,
|
||||
extended_own_pnl: RelativeExtendedOwnPnl::forced_import(cfg)?,
|
||||
invested_capital: RelativeInvestedCapital::forced_import(cfg)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,6 +46,7 @@ impl RelativeWithExtended {
|
||||
max_from: Height,
|
||||
supply: &SupplyCore,
|
||||
unrealized: &UnrealizedFull,
|
||||
realized: &RealizedFull,
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||
own_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
@@ -57,6 +64,8 @@ impl RelativeWithExtended {
|
||||
&unrealized.gross_pnl.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
self.invested_capital
|
||||
.compute(max_from, unrealized, realized, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Sats, StoredU64, Version};
|
||||
use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{indexes, internal::AmountPerBlock, prices};
|
||||
|
||||
/// Average amount held per UTXO and per funded address.
|
||||
///
|
||||
/// `utxo = supply / utxo_count`, `addr = supply / funded_addr_count`.
|
||||
#[derive(Traversable)]
|
||||
pub struct AvgAmountMetrics<M: StorageMode = Rw> {
|
||||
pub utxo: AmountPerBlock<M>,
|
||||
pub addr: AmountPerBlock<M>,
|
||||
}
|
||||
|
||||
impl AvgAmountMetrics {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
prefix: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let name = |suffix: &str| {
|
||||
if prefix.is_empty() {
|
||||
suffix.to_string()
|
||||
} else {
|
||||
format!("{prefix}_{suffix}")
|
||||
}
|
||||
};
|
||||
Ok(Self {
|
||||
utxo: AmountPerBlock::forced_import(db, &name("avg_utxo_amount"), version, indexes)?,
|
||||
addr: AmountPerBlock::forced_import(db, &name("avg_addr_amount"), version, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.utxo.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.utxo.cents.height,
|
||||
&mut self.addr.sats.height,
|
||||
&mut self.addr.cents.height,
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.utxo.sats.height.reset()?;
|
||||
self.utxo.cents.height.reset()?;
|
||||
self.addr.sats.height.reset()?;
|
||||
self.addr.cents.height.reset()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
supply_sats: &impl ReadableVec<Height, Sats>,
|
||||
utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||
funded_addr_count: &impl ReadableVec<Height, StoredU64>,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo
|
||||
.sats
|
||||
.height
|
||||
.compute_divide(max_from, supply_sats, utxo_count, exit)?;
|
||||
self.utxo.compute(prices, max_from, exit)?;
|
||||
|
||||
self.addr
|
||||
.sats
|
||||
.height
|
||||
.compute_divide(max_from, supply_sats, funded_addr_count, exit)?;
|
||||
self.addr.compute(prices, max_from, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ impl SupplyBase {
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.total.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.total.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.total.cents.height,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
mod avg_amount;
|
||||
mod base;
|
||||
mod core;
|
||||
|
||||
pub use avg_amount::AvgAmountMetrics;
|
||||
pub use self::core::SupplyCore;
|
||||
pub use base::SupplyBase;
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::{
|
||||
},
|
||||
indexes, inputs,
|
||||
internal::{
|
||||
PerBlockCumulativeRolling, WindowStartVec, Windows,
|
||||
PerBlockCumulativeRolling, WindowStartVec, Windows, WithAddrTypes,
|
||||
db_utils::{finalize_db, open_db},
|
||||
},
|
||||
outputs, prices, transactions,
|
||||
@@ -37,6 +37,7 @@ use super::{
|
||||
AddrActivityVecs, AddrCountsVecs, DeltaVecs, ExposedAddrVecs, NewAddrCountVecs,
|
||||
ReusedAddrVecs, TotalAddrCountVecs,
|
||||
},
|
||||
metrics::AvgAmountMetrics,
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(23);
|
||||
@@ -51,6 +52,7 @@ pub struct AddrMetricsVecs<M: StorageMode = Rw> {
|
||||
pub reused: ReusedAddrVecs<M>,
|
||||
pub exposed: ExposedAddrVecs<M>,
|
||||
pub delta: DeltaVecs,
|
||||
pub avg_amount: WithAddrTypes<AvgAmountMetrics<M>>,
|
||||
#[traversable(wrap = "indexes", rename = "funded")]
|
||||
pub funded_index:
|
||||
LazyVecFrom1<FundedAddrIndex, FundedAddrIndex, FundedAddrIndex, FundedAddrData>,
|
||||
@@ -170,6 +172,9 @@ impl Vecs {
|
||||
// Growth rate: delta change + rate (global + per-type)
|
||||
let delta = DeltaVecs::new(version, &addr_count, cached_starts, indexes);
|
||||
|
||||
// Average amount (supply / utxo_count, supply / funded_addr_count) for `all` and per addr type.
|
||||
let avg_amount = WithAddrTypes::<AvgAmountMetrics>::forced_import(&db, version, indexes)?;
|
||||
|
||||
let this = Self {
|
||||
supply_state: BytesVec::forced_import_with(
|
||||
vecdb::ImportOptions::new(&db, "supply_state", version)
|
||||
@@ -185,6 +190,7 @@ impl Vecs {
|
||||
reused: reused_addr_count,
|
||||
exposed: exposed_addr_vecs,
|
||||
delta,
|
||||
avg_amount,
|
||||
funded_index: funded_addr_index,
|
||||
empty_index: empty_addr_index,
|
||||
},
|
||||
@@ -302,6 +308,7 @@ impl Vecs {
|
||||
self.addrs.activity.reset_height()?;
|
||||
self.addrs.reused.reset_height()?;
|
||||
self.addrs.exposed.reset_height()?;
|
||||
self.addrs.avg_amount.reset_height()?;
|
||||
reset_state(
|
||||
&mut self.any_addr_indexes,
|
||||
&mut self.addrs_data,
|
||||
@@ -490,6 +497,34 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Average amount (supply / utxo_count, supply / funded_addr_count) for `all` and per addr type.
|
||||
let all_m = &self.utxo_cohorts.all.metrics;
|
||||
self.addrs.avg_amount.all.compute(
|
||||
prices,
|
||||
&all_m.supply.total.sats.height,
|
||||
&all_m.outputs.unspent_count.height,
|
||||
&self.addrs.funded.all.height,
|
||||
starting_indexes.height,
|
||||
exit,
|
||||
)?;
|
||||
for ((ot, avg), (_, funded)) in self
|
||||
.addrs
|
||||
.avg_amount
|
||||
.by_addr_type
|
||||
.iter_mut()
|
||||
.zip(self.addrs.funded.by_addr_type.iter())
|
||||
{
|
||||
let type_m = &t.get(ot).metrics;
|
||||
avg.compute(
|
||||
prices,
|
||||
&type_m.supply.total.sats.height,
|
||||
&type_m.outputs.unspent_count.height,
|
||||
&funded.height,
|
||||
starting_indexes.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// 6c. Compute total_addr_count = addr_count + empty_addr_count
|
||||
self.addrs.total.compute(
|
||||
starting_indexes.height,
|
||||
|
||||
@@ -267,7 +267,7 @@ impl<T: NumericValue + JsonSchema> PerBlockDistribution<T> {
|
||||
vec.push(zero);
|
||||
}
|
||||
} else {
|
||||
weighted.sort_unstable_by(|a, b| a.0.cmp(&b.0));
|
||||
weighted.sort_unstable_by_key(|a| a.0);
|
||||
|
||||
max.push(weighted.last().unwrap().0);
|
||||
pct90.push(get_weighted_percentile(&weighted, 0.90));
|
||||
|
||||
@@ -24,9 +24,9 @@ pub use derived::{
|
||||
RatioCents64, TimesSqrt,
|
||||
};
|
||||
pub use ratio::{
|
||||
RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDiffCentsBps32,
|
||||
RatioDiffDollarsBps32, RatioDiffF32Bps32, RatioDollarsBp16, RatioDollarsBp32,
|
||||
RatioDollarsBps32, RatioSatsBp16, RatioU64Bp16, RatioU64F32,
|
||||
RatioCentsBp16, RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32,
|
||||
RatioDiffCentsBps32, RatioDiffDollarsBps32, RatioDiffF32Bps32, RatioDollarsBp16,
|
||||
RatioDollarsBp32, RatioDollarsBps32, RatioSatsBp16, RatioU64Bp16, RatioU64F32,
|
||||
};
|
||||
pub use specialized::{
|
||||
BlockCountTarget1m, BlockCountTarget1w, BlockCountTarget1y, BlockCountTarget24h,
|
||||
|
||||
@@ -43,6 +43,19 @@ impl BinaryTransform<Cents, Cents, BasisPoints32> for RatioCentsBp32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RatioCentsBp16;
|
||||
|
||||
impl BinaryTransform<Cents, Cents, BasisPoints16> for RatioCentsBp16 {
|
||||
#[inline(always)]
|
||||
fn apply(numerator: Cents, denominator: Cents) -> BasisPoints16 {
|
||||
if denominator == Cents::ZERO {
|
||||
BasisPoints16::ZERO
|
||||
} else {
|
||||
BasisPoints16::from(numerator.inner() as f64 / denominator.inner() as f64)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RatioDollarsBp16;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, BasisPoints16> for RatioDollarsBp16 {
|
||||
|
||||
@@ -17,6 +17,8 @@ use super::{
|
||||
WindowStartVec, Windows,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::AvgAmountMetrics;
|
||||
|
||||
/// `all` aggregate plus per-`AddrType` breakdown.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct WithAddrTypes<T> {
|
||||
@@ -246,6 +248,42 @@ impl WithAddrTypes<AmountPerBlock> {
|
||||
}
|
||||
}
|
||||
|
||||
impl WithAddrTypes<AvgAmountMetrics> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = AvgAmountMetrics::forced_import(db, "", version, indexes)?;
|
||||
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
||||
AvgAmountMetrics::forced_import(db, type_name, version, indexes)
|
||||
})?;
|
||||
Ok(Self { all, by_addr_type })
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.all.collect_vecs_mut();
|
||||
for v in self.by_addr_type.values_mut() {
|
||||
vecs.extend(v.collect_vecs_mut());
|
||||
}
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.collect_vecs_mut().into_par_iter()
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.reset_height()?;
|
||||
for v in self.by_addr_type.values_mut() {
|
||||
v.reset_height()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BpsType> WithAddrTypes<PercentPerBlock<B>> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cmp::Reverse;
|
||||
|
||||
use super::{BLOCK_VSIZE, package::Package};
|
||||
use crate::types::SelectedTx;
|
||||
|
||||
@@ -13,7 +15,7 @@ pub fn partition_into_blocks(
|
||||
mut packages: Vec<Package>,
|
||||
num_blocks: usize,
|
||||
) -> Vec<Vec<SelectedTx>> {
|
||||
packages.sort_unstable_by(|a, b| b.fee_rate.cmp(&a.fee_rate));
|
||||
packages.sort_unstable_by_key(|p| Reverse(p.fee_rate));
|
||||
|
||||
let mut blocks: Vec<Vec<SelectedTx>> = Vec::with_capacity(num_blocks);
|
||||
let mut current_block: Vec<SelectedTx> = Vec::new();
|
||||
|
||||
@@ -375,11 +375,7 @@ impl Query {
|
||||
slug: pool_slug,
|
||||
miner_names,
|
||||
},
|
||||
avg_fee: Sats::from(if non_coinbase > 0 {
|
||||
total_fees_u64 / non_coinbase
|
||||
} else {
|
||||
0
|
||||
}),
|
||||
avg_fee: Sats::from(total_fees_u64.checked_div(non_coinbase).unwrap_or(0)),
|
||||
avg_fee_rate: FeeRate::from((total_fees, VSize::from(vsize))),
|
||||
coinbase_raw,
|
||||
coinbase_address,
|
||||
|
||||
@@ -80,7 +80,7 @@ impl BlockWindow {
|
||||
.prices
|
||||
.spot.cents.height
|
||||
.collect_range_at(self.start, self.end);
|
||||
let read_start = self.start.saturating_sub(1).max(0);
|
||||
let read_start = self.start.saturating_sub(1);
|
||||
let all_cum = cumulative.collect_range_at(read_start, self.end);
|
||||
let offset = if self.start > 0 { 1 } else { 0 };
|
||||
|
||||
@@ -91,19 +91,19 @@ impl BlockWindow {
|
||||
while pos < total {
|
||||
let window_end = (pos + self.window).min(total);
|
||||
let block_count = (window_end - pos) as u64;
|
||||
if block_count > 0 {
|
||||
let mid = (pos + window_end) / 2;
|
||||
let cum_end = all_cum[window_end - 1 + offset];
|
||||
let cum_start = if pos + offset > 0 {
|
||||
all_cum[pos + offset - 1]
|
||||
} else {
|
||||
Sats::ZERO
|
||||
};
|
||||
let total_sats = cum_end - cum_start;
|
||||
let mid = (pos + window_end) / 2;
|
||||
let cum_end = all_cum[window_end - 1 + offset];
|
||||
let cum_start = if pos + offset > 0 {
|
||||
all_cum[pos + offset - 1]
|
||||
} else {
|
||||
Sats::ZERO
|
||||
};
|
||||
let total_sats = cum_end - cum_start;
|
||||
if let Some(avg) = (*total_sats).checked_div(block_count) {
|
||||
results.push(WindowAvg {
|
||||
avg_height: Height::from(self.start + mid),
|
||||
timestamp: all_ts[mid],
|
||||
avg_value: Sats::from(*total_sats / block_count),
|
||||
avg_value: Sats::from(avg),
|
||||
usd: Dollars::from(all_prices[mid]),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cmp::Reverse;
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_types::{
|
||||
BlockInfoV1, Day1, Height, Pool, PoolBlockCounts, PoolBlockShares, PoolDetail, PoolDetailInfo,
|
||||
@@ -89,7 +91,7 @@ impl Query {
|
||||
}
|
||||
|
||||
// Sort by block count descending
|
||||
pool_data.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
pool_data.sort_by_key(|p| Reverse(p.1));
|
||||
|
||||
let total_blocks: u64 = pool_data.iter().map(|(_, count)| count).sum();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//! Run with:
|
||||
//! cargo run -p brk_rpc --example compare_backends --features corepc
|
||||
|
||||
#[cfg(all(feature = "bitcoincore-rpc", feature = "corepc"))]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(not(all(feature = "bitcoincore-rpc", feature = "corepc")))]
|
||||
@@ -260,6 +261,7 @@ fn main() {
|
||||
println!("=== All checks passed ===");
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "bitcoincore-rpc", feature = "corepc"))]
|
||||
fn timed<T>(f: impl FnOnce() -> T) -> (Duration, T) {
|
||||
let start = Instant::now();
|
||||
let result = f();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(unreachable_patterns, reason = "P2PK65 and P2PK33 both serialize as 'p2pk'")]
|
||||
|
||||
use bitcoin::{AddressType, ScriptBuf, opcodes::all::OP_PUSHBYTES_2};
|
||||
use brk_error::Error;
|
||||
use schemars::JsonSchema;
|
||||
|
||||
@@ -9,7 +9,7 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::{CheckedSub, Formattable, Pco, SaturatingAdd};
|
||||
|
||||
use crate::StoredF64;
|
||||
use crate::{StoredF64, StoredU64};
|
||||
|
||||
use super::{Bitcoin, Cents, Dollars, Height};
|
||||
|
||||
@@ -204,35 +204,34 @@ impl Sum for Sats {
|
||||
|
||||
impl Div<Dollars> for Sats {
|
||||
type Output = Self;
|
||||
#[allow(clippy::suspicious_arithmetic_impl, reason = "cents-precision upscale before division")]
|
||||
fn div(self, rhs: Dollars) -> Self::Output {
|
||||
let raw_cents = u64::from(Cents::from(rhs));
|
||||
if raw_cents != 0 {
|
||||
Self(self.0 * 100 / raw_cents)
|
||||
} else {
|
||||
Self::MAX
|
||||
}
|
||||
(self.0 * 100)
|
||||
.checked_div(raw_cents)
|
||||
.map(Self)
|
||||
.unwrap_or(Self::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Sats> for Sats {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: Sats) -> Self::Output {
|
||||
if rhs.0 == 0 {
|
||||
Self(0)
|
||||
} else {
|
||||
Self(self.0 / rhs.0)
|
||||
}
|
||||
Self(self.0.checked_div(rhs.0).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<usize> for Sats {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: usize) -> Self::Output {
|
||||
if rhs == 0 {
|
||||
Self::ZERO
|
||||
} else {
|
||||
Self(self.0 / rhs as u64)
|
||||
}
|
||||
Self(self.0.checked_div(rhs as u64).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<StoredU64> for Sats {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: StoredU64) -> Self::Output {
|
||||
Self(self.0.checked_div(u64::from(rhs)).unwrap_or(0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,10 +99,8 @@ fn sanitize(dirty: impl Iterator<Item = String>) -> Vec<String> {
|
||||
let mut current = String::new();
|
||||
for c in s.to_lowercase().chars() {
|
||||
match c {
|
||||
' ' | ',' | '+' => {
|
||||
if !current.is_empty() {
|
||||
clean.push(mem::take(&mut current));
|
||||
}
|
||||
' ' | ',' | '+' if !current.is_empty() => {
|
||||
clean.push(mem::take(&mut current));
|
||||
}
|
||||
'-' => current.push('_'),
|
||||
c if c.is_alphanumeric() || c == '_' => current.push(c),
|
||||
|
||||
Reference in New Issue
Block a user