mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snap
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -592,6 +592,7 @@ dependencies = [
|
|||||||
name = "brk_mempool"
|
name = "brk_mempool"
|
||||||
version = "0.3.0-beta.2"
|
version = "0.3.0-beta.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitcoin",
|
||||||
"brk_error",
|
"brk_error",
|
||||||
"brk_logger",
|
"brk_logger",
|
||||||
"brk_rpc",
|
"brk_rpc",
|
||||||
|
|||||||
@@ -1124,10 +1124,10 @@ pub struct AllEmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pattern struct for repeated tree structure.
|
/// Pattern struct for repeated tree structure.
|
||||||
pub struct CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern {
|
pub struct CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern {
|
||||||
pub cap: CentsDeltaToUsdPattern,
|
pub cap: CentsDeltaToUsdPattern,
|
||||||
|
pub capitalized: PricePattern,
|
||||||
pub gross_pnl: BlockCumulativeSumPattern,
|
pub gross_pnl: BlockCumulativeSumPattern,
|
||||||
pub investor: PricePattern,
|
|
||||||
pub loss: BlockCumulativeNegativeSumPattern,
|
pub loss: BlockCumulativeNegativeSumPattern,
|
||||||
pub mvrv: SeriesPattern1<StoredF32>,
|
pub mvrv: SeriesPattern1<StoredF32>,
|
||||||
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
|
pub net_pnl: BlockChangeCumulativeDeltaSumPattern,
|
||||||
@@ -1430,11 +1430,11 @@ impl AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Pattern struct for repeated tree structure.
|
/// Pattern struct for repeated tree structure.
|
||||||
pub struct GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 {
|
pub struct CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 {
|
||||||
|
pub capitalized_cap_in_loss_raw: SeriesPattern18<CentsSquaredSats>,
|
||||||
|
pub capitalized_cap_in_profit_raw: SeriesPattern18<CentsSquaredSats>,
|
||||||
pub gross_pnl: CentsUsdPattern3,
|
pub gross_pnl: CentsUsdPattern3,
|
||||||
pub invested_capital: InPattern,
|
pub invested_capital: InPattern,
|
||||||
pub investor_cap_in_loss_raw: SeriesPattern18<CentsSquaredSats>,
|
|
||||||
pub investor_cap_in_profit_raw: SeriesPattern18<CentsSquaredSats>,
|
|
||||||
pub loss: CentsNegativeToUsdPattern2,
|
pub loss: CentsNegativeToUsdPattern2,
|
||||||
pub net_pnl: CentsToUsdPattern3,
|
pub net_pnl: CentsToUsdPattern3,
|
||||||
pub nupl: BpsRatioPattern,
|
pub nupl: BpsRatioPattern,
|
||||||
@@ -1442,14 +1442,14 @@ pub struct GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 {
|
|||||||
pub sentiment: GreedNetPainPattern,
|
pub sentiment: GreedNetPainPattern,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 {
|
impl CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 {
|
||||||
/// Create a new pattern node with accumulated series name.
|
/// Create a new pattern node with accumulated series name.
|
||||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
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")),
|
gross_pnl: CentsUsdPattern3::new(client.clone(), _m(&acc, "unrealized_gross_pnl")),
|
||||||
invested_capital: InPattern::new(client.clone(), _m(&acc, "invested_capital_in")),
|
invested_capital: InPattern::new(client.clone(), _m(&acc, "invested_capital_in")),
|
||||||
investor_cap_in_loss_raw: SeriesPattern18::new(client.clone(), _m(&acc, "investor_cap_in_loss_raw")),
|
|
||||||
investor_cap_in_profit_raw: SeriesPattern18::new(client.clone(), _m(&acc, "investor_cap_in_profit_raw")),
|
|
||||||
loss: CentsNegativeToUsdPattern2::new(client.clone(), _m(&acc, "unrealized_loss")),
|
loss: CentsNegativeToUsdPattern2::new(client.clone(), _m(&acc, "unrealized_loss")),
|
||||||
net_pnl: CentsToUsdPattern3::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
|
net_pnl: CentsToUsdPattern3::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
|
||||||
nupl: BpsRatioPattern::new(client.clone(), _m(&acc, "nupl")),
|
nupl: BpsRatioPattern::new(client.clone(), _m(&acc, "nupl")),
|
||||||
@@ -4555,20 +4555,51 @@ pub struct SeriesTree_Addrs_Exposed_Supply {
|
|||||||
pub p2wsh: BtcCentsSatsUsdPattern,
|
pub p2wsh: BtcCentsSatsUsdPattern,
|
||||||
pub p2tr: BtcCentsSatsUsdPattern,
|
pub p2tr: BtcCentsSatsUsdPattern,
|
||||||
pub p2a: BtcCentsSatsUsdPattern,
|
pub p2a: BtcCentsSatsUsdPattern,
|
||||||
|
pub share: SeriesTree_Addrs_Exposed_Supply_Share,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SeriesTree_Addrs_Exposed_Supply {
|
impl SeriesTree_Addrs_Exposed_Supply {
|
||||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
all: BtcCentsSatsUsdPattern::new(client.clone(), "exposed_addr_supply".to_string()),
|
all: BtcCentsSatsUsdPattern::new(client.clone(), "exposed_supply".to_string()),
|
||||||
p2pk65: BtcCentsSatsUsdPattern::new(client.clone(), "p2pk65_exposed_addr_supply".to_string()),
|
p2pk65: BtcCentsSatsUsdPattern::new(client.clone(), "p2pk65_exposed_supply".to_string()),
|
||||||
p2pk33: BtcCentsSatsUsdPattern::new(client.clone(), "p2pk33_exposed_addr_supply".to_string()),
|
p2pk33: BtcCentsSatsUsdPattern::new(client.clone(), "p2pk33_exposed_supply".to_string()),
|
||||||
p2pkh: BtcCentsSatsUsdPattern::new(client.clone(), "p2pkh_exposed_addr_supply".to_string()),
|
p2pkh: BtcCentsSatsUsdPattern::new(client.clone(), "p2pkh_exposed_supply".to_string()),
|
||||||
p2sh: BtcCentsSatsUsdPattern::new(client.clone(), "p2sh_exposed_addr_supply".to_string()),
|
p2sh: BtcCentsSatsUsdPattern::new(client.clone(), "p2sh_exposed_supply".to_string()),
|
||||||
p2wpkh: BtcCentsSatsUsdPattern::new(client.clone(), "p2wpkh_exposed_addr_supply".to_string()),
|
p2wpkh: BtcCentsSatsUsdPattern::new(client.clone(), "p2wpkh_exposed_supply".to_string()),
|
||||||
p2wsh: BtcCentsSatsUsdPattern::new(client.clone(), "p2wsh_exposed_addr_supply".to_string()),
|
p2wsh: BtcCentsSatsUsdPattern::new(client.clone(), "p2wsh_exposed_supply".to_string()),
|
||||||
p2tr: BtcCentsSatsUsdPattern::new(client.clone(), "p2tr_exposed_addr_supply".to_string()),
|
p2tr: BtcCentsSatsUsdPattern::new(client.clone(), "p2tr_exposed_supply".to_string()),
|
||||||
p2a: BtcCentsSatsUsdPattern::new(client.clone(), "p2a_exposed_addr_supply".to_string()),
|
p2a: BtcCentsSatsUsdPattern::new(client.clone(), "p2a_exposed_supply".to_string()),
|
||||||
|
share: SeriesTree_Addrs_Exposed_Supply_Share::new(client.clone(), format!("{base_path}_share")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Series tree node.
|
||||||
|
pub struct SeriesTree_Addrs_Exposed_Supply_Share {
|
||||||
|
pub all: BpsPercentRatioPattern2,
|
||||||
|
pub p2pk65: BpsPercentRatioPattern2,
|
||||||
|
pub p2pk33: BpsPercentRatioPattern2,
|
||||||
|
pub p2pkh: BpsPercentRatioPattern2,
|
||||||
|
pub p2sh: BpsPercentRatioPattern2,
|
||||||
|
pub p2wpkh: BpsPercentRatioPattern2,
|
||||||
|
pub p2wsh: BpsPercentRatioPattern2,
|
||||||
|
pub p2tr: BpsPercentRatioPattern2,
|
||||||
|
pub p2a: BpsPercentRatioPattern2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SeriesTree_Addrs_Exposed_Supply_Share {
|
||||||
|
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
all: BpsPercentRatioPattern2::new(client.clone(), "exposed_supply_share".to_string()),
|
||||||
|
p2pk65: BpsPercentRatioPattern2::new(client.clone(), "p2pk65_exposed_supply_share".to_string()),
|
||||||
|
p2pk33: BpsPercentRatioPattern2::new(client.clone(), "p2pk33_exposed_supply_share".to_string()),
|
||||||
|
p2pkh: BpsPercentRatioPattern2::new(client.clone(), "p2pkh_exposed_supply_share".to_string()),
|
||||||
|
p2sh: BpsPercentRatioPattern2::new(client.clone(), "p2sh_exposed_supply_share".to_string()),
|
||||||
|
p2wpkh: BpsPercentRatioPattern2::new(client.clone(), "p2wpkh_exposed_supply_share".to_string()),
|
||||||
|
p2wsh: BpsPercentRatioPattern2::new(client.clone(), "p2wsh_exposed_supply_share".to_string()),
|
||||||
|
p2tr: BpsPercentRatioPattern2::new(client.clone(), "p2tr_exposed_supply_share".to_string()),
|
||||||
|
p2a: BpsPercentRatioPattern2::new(client.clone(), "p2a_exposed_supply_share".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7028,7 +7059,7 @@ pub struct SeriesTree_Cohorts_Utxo_All_Realized {
|
|||||||
pub gross_pnl: BlockCumulativeSumPattern,
|
pub gross_pnl: BlockCumulativeSumPattern,
|
||||||
pub sell_side_risk_ratio: _1m1w1y24hPattern7,
|
pub sell_side_risk_ratio: _1m1w1y24hPattern7,
|
||||||
pub peak_regret: BlockCumulativeSumPattern,
|
pub peak_regret: BlockCumulativeSumPattern,
|
||||||
pub investor: PricePattern,
|
pub capitalized: PricePattern,
|
||||||
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7045,7 +7076,7 @@ impl SeriesTree_Cohorts_Utxo_All_Realized {
|
|||||||
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "realized_gross_pnl".to_string()),
|
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "realized_gross_pnl".to_string()),
|
||||||
sell_side_risk_ratio: _1m1w1y24hPattern7::new(client.clone(), "sell_side_risk_ratio".to_string()),
|
sell_side_risk_ratio: _1m1w1y24hPattern7::new(client.clone(), "sell_side_risk_ratio".to_string()),
|
||||||
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "realized_peak_regret".to_string()),
|
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "realized_peak_regret".to_string()),
|
||||||
investor: PricePattern::new(client.clone(), "investor_price".to_string()),
|
capitalized: PricePattern::new(client.clone(), "capitalized_price".to_string()),
|
||||||
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "realized_profit_to_loss_ratio".to_string()),
|
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "realized_profit_to_loss_ratio".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7328,8 +7359,8 @@ pub struct SeriesTree_Cohorts_Utxo_All_Unrealized {
|
|||||||
pub net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl,
|
pub net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl,
|
||||||
pub gross_pnl: CentsUsdPattern3,
|
pub gross_pnl: CentsUsdPattern3,
|
||||||
pub invested_capital: InPattern,
|
pub invested_capital: InPattern,
|
||||||
pub investor_cap_in_profit_raw: SeriesPattern18<CentsSquaredSats>,
|
pub capitalized_cap_in_profit_raw: SeriesPattern18<CentsSquaredSats>,
|
||||||
pub investor_cap_in_loss_raw: SeriesPattern18<CentsSquaredSats>,
|
pub capitalized_cap_in_loss_raw: SeriesPattern18<CentsSquaredSats>,
|
||||||
pub sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment,
|
pub sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7342,8 +7373,8 @@ impl SeriesTree_Cohorts_Utxo_All_Unrealized {
|
|||||||
net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl::new(client.clone(), format!("{base_path}_net_pnl")),
|
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()),
|
gross_pnl: CentsUsdPattern3::new(client.clone(), "unrealized_gross_pnl".to_string()),
|
||||||
invested_capital: InPattern::new(client.clone(), "invested_capital_in".to_string()),
|
invested_capital: InPattern::new(client.clone(), "invested_capital_in".to_string()),
|
||||||
investor_cap_in_profit_raw: SeriesPattern18::new(client.clone(), "investor_cap_in_profit_raw".to_string()),
|
capitalized_cap_in_profit_raw: SeriesPattern18::new(client.clone(), "capitalized_cap_in_profit_raw".to_string()),
|
||||||
investor_cap_in_loss_raw: SeriesPattern18::new(client.clone(), "investor_cap_in_loss_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")),
|
sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment::new(client.clone(), format!("{base_path}_sentiment")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7430,7 +7461,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth {
|
|||||||
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
||||||
pub realized: SeriesTree_Cohorts_Utxo_Sth_Realized,
|
pub realized: SeriesTree_Cohorts_Utxo_Sth_Realized,
|
||||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||||
pub unrealized: GrossInvestedInvestorLossNetNuplProfitSentimentPattern2,
|
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SeriesTree_Cohorts_Utxo_Sth {
|
impl SeriesTree_Cohorts_Utxo_Sth {
|
||||||
@@ -7441,7 +7472,7 @@ impl SeriesTree_Cohorts_Utxo_Sth {
|
|||||||
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "sth".to_string()),
|
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "sth".to_string()),
|
||||||
realized: SeriesTree_Cohorts_Utxo_Sth_Realized::new(client.clone(), format!("{base_path}_realized")),
|
realized: SeriesTree_Cohorts_Utxo_Sth_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||||
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "sth".to_string()),
|
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "sth".to_string()),
|
||||||
unrealized: GrossInvestedInvestorLossNetNuplProfitSentimentPattern2::new(client.clone(), "sth".to_string()),
|
unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "sth".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7458,7 +7489,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth_Realized {
|
|||||||
pub gross_pnl: BlockCumulativeSumPattern,
|
pub gross_pnl: BlockCumulativeSumPattern,
|
||||||
pub sell_side_risk_ratio: _1m1w1y24hPattern7,
|
pub sell_side_risk_ratio: _1m1w1y24hPattern7,
|
||||||
pub peak_regret: BlockCumulativeSumPattern,
|
pub peak_regret: BlockCumulativeSumPattern,
|
||||||
pub investor: PricePattern,
|
pub capitalized: PricePattern,
|
||||||
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7475,7 +7506,7 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized {
|
|||||||
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_gross_pnl".to_string()),
|
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_gross_pnl".to_string()),
|
||||||
sell_side_risk_ratio: _1m1w1y24hPattern7::new(client.clone(), "sth_sell_side_risk_ratio".to_string()),
|
sell_side_risk_ratio: _1m1w1y24hPattern7::new(client.clone(), "sth_sell_side_risk_ratio".to_string()),
|
||||||
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_peak_regret".to_string()),
|
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "sth_realized_peak_regret".to_string()),
|
||||||
investor: PricePattern::new(client.clone(), "sth_investor_price".to_string()),
|
capitalized: PricePattern::new(client.clone(), "sth_capitalized_price".to_string()),
|
||||||
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "sth_realized_profit_to_loss_ratio".to_string()),
|
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "sth_realized_profit_to_loss_ratio".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7698,7 +7729,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth {
|
|||||||
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
pub activity: CoindaysCoinyearsDormancyTransferPattern,
|
||||||
pub realized: SeriesTree_Cohorts_Utxo_Lth_Realized,
|
pub realized: SeriesTree_Cohorts_Utxo_Lth_Realized,
|
||||||
pub cost_basis: InMaxMinPerSupplyPattern,
|
pub cost_basis: InMaxMinPerSupplyPattern,
|
||||||
pub unrealized: GrossInvestedInvestorLossNetNuplProfitSentimentPattern2,
|
pub unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SeriesTree_Cohorts_Utxo_Lth {
|
impl SeriesTree_Cohorts_Utxo_Lth {
|
||||||
@@ -7709,7 +7740,7 @@ impl SeriesTree_Cohorts_Utxo_Lth {
|
|||||||
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "lth".to_string()),
|
activity: CoindaysCoinyearsDormancyTransferPattern::new(client.clone(), "lth".to_string()),
|
||||||
realized: SeriesTree_Cohorts_Utxo_Lth_Realized::new(client.clone(), format!("{base_path}_realized")),
|
realized: SeriesTree_Cohorts_Utxo_Lth_Realized::new(client.clone(), format!("{base_path}_realized")),
|
||||||
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "lth".to_string()),
|
cost_basis: InMaxMinPerSupplyPattern::new(client.clone(), "lth".to_string()),
|
||||||
unrealized: GrossInvestedInvestorLossNetNuplProfitSentimentPattern2::new(client.clone(), "lth".to_string()),
|
unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2::new(client.clone(), "lth".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7726,7 +7757,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth_Realized {
|
|||||||
pub gross_pnl: BlockCumulativeSumPattern,
|
pub gross_pnl: BlockCumulativeSumPattern,
|
||||||
pub sell_side_risk_ratio: _1m1w1y24hPattern7,
|
pub sell_side_risk_ratio: _1m1w1y24hPattern7,
|
||||||
pub peak_regret: BlockCumulativeSumPattern,
|
pub peak_regret: BlockCumulativeSumPattern,
|
||||||
pub investor: PricePattern,
|
pub capitalized: PricePattern,
|
||||||
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
pub profit_to_loss_ratio: _1m1w1y24hPattern<StoredF64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7743,7 +7774,7 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized {
|
|||||||
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_gross_pnl".to_string()),
|
gross_pnl: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_gross_pnl".to_string()),
|
||||||
sell_side_risk_ratio: _1m1w1y24hPattern7::new(client.clone(), "lth_sell_side_risk_ratio".to_string()),
|
sell_side_risk_ratio: _1m1w1y24hPattern7::new(client.clone(), "lth_sell_side_risk_ratio".to_string()),
|
||||||
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_peak_regret".to_string()),
|
peak_regret: BlockCumulativeSumPattern::new(client.clone(), "lth_realized_peak_regret".to_string()),
|
||||||
investor: PricePattern::new(client.clone(), "lth_investor_price".to_string()),
|
capitalized: PricePattern::new(client.clone(), "lth_capitalized_price".to_string()),
|
||||||
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "lth_realized_profit_to_loss_ratio".to_string()),
|
profit_to_loss_ratio: _1m1w1y24hPattern::new(client.clone(), "lth_realized_profit_to_loss_ratio".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8676,7 +8707,7 @@ pub struct BrkClient {
|
|||||||
|
|
||||||
impl BrkClient {
|
impl BrkClient {
|
||||||
/// Client version.
|
/// Client version.
|
||||||
pub const VERSION: &'static str = "v0.3.0-beta.1";
|
pub const VERSION: &'static str = "v0.3.0-beta.2";
|
||||||
|
|
||||||
/// Create a new client with the given base URL.
|
/// Create a new client with the given base URL.
|
||||||
pub fn new(base_url: impl Into<String>) -> Self {
|
pub fn new(base_url: impl Into<String>) -> Self {
|
||||||
|
|||||||
@@ -37,22 +37,25 @@ mod count;
|
|||||||
mod supply;
|
mod supply;
|
||||||
|
|
||||||
pub use count::{AddrTypeToExposedAddrCount, ExposedAddrCountsVecs};
|
pub use count::{AddrTypeToExposedAddrCount, ExposedAddrCountsVecs};
|
||||||
pub use supply::{AddrTypeToExposedAddrSupply, ExposedAddrSupplyVecs};
|
pub use supply::{AddrTypeToExposedSupply, ExposedAddrSupplyVecs, ExposedSupplyShareVecs};
|
||||||
|
|
||||||
|
use brk_cohort::ByAddrType;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Indexes, Version};
|
use brk_types::{Height, Indexes, Sats, Version};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{indexes, prices};
|
use crate::{indexes, internal::RatioSatsBp16, prices};
|
||||||
|
|
||||||
/// Top-level container for all exposed address tracking: counts (funded +
|
/// Top-level container for all exposed address tracking: counts (funded +
|
||||||
/// total) plus the funded supply.
|
/// total), the funded supply, and share of supply.
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct ExposedAddrVecs<M: StorageMode = Rw> {
|
pub struct ExposedAddrVecs<M: StorageMode = Rw> {
|
||||||
pub count: ExposedAddrCountsVecs<M>,
|
pub count: ExposedAddrCountsVecs<M>,
|
||||||
pub supply: ExposedAddrSupplyVecs<M>,
|
pub supply: ExposedAddrSupplyVecs<M>,
|
||||||
|
#[traversable(wrap = "supply", rename = "share")]
|
||||||
|
pub supply_share: ExposedSupplyShareVecs<M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExposedAddrVecs {
|
impl ExposedAddrVecs {
|
||||||
@@ -64,6 +67,7 @@ impl ExposedAddrVecs {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
count: ExposedAddrCountsVecs::forced_import(db, version, indexes)?,
|
count: ExposedAddrCountsVecs::forced_import(db, version, indexes)?,
|
||||||
supply: ExposedAddrSupplyVecs::forced_import(db, version, indexes)?,
|
supply: ExposedAddrSupplyVecs::forced_import(db, version, indexes)?,
|
||||||
|
supply_share: ExposedSupplyShareVecs::forced_import(db, version, indexes)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +88,7 @@ impl ExposedAddrVecs {
|
|||||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||||
self.count.reset_height()?;
|
self.count.reset_height()?;
|
||||||
self.supply.reset_height()?;
|
self.supply.reset_height()?;
|
||||||
|
self.supply_share.reset_height()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,11 +96,39 @@ impl ExposedAddrVecs {
|
|||||||
&mut self,
|
&mut self,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
|
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||||
|
type_supply_sats: &ByAddrType<&impl ReadableVec<Height, Sats>>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.count.compute_rest(starting_indexes, exit)?;
|
self.count.compute_rest(starting_indexes, exit)?;
|
||||||
self.supply
|
self.supply
|
||||||
.compute_rest(starting_indexes.height, prices, exit)?;
|
.compute_rest(starting_indexes.height, prices, exit)?;
|
||||||
|
|
||||||
|
let max_from = starting_indexes.height;
|
||||||
|
|
||||||
|
self.supply_share
|
||||||
|
.all
|
||||||
|
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||||
|
max_from,
|
||||||
|
&self.supply.all.sats.height,
|
||||||
|
all_supply_sats,
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for ((_, share), ((_, exposed), (_, denom))) in self
|
||||||
|
.supply_share
|
||||||
|
.by_addr_type
|
||||||
|
.iter_mut()
|
||||||
|
.zip(self.supply.by_addr_type.iter().zip(type_supply_sats.iter()))
|
||||||
|
{
|
||||||
|
share.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||||
|
max_from,
|
||||||
|
&exposed.sats.height,
|
||||||
|
*denom,
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
//! aggregated `all`. See the parent [`super`] module for the definition of
|
//! aggregated `all`. See the parent [`super`] module for the definition of
|
||||||
//! "exposed" and how it varies by address type.
|
//! "exposed" and how it varies by address type.
|
||||||
|
|
||||||
|
mod share;
|
||||||
mod state;
|
mod state;
|
||||||
mod vecs;
|
mod vecs;
|
||||||
|
|
||||||
pub use state::AddrTypeToExposedAddrSupply;
|
pub use share::ExposedSupplyShareVecs;
|
||||||
|
pub use state::AddrTypeToExposedSupply;
|
||||||
pub use vecs::ExposedAddrSupplyVecs;
|
pub use vecs::ExposedAddrSupplyVecs;
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
use brk_error::Result;
|
||||||
|
use brk_traversable::Traversable;
|
||||||
|
use brk_types::{BasisPoints16, Version};
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
use vecdb::{Database, Rw, StorageMode};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
indexes,
|
||||||
|
internal::{PercentPerBlock, WithAddrTypes},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Share of exposed supply relative to total supply.
|
||||||
|
///
|
||||||
|
/// - `all`: exposed_supply / circulating_supply
|
||||||
|
/// - Per-type: type's exposed_supply / type's total supply
|
||||||
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
|
pub struct ExposedSupplyShareVecs<M: StorageMode = Rw>(
|
||||||
|
#[traversable(flatten)] pub WithAddrTypes<PercentPerBlock<BasisPoints16, M>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl ExposedSupplyShareVecs {
|
||||||
|
pub(crate) fn forced_import(
|
||||||
|
db: &Database,
|
||||||
|
version: Version,
|
||||||
|
indexes: &indexes::Vecs,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self(
|
||||||
|
WithAddrTypes::<PercentPerBlock<BasisPoints16>>::forced_import(
|
||||||
|
db,
|
||||||
|
"exposed_supply_share",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use brk_cohort::ByAddrType;
|
use brk_cohort::ByAddrType;
|
||||||
use brk_types::Height;
|
use brk_types::{Height, Sats};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use vecdb::ReadableVec;
|
use vecdb::ReadableVec;
|
||||||
|
|
||||||
@@ -10,22 +10,21 @@ use super::vecs::ExposedAddrSupplyVecs;
|
|||||||
/// Runtime running counter for the total balance (sats) held by funded
|
/// Runtime running counter for the total balance (sats) held by funded
|
||||||
/// exposed addresses, per address type.
|
/// exposed addresses, per address type.
|
||||||
#[derive(Debug, Default, Deref, DerefMut)]
|
#[derive(Debug, Default, Deref, DerefMut)]
|
||||||
pub struct AddrTypeToExposedAddrSupply(ByAddrType<u64>);
|
pub struct AddrTypeToExposedSupply(ByAddrType<Sats>);
|
||||||
|
|
||||||
impl AddrTypeToExposedAddrSupply {
|
impl AddrTypeToExposedSupply {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn sum(&self) -> u64 {
|
pub(crate) fn sum(&self) -> Sats {
|
||||||
self.0.values().sum()
|
self.0.values().copied().sum()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(&ExposedAddrSupplyVecs, Height)> for AddrTypeToExposedAddrSupply {
|
impl From<(&ExposedAddrSupplyVecs, Height)> for AddrTypeToExposedSupply {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from((vecs, starting_height): (&ExposedAddrSupplyVecs, Height)) -> Self {
|
fn from((vecs, starting_height): (&ExposedAddrSupplyVecs, Height)) -> Self {
|
||||||
if let Some(prev_height) = starting_height.decremented() {
|
if let Some(prev_height) = starting_height.decremented() {
|
||||||
let read = |v: &AmountPerBlock| -> u64 {
|
let read =
|
||||||
u64::from(v.sats.height.collect_one(prev_height).unwrap())
|
|v: &AmountPerBlock| -> Sats { v.sats.height.collect_one(prev_height).unwrap() };
|
||||||
};
|
|
||||||
Self(ByAddrType {
|
Self(ByAddrType {
|
||||||
p2pk65: read(&vecs.by_addr_type.p2pk65),
|
p2pk65: read(&vecs.by_addr_type.p2pk65),
|
||||||
p2pk33: read(&vecs.by_addr_type.p2pk33),
|
p2pk33: read(&vecs.by_addr_type.p2pk33),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl ExposedAddrSupplyVecs {
|
|||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
Ok(Self(WithAddrTypes::<AmountPerBlock>::forced_import(
|
Ok(Self(WithAddrTypes::<AmountPerBlock>::forced_import(
|
||||||
db,
|
db,
|
||||||
"exposed_addr_supply",
|
"exposed_supply",
|
||||||
version,
|
version,
|
||||||
indexes,
|
indexes,
|
||||||
)?))
|
)?))
|
||||||
|
|||||||
@@ -13,13 +13,9 @@ pub use activity::{AddrActivityVecs, AddrTypeToActivityCounts};
|
|||||||
pub use addr_count::{AddrCountsVecs, AddrTypeToAddrCount};
|
pub use addr_count::{AddrCountsVecs, AddrTypeToAddrCount};
|
||||||
pub use data::AddrsDataVecs;
|
pub use data::AddrsDataVecs;
|
||||||
pub use delta::DeltaVecs;
|
pub use delta::DeltaVecs;
|
||||||
pub use exposed::{
|
pub use exposed::{AddrTypeToExposedAddrCount, AddrTypeToExposedSupply, ExposedAddrVecs,};
|
||||||
AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply, ExposedAddrVecs,
|
|
||||||
};
|
|
||||||
pub use indexes::AnyAddrIndexesVecs;
|
pub use indexes::AnyAddrIndexesVecs;
|
||||||
pub use new_addr_count::NewAddrCountVecs;
|
pub use new_addr_count::NewAddrCountVecs;
|
||||||
pub use reused::{
|
pub use reused::{AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, ReusedAddrVecs};
|
||||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, ReusedAddrVecs,
|
|
||||||
};
|
|
||||||
pub use total_addr_count::TotalAddrCountVecs;
|
pub use total_addr_count::TotalAddrCountVecs;
|
||||||
pub use type_map::{AddrTypeToTypeIndexMap, AddrTypeToVec, HeightToAddrTypeToVec};
|
pub use type_map::{AddrTypeToTypeIndexMap, AddrTypeToVec, HeightToAddrTypeToVec};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
|
|||||||
|
|
||||||
use crate::distribution::{
|
use crate::distribution::{
|
||||||
addr::{
|
addr::{
|
||||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply,
|
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedSupply,
|
||||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, AddrTypeToVec,
|
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, AddrTypeToVec,
|
||||||
},
|
},
|
||||||
cohorts::AddrCohorts,
|
cohorts::AddrCohorts,
|
||||||
@@ -34,7 +34,7 @@ pub(crate) fn process_received(
|
|||||||
active_reused_addr_count: &mut AddrTypeToReusedAddrEventCount,
|
active_reused_addr_count: &mut AddrTypeToReusedAddrEventCount,
|
||||||
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||||
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||||
exposed_addr_supply: &mut AddrTypeToExposedAddrSupply,
|
exposed_supply: &mut AddrTypeToExposedSupply,
|
||||||
) {
|
) {
|
||||||
let max_type_len = received_data
|
let max_type_len = received_data
|
||||||
.iter()
|
.iter()
|
||||||
@@ -56,11 +56,10 @@ pub(crate) fn process_received(
|
|||||||
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_total_reused_count = total_reused_addr_count.get_mut(output_type).unwrap();
|
let type_total_reused_count = total_reused_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_output_to_reused_count = output_to_reused_addr_count.get_mut(output_type).unwrap();
|
let type_output_to_reused_count = output_to_reused_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_active_reused_count =
|
let type_active_reused_count = active_reused_addr_count.get_mut(output_type).unwrap();
|
||||||
active_reused_addr_count.get_mut(output_type).unwrap();
|
|
||||||
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_exposed_supply = exposed_addr_supply.get_mut(output_type).unwrap();
|
let type_exposed_supply = exposed_supply.get_mut(output_type).unwrap();
|
||||||
|
|
||||||
// Aggregate receives by address - each address processed exactly once
|
// Aggregate receives by address - each address processed exactly once
|
||||||
for (type_index, value) in vec {
|
for (type_index, value) in vec {
|
||||||
@@ -206,15 +205,12 @@ pub(crate) fn process_received(
|
|||||||
if !was_funded && was_pubkey_exposed {
|
if !was_funded && was_pubkey_exposed {
|
||||||
*type_exposed_count += 1;
|
*type_exposed_count += 1;
|
||||||
}
|
}
|
||||||
if output_type.pubkey_exposed_at_funding()
|
if output_type.pubkey_exposed_at_funding() && matches!(status, TrackingStatus::New) {
|
||||||
&& matches!(status, TrackingStatus::New)
|
|
||||||
{
|
|
||||||
*type_total_exposed_count += 1;
|
*type_total_exposed_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update exposed supply via post-receive contribution delta.
|
// Update exposed supply via post-receive contribution delta.
|
||||||
let exposed_contribution_after =
|
let exposed_contribution_after = addr_data.exposed_supply_contribution(output_type);
|
||||||
addr_data.exposed_supply_contribution(output_type);
|
|
||||||
// Receives can only add to balance and membership, so the delta
|
// Receives can only add to balance and membership, so the delta
|
||||||
// is always non-negative.
|
// is always non-negative.
|
||||||
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use vecdb::VecIndex;
|
|||||||
|
|
||||||
use crate::distribution::{
|
use crate::distribution::{
|
||||||
addr::{
|
addr::{
|
||||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply,
|
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedSupply,
|
||||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, HeightToAddrTypeToVec,
|
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, HeightToAddrTypeToVec,
|
||||||
},
|
},
|
||||||
cohorts::AddrCohorts,
|
cohorts::AddrCohorts,
|
||||||
@@ -43,7 +43,7 @@ pub(crate) fn process_sent(
|
|||||||
active_reused_addr_count: &mut AddrTypeToReusedAddrEventCount,
|
active_reused_addr_count: &mut AddrTypeToReusedAddrEventCount,
|
||||||
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||||
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||||
exposed_addr_supply: &mut AddrTypeToExposedAddrSupply,
|
exposed_supply: &mut AddrTypeToExposedSupply,
|
||||||
received_addrs: &ByAddrType<FxHashSet<TypeIndex>>,
|
received_addrs: &ByAddrType<FxHashSet<TypeIndex>>,
|
||||||
height_to_price: &[Cents],
|
height_to_price: &[Cents],
|
||||||
height_to_timestamp: &[Timestamp],
|
height_to_timestamp: &[Timestamp],
|
||||||
@@ -69,11 +69,10 @@ pub(crate) fn process_sent(
|
|||||||
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_input_from_reused_count =
|
let type_input_from_reused_count =
|
||||||
input_from_reused_addr_count.get_mut(output_type).unwrap();
|
input_from_reused_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_active_reused_count =
|
let type_active_reused_count = active_reused_addr_count.get_mut(output_type).unwrap();
|
||||||
active_reused_addr_count.get_mut(output_type).unwrap();
|
|
||||||
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
||||||
let type_exposed_supply = exposed_addr_supply.get_mut(output_type).unwrap();
|
let type_exposed_supply = exposed_supply.get_mut(output_type).unwrap();
|
||||||
let type_received = received_addrs.get(output_type);
|
let type_received = received_addrs.get(output_type);
|
||||||
let type_seen = seen_senders.get_mut_unwrap(output_type);
|
let type_seen = seen_senders.get_mut_unwrap(output_type);
|
||||||
|
|
||||||
@@ -96,8 +95,7 @@ pub(crate) fn process_sent(
|
|||||||
if type_seen.insert(type_index) {
|
if type_seen.insert(type_index) {
|
||||||
type_activity.sending += 1;
|
type_activity.sending += 1;
|
||||||
|
|
||||||
let also_received =
|
let also_received = type_received.is_some_and(|s| s.contains(&type_index));
|
||||||
type_received.is_some_and(|s| s.contains(&type_index));
|
|
||||||
// Track "bidirectional": addresses that sent AND
|
// Track "bidirectional": addresses that sent AND
|
||||||
// received this block.
|
// received this block.
|
||||||
if also_received {
|
if also_received {
|
||||||
@@ -136,12 +134,13 @@ pub(crate) fn process_sent(
|
|||||||
// addr_data.spent_txo_count is now incremented by 1.
|
// addr_data.spent_txo_count is now incremented by 1.
|
||||||
|
|
||||||
// Update exposed supply via post-spend contribution delta.
|
// Update exposed supply via post-spend contribution delta.
|
||||||
let exposed_contribution_after =
|
let exposed_contribution_after = addr_data.exposed_supply_contribution(output_type);
|
||||||
addr_data.exposed_supply_contribution(output_type);
|
|
||||||
if exposed_contribution_after >= exposed_contribution_before {
|
if exposed_contribution_after >= exposed_contribution_before {
|
||||||
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
*type_exposed_supply +=
|
||||||
|
exposed_contribution_after - exposed_contribution_before;
|
||||||
} else {
|
} else {
|
||||||
*type_exposed_supply -= exposed_contribution_before - exposed_contribution_after;
|
*type_exposed_supply -=
|
||||||
|
exposed_contribution_before - exposed_contribution_after;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update exposed counts on first-ever pubkey exposure.
|
// Update exposed counts on first-ever pubkey exposure.
|
||||||
|
|||||||
@@ -830,26 +830,26 @@ impl UTXOCohorts<Rw> {
|
|||||||
let mut sth_acc = RealizedFullAccum::default();
|
let mut sth_acc = RealizedFullAccum::default();
|
||||||
let mut lth_acc = RealizedFullAccum::default();
|
let mut lth_acc = RealizedFullAccum::default();
|
||||||
|
|
||||||
let mut all_icap = (0u128, 0u128);
|
let mut all_ccap = (0u128, 0u128);
|
||||||
let mut sth_icap = (0u128, 0u128);
|
let mut sth_ccap = (0u128, 0u128);
|
||||||
let mut lth_icap = (0u128, 0u128);
|
let mut lth_ccap = (0u128, 0u128);
|
||||||
|
|
||||||
for ar in age_range.iter_mut() {
|
for ar in age_range.iter_mut() {
|
||||||
if let Some(state) = ar.state.as_mut() {
|
if let Some(state) = ar.state.as_mut() {
|
||||||
all_acc.add(&state.realized);
|
all_acc.add(&state.realized);
|
||||||
|
|
||||||
let u = state.compute_unrealized_state(height_price);
|
let u = state.compute_unrealized_state(height_price);
|
||||||
all_icap.0 += u.investor_cap_in_profit_raw;
|
all_ccap.0 += u.capitalized_cap_in_profit_raw;
|
||||||
all_icap.1 += u.investor_cap_in_loss_raw;
|
all_ccap.1 += u.capitalized_cap_in_loss_raw;
|
||||||
|
|
||||||
if sth_filter.includes(&ar.metrics.filter) {
|
if sth_filter.includes(&ar.metrics.filter) {
|
||||||
sth_acc.add(&state.realized);
|
sth_acc.add(&state.realized);
|
||||||
sth_icap.0 += u.investor_cap_in_profit_raw;
|
sth_ccap.0 += u.capitalized_cap_in_profit_raw;
|
||||||
sth_icap.1 += u.investor_cap_in_loss_raw;
|
sth_ccap.1 += u.capitalized_cap_in_loss_raw;
|
||||||
} else {
|
} else {
|
||||||
lth_acc.add(&state.realized);
|
lth_acc.add(&state.realized);
|
||||||
lth_icap.0 += u.investor_cap_in_profit_raw;
|
lth_ccap.0 += u.capitalized_cap_in_profit_raw;
|
||||||
lth_icap.1 += u.investor_cap_in_loss_raw;
|
lth_ccap.1 += u.capitalized_cap_in_loss_raw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -860,28 +860,28 @@ impl UTXOCohorts<Rw> {
|
|||||||
|
|
||||||
all.metrics
|
all.metrics
|
||||||
.unrealized
|
.unrealized
|
||||||
.investor_cap_in_profit_raw
|
.capitalized_cap_in_profit_raw
|
||||||
.push(CentsSquaredSats::new(all_icap.0));
|
.push(CentsSquaredSats::new(all_ccap.0));
|
||||||
all.metrics
|
all.metrics
|
||||||
.unrealized
|
.unrealized
|
||||||
.investor_cap_in_loss_raw
|
.capitalized_cap_in_loss_raw
|
||||||
.push(CentsSquaredSats::new(all_icap.1));
|
.push(CentsSquaredSats::new(all_ccap.1));
|
||||||
sth.metrics
|
sth.metrics
|
||||||
.unrealized
|
.unrealized
|
||||||
.investor_cap_in_profit_raw
|
.capitalized_cap_in_profit_raw
|
||||||
.push(CentsSquaredSats::new(sth_icap.0));
|
.push(CentsSquaredSats::new(sth_ccap.0));
|
||||||
sth.metrics
|
sth.metrics
|
||||||
.unrealized
|
.unrealized
|
||||||
.investor_cap_in_loss_raw
|
.capitalized_cap_in_loss_raw
|
||||||
.push(CentsSquaredSats::new(sth_icap.1));
|
.push(CentsSquaredSats::new(sth_ccap.1));
|
||||||
lth.metrics
|
lth.metrics
|
||||||
.unrealized
|
.unrealized
|
||||||
.investor_cap_in_profit_raw
|
.capitalized_cap_in_profit_raw
|
||||||
.push(CentsSquaredSats::new(lth_icap.0));
|
.push(CentsSquaredSats::new(lth_ccap.0));
|
||||||
lth.metrics
|
lth.metrics
|
||||||
.unrealized
|
.unrealized
|
||||||
.investor_cap_in_loss_raw
|
.capitalized_cap_in_loss_raw
|
||||||
.push(CentsSquaredSats::new(lth_icap.1));
|
.push(CentsSquaredSats::new(lth_ccap.1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ use crate::{
|
|||||||
distribution::{
|
distribution::{
|
||||||
addr::{
|
addr::{
|
||||||
AddrTypeToActivityCounts, AddrTypeToAddrCount, AddrTypeToExposedAddrCount,
|
AddrTypeToActivityCounts, AddrTypeToAddrCount, AddrTypeToExposedAddrCount,
|
||||||
AddrTypeToExposedAddrSupply, AddrTypeToReusedAddrCount,
|
AddrTypeToExposedSupply, AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount,
|
||||||
AddrTypeToReusedAddrEventCount,
|
|
||||||
},
|
},
|
||||||
block::{
|
block::{
|
||||||
AddrCache, InputsResult, process_inputs, process_outputs, process_received,
|
AddrCache, InputsResult, process_inputs, process_outputs, process_received,
|
||||||
@@ -203,7 +202,7 @@ pub(crate) fn process_blocks(
|
|||||||
mut total_reused_addr_counts,
|
mut total_reused_addr_counts,
|
||||||
mut exposed_addr_counts,
|
mut exposed_addr_counts,
|
||||||
mut total_exposed_addr_counts,
|
mut total_exposed_addr_counts,
|
||||||
mut exposed_addr_supply,
|
mut exposed_supply,
|
||||||
) = if starting_height > Height::ZERO {
|
) = if starting_height > Height::ZERO {
|
||||||
(
|
(
|
||||||
AddrTypeToAddrCount::from((&vecs.addrs.funded.by_addr_type, starting_height)),
|
AddrTypeToAddrCount::from((&vecs.addrs.funded.by_addr_type, starting_height)),
|
||||||
@@ -212,7 +211,7 @@ pub(crate) fn process_blocks(
|
|||||||
AddrTypeToReusedAddrCount::from((&vecs.addrs.reused.count.total, starting_height)),
|
AddrTypeToReusedAddrCount::from((&vecs.addrs.reused.count.total, starting_height)),
|
||||||
AddrTypeToExposedAddrCount::from((&vecs.addrs.exposed.count.funded, starting_height)),
|
AddrTypeToExposedAddrCount::from((&vecs.addrs.exposed.count.funded, starting_height)),
|
||||||
AddrTypeToExposedAddrCount::from((&vecs.addrs.exposed.count.total, starting_height)),
|
AddrTypeToExposedAddrCount::from((&vecs.addrs.exposed.count.total, starting_height)),
|
||||||
AddrTypeToExposedAddrSupply::from((&vecs.addrs.exposed.supply, starting_height)),
|
AddrTypeToExposedSupply::from((&vecs.addrs.exposed.supply, starting_height)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -222,7 +221,7 @@ pub(crate) fn process_blocks(
|
|||||||
AddrTypeToReusedAddrCount::default(),
|
AddrTypeToReusedAddrCount::default(),
|
||||||
AddrTypeToExposedAddrCount::default(),
|
AddrTypeToExposedAddrCount::default(),
|
||||||
AddrTypeToExposedAddrCount::default(),
|
AddrTypeToExposedAddrCount::default(),
|
||||||
AddrTypeToExposedAddrSupply::default(),
|
AddrTypeToExposedSupply::default(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
debug!("addr_counts recovered");
|
debug!("addr_counts recovered");
|
||||||
@@ -489,7 +488,7 @@ pub(crate) fn process_blocks(
|
|||||||
&mut active_reused_addr_counts,
|
&mut active_reused_addr_counts,
|
||||||
&mut exposed_addr_counts,
|
&mut exposed_addr_counts,
|
||||||
&mut total_exposed_addr_counts,
|
&mut total_exposed_addr_counts,
|
||||||
&mut exposed_addr_supply,
|
&mut exposed_supply,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Process sent inputs (addresses sending funds)
|
// Process sent inputs (addresses sending funds)
|
||||||
@@ -507,7 +506,7 @@ pub(crate) fn process_blocks(
|
|||||||
&mut active_reused_addr_counts,
|
&mut active_reused_addr_counts,
|
||||||
&mut exposed_addr_counts,
|
&mut exposed_addr_counts,
|
||||||
&mut total_exposed_addr_counts,
|
&mut total_exposed_addr_counts,
|
||||||
&mut exposed_addr_supply,
|
&mut exposed_supply,
|
||||||
&received_addrs,
|
&received_addrs,
|
||||||
height_to_price_vec,
|
height_to_price_vec,
|
||||||
height_to_timestamp_vec,
|
height_to_timestamp_vec,
|
||||||
@@ -539,8 +538,8 @@ pub(crate) fn process_blocks(
|
|||||||
total_reused_addr_counts.values().copied(),
|
total_reused_addr_counts.values().copied(),
|
||||||
);
|
);
|
||||||
let activity_totals = activity_counts.totals();
|
let activity_totals = activity_counts.totals();
|
||||||
let active_addr_count = activity_totals.sending + activity_totals.receiving
|
let active_addr_count =
|
||||||
- activity_totals.bidirectional;
|
activity_totals.sending + activity_totals.receiving - activity_totals.bidirectional;
|
||||||
let active_reused = u32::try_from(active_reused_addr_counts.sum()).unwrap();
|
let active_reused = u32::try_from(active_reused_addr_counts.sum()).unwrap();
|
||||||
vecs.addrs.reused.events.push_height(
|
vecs.addrs.reused.events.push_height(
|
||||||
&output_to_reused_addr_counts,
|
&output_to_reused_addr_counts,
|
||||||
@@ -556,10 +555,10 @@ pub(crate) fn process_blocks(
|
|||||||
total_exposed_addr_counts.sum(),
|
total_exposed_addr_counts.sum(),
|
||||||
total_exposed_addr_counts.values().copied(),
|
total_exposed_addr_counts.values().copied(),
|
||||||
);
|
);
|
||||||
vecs.addrs.exposed.supply.push_height(
|
vecs.addrs
|
||||||
exposed_addr_supply.sum(),
|
.exposed
|
||||||
exposed_addr_supply.values().copied(),
|
.supply
|
||||||
);
|
.push_height(exposed_supply.sum(), exposed_supply.values().copied());
|
||||||
|
|
||||||
let is_last_of_day = is_last_of_day[offset];
|
let is_last_of_day = is_last_of_day[offset];
|
||||||
let date_opt = is_last_of_day.then(|| Date::from(timestamp));
|
let date_opt = is_last_of_day.then(|| Date::from(timestamp));
|
||||||
|
|||||||
@@ -144,8 +144,8 @@ impl AllCohortMetrics {
|
|||||||
&self.unrealized.invested_capital.in_loss.cents.height,
|
&self.unrealized.invested_capital.in_loss.cents.height,
|
||||||
&self.supply.in_profit.sats.height,
|
&self.supply.in_profit.sats.height,
|
||||||
&self.supply.in_loss.sats.height,
|
&self.supply.in_loss.sats.height,
|
||||||
&self.unrealized.investor_cap_in_profit_raw,
|
&self.unrealized.capitalized_cap_in_profit_raw,
|
||||||
&self.unrealized.investor_cap_in_loss_raw,
|
&self.unrealized.capitalized_cap_in_loss_raw,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ impl ExtendedCohortMetrics {
|
|||||||
&self.unrealized.invested_capital.in_loss.cents.height,
|
&self.unrealized.invested_capital.in_loss.cents.height,
|
||||||
&self.supply.in_profit.sats.height,
|
&self.supply.in_profit.sats.height,
|
||||||
&self.supply.in_loss.sats.height,
|
&self.supply.in_loss.sats.height,
|
||||||
&self.unrealized.investor_cap_in_profit_raw,
|
&self.unrealized.capitalized_cap_in_profit_raw,
|
||||||
&self.unrealized.investor_cap_in_loss_raw,
|
&self.unrealized.capitalized_cap_in_loss_raw,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ impl CostBasis {
|
|||||||
invested_cap_in_loss: &impl ReadableVec<Height, Cents>,
|
invested_cap_in_loss: &impl ReadableVec<Height, Cents>,
|
||||||
supply_in_profit_sats: &impl ReadableVec<Height, Sats>,
|
supply_in_profit_sats: &impl ReadableVec<Height, Sats>,
|
||||||
supply_in_loss_sats: &impl ReadableVec<Height, Sats>,
|
supply_in_loss_sats: &impl ReadableVec<Height, Sats>,
|
||||||
investor_cap_in_profit_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
capitalized_cap_in_profit_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
||||||
investor_cap_in_loss_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
capitalized_cap_in_loss_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.in_profit.per_coin.cents.height.compute_transform3(
|
self.in_profit.per_coin.cents.height.compute_transform3(
|
||||||
@@ -193,29 +193,29 @@ impl CostBasis {
|
|||||||
)?;
|
)?;
|
||||||
self.in_profit.per_dollar.cents.height.compute_transform3(
|
self.in_profit.per_dollar.cents.height.compute_transform3(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
investor_cap_in_profit_raw,
|
capitalized_cap_in_profit_raw,
|
||||||
invested_cap_in_profit,
|
invested_cap_in_profit,
|
||||||
spot,
|
spot,
|
||||||
|(h, investor_cap, invested_cents, spot, ..)| {
|
|(h, capitalized_cap, invested_cents, spot, ..)| {
|
||||||
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
|
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||||
if invested_raw == 0 {
|
if invested_raw == 0 {
|
||||||
return (h, spot);
|
return (h, spot);
|
||||||
}
|
}
|
||||||
(h, Cents::new((investor_cap.inner() / invested_raw) as u64))
|
(h, Cents::new((capitalized_cap.inner() / invested_raw) as u64))
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.in_loss.per_dollar.cents.height.compute_transform3(
|
self.in_loss.per_dollar.cents.height.compute_transform3(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
investor_cap_in_loss_raw,
|
capitalized_cap_in_loss_raw,
|
||||||
invested_cap_in_loss,
|
invested_cap_in_loss,
|
||||||
spot,
|
spot,
|
||||||
|(h, investor_cap, invested_cents, spot, ..)| {
|
|(h, capitalized_cap, invested_cents, spot, ..)| {
|
||||||
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
|
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||||
if invested_raw == 0 {
|
if invested_raw == 0 {
|
||||||
return (h, spot);
|
return (h, spot);
|
||||||
}
|
}
|
||||||
(h, Cents::new((investor_cap.inner() / invested_raw) as u64))
|
(h, Cents::new((capitalized_cap.inner() / invested_raw) as u64))
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ pub struct RealizedPeakRegret<M: StorageMode = Rw> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct RealizedInvestor<M: StorageMode = Rw> {
|
pub struct RealizedCapitalized<M: StorageMode = Rw> {
|
||||||
pub price: PriceWithRatioExtendedPerBlock<M>,
|
pub price: PriceWithRatioExtendedPerBlock<M>,
|
||||||
#[traversable(hidden)]
|
#[traversable(hidden)]
|
||||||
pub cap_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
pub cap_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||||
@@ -63,7 +63,7 @@ pub struct RealizedFull<M: StorageMode = Rw> {
|
|||||||
pub net_pnl: RealizedNetPnl<M>,
|
pub net_pnl: RealizedNetPnl<M>,
|
||||||
pub sopr: RealizedSopr<M>,
|
pub sopr: RealizedSopr<M>,
|
||||||
pub peak_regret: RealizedPeakRegret<M>,
|
pub peak_regret: RealizedPeakRegret<M>,
|
||||||
pub investor: RealizedInvestor<M>,
|
pub capitalized: RealizedCapitalized<M>,
|
||||||
|
|
||||||
pub profit_to_loss_ratio: RollingWindows<StoredF64, M>,
|
pub profit_to_loss_ratio: RollingWindows<StoredF64, M>,
|
||||||
|
|
||||||
@@ -108,10 +108,10 @@ impl RealizedFull {
|
|||||||
value: cfg.import("realized_peak_regret", Version::new(3))?,
|
value: cfg.import("realized_peak_regret", Version::new(3))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Investor
|
// Capitalized
|
||||||
let investor = RealizedInvestor {
|
let capitalized = RealizedCapitalized {
|
||||||
price: cfg.import("investor_price", v0)?,
|
price: cfg.import("capitalized_price", v0)?,
|
||||||
cap_raw: cfg.import("investor_cap_raw", v0)?,
|
cap_raw: cfg.import("capitalized_cap_raw", v0)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Price ratio stats
|
// Price ratio stats
|
||||||
@@ -125,7 +125,7 @@ impl RealizedFull {
|
|||||||
net_pnl,
|
net_pnl,
|
||||||
sopr,
|
sopr,
|
||||||
peak_regret,
|
peak_regret,
|
||||||
investor,
|
capitalized,
|
||||||
profit_to_loss_ratio: cfg.import("realized_profit_to_loss_ratio", v1)?,
|
profit_to_loss_ratio: cfg.import("realized_profit_to_loss_ratio", v1)?,
|
||||||
cap_raw: cfg.import("cap_raw", v0)?,
|
cap_raw: cfg.import("cap_raw", v0)?,
|
||||||
cap_to_own_mcap: cfg.import("realized_cap_to_own_mcap", v1)?,
|
cap_to_own_mcap: cfg.import("realized_cap_to_own_mcap", v1)?,
|
||||||
@@ -151,13 +151,13 @@ impl RealizedFull {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||||
self.investor
|
self.capitalized
|
||||||
.price
|
.price
|
||||||
.cents
|
.cents
|
||||||
.height
|
.height
|
||||||
.len()
|
.len()
|
||||||
.min(self.cap_raw.len())
|
.min(self.cap_raw.len())
|
||||||
.min(self.investor.cap_raw.len())
|
.min(self.capitalized.cap_raw.len())
|
||||||
.min(self.peak_regret.value.block.cents.len())
|
.min(self.peak_regret.value.block.cents.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,15 +167,15 @@ impl RealizedFull {
|
|||||||
state: &CohortState<RealizedState, CostBasisData<WithCapital>>,
|
state: &CohortState<RealizedState, CostBasisData<WithCapital>>,
|
||||||
) {
|
) {
|
||||||
self.core.push_state(state);
|
self.core.push_state(state);
|
||||||
self.investor
|
self.capitalized
|
||||||
.price
|
.price
|
||||||
.cents
|
.cents
|
||||||
.height
|
.height
|
||||||
.push(state.realized.investor_price());
|
.push(state.realized.capitalized_price());
|
||||||
self.cap_raw.push(state.realized.cap_raw());
|
self.cap_raw.push(state.realized.cap_raw());
|
||||||
self.investor
|
self.capitalized
|
||||||
.cap_raw
|
.cap_raw
|
||||||
.push(state.realized.investor_cap_raw());
|
.push(state.realized.capitalized_cap_raw());
|
||||||
self.peak_regret
|
self.peak_regret
|
||||||
.value
|
.value
|
||||||
.block
|
.block
|
||||||
@@ -185,9 +185,9 @@ impl RealizedFull {
|
|||||||
|
|
||||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||||
let mut vecs = self.core.collect_vecs_mut();
|
let mut vecs = self.core.collect_vecs_mut();
|
||||||
vecs.push(&mut self.investor.price.cents.height);
|
vecs.push(&mut self.capitalized.price.cents.height);
|
||||||
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
|
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
|
||||||
vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec);
|
vecs.push(&mut self.capitalized.cap_raw as &mut dyn AnyStoredVec);
|
||||||
vecs.push(&mut self.peak_regret.value.block.cents);
|
vecs.push(&mut self.peak_regret.value.block.cents);
|
||||||
vecs
|
vecs
|
||||||
}
|
}
|
||||||
@@ -207,17 +207,17 @@ impl RealizedFull {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_accum(&mut self, accum: &RealizedFullAccum) {
|
pub(crate) fn push_accum(&mut self, accum: &RealizedFullAccum) {
|
||||||
self.cap_raw.push(accum.cap_raw);
|
self.cap_raw.push(accum.cap_raw);
|
||||||
self.investor.cap_raw.push(accum.investor_cap_raw);
|
self.capitalized.cap_raw.push(accum.capitalized_cap_raw);
|
||||||
|
|
||||||
let investor_price = {
|
let capitalized_price = {
|
||||||
let cap = accum.cap_raw.as_u128();
|
let cap = accum.cap_raw.as_u128();
|
||||||
if cap == 0 {
|
if cap == 0 {
|
||||||
Cents::ZERO
|
Cents::ZERO
|
||||||
} else {
|
} else {
|
||||||
Cents::new((accum.investor_cap_raw / cap) as u64)
|
Cents::new((accum.capitalized_cap_raw / cap) as u64)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.investor.price.cents.height.push(investor_price);
|
self.capitalized.price.cents.height.push(capitalized_price);
|
||||||
|
|
||||||
self.peak_regret.value.block.cents.push(accum.peak_regret());
|
self.peak_regret.value.block.cents.push(accum.peak_regret());
|
||||||
}
|
}
|
||||||
@@ -298,8 +298,8 @@ impl RealizedFull {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Investor price ratio, percentiles and bands
|
// Capitalized price ratio, percentiles and bands
|
||||||
self.investor
|
self.capitalized
|
||||||
.price
|
.price
|
||||||
.compute_rest(prices, starting_indexes, exit)?;
|
.compute_rest(prices, starting_indexes, exit)?;
|
||||||
|
|
||||||
@@ -374,14 +374,14 @@ impl RealizedFull {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RealizedFullAccum {
|
pub struct RealizedFullAccum {
|
||||||
pub(crate) cap_raw: CentsSats,
|
pub(crate) cap_raw: CentsSats,
|
||||||
pub(crate) investor_cap_raw: CentsSquaredSats,
|
pub(crate) capitalized_cap_raw: CentsSquaredSats,
|
||||||
peak_regret: CentsSats,
|
peak_regret: CentsSats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RealizedFullAccum {
|
impl RealizedFullAccum {
|
||||||
pub(crate) fn add(&mut self, state: &RealizedState) {
|
pub(crate) fn add(&mut self, state: &RealizedState) {
|
||||||
self.cap_raw += state.cap_raw();
|
self.cap_raw += state.cap_raw();
|
||||||
self.investor_cap_raw += state.investor_cap_raw();
|
self.capitalized_cap_raw += state.capitalized_cap_raw();
|
||||||
self.peak_regret += CentsSats::new(state.peak_regret_raw());
|
self.peak_regret += CentsSats::new(state.peak_regret_raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ pub struct UnrealizedFull<M: StorageMode = Rw> {
|
|||||||
pub gross_pnl: FiatPerBlock<Cents, M>,
|
pub gross_pnl: FiatPerBlock<Cents, M>,
|
||||||
pub invested_capital: UnrealizedInvestedCapital<M>,
|
pub invested_capital: UnrealizedInvestedCapital<M>,
|
||||||
|
|
||||||
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
pub capitalized_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||||
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
pub capitalized_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||||
|
|
||||||
pub sentiment: UnrealizedSentiment<M>,
|
pub sentiment: UnrealizedSentiment<M>,
|
||||||
}
|
}
|
||||||
@@ -53,8 +53,8 @@ impl UnrealizedFull {
|
|||||||
in_loss: cfg.import("invested_capital_in_loss", v1)?,
|
in_loss: cfg.import("invested_capital_in_loss", v1)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let investor_cap_in_profit_raw = cfg.import("investor_cap_in_profit_raw", v0)?;
|
let capitalized_cap_in_profit_raw = cfg.import("capitalized_cap_in_profit_raw", v0)?;
|
||||||
let investor_cap_in_loss_raw = cfg.import("investor_cap_in_loss_raw", v0)?;
|
let capitalized_cap_in_loss_raw = cfg.import("capitalized_cap_in_loss_raw", v0)?;
|
||||||
|
|
||||||
let sentiment = UnrealizedSentiment {
|
let sentiment = UnrealizedSentiment {
|
||||||
pain_index: cfg.import("pain_index", v1)?,
|
pain_index: cfg.import("pain_index", v1)?,
|
||||||
@@ -66,33 +66,33 @@ impl UnrealizedFull {
|
|||||||
inner,
|
inner,
|
||||||
gross_pnl,
|
gross_pnl,
|
||||||
invested_capital,
|
invested_capital,
|
||||||
investor_cap_in_profit_raw,
|
capitalized_cap_in_profit_raw,
|
||||||
investor_cap_in_loss_raw,
|
capitalized_cap_in_loss_raw,
|
||||||
sentiment,
|
sentiment,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||||
// Only check per-block pushed vecs (investor_cap_raw).
|
// Only check per-block pushed vecs (capitalized_cap_raw).
|
||||||
// Core-level vecs (profit/loss) are aggregated from age_range, not stateful.
|
// Core-level vecs (profit/loss) are aggregated from age_range, not stateful.
|
||||||
self.investor_cap_in_profit_raw
|
self.capitalized_cap_in_profit_raw
|
||||||
.len()
|
.len()
|
||||||
.min(self.investor_cap_in_loss_raw.len())
|
.min(self.capitalized_cap_in_loss_raw.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_state_all(&mut self, state: &UnrealizedState) {
|
pub(crate) fn push_state_all(&mut self, state: &UnrealizedState) {
|
||||||
self.inner.push_state(state);
|
self.inner.push_state(state);
|
||||||
self.investor_cap_in_profit_raw
|
self.capitalized_cap_in_profit_raw
|
||||||
.push(CentsSquaredSats::new(state.investor_cap_in_profit_raw));
|
.push(CentsSquaredSats::new(state.capitalized_cap_in_profit_raw));
|
||||||
self.investor_cap_in_loss_raw
|
self.capitalized_cap_in_loss_raw
|
||||||
.push(CentsSquaredSats::new(state.investor_cap_in_loss_raw));
|
.push(CentsSquaredSats::new(state.capitalized_cap_in_loss_raw));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||||
let mut vecs = self.inner.collect_vecs_mut();
|
let mut vecs = self.inner.collect_vecs_mut();
|
||||||
vecs.push(&mut self.investor_cap_in_profit_raw as &mut dyn AnyStoredVec);
|
vecs.push(&mut self.capitalized_cap_in_profit_raw as &mut dyn AnyStoredVec);
|
||||||
vecs.push(&mut self.investor_cap_in_loss_raw as &mut dyn AnyStoredVec);
|
vecs.push(&mut self.capitalized_cap_in_loss_raw as &mut dyn AnyStoredVec);
|
||||||
vecs
|
vecs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ impl UnrealizedFull {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute sentiment using investor_price (original formula).
|
/// Compute sentiment using capitalized_price (original formula).
|
||||||
/// Called after cost_basis.in_profit/loss are computed at the cohort level.
|
/// Called after cost_basis.in_profit/loss are computed at the cohort level.
|
||||||
pub(crate) fn compute_sentiment(
|
pub(crate) fn compute_sentiment(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -162,45 +162,45 @@ impl UnrealizedFull {
|
|||||||
spot: &impl ReadableVec<Height, Cents>,
|
spot: &impl ReadableVec<Height, Cents>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// greed = spot - investor_price_winners
|
// greed = spot - capitalized_price_winners
|
||||||
// investor_price = investor_cap / invested_cap
|
// capitalized_price = capitalized_cap / invested_cap
|
||||||
// invested_cap is in Cents (already / ONE_BTC), multiply back for CentsSats scale
|
// invested_cap is in Cents (already / ONE_BTC), multiply back for CentsSats scale
|
||||||
self.sentiment.greed_index.cents.height.compute_transform3(
|
self.sentiment.greed_index.cents.height.compute_transform3(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
&self.investor_cap_in_profit_raw,
|
&self.capitalized_cap_in_profit_raw,
|
||||||
&self.invested_capital.in_profit.cents.height,
|
&self.invested_capital.in_profit.cents.height,
|
||||||
spot,
|
spot,
|
||||||
|(h, investor_cap, invested_cap_cents, spot, ..)| {
|
|(h, capitalized_cap, invested_cap_cents, spot, ..)| {
|
||||||
let invested_cap_raw = invested_cap_cents.as_u128() * Sats::ONE_BTC_U128;
|
let invested_cap_raw = invested_cap_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||||
if invested_cap_raw == 0 {
|
if invested_cap_raw == 0 {
|
||||||
return (h, Cents::ZERO);
|
return (h, Cents::ZERO);
|
||||||
}
|
}
|
||||||
let investor_price = investor_cap.inner() / invested_cap_raw;
|
let capitalized_price = capitalized_cap.inner() / invested_cap_raw;
|
||||||
let spot_u128 = spot.as_u128();
|
let spot_u128 = spot.as_u128();
|
||||||
(
|
(
|
||||||
h,
|
h,
|
||||||
Cents::new(spot_u128.saturating_sub(investor_price) as u64),
|
Cents::new(spot_u128.saturating_sub(capitalized_price) as u64),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// pain = investor_price_losers - spot
|
// pain = capitalized_price_losers - spot
|
||||||
self.sentiment.pain_index.cents.height.compute_transform3(
|
self.sentiment.pain_index.cents.height.compute_transform3(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
&self.investor_cap_in_loss_raw,
|
&self.capitalized_cap_in_loss_raw,
|
||||||
&self.invested_capital.in_loss.cents.height,
|
&self.invested_capital.in_loss.cents.height,
|
||||||
spot,
|
spot,
|
||||||
|(h, investor_cap, invested_cap_cents, spot, ..)| {
|
|(h, capitalized_cap, invested_cap_cents, spot, ..)| {
|
||||||
let invested_cap_raw = invested_cap_cents.as_u128() * Sats::ONE_BTC_U128;
|
let invested_cap_raw = invested_cap_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||||
if invested_cap_raw == 0 {
|
if invested_cap_raw == 0 {
|
||||||
return (h, Cents::ZERO);
|
return (h, Cents::ZERO);
|
||||||
}
|
}
|
||||||
let investor_price = investor_cap.inner() / invested_cap_raw;
|
let capitalized_price = capitalized_cap.inner() / invested_cap_raw;
|
||||||
let spot_u128 = spot.as_u128();
|
let spot_u128 = spot.as_u128();
|
||||||
(
|
(
|
||||||
h,
|
h,
|
||||||
Cents::new(investor_price.saturating_sub(spot_u128) as u64),
|
Cents::new(capitalized_price.saturating_sub(spot_u128) as u64),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub struct SendPrecomputed {
|
|||||||
pub current_ps: CentsSats,
|
pub current_ps: CentsSats,
|
||||||
pub prev_ps: CentsSats,
|
pub prev_ps: CentsSats,
|
||||||
pub ath_ps: CentsSats,
|
pub ath_ps: CentsSats,
|
||||||
pub prev_investor_cap: CentsSquaredSats,
|
pub prev_capitalized_cap: CentsSquaredSats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SendPrecomputed {
|
impl SendPrecomputed {
|
||||||
@@ -42,7 +42,7 @@ impl SendPrecomputed {
|
|||||||
} else {
|
} else {
|
||||||
CentsSats::from_price_sats(ath, sats)
|
CentsSats::from_price_sats(ath, sats)
|
||||||
};
|
};
|
||||||
let prev_investor_cap = prev_ps.to_investor_cap(prev_price);
|
let prev_capitalized_cap = prev_ps.to_capitalized_cap(prev_price);
|
||||||
Some(Self {
|
Some(Self {
|
||||||
sats,
|
sats,
|
||||||
prev_price,
|
prev_price,
|
||||||
@@ -50,7 +50,7 @@ impl SendPrecomputed {
|
|||||||
current_ps,
|
current_ps,
|
||||||
prev_ps,
|
prev_ps,
|
||||||
ath_ps,
|
ath_ps,
|
||||||
prev_investor_cap,
|
prev_capitalized_cap,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +90,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
pub(crate) fn restore_realized_cap(&mut self) {
|
pub(crate) fn restore_realized_cap(&mut self) {
|
||||||
self.realized.set_cap_raw(self.cost_basis.cap_raw());
|
self.realized.set_cap_raw(self.cost_basis.cap_raw());
|
||||||
self.realized
|
self.realized
|
||||||
.set_investor_cap_raw(self.cost_basis.investor_cap_raw());
|
.set_capitalized_cap_raw(self.cost_basis.capitalized_cap_raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
pub(crate) fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||||
@@ -117,12 +117,12 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
|
|
||||||
if s.supply_state.value > Sats::ZERO {
|
if s.supply_state.value > Sats::ZERO {
|
||||||
self.realized
|
self.realized
|
||||||
.increment_snapshot(s.price_sats, s.investor_cap);
|
.increment_snapshot(s.price_sats, s.capitalized_cap_raw);
|
||||||
self.cost_basis.increment(
|
self.cost_basis.increment(
|
||||||
s.realized_price,
|
s.realized_price,
|
||||||
s.supply_state.value,
|
s.supply_state.value,
|
||||||
s.price_sats,
|
s.price_sats,
|
||||||
s.investor_cap,
|
s.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,12 +132,12 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
|
|
||||||
if s.supply_state.value > Sats::ZERO {
|
if s.supply_state.value > Sats::ZERO {
|
||||||
self.realized
|
self.realized
|
||||||
.decrement_snapshot(s.price_sats, s.investor_cap);
|
.decrement_snapshot(s.price_sats, s.capitalized_cap_raw);
|
||||||
self.cost_basis.decrement(
|
self.cost_basis.decrement(
|
||||||
s.realized_price,
|
s.realized_price,
|
||||||
s.supply_state.value,
|
s.supply_state.value,
|
||||||
s.price_sats,
|
s.price_sats,
|
||||||
s.investor_cap,
|
s.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,7 +162,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
snapshot.realized_price,
|
snapshot.realized_price,
|
||||||
supply.value,
|
supply.value,
|
||||||
snapshot.price_sats,
|
snapshot.price_sats,
|
||||||
snapshot.investor_cap,
|
snapshot.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,7 +184,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
current.realized_price,
|
current.realized_price,
|
||||||
current.supply_state.value,
|
current.supply_state.value,
|
||||||
current.price_sats,
|
current.price_sats,
|
||||||
current.investor_cap,
|
current.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
prev.realized_price,
|
prev.realized_price,
|
||||||
prev.supply_state.value,
|
prev.supply_state.value,
|
||||||
prev.price_sats,
|
prev.price_sats,
|
||||||
prev.investor_cap,
|
prev.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,11 +212,11 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
pre.current_ps,
|
pre.current_ps,
|
||||||
pre.prev_ps,
|
pre.prev_ps,
|
||||||
pre.ath_ps,
|
pre.ath_ps,
|
||||||
pre.prev_investor_cap,
|
pre.prev_capitalized_cap,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.cost_basis
|
self.cost_basis
|
||||||
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_investor_cap);
|
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_capitalized_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_utxo(
|
pub(crate) fn send_utxo(
|
||||||
@@ -265,17 +265,17 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
let current_ps = CentsSats::from_price_sats(current_price, sats);
|
let current_ps = CentsSats::from_price_sats(current_price, sats);
|
||||||
let prev_ps = CentsSats::from_price_sats(prev_price, sats);
|
let prev_ps = CentsSats::from_price_sats(prev_price, sats);
|
||||||
let ath_ps = CentsSats::from_price_sats(ath, sats);
|
let ath_ps = CentsSats::from_price_sats(ath, sats);
|
||||||
let prev_investor_cap = prev_ps.to_investor_cap(prev_price);
|
let prev_capitalized_cap = prev_ps.to_capitalized_cap(prev_price);
|
||||||
|
|
||||||
self.realized
|
self.realized
|
||||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
.send(sats, current_ps, prev_ps, ath_ps, prev_capitalized_cap);
|
||||||
|
|
||||||
if current.supply_state.value.is_not_zero() {
|
if current.supply_state.value.is_not_zero() {
|
||||||
self.cost_basis.increment(
|
self.cost_basis.increment(
|
||||||
current.realized_price,
|
current.realized_price,
|
||||||
current.supply_state.value,
|
current.supply_state.value,
|
||||||
current.price_sats,
|
current.price_sats,
|
||||||
current.investor_cap,
|
current.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
prev.realized_price,
|
prev.realized_price,
|
||||||
prev.supply_state.value,
|
prev.supply_state.value,
|
||||||
prev.price_sats,
|
prev.price_sats,
|
||||||
prev.investor_cap,
|
prev.capitalized_cap_raw,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use rustc_hash::FxHashMap;
|
|||||||
use vecdb::{Bytes, unlikely};
|
use vecdb::{Bytes, unlikely};
|
||||||
|
|
||||||
use super::{Accumulate, CachedUnrealizedState, UnrealizedState};
|
use super::{Accumulate, CachedUnrealizedState, UnrealizedState};
|
||||||
use crate::distribution::state::pending::{PendingCapDelta, PendingDelta, PendingInvestorCapDelta};
|
use crate::distribution::state::pending::{PendingCapDelta, PendingDelta, PendingCapitalizedCapRawDelta};
|
||||||
|
|
||||||
/// Type alias for the price-to-sats map used in cost basis data.
|
/// Type alias for the price-to-sats map used in cost basis data.
|
||||||
pub(super) type CostBasisMap = BTreeMap<CentsCompact, Sats>;
|
pub(super) type CostBasisMap = BTreeMap<CentsCompact, Sats>;
|
||||||
@@ -27,20 +27,20 @@ pub trait CostBasisOps: Send + Sync + 'static {
|
|||||||
fn with_price_rounding(self, digits: i32) -> Self;
|
fn with_price_rounding(self, digits: i32) -> Self;
|
||||||
fn import_at_or_before(&mut self, height: Height) -> Result<Height>;
|
fn import_at_or_before(&mut self, height: Height) -> Result<Height>;
|
||||||
fn cap_raw(&self) -> CentsSats;
|
fn cap_raw(&self) -> CentsSats;
|
||||||
fn investor_cap_raw(&self) -> CentsSquaredSats;
|
fn capitalized_cap_raw(&self) -> CentsSquaredSats;
|
||||||
fn increment(
|
fn increment(
|
||||||
&mut self,
|
&mut self,
|
||||||
price: Cents,
|
price: Cents,
|
||||||
sats: Sats,
|
sats: Sats,
|
||||||
price_sats: CentsSats,
|
price_sats: CentsSats,
|
||||||
investor_cap: CentsSquaredSats,
|
capitalized_cap: CentsSquaredSats,
|
||||||
);
|
);
|
||||||
fn decrement(
|
fn decrement(
|
||||||
&mut self,
|
&mut self,
|
||||||
price: Cents,
|
price: Cents,
|
||||||
sats: Sats,
|
sats: Sats,
|
||||||
price_sats: CentsSats,
|
price_sats: CentsSats,
|
||||||
investor_cap: CentsSquaredSats,
|
capitalized_cap: CentsSquaredSats,
|
||||||
);
|
);
|
||||||
fn apply_pending(&mut self);
|
fn apply_pending(&mut self);
|
||||||
fn init(&mut self);
|
fn init(&mut self);
|
||||||
@@ -182,7 +182,7 @@ impl CostBasisOps for CostBasisRaw {
|
|||||||
self.state.as_ref().unwrap().cap_raw
|
self.state.as_ref().unwrap().cap_raw
|
||||||
}
|
}
|
||||||
|
|
||||||
fn investor_cap_raw(&self) -> CentsSquaredSats {
|
fn capitalized_cap_raw(&self) -> CentsSquaredSats {
|
||||||
CentsSquaredSats::ZERO
|
CentsSquaredSats::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ impl CostBasisOps for CostBasisRaw {
|
|||||||
_price: Cents,
|
_price: Cents,
|
||||||
_sats: Sats,
|
_sats: Sats,
|
||||||
price_sats: CentsSats,
|
price_sats: CentsSats,
|
||||||
_investor_cap: CentsSquaredSats,
|
_capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
self.pending_cap.inc += price_sats;
|
self.pending_cap.inc += price_sats;
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ impl CostBasisOps for CostBasisRaw {
|
|||||||
_price: Cents,
|
_price: Cents,
|
||||||
_sats: Sats,
|
_sats: Sats,
|
||||||
price_sats: CentsSats,
|
price_sats: CentsSats,
|
||||||
_investor_cap: CentsSquaredSats,
|
_capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
self.pending_cap.dec += price_sats;
|
self.pending_cap.dec += price_sats;
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ impl CostBasisOps for CostBasisRaw {
|
|||||||
/// Composes `CostBasisRaw` for scalar tracking, adds map, pending, and cache.
|
/// Composes `CostBasisRaw` for scalar tracking, adds map, pending, and cache.
|
||||||
///
|
///
|
||||||
/// Generic over the accumulator `S`:
|
/// Generic over the accumulator `S`:
|
||||||
/// - `WithCapital`: tracks all fields including invested capital + investor cap (128 bytes)
|
/// - `WithCapital`: tracks all fields including invested capital + capitalized cap (128 bytes)
|
||||||
/// - `WithoutCapital`: tracks only supply + unrealized profit/loss (64 bytes, 1 cache line)
|
/// - `WithoutCapital`: tracks only supply + unrealized profit/loss (64 bytes, 1 cache line)
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CostBasisData<S: Accumulate> {
|
pub struct CostBasisData<S: Accumulate> {
|
||||||
@@ -249,8 +249,8 @@ pub struct CostBasisData<S: Accumulate> {
|
|||||||
pending: FxHashMap<CentsCompact, PendingDelta>,
|
pending: FxHashMap<CentsCompact, PendingDelta>,
|
||||||
cache: Option<CachedUnrealizedState<S>>,
|
cache: Option<CachedUnrealizedState<S>>,
|
||||||
rounding_digits: Option<i32>,
|
rounding_digits: Option<i32>,
|
||||||
investor_cap_raw: CentsSquaredSats,
|
capitalized_cap_raw: CentsSquaredSats,
|
||||||
pending_investor_cap: PendingInvestorCapDelta,
|
pending_capitalized_cap: PendingCapitalizedCapRawDelta,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Accumulate> CostBasisData<S> {
|
impl<S: Accumulate> CostBasisData<S> {
|
||||||
@@ -351,8 +351,8 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
pending: FxHashMap::default(),
|
pending: FxHashMap::default(),
|
||||||
cache: None,
|
cache: None,
|
||||||
rounding_digits: None,
|
rounding_digits: None,
|
||||||
investor_cap_raw: CentsSquaredSats::ZERO,
|
capitalized_cap_raw: CentsSquaredSats::ZERO,
|
||||||
pending_investor_cap: PendingInvestorCapDelta::default(),
|
pending_capitalized_cap: PendingCapitalizedCapRawDelta::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,10 +375,10 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
"CostBasisData state too short: {} bytes",
|
"CostBasisData state too short: {} bytes",
|
||||||
rest.len()
|
rest.len()
|
||||||
);
|
);
|
||||||
self.investor_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
self.capitalized_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
||||||
self.pending.clear();
|
self.pending.clear();
|
||||||
self.raw.pending_cap = PendingCapDelta::default();
|
self.raw.pending_cap = PendingCapDelta::default();
|
||||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
self.pending_capitalized_cap = PendingCapitalizedCapRawDelta::default();
|
||||||
self.cache = None;
|
self.cache = None;
|
||||||
Ok(height)
|
Ok(height)
|
||||||
}
|
}
|
||||||
@@ -387,8 +387,8 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
self.raw.cap_raw()
|
self.raw.cap_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn investor_cap_raw(&self) -> CentsSquaredSats {
|
fn capitalized_cap_raw(&self) -> CentsSquaredSats {
|
||||||
self.investor_cap_raw
|
self.capitalized_cap_raw
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -397,13 +397,13 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
price: Cents,
|
price: Cents,
|
||||||
sats: Sats,
|
sats: Sats,
|
||||||
price_sats: CentsSats,
|
price_sats: CentsSats,
|
||||||
investor_cap: CentsSquaredSats,
|
capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
let price = self.round_price(price);
|
let price = self.round_price(price);
|
||||||
self.pending.entry(price.into()).or_default().inc += sats;
|
self.pending.entry(price.into()).or_default().inc += sats;
|
||||||
self.raw.pending_cap.inc += price_sats;
|
self.raw.pending_cap.inc += price_sats;
|
||||||
if investor_cap != CentsSquaredSats::ZERO {
|
if capitalized_cap != CentsSquaredSats::ZERO {
|
||||||
self.pending_investor_cap.inc += investor_cap;
|
self.pending_capitalized_cap.inc += capitalized_cap;
|
||||||
}
|
}
|
||||||
if let Some(cache) = self.cache.as_mut() {
|
if let Some(cache) = self.cache.as_mut() {
|
||||||
cache.on_receive(price, sats);
|
cache.on_receive(price, sats);
|
||||||
@@ -416,13 +416,13 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
price: Cents,
|
price: Cents,
|
||||||
sats: Sats,
|
sats: Sats,
|
||||||
price_sats: CentsSats,
|
price_sats: CentsSats,
|
||||||
investor_cap: CentsSquaredSats,
|
capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
let price = self.round_price(price);
|
let price = self.round_price(price);
|
||||||
self.pending.entry(price.into()).or_default().dec += sats;
|
self.pending.entry(price.into()).or_default().dec += sats;
|
||||||
self.raw.pending_cap.dec += price_sats;
|
self.raw.pending_cap.dec += price_sats;
|
||||||
if investor_cap != CentsSquaredSats::ZERO {
|
if capitalized_cap != CentsSquaredSats::ZERO {
|
||||||
self.pending_investor_cap.dec += investor_cap;
|
self.pending_capitalized_cap.dec += capitalized_cap;
|
||||||
}
|
}
|
||||||
if let Some(cache) = self.cache.as_mut() {
|
if let Some(cache) = self.cache.as_mut() {
|
||||||
cache.on_send(price, sats);
|
cache.on_send(price, sats);
|
||||||
@@ -432,19 +432,19 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
fn apply_pending(&mut self) {
|
fn apply_pending(&mut self) {
|
||||||
self.apply_map_pending();
|
self.apply_map_pending();
|
||||||
self.raw.apply_pending_cap();
|
self.raw.apply_pending_cap();
|
||||||
self.investor_cap_raw += self.pending_investor_cap.inc;
|
self.capitalized_cap_raw += self.pending_capitalized_cap.inc;
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.investor_cap_raw >= self.pending_investor_cap.dec,
|
self.capitalized_cap_raw >= self.pending_capitalized_cap.dec,
|
||||||
"CostBasis investor_cap_raw underflow!\n\
|
"CostBasis capitalized_cap_raw underflow!\n\
|
||||||
Path: {:?}\n\
|
Path: {:?}\n\
|
||||||
Current (after increments): {:?}\n\
|
Current (after increments): {:?}\n\
|
||||||
Trying to decrement by: {:?}",
|
Trying to decrement by: {:?}",
|
||||||
self.raw.pathbuf,
|
self.raw.pathbuf,
|
||||||
self.investor_cap_raw,
|
self.capitalized_cap_raw,
|
||||||
self.pending_investor_cap.dec
|
self.pending_capitalized_cap.dec
|
||||||
);
|
);
|
||||||
self.investor_cap_raw -= self.pending_investor_cap.dec;
|
self.capitalized_cap_raw -= self.pending_capitalized_cap.dec;
|
||||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
self.pending_capitalized_cap = PendingCapitalizedCapRawDelta::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
@@ -452,8 +452,8 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
self.map.replace(CostBasisDistribution::default());
|
self.map.replace(CostBasisDistribution::default());
|
||||||
self.pending.clear();
|
self.pending.clear();
|
||||||
self.cache = None;
|
self.cache = None;
|
||||||
self.investor_cap_raw = CentsSquaredSats::ZERO;
|
self.capitalized_cap_raw = CentsSquaredSats::ZERO;
|
||||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
self.pending_capitalized_cap = PendingCapitalizedCapRawDelta::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean(&mut self) -> Result<()> {
|
fn clean(&mut self) -> Result<()> {
|
||||||
@@ -469,7 +469,7 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
let raw_state = self.raw.state.as_ref().unwrap();
|
let raw_state = self.raw.state.as_ref().unwrap();
|
||||||
let mut buffer = self.map.as_ref().unwrap().serialize()?;
|
let mut buffer = self.map.as_ref().unwrap().serialize()?;
|
||||||
buffer.extend(raw_state.cap_raw.to_bytes());
|
buffer.extend(raw_state.cap_raw.to_bytes());
|
||||||
buffer.extend(self.investor_cap_raw.to_bytes());
|
buffer.extend(self.capitalized_cap_raw.to_bytes());
|
||||||
fs::write(self.raw.path_state(height), buffer)?;
|
fs::write(self.raw.path_state(height), buffer)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static {
|
|||||||
Sats::ZERO
|
Sats::ZERO
|
||||||
}
|
}
|
||||||
fn set_cap_raw(&mut self, cap_raw: CentsSats);
|
fn set_cap_raw(&mut self, cap_raw: CentsSats);
|
||||||
fn set_investor_cap_raw(&mut self, investor_cap_raw: CentsSquaredSats);
|
fn set_capitalized_cap_raw(&mut self, capitalized_cap_raw: CentsSquaredSats);
|
||||||
fn reset_single_iteration_values(&mut self);
|
fn reset_single_iteration_values(&mut self);
|
||||||
fn increment(&mut self, price: Cents, sats: Sats);
|
fn increment(&mut self, price: Cents, sats: Sats);
|
||||||
fn increment_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats);
|
fn increment_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats);
|
||||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats);
|
fn decrement_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats);
|
||||||
fn receive(&mut self, price: Cents, sats: Sats) {
|
fn receive(&mut self, price: Cents, sats: Sats) {
|
||||||
self.increment(price, sats);
|
self.increment(price, sats);
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static {
|
|||||||
current_ps: CentsSats,
|
current_ps: CentsSats,
|
||||||
prev_ps: CentsSats,
|
prev_ps: CentsSats,
|
||||||
ath_ps: CentsSats,
|
ath_ps: CentsSats,
|
||||||
prev_investor_cap: CentsSquaredSats,
|
prev_capitalized_cap: CentsSquaredSats,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ impl RealizedOps for MinimalRealizedState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_investor_cap_raw(&mut self, _investor_cap_raw: CentsSquaredSats) {}
|
fn set_capitalized_cap_raw(&mut self, _capitalized_cap_raw: CentsSquaredSats) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn reset_single_iteration_values(&mut self) {
|
fn reset_single_iteration_values(&mut self) {
|
||||||
@@ -104,12 +104,12 @@ impl RealizedOps for MinimalRealizedState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
fn increment_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||||
self.cap_raw += price_sats.as_u128();
|
self.cap_raw += price_sats.as_u128();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
fn decrement_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||||
self.cap_raw -= price_sats.as_u128();
|
self.cap_raw -= price_sats.as_u128();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ impl RealizedOps for MinimalRealizedState {
|
|||||||
current_ps: CentsSats,
|
current_ps: CentsSats,
|
||||||
prev_ps: CentsSats,
|
prev_ps: CentsSats,
|
||||||
_ath_ps: CentsSats,
|
_ath_ps: CentsSats,
|
||||||
_prev_investor_cap: CentsSquaredSats,
|
_prev_capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
match current_ps.cmp(&prev_ps) {
|
match current_ps.cmp(&prev_ps) {
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
@@ -184,7 +184,7 @@ impl RealizedOps for CoreRealizedState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_investor_cap_raw(&mut self, _investor_cap_raw: CentsSquaredSats) {}
|
fn set_capitalized_cap_raw(&mut self, _capitalized_cap_raw: CentsSquaredSats) {}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn reset_single_iteration_values(&mut self) {
|
fn reset_single_iteration_values(&mut self) {
|
||||||
@@ -199,13 +199,13 @@ impl RealizedOps for CoreRealizedState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
fn increment_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||||
self.minimal.increment_snapshot(price_sats, _investor_cap);
|
self.minimal.increment_snapshot(price_sats, _capitalized_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
fn decrement_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||||
self.minimal.decrement_snapshot(price_sats, _investor_cap);
|
self.minimal.decrement_snapshot(price_sats, _capitalized_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -215,10 +215,10 @@ impl RealizedOps for CoreRealizedState {
|
|||||||
current_ps: CentsSats,
|
current_ps: CentsSats,
|
||||||
prev_ps: CentsSats,
|
prev_ps: CentsSats,
|
||||||
ath_ps: CentsSats,
|
ath_ps: CentsSats,
|
||||||
prev_investor_cap: CentsSquaredSats,
|
prev_capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
self.minimal
|
self.minimal
|
||||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
.send(sats, current_ps, prev_ps, ath_ps, prev_capitalized_cap);
|
||||||
match current_ps.cmp(&prev_ps) {
|
match current_ps.cmp(&prev_ps) {
|
||||||
Ordering::Greater | Ordering::Equal => {
|
Ordering::Greater | Ordering::Equal => {
|
||||||
self.sent_in_profit += sats;
|
self.sent_in_profit += sats;
|
||||||
@@ -242,8 +242,8 @@ impl CoreRealizedState {
|
|||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct RealizedState {
|
pub struct RealizedState {
|
||||||
core: CoreRealizedState,
|
core: CoreRealizedState,
|
||||||
/// Raw investor cap: Σ(price² × sats)
|
/// Raw capitalized cap: Σ(price² × sats)
|
||||||
investor_cap_raw: CentsSquaredSats,
|
capitalized_cap_raw: CentsSquaredSats,
|
||||||
/// Raw realized peak regret: Σ((peak - sell_price) × sats)
|
/// Raw realized peak regret: Σ((peak - sell_price) × sats)
|
||||||
peak_regret_raw: u128,
|
peak_regret_raw: u128,
|
||||||
}
|
}
|
||||||
@@ -287,8 +287,8 @@ impl RealizedOps for RealizedState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_investor_cap_raw(&mut self, investor_cap_raw: CentsSquaredSats) {
|
fn set_capitalized_cap_raw(&mut self, capitalized_cap_raw: CentsSquaredSats) {
|
||||||
self.investor_cap_raw = investor_cap_raw;
|
self.capitalized_cap_raw = capitalized_cap_raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -301,20 +301,20 @@ impl RealizedOps for RealizedState {
|
|||||||
fn increment(&mut self, price: Cents, sats: Sats) {
|
fn increment(&mut self, price: Cents, sats: Sats) {
|
||||||
self.core.increment(price, sats);
|
self.core.increment(price, sats);
|
||||||
if sats.is_not_zero() {
|
if sats.is_not_zero() {
|
||||||
self.investor_cap_raw += CentsSats::from_price_sats(price, sats).to_investor_cap(price);
|
self.capitalized_cap_raw += CentsSats::from_price_sats(price, sats).to_capitalized_cap(price);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn increment_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats) {
|
fn increment_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats) {
|
||||||
self.core.increment_snapshot(price_sats, investor_cap);
|
self.core.increment_snapshot(price_sats, capitalized_cap);
|
||||||
self.investor_cap_raw += investor_cap;
|
self.capitalized_cap_raw += capitalized_cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats) {
|
fn decrement_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats) {
|
||||||
self.core.decrement_snapshot(price_sats, investor_cap);
|
self.core.decrement_snapshot(price_sats, capitalized_cap);
|
||||||
self.investor_cap_raw -= investor_cap;
|
self.capitalized_cap_raw -= capitalized_cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -324,26 +324,26 @@ impl RealizedOps for RealizedState {
|
|||||||
current_ps: CentsSats,
|
current_ps: CentsSats,
|
||||||
prev_ps: CentsSats,
|
prev_ps: CentsSats,
|
||||||
ath_ps: CentsSats,
|
ath_ps: CentsSats,
|
||||||
prev_investor_cap: CentsSquaredSats,
|
prev_capitalized_cap: CentsSquaredSats,
|
||||||
) {
|
) {
|
||||||
self.core
|
self.core
|
||||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
.send(sats, current_ps, prev_ps, ath_ps, prev_capitalized_cap);
|
||||||
|
|
||||||
self.peak_regret_raw += (ath_ps - current_ps).as_u128();
|
self.peak_regret_raw += (ath_ps - current_ps).as_u128();
|
||||||
self.investor_cap_raw -= prev_investor_cap;
|
self.capitalized_cap_raw -= prev_capitalized_cap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RealizedState {
|
impl RealizedState {
|
||||||
/// Get investor price as CentsUnsigned.
|
/// Get capitalized price as CentsUnsigned.
|
||||||
/// investor_price = Σ(price² × sats) / Σ(price × sats)
|
/// capitalized_price = Σ(price² × sats) / Σ(price × sats)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn investor_price(&self) -> Cents {
|
pub(crate) fn capitalized_price(&self) -> Cents {
|
||||||
let cap_raw = self.core.cap_raw_u128();
|
let cap_raw = self.core.cap_raw_u128();
|
||||||
if cap_raw == 0 {
|
if cap_raw == 0 {
|
||||||
return Cents::ZERO;
|
return Cents::ZERO;
|
||||||
}
|
}
|
||||||
Cents::new((self.investor_cap_raw / cap_raw) as u64)
|
Cents::new((self.capitalized_cap_raw / cap_raw) as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get raw realized cap for aggregation.
|
/// Get raw realized cap for aggregation.
|
||||||
@@ -352,10 +352,10 @@ impl RealizedState {
|
|||||||
CentsSats::new(self.core.cap_raw_u128())
|
CentsSats::new(self.core.cap_raw_u128())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get raw investor cap for aggregation.
|
/// Get raw capitalized cap for aggregation.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn investor_cap_raw(&self) -> CentsSquaredSats {
|
pub(crate) fn capitalized_cap_raw(&self) -> CentsSquaredSats {
|
||||||
self.investor_cap_raw
|
self.capitalized_cap_raw
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get realized peak regret as CentsUnsigned.
|
/// Get realized peak regret as CentsUnsigned.
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ pub struct UnrealizedState {
|
|||||||
pub supply_in_loss: Sats,
|
pub supply_in_loss: Sats,
|
||||||
pub unrealized_profit: Cents,
|
pub unrealized_profit: Cents,
|
||||||
pub unrealized_loss: Cents,
|
pub unrealized_loss: Cents,
|
||||||
pub investor_cap_in_profit_raw: u128,
|
pub capitalized_cap_in_profit_raw: u128,
|
||||||
pub investor_cap_in_loss_raw: u128,
|
pub capitalized_cap_in_loss_raw: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnrealizedState {
|
impl UnrealizedState {
|
||||||
@@ -20,8 +20,8 @@ impl UnrealizedState {
|
|||||||
supply_in_loss: Sats::ZERO,
|
supply_in_loss: Sats::ZERO,
|
||||||
unrealized_profit: Cents::ZERO,
|
unrealized_profit: Cents::ZERO,
|
||||||
unrealized_loss: Cents::ZERO,
|
unrealized_loss: Cents::ZERO,
|
||||||
investor_cap_in_profit_raw: 0,
|
capitalized_cap_in_profit_raw: 0,
|
||||||
investor_cap_in_loss_raw: 0,
|
capitalized_cap_in_loss_raw: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,12 +34,12 @@ pub struct WithoutCapital {
|
|||||||
pub(crate) unrealized_loss: u128,
|
pub(crate) unrealized_loss: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Full cache state: core + investor cap (for sentiment computation).
|
/// Full cache state: core + capitalized cap (for sentiment computation).
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct WithCapital {
|
pub struct WithCapital {
|
||||||
core: WithoutCapital,
|
core: WithoutCapital,
|
||||||
investor_cap_in_profit: u128,
|
capitalized_cap_in_profit: u128,
|
||||||
investor_cap_in_loss: u128,
|
capitalized_cap_in_loss: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -116,8 +116,8 @@ impl Accumulate for WithoutCapital {
|
|||||||
impl Accumulate for WithCapital {
|
impl Accumulate for WithCapital {
|
||||||
fn to_output(&self) -> UnrealizedState {
|
fn to_output(&self) -> UnrealizedState {
|
||||||
UnrealizedState {
|
UnrealizedState {
|
||||||
investor_cap_in_profit_raw: self.investor_cap_in_profit,
|
capitalized_cap_in_profit_raw: self.capitalized_cap_in_profit,
|
||||||
investor_cap_in_loss_raw: self.investor_cap_in_loss,
|
capitalized_cap_in_loss_raw: self.capitalized_cap_in_loss,
|
||||||
..Accumulate::to_output(&self.core)
|
..Accumulate::to_output(&self.core)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,25 +133,25 @@ impl Accumulate for WithCapital {
|
|||||||
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
||||||
self.core.supply_in_profit += sats;
|
self.core.supply_in_profit += sats;
|
||||||
let invested = price_u128 * sats.as_u128();
|
let invested = price_u128 * sats.as_u128();
|
||||||
self.investor_cap_in_profit += price_u128 * invested;
|
self.capitalized_cap_in_profit += price_u128 * invested;
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn accumulate_loss(&mut self, price_u128: u128, sats: Sats) {
|
fn accumulate_loss(&mut self, price_u128: u128, sats: Sats) {
|
||||||
self.core.supply_in_loss += sats;
|
self.core.supply_in_loss += sats;
|
||||||
let invested = price_u128 * sats.as_u128();
|
let invested = price_u128 * sats.as_u128();
|
||||||
self.investor_cap_in_loss += price_u128 * invested;
|
self.capitalized_cap_in_loss += price_u128 * invested;
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deaccumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
fn deaccumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
||||||
self.core.supply_in_profit -= sats;
|
self.core.supply_in_profit -= sats;
|
||||||
let invested = price_u128 * sats.as_u128();
|
let invested = price_u128 * sats.as_u128();
|
||||||
self.investor_cap_in_profit -= price_u128 * invested;
|
self.capitalized_cap_in_profit -= price_u128 * invested;
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deaccumulate_loss(&mut self, price_u128: u128, sats: Sats) {
|
fn deaccumulate_loss(&mut self, price_u128: u128, sats: Sats) {
|
||||||
self.core.supply_in_loss -= sats;
|
self.core.supply_in_loss -= sats;
|
||||||
let invested = price_u128 * sats.as_u128();
|
let invested = price_u128 * sats.as_u128();
|
||||||
self.investor_cap_in_loss -= price_u128 * invested;
|
self.capitalized_cap_in_loss -= price_u128 * invested;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ impl PendingCapDelta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub(crate) struct PendingInvestorCapDelta {
|
pub(crate) struct PendingCapitalizedCapRawDelta {
|
||||||
pub inc: CentsSquaredSats,
|
pub inc: CentsSquaredSats,
|
||||||
pub dec: CentsSquaredSats,
|
pub dec: CentsSquaredSats,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use brk_cohort::{ByAddrType, Filter};
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
@@ -476,9 +477,18 @@ impl Vecs {
|
|||||||
&inputs.by_type,
|
&inputs.by_type,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.addrs
|
let t = &self.utxo_cohorts.type_;
|
||||||
.exposed
|
let type_supply_sats = ByAddrType::new(|filter| {
|
||||||
.compute_rest(starting_indexes, prices, exit)?;
|
let Filter::Type(ot) = filter else { unreachable!() };
|
||||||
|
&t.get(ot).metrics.supply.total.sats.height
|
||||||
|
});
|
||||||
|
self.addrs.exposed.compute_rest(
|
||||||
|
starting_indexes,
|
||||||
|
prices,
|
||||||
|
&self.utxo_cohorts.all.metrics.supply.total.sats.height,
|
||||||
|
&type_supply_sats,
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
|
||||||
// 6c. Compute total_addr_count = addr_count + empty_addr_count
|
// 6c. Compute total_addr_count = addr_count + empty_addr_count
|
||||||
self.addrs.total.compute(
|
self.addrs.total.compute(
|
||||||
|
|||||||
@@ -44,15 +44,15 @@ impl RarityMeter {
|
|||||||
let lth_realized = &distribution.utxo_cohorts.lth.metrics.realized;
|
let lth_realized = &distribution.utxo_cohorts.lth.metrics.realized;
|
||||||
let spot = &prices.spot.cents.height;
|
let spot = &prices.spot.cents.height;
|
||||||
|
|
||||||
// Full: all + sth + lth (rp + ip), 6 models
|
// Full: all + sth + lth (rp + cp), 6 models
|
||||||
self.full.compute(
|
self.full.compute(
|
||||||
&[
|
&[
|
||||||
&realized.price_ratio_percentiles,
|
&realized.price_ratio_percentiles,
|
||||||
&realized.investor.price.percentiles,
|
&realized.capitalized.price.percentiles,
|
||||||
&sth_realized.price_ratio_percentiles,
|
&sth_realized.price_ratio_percentiles,
|
||||||
&sth_realized.investor.price.percentiles,
|
&sth_realized.capitalized.price.percentiles,
|
||||||
<h_realized.price_ratio_percentiles,
|
<h_realized.price_ratio_percentiles,
|
||||||
<h_realized.investor.price.percentiles,
|
<h_realized.capitalized.price.percentiles,
|
||||||
],
|
],
|
||||||
spot,
|
spot,
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
@@ -63,7 +63,7 @@ impl RarityMeter {
|
|||||||
self.local.compute(
|
self.local.compute(
|
||||||
&[
|
&[
|
||||||
&sth_realized.price_ratio_percentiles,
|
&sth_realized.price_ratio_percentiles,
|
||||||
&sth_realized.investor.price.percentiles,
|
&sth_realized.capitalized.price.percentiles,
|
||||||
],
|
],
|
||||||
spot,
|
spot,
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
@@ -74,9 +74,9 @@ impl RarityMeter {
|
|||||||
self.cycle.compute(
|
self.cycle.compute(
|
||||||
&[
|
&[
|
||||||
&realized.price_ratio_percentiles,
|
&realized.price_ratio_percentiles,
|
||||||
&realized.investor.price.percentiles,
|
&realized.capitalized.price.percentiles,
|
||||||
<h_realized.price_ratio_percentiles,
|
<h_realized.price_ratio_percentiles,
|
||||||
<h_realized.investor.price.percentiles,
|
<h_realized.capitalized.price.percentiles,
|
||||||
],
|
],
|
||||||
spot,
|
spot,
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, WritableVec}
|
|||||||
use crate::{indexes, prices};
|
use crate::{indexes, prices};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AmountPerBlock, NumericValue, PerBlock, PerBlockCumulativeRolling, WindowStartVec, Windows,
|
AmountPerBlock, BpsType, NumericValue, PerBlock, PerBlockCumulativeRolling, PercentPerBlock,
|
||||||
|
WindowStartVec, Windows,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `all` aggregate plus per-`AddrType` breakdown.
|
/// `all` aggregate plus per-`AddrType` breakdown.
|
||||||
@@ -244,3 +245,26 @@ impl WithAddrTypes<AmountPerBlock> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<B: BpsType> WithAddrTypes<PercentPerBlock<B>> {
|
||||||
|
pub(crate) fn forced_import(
|
||||||
|
db: &Database,
|
||||||
|
name: &str,
|
||||||
|
version: Version,
|
||||||
|
indexes: &indexes::Vecs,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let all = PercentPerBlock::forced_import(db, name, version, indexes)?;
|
||||||
|
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
||||||
|
PercentPerBlock::forced_import(db, &format!("{type_name}_{name}"), version, indexes)
|
||||||
|
})?;
|
||||||
|
Ok(Self { all, by_addr_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||||
|
self.all.bps.height.reset()?;
|
||||||
|
for v in self.by_addr_type.values_mut() {
|
||||||
|
v.bps.height.reset()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ repository.workspace = true
|
|||||||
exclude = ["examples/"]
|
exclude = ["examples/"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitcoin = { workspace = true }
|
||||||
brk_error = { workspace = true }
|
brk_error = { workspace = true }
|
||||||
brk_rpc = { workspace = true, features = ["corepc"] }
|
brk_rpc = { workspace = true, features = ["corepc"] }
|
||||||
brk_types = { workspace = true }
|
brk_types = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
|
mem,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||||
@@ -8,9 +9,13 @@ use std::{
|
|||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use bitcoin::hex::DisplayHex;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_rpc::Client;
|
use brk_rpc::Client;
|
||||||
use brk_types::{AddrBytes, MempoolEntryInfo, MempoolInfo, TxWithHex, Txid, TxidPrefix};
|
use brk_types::{
|
||||||
|
AddrBytes, BlockHash, MempoolEntryInfo, MempoolInfo, Transaction, TxIn, TxOut, TxStatus,
|
||||||
|
TxWithHex, Txid, TxidPrefix, Vout,
|
||||||
|
};
|
||||||
use derive_more::Deref;
|
use derive_more::Deref;
|
||||||
use parking_lot::{RwLock, RwLockReadGuard};
|
use parking_lot::{RwLock, RwLockReadGuard};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
@@ -142,28 +147,101 @@ impl MempoolInner {
|
|||||||
|
|
||||||
/// Fetch full transaction data for new txids (needed for address tracking).
|
/// Fetch full transaction data for new txids (needed for address tracking).
|
||||||
fn fetch_new_txs(&self, entries_info: &[MempoolEntryInfo]) -> FxHashMap<Txid, TxWithHex> {
|
fn fetch_new_txs(&self, entries_info: &[MempoolEntryInfo]) -> FxHashMap<Txid, TxWithHex> {
|
||||||
let txids_to_fetch: Vec<Txid> = {
|
let txs = self.txs.read();
|
||||||
let txs = self.txs.read();
|
entries_info
|
||||||
entries_info
|
.iter()
|
||||||
.iter()
|
.filter(|e| !txs.contains(&e.txid))
|
||||||
.map(|e| &e.txid)
|
.take(MAX_TX_FETCHES_PER_CYCLE)
|
||||||
.filter(|txid| !txs.contains(txid))
|
.filter_map(|entry| {
|
||||||
.take(MAX_TX_FETCHES_PER_CYCLE)
|
self.build_transaction(entry, &txs)
|
||||||
.cloned()
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
txids_to_fetch
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|txid| {
|
|
||||||
self.client
|
|
||||||
.get_mempool_transaction(&txid)
|
|
||||||
.ok()
|
.ok()
|
||||||
.map(|tx| (txid, tx))
|
.map(|tx| (entry.txid.clone(), tx))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_transaction(
|
||||||
|
&self,
|
||||||
|
entry: &MempoolEntryInfo,
|
||||||
|
mempool_txs: &TxStore,
|
||||||
|
) -> Result<TxWithHex> {
|
||||||
|
let (mut btc_tx, hex) = self.client.get_mempool_raw_tx(&entry.txid)?;
|
||||||
|
|
||||||
|
let total_size = hex.len() / 2;
|
||||||
|
let total_sigop_cost = btc_tx.total_sigop_cost(|_| None);
|
||||||
|
|
||||||
|
// Collect unique parent txids not in the mempool store,
|
||||||
|
// fetch each once instead of one get_tx_out per input
|
||||||
|
let mut parent_cache: FxHashMap<Txid, Vec<bitcoin::TxOut>> = FxHashMap::default();
|
||||||
|
for txin in &btc_tx.input {
|
||||||
|
let prev_txid: Txid = txin.previous_output.txid.into();
|
||||||
|
if !mempool_txs.contains_key(&prev_txid)
|
||||||
|
&& !parent_cache.contains_key(&prev_txid)
|
||||||
|
{
|
||||||
|
if let Ok(prev) = self
|
||||||
|
.client
|
||||||
|
.get_raw_transaction(&prev_txid, None as Option<&BlockHash>)
|
||||||
|
{
|
||||||
|
parent_cache.insert(prev_txid, prev.output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let input = mem::take(&mut btc_tx.input)
|
||||||
|
.into_iter()
|
||||||
|
.map(|txin| {
|
||||||
|
let prev_txid: Txid = txin.previous_output.txid.into();
|
||||||
|
let prev_vout = usize::from(Vout::from(txin.previous_output.vout));
|
||||||
|
|
||||||
|
let prevout = if let Some(prev) = mempool_txs.get(&prev_txid) {
|
||||||
|
prev.tx()
|
||||||
|
.output
|
||||||
|
.get(prev_vout)
|
||||||
|
.map(|o| TxOut::from((o.script_pubkey.clone(), o.value)))
|
||||||
|
} else if let Some(outputs) = parent_cache.get(&prev_txid) {
|
||||||
|
outputs
|
||||||
|
.get(prev_vout)
|
||||||
|
.map(|o| TxOut::from((o.script_pubkey.clone(), o.value.into())))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
TxIn {
|
||||||
|
is_coinbase: prevout.is_none(),
|
||||||
|
prevout,
|
||||||
|
txid: prev_txid,
|
||||||
|
vout: txin.previous_output.vout.into(),
|
||||||
|
script_sig: txin.script_sig,
|
||||||
|
script_sig_asm: (),
|
||||||
|
witness: txin
|
||||||
|
.witness
|
||||||
|
.iter()
|
||||||
|
.map(|w| w.to_lower_hex_string())
|
||||||
|
.collect(),
|
||||||
|
sequence: txin.sequence.into(),
|
||||||
|
inner_redeem_script_asm: (),
|
||||||
|
inner_witness_script_asm: (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let tx = Transaction {
|
||||||
|
index: None,
|
||||||
|
txid: entry.txid.clone(),
|
||||||
|
version: btc_tx.version.into(),
|
||||||
|
total_sigop_cost,
|
||||||
|
weight: entry.weight.into(),
|
||||||
|
lock_time: btc_tx.lock_time.into(),
|
||||||
|
total_size,
|
||||||
|
fee: entry.fee,
|
||||||
|
input,
|
||||||
|
output: btc_tx.output.into_iter().map(TxOut::from).collect(),
|
||||||
|
status: TxStatus::UNCONFIRMED,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(TxWithHex::new(tx, hex))
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply transaction additions and removals. Returns true if there were changes.
|
/// Apply transaction additions and removals. Returns true if there were changes.
|
||||||
fn apply_changes(
|
fn apply_changes(
|
||||||
&self,
|
&self,
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ use std::{
|
|||||||
|
|
||||||
use bitcoin::consensus::encode;
|
use bitcoin::consensus::encode;
|
||||||
use brk_error::{Error, Result};
|
use brk_error::{Error, Result};
|
||||||
use brk_types::{
|
use brk_types::{BlockHash, Height, MempoolEntryInfo, Sats, Txid, Vout};
|
||||||
BlockHash, Height, MempoolEntryInfo, Sats, Transaction, TxIn, TxOut, TxStatus, TxWithHex, Txid,
|
|
||||||
Vout,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
|
|
||||||
@@ -128,69 +125,13 @@ impl Client {
|
|||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mempool_transaction(&self, txid: &Txid) -> Result<TxWithHex> {
|
pub fn get_mempool_raw_tx(
|
||||||
// Get hex first, then deserialize from it
|
&self,
|
||||||
|
txid: &Txid,
|
||||||
|
) -> Result<(bitcoin::Transaction, String)> {
|
||||||
let hex = self.get_raw_transaction_hex(txid, None as Option<&BlockHash>)?;
|
let hex = self.get_raw_transaction_hex(txid, None as Option<&BlockHash>)?;
|
||||||
let mut tx = encode::deserialize_hex::<bitcoin::Transaction>(&hex)?;
|
let tx = encode::deserialize_hex::<bitcoin::Transaction>(&hex)?;
|
||||||
|
Ok((tx, hex))
|
||||||
let input = mem::take(&mut tx.input)
|
|
||||||
.into_iter()
|
|
||||||
.map(|txin| -> Result<TxIn> {
|
|
||||||
let txout_result = self.get_tx_out(
|
|
||||||
(&txin.previous_output.txid).into(),
|
|
||||||
txin.previous_output.vout.into(),
|
|
||||||
Some(true),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let is_coinbase = txout_result.as_ref().is_none_or(|r| r.coinbase);
|
|
||||||
|
|
||||||
let txout = if let Some(txout_result) = txout_result {
|
|
||||||
Some(TxOut::from((
|
|
||||||
txout_result.script_pub_key,
|
|
||||||
txout_result.value,
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let witness = txin
|
|
||||||
.witness
|
|
||||||
.iter()
|
|
||||||
.map(bitcoin::hex::DisplayHex::to_lower_hex_string)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(TxIn {
|
|
||||||
is_coinbase,
|
|
||||||
prevout: txout,
|
|
||||||
txid: txin.previous_output.txid.into(),
|
|
||||||
vout: txin.previous_output.vout.into(),
|
|
||||||
script_sig: txin.script_sig,
|
|
||||||
script_sig_asm: (),
|
|
||||||
witness,
|
|
||||||
sequence: txin.sequence.into(),
|
|
||||||
inner_redeem_script_asm: (),
|
|
||||||
inner_witness_script_asm: (),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
|
|
||||||
let mut tx = Transaction {
|
|
||||||
index: None,
|
|
||||||
txid: txid.clone(),
|
|
||||||
version: tx.version.into(),
|
|
||||||
total_sigop_cost: tx.total_sigop_cost(|_| None),
|
|
||||||
weight: tx.weight().into(),
|
|
||||||
lock_time: tx.lock_time.into(),
|
|
||||||
total_size: tx.total_size(),
|
|
||||||
fee: Sats::default(),
|
|
||||||
input,
|
|
||||||
output: tx.output.into_iter().map(TxOut::from).collect(),
|
|
||||||
status: TxStatus::UNCONFIRMED,
|
|
||||||
};
|
|
||||||
|
|
||||||
tx.compute_fee();
|
|
||||||
|
|
||||||
Ok(TxWithHex::new(tx, hex))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tx_out(
|
pub fn get_tx_out(
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ impl CentsSats {
|
|||||||
Cents::new(result.min(u32::MAX as u128) as u64)
|
Cents::new(result.min(u32::MAX as u128) as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute investor cap (price² × sats) = price × (price × sats)
|
/// Compute capitalized cap (price² × sats) = price × (price × sats)
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_investor_cap(self, price: Cents) -> CentsSquaredSats {
|
pub fn to_capitalized_cap(self, price: Cents) -> CentsSquaredSats {
|
||||||
CentsSquaredSats::new(price.inner() as u128 * self.0)
|
CentsSquaredSats::new(price.inner() as u128 * self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use vecdb::{Bytes, Formattable};
|
use vecdb::{Bytes, Formattable};
|
||||||
|
|
||||||
/// Raw cents squared (u128) - stores cents² × sats without division.
|
/// Raw cents squared (u128) - stores cents² × sats without division.
|
||||||
/// Used for precise accumulation of investor cap values: Σ(price² × sats).
|
/// Used for precise accumulation of capitalized cap values: Σ(price² × sats).
|
||||||
/// investor_price = investor_cap_raw / realized_cap_raw
|
/// capitalized_price = capitalized_cap_raw / realized_cap_raw
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
|
Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
|
||||||
)]
|
)]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use vecdb::{Bytes, Formattable};
|
|||||||
use crate::{Cents, CentsSats, CentsSquaredSats, EmptyAddrData, OutputType, Sats, SupplyState};
|
use crate::{Cents, CentsSats, CentsSquaredSats, EmptyAddrData, OutputType, Sats, SupplyState};
|
||||||
|
|
||||||
/// Snapshot of cost basis related state.
|
/// Snapshot of cost basis related state.
|
||||||
/// Uses CentsSats (u64) for single-UTXO values, CentsSquaredSats (u128) for investor cap.
|
/// Uses CentsSats (u64) for single-UTXO values, CentsSquaredSats (u128) for capitalized cap.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CostBasisSnapshot {
|
pub struct CostBasisSnapshot {
|
||||||
pub realized_price: Cents,
|
pub realized_price: Cents,
|
||||||
@@ -14,7 +14,7 @@ pub struct CostBasisSnapshot {
|
|||||||
/// price × sats (fits u64 for individual UTXOs)
|
/// price × sats (fits u64 for individual UTXOs)
|
||||||
pub price_sats: CentsSats,
|
pub price_sats: CentsSats,
|
||||||
/// price² × sats (needs u128)
|
/// price² × sats (needs u128)
|
||||||
pub investor_cap: CentsSquaredSats,
|
pub capitalized_cap_raw: CentsSquaredSats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CostBasisSnapshot {
|
impl CostBasisSnapshot {
|
||||||
@@ -26,7 +26,7 @@ impl CostBasisSnapshot {
|
|||||||
realized_price: price,
|
realized_price: price,
|
||||||
supply_state: *supply,
|
supply_state: *supply,
|
||||||
price_sats,
|
price_sats,
|
||||||
investor_cap: price_sats.to_investor_cap(price),
|
capitalized_cap_raw: price_sats.to_capitalized_cap(price),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,8 +49,8 @@ pub struct FundedAddrData {
|
|||||||
pub sent: Sats,
|
pub sent: Sats,
|
||||||
/// The realized capitalization: Σ(price × sats)
|
/// The realized capitalization: Σ(price × sats)
|
||||||
pub realized_cap_raw: CentsSats,
|
pub realized_cap_raw: CentsSats,
|
||||||
/// The investor capitalization: Σ(price² × sats)
|
/// The capitalized cap: Σ(price² × sats)
|
||||||
pub investor_cap_raw: CentsSquaredSats,
|
pub capitalized_cap_raw: CentsSquaredSats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FundedAddrData {
|
impl FundedAddrData {
|
||||||
@@ -72,7 +72,7 @@ impl FundedAddrData {
|
|||||||
},
|
},
|
||||||
// Use exact value to avoid rounding errors from realized_price × balance
|
// Use exact value to avoid rounding errors from realized_price × balance
|
||||||
price_sats: CentsSats::new(self.realized_cap_raw.inner()),
|
price_sats: CentsSats::new(self.realized_cap_raw.inner()),
|
||||||
investor_cap: self.investor_cap_raw,
|
capitalized_cap_raw: self.capitalized_cap_raw,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +137,11 @@ impl FundedAddrData {
|
|||||||
/// This address's contribution (in sats) to the "funds at quantum risk"
|
/// This address's contribution (in sats) to the "funds at quantum risk"
|
||||||
/// supply: its balance if currently in the funded-exposed set, else 0.
|
/// supply: its balance if currently in the funded-exposed set, else 0.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn exposed_supply_contribution(&self, output_type: OutputType) -> u64 {
|
pub fn exposed_supply_contribution(&self, output_type: OutputType) -> Sats {
|
||||||
if self.is_funded_with_exposed_pubkey(output_type) {
|
if self.is_funded_with_exposed_pubkey(output_type) {
|
||||||
u64::from(self.balance())
|
self.balance()
|
||||||
} else {
|
} else {
|
||||||
0
|
Sats::ZERO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ impl FundedAddrData {
|
|||||||
self.funded_txo_count += output_count;
|
self.funded_txo_count += output_count;
|
||||||
let ps = CentsSats::from_price_sats(price, amount);
|
let ps = CentsSats::from_price_sats(price, amount);
|
||||||
self.realized_cap_raw += ps;
|
self.realized_cap_raw += ps;
|
||||||
self.investor_cap_raw += ps.to_investor_cap(price);
|
self.capitalized_cap_raw += ps.to_capitalized_cap(price);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(&mut self, amount: Sats, previous_price: Cents) -> Result<()> {
|
pub fn send(&mut self, amount: Sats, previous_price: Cents) -> Result<()> {
|
||||||
@@ -165,7 +165,7 @@ impl FundedAddrData {
|
|||||||
self.spent_txo_count += 1;
|
self.spent_txo_count += 1;
|
||||||
let ps = CentsSats::from_price_sats(previous_price, amount);
|
let ps = CentsSats::from_price_sats(previous_price, amount);
|
||||||
self.realized_cap_raw -= ps;
|
self.realized_cap_raw -= ps;
|
||||||
self.investor_cap_raw -= ps.to_investor_cap(previous_price);
|
self.capitalized_cap_raw -= ps.to_capitalized_cap(previous_price);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ impl From<&EmptyAddrData> for FundedAddrData {
|
|||||||
received: value.transfered,
|
received: value.transfered,
|
||||||
sent: value.transfered,
|
sent: value.transfered,
|
||||||
realized_cap_raw: CentsSats::ZERO,
|
realized_cap_raw: CentsSats::ZERO,
|
||||||
investor_cap_raw: CentsSquaredSats::ZERO,
|
capitalized_cap_raw: CentsSquaredSats::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,14 +197,14 @@ impl std::fmt::Display for FundedAddrData {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"tx_count: {}, funded_txo_count: {}, spent_txo_count: {}, received: {}, sent: {}, realized_cap_raw: {}, investor_cap_raw: {}",
|
"tx_count: {}, funded_txo_count: {}, spent_txo_count: {}, received: {}, sent: {}, realized_cap_raw: {}, capitalized_cap_raw: {}",
|
||||||
self.tx_count,
|
self.tx_count,
|
||||||
self.funded_txo_count,
|
self.funded_txo_count,
|
||||||
self.spent_txo_count,
|
self.spent_txo_count,
|
||||||
self.received,
|
self.received,
|
||||||
self.sent,
|
self.sent,
|
||||||
self.realized_cap_raw,
|
self.realized_cap_raw,
|
||||||
self.investor_cap_raw,
|
self.capitalized_cap_raw,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,7 @@ impl Bytes for FundedAddrData {
|
|||||||
arr[16..24].copy_from_slice(self.received.to_bytes().as_ref());
|
arr[16..24].copy_from_slice(self.received.to_bytes().as_ref());
|
||||||
arr[24..32].copy_from_slice(self.sent.to_bytes().as_ref());
|
arr[24..32].copy_from_slice(self.sent.to_bytes().as_ref());
|
||||||
arr[32..48].copy_from_slice(self.realized_cap_raw.to_bytes().as_ref());
|
arr[32..48].copy_from_slice(self.realized_cap_raw.to_bytes().as_ref());
|
||||||
arr[48..64].copy_from_slice(self.investor_cap_raw.to_bytes().as_ref());
|
arr[48..64].copy_from_slice(self.capitalized_cap_raw.to_bytes().as_ref());
|
||||||
arr
|
arr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,7 +259,7 @@ impl Bytes for FundedAddrData {
|
|||||||
received: Sats::from_bytes(&bytes[16..24])?,
|
received: Sats::from_bytes(&bytes[16..24])?,
|
||||||
sent: Sats::from_bytes(&bytes[24..32])?,
|
sent: Sats::from_bytes(&bytes[24..32])?,
|
||||||
realized_cap_raw: CentsSats::from_bytes(&bytes[32..48])?,
|
realized_cap_raw: CentsSats::from_bytes(&bytes[32..48])?,
|
||||||
investor_cap_raw: CentsSquaredSats::from_bytes(&bytes[48..64])?,
|
capitalized_cap_raw: CentsSquaredSats::from_bytes(&bytes[48..64])?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,8 +314,8 @@ Matches mempool.space/bitcoin-cli behavior.
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Raw cents squared (u128) - stores cents² × sats without division.
|
* Raw cents squared (u128) - stores cents² × sats without division.
|
||||||
* Used for precise accumulation of investor cap values: Σ(price² × sats).
|
* Used for precise accumulation of capitalized cap values: Σ(price² × sats).
|
||||||
* investor_price = investor_cap_raw / realized_cap_raw
|
* capitalized_price = capitalized_cap_raw / realized_cap_raw
|
||||||
*
|
*
|
||||||
* @typedef {number} CentsSquaredSats
|
* @typedef {number} CentsSquaredSats
|
||||||
*/
|
*/
|
||||||
@@ -516,7 +516,7 @@ Matches mempool.space/bitcoin-cli behavior.
|
|||||||
* @property {Sats} received - Satoshis received by this address
|
* @property {Sats} received - Satoshis received by this address
|
||||||
* @property {Sats} sent - Satoshis sent by this address
|
* @property {Sats} sent - Satoshis sent by this address
|
||||||
* @property {CentsSats} realizedCapRaw - The realized capitalization: Σ(price × sats)
|
* @property {CentsSats} realizedCapRaw - The realized capitalization: Σ(price × sats)
|
||||||
* @property {CentsSquaredSats} investorCapRaw - The investor capitalization: Σ(price² × sats)
|
* @property {CentsSquaredSats} capitalizedCapRaw - The capitalized cap: Σ(price² × sats)
|
||||||
*/
|
*/
|
||||||
/** @typedef {TypeIndex} FundedAddrIndex */
|
/** @typedef {TypeIndex} FundedAddrIndex */
|
||||||
/** @typedef {number} Halving */
|
/** @typedef {number} Halving */
|
||||||
@@ -2098,10 +2098,10 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, acc) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern
|
* @typedef {Object} CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern
|
||||||
* @property {CentsDeltaToUsdPattern} cap
|
* @property {CentsDeltaToUsdPattern} cap
|
||||||
|
* @property {PricePattern} capitalized
|
||||||
* @property {BlockCumulativeSumPattern} grossPnl
|
* @property {BlockCumulativeSumPattern} grossPnl
|
||||||
* @property {PricePattern} investor
|
|
||||||
* @property {BlockCumulativeNegativeSumPattern} loss
|
* @property {BlockCumulativeNegativeSumPattern} loss
|
||||||
* @property {SeriesPattern1<StoredF32>} mvrv
|
* @property {SeriesPattern1<StoredF32>} mvrv
|
||||||
* @property {BlockChangeCumulativeDeltaSumPattern} netPnl
|
* @property {BlockChangeCumulativeDeltaSumPattern} netPnl
|
||||||
@@ -2433,11 +2433,11 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} GrossInvestedInvestorLossNetNuplProfitSentimentPattern2
|
* @typedef {Object} CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2
|
||||||
|
* @property {SeriesPattern18<CentsSquaredSats>} capitalizedCapInLossRaw
|
||||||
|
* @property {SeriesPattern18<CentsSquaredSats>} capitalizedCapInProfitRaw
|
||||||
* @property {CentsUsdPattern3} grossPnl
|
* @property {CentsUsdPattern3} grossPnl
|
||||||
* @property {InPattern} investedCapital
|
* @property {InPattern} investedCapital
|
||||||
* @property {SeriesPattern18<CentsSquaredSats>} investorCapInLossRaw
|
|
||||||
* @property {SeriesPattern18<CentsSquaredSats>} investorCapInProfitRaw
|
|
||||||
* @property {CentsNegativeToUsdPattern2} loss
|
* @property {CentsNegativeToUsdPattern2} loss
|
||||||
* @property {CentsToUsdPattern3} netPnl
|
* @property {CentsToUsdPattern3} netPnl
|
||||||
* @property {BpsRatioPattern} nupl
|
* @property {BpsRatioPattern} nupl
|
||||||
@@ -2446,17 +2446,17 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 pattern node
|
* Create a CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 pattern node
|
||||||
* @param {BrkClientBase} client
|
* @param {BrkClientBase} client
|
||||||
* @param {string} acc - Accumulated series name
|
* @param {string} acc - Accumulated series name
|
||||||
* @returns {GrossInvestedInvestorLossNetNuplProfitSentimentPattern2}
|
* @returns {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2}
|
||||||
*/
|
*/
|
||||||
function createGrossInvestedInvestorLossNetNuplProfitSentimentPattern2(client, acc) {
|
function createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, acc) {
|
||||||
return {
|
return {
|
||||||
|
capitalizedCapInLossRaw: createSeriesPattern18(client, _m(acc, 'capitalized_cap_in_loss_raw')),
|
||||||
|
capitalizedCapInProfitRaw: createSeriesPattern18(client, _m(acc, 'capitalized_cap_in_profit_raw')),
|
||||||
grossPnl: createCentsUsdPattern3(client, _m(acc, 'unrealized_gross_pnl')),
|
grossPnl: createCentsUsdPattern3(client, _m(acc, 'unrealized_gross_pnl')),
|
||||||
investedCapital: createInPattern(client, _m(acc, 'invested_capital_in')),
|
investedCapital: createInPattern(client, _m(acc, 'invested_capital_in')),
|
||||||
investorCapInLossRaw: createSeriesPattern18(client, _m(acc, 'investor_cap_in_loss_raw')),
|
|
||||||
investorCapInProfitRaw: createSeriesPattern18(client, _m(acc, 'investor_cap_in_profit_raw')),
|
|
||||||
loss: createCentsNegativeToUsdPattern2(client, _m(acc, 'unrealized_loss')),
|
loss: createCentsNegativeToUsdPattern2(client, _m(acc, 'unrealized_loss')),
|
||||||
netPnl: createCentsToUsdPattern3(client, _m(acc, 'net_unrealized_pnl')),
|
netPnl: createCentsToUsdPattern3(client, _m(acc, 'net_unrealized_pnl')),
|
||||||
nupl: createBpsRatioPattern(client, _m(acc, 'nupl')),
|
nupl: createBpsRatioPattern(client, _m(acc, 'nupl')),
|
||||||
@@ -5183,6 +5183,20 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {BtcCentsSatsUsdPattern} p2wsh
|
* @property {BtcCentsSatsUsdPattern} p2wsh
|
||||||
* @property {BtcCentsSatsUsdPattern} p2tr
|
* @property {BtcCentsSatsUsdPattern} p2tr
|
||||||
* @property {BtcCentsSatsUsdPattern} p2a
|
* @property {BtcCentsSatsUsdPattern} p2a
|
||||||
|
* @property {SeriesTree_Addrs_Exposed_Supply_Share} share
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} SeriesTree_Addrs_Exposed_Supply_Share
|
||||||
|
* @property {BpsPercentRatioPattern2} all
|
||||||
|
* @property {BpsPercentRatioPattern2} p2pk65
|
||||||
|
* @property {BpsPercentRatioPattern2} p2pk33
|
||||||
|
* @property {BpsPercentRatioPattern2} p2pkh
|
||||||
|
* @property {BpsPercentRatioPattern2} p2sh
|
||||||
|
* @property {BpsPercentRatioPattern2} p2wpkh
|
||||||
|
* @property {BpsPercentRatioPattern2} p2wsh
|
||||||
|
* @property {BpsPercentRatioPattern2} p2tr
|
||||||
|
* @property {BpsPercentRatioPattern2} p2a
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6261,7 +6275,7 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {BlockCumulativeSumPattern} grossPnl
|
* @property {BlockCumulativeSumPattern} grossPnl
|
||||||
* @property {_1m1w1y24hPattern7} sellSideRiskRatio
|
* @property {_1m1w1y24hPattern7} sellSideRiskRatio
|
||||||
* @property {BlockCumulativeSumPattern} peakRegret
|
* @property {BlockCumulativeSumPattern} peakRegret
|
||||||
* @property {PricePattern} investor
|
* @property {PricePattern} capitalized
|
||||||
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
|
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -6394,8 +6408,8 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl} netPnl
|
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl} netPnl
|
||||||
* @property {CentsUsdPattern3} grossPnl
|
* @property {CentsUsdPattern3} grossPnl
|
||||||
* @property {InPattern} investedCapital
|
* @property {InPattern} investedCapital
|
||||||
* @property {SeriesPattern18<CentsSquaredSats>} investorCapInProfitRaw
|
* @property {SeriesPattern18<CentsSquaredSats>} capitalizedCapInProfitRaw
|
||||||
* @property {SeriesPattern18<CentsSquaredSats>} investorCapInLossRaw
|
* @property {SeriesPattern18<CentsSquaredSats>} capitalizedCapInLossRaw
|
||||||
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment} sentiment
|
* @property {SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment} sentiment
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -6437,7 +6451,7 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {CoindaysCoinyearsDormancyTransferPattern} activity
|
* @property {CoindaysCoinyearsDormancyTransferPattern} activity
|
||||||
* @property {SeriesTree_Cohorts_Utxo_Sth_Realized} realized
|
* @property {SeriesTree_Cohorts_Utxo_Sth_Realized} realized
|
||||||
* @property {InMaxMinPerSupplyPattern} costBasis
|
* @property {InMaxMinPerSupplyPattern} costBasis
|
||||||
* @property {GrossInvestedInvestorLossNetNuplProfitSentimentPattern2} unrealized
|
* @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6452,7 +6466,7 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {BlockCumulativeSumPattern} grossPnl
|
* @property {BlockCumulativeSumPattern} grossPnl
|
||||||
* @property {_1m1w1y24hPattern7} sellSideRiskRatio
|
* @property {_1m1w1y24hPattern7} sellSideRiskRatio
|
||||||
* @property {BlockCumulativeSumPattern} peakRegret
|
* @property {BlockCumulativeSumPattern} peakRegret
|
||||||
* @property {PricePattern} investor
|
* @property {PricePattern} capitalized
|
||||||
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
|
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -6559,7 +6573,7 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {CoindaysCoinyearsDormancyTransferPattern} activity
|
* @property {CoindaysCoinyearsDormancyTransferPattern} activity
|
||||||
* @property {SeriesTree_Cohorts_Utxo_Lth_Realized} realized
|
* @property {SeriesTree_Cohorts_Utxo_Lth_Realized} realized
|
||||||
* @property {InMaxMinPerSupplyPattern} costBasis
|
* @property {InMaxMinPerSupplyPattern} costBasis
|
||||||
* @property {GrossInvestedInvestorLossNetNuplProfitSentimentPattern2} unrealized
|
* @property {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} unrealized
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6574,7 +6588,7 @@ function createTransferPattern(client, acc) {
|
|||||||
* @property {BlockCumulativeSumPattern} grossPnl
|
* @property {BlockCumulativeSumPattern} grossPnl
|
||||||
* @property {_1m1w1y24hPattern7} sellSideRiskRatio
|
* @property {_1m1w1y24hPattern7} sellSideRiskRatio
|
||||||
* @property {BlockCumulativeSumPattern} peakRegret
|
* @property {BlockCumulativeSumPattern} peakRegret
|
||||||
* @property {PricePattern} investor
|
* @property {PricePattern} capitalized
|
||||||
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
|
* @property {_1m1w1y24hPattern<StoredF64>} profitToLossRatio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -8543,15 +8557,26 @@ class BrkClient extends BrkClientBase {
|
|||||||
exposed: {
|
exposed: {
|
||||||
count: createFundedTotalPattern(this, 'exposed_addr_count'),
|
count: createFundedTotalPattern(this, 'exposed_addr_count'),
|
||||||
supply: {
|
supply: {
|
||||||
all: createBtcCentsSatsUsdPattern(this, 'exposed_addr_supply'),
|
all: createBtcCentsSatsUsdPattern(this, 'exposed_supply'),
|
||||||
p2pk65: createBtcCentsSatsUsdPattern(this, 'p2pk65_exposed_addr_supply'),
|
p2pk65: createBtcCentsSatsUsdPattern(this, 'p2pk65_exposed_supply'),
|
||||||
p2pk33: createBtcCentsSatsUsdPattern(this, 'p2pk33_exposed_addr_supply'),
|
p2pk33: createBtcCentsSatsUsdPattern(this, 'p2pk33_exposed_supply'),
|
||||||
p2pkh: createBtcCentsSatsUsdPattern(this, 'p2pkh_exposed_addr_supply'),
|
p2pkh: createBtcCentsSatsUsdPattern(this, 'p2pkh_exposed_supply'),
|
||||||
p2sh: createBtcCentsSatsUsdPattern(this, 'p2sh_exposed_addr_supply'),
|
p2sh: createBtcCentsSatsUsdPattern(this, 'p2sh_exposed_supply'),
|
||||||
p2wpkh: createBtcCentsSatsUsdPattern(this, 'p2wpkh_exposed_addr_supply'),
|
p2wpkh: createBtcCentsSatsUsdPattern(this, 'p2wpkh_exposed_supply'),
|
||||||
p2wsh: createBtcCentsSatsUsdPattern(this, 'p2wsh_exposed_addr_supply'),
|
p2wsh: createBtcCentsSatsUsdPattern(this, 'p2wsh_exposed_supply'),
|
||||||
p2tr: createBtcCentsSatsUsdPattern(this, 'p2tr_exposed_addr_supply'),
|
p2tr: createBtcCentsSatsUsdPattern(this, 'p2tr_exposed_supply'),
|
||||||
p2a: createBtcCentsSatsUsdPattern(this, 'p2a_exposed_addr_supply'),
|
p2a: createBtcCentsSatsUsdPattern(this, 'p2a_exposed_supply'),
|
||||||
|
share: {
|
||||||
|
all: createBpsPercentRatioPattern2(this, 'exposed_supply_share'),
|
||||||
|
p2pk65: createBpsPercentRatioPattern2(this, 'p2pk65_exposed_supply_share'),
|
||||||
|
p2pk33: createBpsPercentRatioPattern2(this, 'p2pk33_exposed_supply_share'),
|
||||||
|
p2pkh: createBpsPercentRatioPattern2(this, 'p2pkh_exposed_supply_share'),
|
||||||
|
p2sh: createBpsPercentRatioPattern2(this, 'p2sh_exposed_supply_share'),
|
||||||
|
p2wpkh: createBpsPercentRatioPattern2(this, 'p2wpkh_exposed_supply_share'),
|
||||||
|
p2wsh: createBpsPercentRatioPattern2(this, 'p2wsh_exposed_supply_share'),
|
||||||
|
p2tr: createBpsPercentRatioPattern2(this, 'p2tr_exposed_supply_share'),
|
||||||
|
p2a: createBpsPercentRatioPattern2(this, 'p2a_exposed_supply_share'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delta: {
|
delta: {
|
||||||
@@ -9394,7 +9419,7 @@ class BrkClient extends BrkClientBase {
|
|||||||
grossPnl: createBlockCumulativeSumPattern(this, 'realized_gross_pnl'),
|
grossPnl: createBlockCumulativeSumPattern(this, 'realized_gross_pnl'),
|
||||||
sellSideRiskRatio: create_1m1w1y24hPattern7(this, 'sell_side_risk_ratio'),
|
sellSideRiskRatio: create_1m1w1y24hPattern7(this, 'sell_side_risk_ratio'),
|
||||||
peakRegret: createBlockCumulativeSumPattern(this, 'realized_peak_regret'),
|
peakRegret: createBlockCumulativeSumPattern(this, 'realized_peak_regret'),
|
||||||
investor: createPricePattern(this, 'investor_price'),
|
capitalized: createPricePattern(this, 'capitalized_price'),
|
||||||
profitToLossRatio: create_1m1w1y24hPattern(this, 'realized_profit_to_loss_ratio'),
|
profitToLossRatio: create_1m1w1y24hPattern(this, 'realized_profit_to_loss_ratio'),
|
||||||
},
|
},
|
||||||
costBasis: {
|
costBasis: {
|
||||||
@@ -9428,8 +9453,8 @@ class BrkClient extends BrkClientBase {
|
|||||||
},
|
},
|
||||||
grossPnl: createCentsUsdPattern3(this, 'unrealized_gross_pnl'),
|
grossPnl: createCentsUsdPattern3(this, 'unrealized_gross_pnl'),
|
||||||
investedCapital: createInPattern(this, 'invested_capital_in'),
|
investedCapital: createInPattern(this, 'invested_capital_in'),
|
||||||
investorCapInProfitRaw: createSeriesPattern18(this, 'investor_cap_in_profit_raw'),
|
capitalizedCapInProfitRaw: createSeriesPattern18(this, 'capitalized_cap_in_profit_raw'),
|
||||||
investorCapInLossRaw: createSeriesPattern18(this, 'investor_cap_in_loss_raw'),
|
capitalizedCapInLossRaw: createSeriesPattern18(this, 'capitalized_cap_in_loss_raw'),
|
||||||
sentiment: {
|
sentiment: {
|
||||||
painIndex: createCentsUsdPattern3(this, 'pain_index'),
|
painIndex: createCentsUsdPattern3(this, 'pain_index'),
|
||||||
greedIndex: createCentsUsdPattern3(this, 'greed_index'),
|
greedIndex: createCentsUsdPattern3(this, 'greed_index'),
|
||||||
@@ -9530,11 +9555,11 @@ class BrkClient extends BrkClientBase {
|
|||||||
grossPnl: createBlockCumulativeSumPattern(this, 'sth_realized_gross_pnl'),
|
grossPnl: createBlockCumulativeSumPattern(this, 'sth_realized_gross_pnl'),
|
||||||
sellSideRiskRatio: create_1m1w1y24hPattern7(this, 'sth_sell_side_risk_ratio'),
|
sellSideRiskRatio: create_1m1w1y24hPattern7(this, 'sth_sell_side_risk_ratio'),
|
||||||
peakRegret: createBlockCumulativeSumPattern(this, 'sth_realized_peak_regret'),
|
peakRegret: createBlockCumulativeSumPattern(this, 'sth_realized_peak_regret'),
|
||||||
investor: createPricePattern(this, 'sth_investor_price'),
|
capitalized: createPricePattern(this, 'sth_capitalized_price'),
|
||||||
profitToLossRatio: create_1m1w1y24hPattern(this, 'sth_realized_profit_to_loss_ratio'),
|
profitToLossRatio: create_1m1w1y24hPattern(this, 'sth_realized_profit_to_loss_ratio'),
|
||||||
},
|
},
|
||||||
costBasis: createInMaxMinPerSupplyPattern(this, 'sth'),
|
costBasis: createInMaxMinPerSupplyPattern(this, 'sth'),
|
||||||
unrealized: createGrossInvestedInvestorLossNetNuplProfitSentimentPattern2(this, 'sth'),
|
unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(this, 'sth'),
|
||||||
},
|
},
|
||||||
lth: {
|
lth: {
|
||||||
supply: createDeltaHalfInToTotalPattern2(this, 'lth_supply'),
|
supply: createDeltaHalfInToTotalPattern2(this, 'lth_supply'),
|
||||||
@@ -9632,11 +9657,11 @@ class BrkClient extends BrkClientBase {
|
|||||||
grossPnl: createBlockCumulativeSumPattern(this, 'lth_realized_gross_pnl'),
|
grossPnl: createBlockCumulativeSumPattern(this, 'lth_realized_gross_pnl'),
|
||||||
sellSideRiskRatio: create_1m1w1y24hPattern7(this, 'lth_sell_side_risk_ratio'),
|
sellSideRiskRatio: create_1m1w1y24hPattern7(this, 'lth_sell_side_risk_ratio'),
|
||||||
peakRegret: createBlockCumulativeSumPattern(this, 'lth_realized_peak_regret'),
|
peakRegret: createBlockCumulativeSumPattern(this, 'lth_realized_peak_regret'),
|
||||||
investor: createPricePattern(this, 'lth_investor_price'),
|
capitalized: createPricePattern(this, 'lth_capitalized_price'),
|
||||||
profitToLossRatio: create_1m1w1y24hPattern(this, 'lth_realized_profit_to_loss_ratio'),
|
profitToLossRatio: create_1m1w1y24hPattern(this, 'lth_realized_profit_to_loss_ratio'),
|
||||||
},
|
},
|
||||||
costBasis: createInMaxMinPerSupplyPattern(this, 'lth'),
|
costBasis: createInMaxMinPerSupplyPattern(this, 'lth'),
|
||||||
unrealized: createGrossInvestedInvestorLossNetNuplProfitSentimentPattern2(this, 'lth'),
|
unrealized: createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(this, 'lth'),
|
||||||
},
|
},
|
||||||
ageRange: {
|
ageRange: {
|
||||||
under1h: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'utxos_under_1h_old'),
|
under1h: createActivityOutputsRealizedSupplyUnrealizedPattern(this, 'utxos_under_1h_old'),
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ CentsSats = int
|
|||||||
# Used for profit/loss calculations, deltas, etc.
|
# Used for profit/loss calculations, deltas, etc.
|
||||||
CentsSigned = int
|
CentsSigned = int
|
||||||
# Raw cents squared (u128) - stores cents² × sats without division.
|
# Raw cents squared (u128) - stores cents² × sats without division.
|
||||||
# Used for precise accumulation of investor cap values: Σ(price² × sats).
|
# Used for precise accumulation of capitalized cap values: Σ(price² × sats).
|
||||||
# investor_price = investor_cap_raw / realized_cap_raw
|
# capitalized_price = capitalized_cap_raw / realized_cap_raw
|
||||||
CentsSquaredSats = int
|
CentsSquaredSats = int
|
||||||
# Closing price value for a time period
|
# Closing price value for a time period
|
||||||
Close = Dollars
|
Close = Dollars
|
||||||
@@ -843,7 +843,7 @@ class FundedAddrData(TypedDict):
|
|||||||
received: Satoshis received by this address
|
received: Satoshis received by this address
|
||||||
sent: Satoshis sent by this address
|
sent: Satoshis sent by this address
|
||||||
realized_cap_raw: The realized capitalization: Σ(price × sats)
|
realized_cap_raw: The realized capitalization: Σ(price × sats)
|
||||||
investor_cap_raw: The investor capitalization: Σ(price² × sats)
|
capitalized_cap_raw: The capitalized cap: Σ(price² × sats)
|
||||||
"""
|
"""
|
||||||
tx_count: int
|
tx_count: int
|
||||||
funded_txo_count: int
|
funded_txo_count: int
|
||||||
@@ -851,7 +851,7 @@ class FundedAddrData(TypedDict):
|
|||||||
received: Sats
|
received: Sats
|
||||||
sent: Sats
|
sent: Sats
|
||||||
realized_cap_raw: CentsSats
|
realized_cap_raw: CentsSats
|
||||||
investor_cap_raw: CentsSquaredSats
|
capitalized_cap_raw: CentsSquaredSats
|
||||||
|
|
||||||
class HashrateEntry(TypedDict):
|
class HashrateEntry(TypedDict):
|
||||||
"""
|
"""
|
||||||
@@ -2649,7 +2649,7 @@ class AllEmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern:
|
|||||||
"""Pattern struct for repeated tree structure."""
|
"""Pattern struct for repeated tree structure."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern:
|
class CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern:
|
||||||
"""Pattern struct for repeated tree structure."""
|
"""Pattern struct for repeated tree structure."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -2798,15 +2798,15 @@ class AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern:
|
|||||||
self.pct90: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'pct90'))
|
self.pct90: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'pct90'))
|
||||||
self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
|
self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
|
||||||
|
|
||||||
class GrossInvestedInvestorLossNetNuplProfitSentimentPattern2:
|
class CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2:
|
||||||
"""Pattern struct for repeated tree structure."""
|
"""Pattern struct for repeated tree structure."""
|
||||||
|
|
||||||
def __init__(self, client: BrkClientBase, acc: str):
|
def __init__(self, client: BrkClientBase, acc: str):
|
||||||
"""Create pattern node with accumulated series name."""
|
"""Create pattern node with accumulated series name."""
|
||||||
|
self.capitalized_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, _m(acc, 'capitalized_cap_in_loss_raw'))
|
||||||
|
self.capitalized_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, _m(acc, 'capitalized_cap_in_profit_raw'))
|
||||||
self.gross_pnl: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'unrealized_gross_pnl'))
|
self.gross_pnl: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'unrealized_gross_pnl'))
|
||||||
self.invested_capital: InPattern = InPattern(client, _m(acc, 'invested_capital_in'))
|
self.invested_capital: InPattern = InPattern(client, _m(acc, 'invested_capital_in'))
|
||||||
self.investor_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, _m(acc, 'investor_cap_in_loss_raw'))
|
|
||||||
self.investor_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, _m(acc, 'investor_cap_in_profit_raw'))
|
|
||||||
self.loss: CentsNegativeToUsdPattern2 = CentsNegativeToUsdPattern2(client, _m(acc, 'unrealized_loss'))
|
self.loss: CentsNegativeToUsdPattern2 = CentsNegativeToUsdPattern2(client, _m(acc, 'unrealized_loss'))
|
||||||
self.net_pnl: CentsToUsdPattern3 = CentsToUsdPattern3(client, _m(acc, 'net_unrealized_pnl'))
|
self.net_pnl: CentsToUsdPattern3 = CentsToUsdPattern3(client, _m(acc, 'net_unrealized_pnl'))
|
||||||
self.nupl: BpsRatioPattern = BpsRatioPattern(client, _m(acc, 'nupl'))
|
self.nupl: BpsRatioPattern = BpsRatioPattern(client, _m(acc, 'nupl'))
|
||||||
@@ -4284,19 +4284,34 @@ class SeriesTree_Addrs_Reused:
|
|||||||
self.count: FundedTotalPattern = FundedTotalPattern(client, 'reused_addr_count')
|
self.count: FundedTotalPattern = FundedTotalPattern(client, 'reused_addr_count')
|
||||||
self.events: SeriesTree_Addrs_Reused_Events = SeriesTree_Addrs_Reused_Events(client)
|
self.events: SeriesTree_Addrs_Reused_Events = SeriesTree_Addrs_Reused_Events(client)
|
||||||
|
|
||||||
|
class SeriesTree_Addrs_Exposed_Supply_Share:
|
||||||
|
"""Series tree node."""
|
||||||
|
|
||||||
|
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||||
|
self.all: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'exposed_supply_share')
|
||||||
|
self.p2pk65: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2pk65_exposed_supply_share')
|
||||||
|
self.p2pk33: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2pk33_exposed_supply_share')
|
||||||
|
self.p2pkh: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2pkh_exposed_supply_share')
|
||||||
|
self.p2sh: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2sh_exposed_supply_share')
|
||||||
|
self.p2wpkh: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2wpkh_exposed_supply_share')
|
||||||
|
self.p2wsh: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2wsh_exposed_supply_share')
|
||||||
|
self.p2tr: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2tr_exposed_supply_share')
|
||||||
|
self.p2a: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'p2a_exposed_supply_share')
|
||||||
|
|
||||||
class SeriesTree_Addrs_Exposed_Supply:
|
class SeriesTree_Addrs_Exposed_Supply:
|
||||||
"""Series tree node."""
|
"""Series tree node."""
|
||||||
|
|
||||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||||
self.all: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'exposed_addr_supply')
|
self.all: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'exposed_supply')
|
||||||
self.p2pk65: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk65_exposed_addr_supply')
|
self.p2pk65: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk65_exposed_supply')
|
||||||
self.p2pk33: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk33_exposed_addr_supply')
|
self.p2pk33: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk33_exposed_supply')
|
||||||
self.p2pkh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pkh_exposed_addr_supply')
|
self.p2pkh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pkh_exposed_supply')
|
||||||
self.p2sh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2sh_exposed_addr_supply')
|
self.p2sh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2sh_exposed_supply')
|
||||||
self.p2wpkh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2wpkh_exposed_addr_supply')
|
self.p2wpkh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2wpkh_exposed_supply')
|
||||||
self.p2wsh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2wsh_exposed_addr_supply')
|
self.p2wsh: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2wsh_exposed_supply')
|
||||||
self.p2tr: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2tr_exposed_addr_supply')
|
self.p2tr: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2tr_exposed_supply')
|
||||||
self.p2a: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2a_exposed_addr_supply')
|
self.p2a: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2a_exposed_supply')
|
||||||
|
self.share: SeriesTree_Addrs_Exposed_Supply_Share = SeriesTree_Addrs_Exposed_Supply_Share(client)
|
||||||
|
|
||||||
class SeriesTree_Addrs_Exposed:
|
class SeriesTree_Addrs_Exposed:
|
||||||
"""Series tree node."""
|
"""Series tree node."""
|
||||||
@@ -5585,7 +5600,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized:
|
|||||||
self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'realized_gross_pnl')
|
self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'realized_gross_pnl')
|
||||||
self.sell_side_risk_ratio: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, 'sell_side_risk_ratio')
|
self.sell_side_risk_ratio: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, 'sell_side_risk_ratio')
|
||||||
self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'realized_peak_regret')
|
self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'realized_peak_regret')
|
||||||
self.investor: PricePattern = PricePattern(client, 'investor_price')
|
self.capitalized: PricePattern = PricePattern(client, 'capitalized_price')
|
||||||
self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'realized_profit_to_loss_ratio')
|
self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'realized_profit_to_loss_ratio')
|
||||||
|
|
||||||
class SeriesTree_Cohorts_Utxo_All_CostBasis:
|
class SeriesTree_Cohorts_Utxo_All_CostBasis:
|
||||||
@@ -5645,8 +5660,8 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized:
|
|||||||
self.net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl = SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl(client)
|
self.net_pnl: SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl = SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl(client)
|
||||||
self.gross_pnl: CentsUsdPattern3 = CentsUsdPattern3(client, 'unrealized_gross_pnl')
|
self.gross_pnl: CentsUsdPattern3 = CentsUsdPattern3(client, 'unrealized_gross_pnl')
|
||||||
self.invested_capital: InPattern = InPattern(client, 'invested_capital_in')
|
self.invested_capital: InPattern = InPattern(client, 'invested_capital_in')
|
||||||
self.investor_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'investor_cap_in_profit_raw')
|
self.capitalized_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'capitalized_cap_in_profit_raw')
|
||||||
self.investor_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'investor_cap_in_loss_raw')
|
self.capitalized_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, 'capitalized_cap_in_loss_raw')
|
||||||
self.sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment = SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment(client)
|
self.sentiment: SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment = SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment(client)
|
||||||
|
|
||||||
class SeriesTree_Cohorts_Utxo_All:
|
class SeriesTree_Cohorts_Utxo_All:
|
||||||
@@ -5776,7 +5791,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized:
|
|||||||
self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_gross_pnl')
|
self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_gross_pnl')
|
||||||
self.sell_side_risk_ratio: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, 'sth_sell_side_risk_ratio')
|
self.sell_side_risk_ratio: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, 'sth_sell_side_risk_ratio')
|
||||||
self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_peak_regret')
|
self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_peak_regret')
|
||||||
self.investor: PricePattern = PricePattern(client, 'sth_investor_price')
|
self.capitalized: PricePattern = PricePattern(client, 'sth_capitalized_price')
|
||||||
self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sth_realized_profit_to_loss_ratio')
|
self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sth_realized_profit_to_loss_ratio')
|
||||||
|
|
||||||
class SeriesTree_Cohorts_Utxo_Sth:
|
class SeriesTree_Cohorts_Utxo_Sth:
|
||||||
@@ -5788,7 +5803,7 @@ class SeriesTree_Cohorts_Utxo_Sth:
|
|||||||
self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'sth')
|
self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'sth')
|
||||||
self.realized: SeriesTree_Cohorts_Utxo_Sth_Realized = SeriesTree_Cohorts_Utxo_Sth_Realized(client)
|
self.realized: SeriesTree_Cohorts_Utxo_Sth_Realized = SeriesTree_Cohorts_Utxo_Sth_Realized(client)
|
||||||
self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'sth')
|
self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'sth')
|
||||||
self.unrealized: GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 = GrossInvestedInvestorLossNetNuplProfitSentimentPattern2(client, 'sth')
|
self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, 'sth')
|
||||||
|
|
||||||
class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All:
|
class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All:
|
||||||
"""Series tree node."""
|
"""Series tree node."""
|
||||||
@@ -5913,7 +5928,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized:
|
|||||||
self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_gross_pnl')
|
self.gross_pnl: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_gross_pnl')
|
||||||
self.sell_side_risk_ratio: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, 'lth_sell_side_risk_ratio')
|
self.sell_side_risk_ratio: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, 'lth_sell_side_risk_ratio')
|
||||||
self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_peak_regret')
|
self.peak_regret: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_peak_regret')
|
||||||
self.investor: PricePattern = PricePattern(client, 'lth_investor_price')
|
self.capitalized: PricePattern = PricePattern(client, 'lth_capitalized_price')
|
||||||
self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'lth_realized_profit_to_loss_ratio')
|
self.profit_to_loss_ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'lth_realized_profit_to_loss_ratio')
|
||||||
|
|
||||||
class SeriesTree_Cohorts_Utxo_Lth:
|
class SeriesTree_Cohorts_Utxo_Lth:
|
||||||
@@ -5925,7 +5940,7 @@ class SeriesTree_Cohorts_Utxo_Lth:
|
|||||||
self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'lth')
|
self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'lth')
|
||||||
self.realized: SeriesTree_Cohorts_Utxo_Lth_Realized = SeriesTree_Cohorts_Utxo_Lth_Realized(client)
|
self.realized: SeriesTree_Cohorts_Utxo_Lth_Realized = SeriesTree_Cohorts_Utxo_Lth_Realized(client)
|
||||||
self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'lth')
|
self.cost_basis: InMaxMinPerSupplyPattern = InMaxMinPerSupplyPattern(client, 'lth')
|
||||||
self.unrealized: GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 = GrossInvestedInvestorLossNetNuplProfitSentimentPattern2(client, 'lth')
|
self.unrealized: CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 = CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client, 'lth')
|
||||||
|
|
||||||
class SeriesTree_Cohorts_Utxo_AgeRange:
|
class SeriesTree_Cohorts_Utxo_AgeRange:
|
||||||
"""Series tree node."""
|
"""Series tree node."""
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
* Profitability bucket pattern (supply + realized_cap + unrealized_pnl + nupl)
|
* Profitability bucket pattern (supply + realized_cap + unrealized_pnl + nupl)
|
||||||
* @typedef {Brk.NuplRealizedSupplyUnrealizedPattern} RealizedSupplyPattern
|
* @typedef {Brk.NuplRealizedSupplyUnrealizedPattern} RealizedSupplyPattern
|
||||||
*
|
*
|
||||||
* Realized pattern (full: cap + gross + investor + loss + mvrv + net + peak + price + profit + sell + sopr)
|
* Realized pattern (full: cap + gross + capitalized + loss + mvrv + net + peak + price + profit + sell + sopr)
|
||||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
|
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
|
||||||
*
|
*
|
||||||
* Transfer volume pattern (block + cumulative + inProfit/inLoss + sum windows)
|
* Transfer volume pattern (block + cumulative + inProfit/inLoss + sum windows)
|
||||||
@@ -256,9 +256,9 @@
|
|||||||
* @typedef {Brk.AbsoluteRatePattern} DeltaPattern
|
* @typedef {Brk.AbsoluteRatePattern} DeltaPattern
|
||||||
* @typedef {Brk.AbsoluteRatePattern2} FiatDeltaPattern
|
* @typedef {Brk.AbsoluteRatePattern2} FiatDeltaPattern
|
||||||
*
|
*
|
||||||
* Investor price percentiles (pct1/2/5/95/98/99)
|
* Capitalized price percentiles (pct1/2/5/95/98/99)
|
||||||
* @typedef {Brk.Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} InvestorPercentilesPattern
|
* @typedef {Brk.Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} CapitalizedPercentilesPattern
|
||||||
* @typedef {Brk.BpsPriceRatioPattern} InvestorPercentileEntry
|
* @typedef {Brk.BpsPriceRatioPattern} CapitalizedPercentileEntry
|
||||||
*
|
*
|
||||||
* Generic tree node type for walking
|
* Generic tree node type for walking
|
||||||
* @typedef {AnySeriesPattern | Record<string, unknown>} TreeNode
|
* @typedef {AnySeriesPattern | Record<string, unknown>} TreeNode
|
||||||
|
|||||||
@@ -117,12 +117,13 @@ function appendNewerBlocks(blocks) {
|
|||||||
for (let i = blocks.length - 1; i >= 0; i--) {
|
for (let i = blocks.length - 1; i >= 0; i--) {
|
||||||
const b = blocks[i];
|
const b = blocks[i];
|
||||||
if (b.height > newestHeight) {
|
if (b.height > newestHeight) {
|
||||||
blocksEl.append(createBlockCube(b));
|
appendCube(createBlockCube(b));
|
||||||
} else {
|
} else {
|
||||||
blocksByHash.set(b.id, b);
|
blocksByHash.set(b.id, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newestHeight = Math.max(newestHeight, blocks[0].height);
|
newestHeight = Math.max(newestHeight, blocks[0].height);
|
||||||
|
|
||||||
if (anchor && anchorRect) {
|
if (anchor && anchorRect) {
|
||||||
const r = anchor.getBoundingClientRect();
|
const r = anchor.getBoundingClientRect();
|
||||||
chainEl.scrollTop += r.top - anchorRect.top;
|
chainEl.scrollTop += r.top - anchorRect.top;
|
||||||
@@ -139,11 +140,12 @@ async function loadInitial(height) {
|
|||||||
: await brk.getBlocksV1();
|
: await brk.getBlocksV1();
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
for (const b of blocks) blocksEl.prepend(createBlockCube(b));
|
for (const b of blocks) prependCube(createBlockCube(b));
|
||||||
newestHeight = blocks[0].height;
|
newestHeight = blocks[0].height;
|
||||||
oldestHeight = blocks[blocks.length - 1].height;
|
oldestHeight = blocks[blocks.length - 1].height;
|
||||||
reachedTip = height == null;
|
reachedTip = height == null;
|
||||||
observeOldestEdge();
|
observeOldestEdge();
|
||||||
|
|
||||||
if (!reachedTip) await loadNewer();
|
if (!reachedTip) await loadNewer();
|
||||||
return blocks[0].id;
|
return blocks[0].id;
|
||||||
}
|
}
|
||||||
@@ -197,11 +199,12 @@ async function loadOlder() {
|
|||||||
loadingOlder = true;
|
loadingOlder = true;
|
||||||
try {
|
try {
|
||||||
const blocks = await brk.getBlocksV1FromHeight(oldestHeight - 1);
|
const blocks = await brk.getBlocksV1FromHeight(oldestHeight - 1);
|
||||||
for (const block of blocks) blocksEl.prepend(createBlockCube(block));
|
for (const block of blocks) prependCube(createBlockCube(block));
|
||||||
if (blocks.length) {
|
if (blocks.length) {
|
||||||
oldestHeight = blocks[blocks.length - 1].height;
|
oldestHeight = blocks[blocks.length - 1].height;
|
||||||
observeOldestEdge();
|
observeOldestEdge();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("explorer loadOlder:", e);
|
console.error("explorer loadOlder:", e);
|
||||||
}
|
}
|
||||||
@@ -227,6 +230,8 @@ function createBlockCube(block) {
|
|||||||
|
|
||||||
cubeElement.dataset.hash = block.id;
|
cubeElement.dataset.hash = block.id;
|
||||||
cubeElement.dataset.height = String(block.height);
|
cubeElement.dataset.height = String(block.height);
|
||||||
|
cubeElement.dataset.timestamp = String(block.timestamp);
|
||||||
|
cubeElement.style.setProperty("--fill", String(Math.min(1, block.weight / 3_990_000)));
|
||||||
blocksByHash.set(block.id, block);
|
blocksByHash.set(block.id, block);
|
||||||
cubeElement.addEventListener("click", () => onCubeClick(cubeElement));
|
cubeElement.addEventListener("click", () => onCubeClick(cubeElement));
|
||||||
|
|
||||||
@@ -268,6 +273,9 @@ function createBlockCube(block) {
|
|||||||
function createCube() {
|
function createCube() {
|
||||||
const cubeElement = document.createElement("div");
|
const cubeElement = document.createElement("div");
|
||||||
cubeElement.classList.add("cube");
|
cubeElement.classList.add("cube");
|
||||||
|
const innerTopElement = document.createElement("div");
|
||||||
|
innerTopElement.classList.add("face", "inner-top");
|
||||||
|
cubeElement.append(innerTopElement);
|
||||||
const rightFaceElement = document.createElement("div");
|
const rightFaceElement = document.createElement("div");
|
||||||
rightFaceElement.classList.add("face", "right");
|
rightFaceElement.classList.add("face", "right");
|
||||||
cubeElement.append(rightFaceElement);
|
cubeElement.append(rightFaceElement);
|
||||||
@@ -279,3 +287,25 @@ function createCube() {
|
|||||||
cubeElement.append(topFaceElement);
|
cubeElement.append(topFaceElement);
|
||||||
return { cubeElement, leftFaceElement, rightFaceElement, topFaceElement };
|
return { cubeElement, leftFaceElement, rightFaceElement, topFaceElement };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @param {HTMLElement} cube */
|
||||||
|
function setGap(cube) {
|
||||||
|
const prev = /** @type {HTMLElement | null} */ (cube.previousElementSibling);
|
||||||
|
if (!prev) return;
|
||||||
|
const dt = Math.max(0, Number(cube.dataset.timestamp) - Number(prev.dataset.timestamp));
|
||||||
|
cube.style.setProperty("--dt", String(dt));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {HTMLDivElement} cube */
|
||||||
|
function prependCube(cube) {
|
||||||
|
const next = /** @type {HTMLElement | null} */ (blocksEl.firstElementChild);
|
||||||
|
blocksEl.prepend(cube);
|
||||||
|
if (next) setGap(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {HTMLDivElement} cube */
|
||||||
|
function appendCube(cube) {
|
||||||
|
blocksEl.append(cube);
|
||||||
|
setGap(cube);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -178,9 +178,9 @@ export function createCointimeSection() {
|
|||||||
color: colors.realized,
|
color: colors.realized,
|
||||||
}),
|
}),
|
||||||
price({
|
price({
|
||||||
series: all.realized.investor.price,
|
series: all.realized.capitalized.price,
|
||||||
name: "Investor",
|
name: "Capitalized",
|
||||||
color: colors.investor,
|
color: colors.capitalized,
|
||||||
}),
|
}),
|
||||||
...prices.map(({ pattern, name, color, defaultActive }) =>
|
...prices.map(({ pattern, name, color, defaultActive }) =>
|
||||||
price({ series: pattern, name, color, defaultActive }),
|
price({ series: pattern, name, color, defaultActive }),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ function percentileSeries(p, n = (x) => x) {
|
|||||||
/**
|
/**
|
||||||
* Per Coin or Per Dollar folder for a single cohort
|
* Per Coin or Per Dollar folder for a single cohort
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {AnyPricePattern} args.avgPrice - realized price (per coin) or investor price (per dollar)
|
* @param {AnyPricePattern} args.avgPrice - realized price (per coin) or capitalized price (per dollar)
|
||||||
* @param {string} args.avgName
|
* @param {string} args.avgName
|
||||||
* @param {AnyPricePattern} args.inProfit
|
* @param {AnyPricePattern} args.inProfit
|
||||||
* @param {AnyPricePattern} args.inLoss
|
* @param {AnyPricePattern} args.inLoss
|
||||||
@@ -100,7 +100,7 @@ export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
|||||||
{
|
{
|
||||||
name: "Per Dollar",
|
name: "Per Dollar",
|
||||||
tree: singleWeightFolder({
|
tree: singleWeightFolder({
|
||||||
avgPrice: tree.realized.investor.price, avgName: "All",
|
avgPrice: tree.realized.capitalized.price, avgName: "All",
|
||||||
inProfit: cb.inProfit.perDollar, inLoss: cb.inLoss.perDollar,
|
inProfit: cb.inProfit.perDollar, inLoss: cb.inLoss.perDollar,
|
||||||
percentiles: cb.perDollar, color, weightLabel: "USD-weighted", title,
|
percentiles: cb.perDollar, color, weightLabel: "USD-weighted", title,
|
||||||
}),
|
}),
|
||||||
@@ -192,7 +192,7 @@ export function createGroupedCostBasisSectionWithPercentiles({ list, all, title
|
|||||||
name: "Per Dollar",
|
name: "Per Dollar",
|
||||||
tree: groupedWeightFolder({
|
tree: groupedWeightFolder({
|
||||||
list, all, title,
|
list, all, title,
|
||||||
getAvgPrice: (c) => c.tree.realized.investor.price,
|
getAvgPrice: (c) => c.tree.realized.capitalized.price,
|
||||||
getInProfit: (c) => c.tree.costBasis.inProfit.perDollar,
|
getInProfit: (c) => c.tree.costBasis.inProfit.perDollar,
|
||||||
getInLoss: (c) => c.tree.costBasis.inLoss.perDollar,
|
getInLoss: (c) => c.tree.costBasis.inLoss.perDollar,
|
||||||
getPercentiles: (c) => c.tree.costBasis.perDollar,
|
getPercentiles: (c) => c.tree.costBasis.perDollar,
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
* Structure (single cohort):
|
* Structure (single cohort):
|
||||||
* - Compare: Both prices on one chart
|
* - Compare: Both prices on one chart
|
||||||
* - Realized: Price + Ratio (MVRV) + Z-Scores (for full cohorts)
|
* - Realized: Price + Ratio (MVRV) + Z-Scores (for full cohorts)
|
||||||
* - Investor: Price + Ratio + Z-Scores (for full cohorts)
|
* - Capitalized: Price + Ratio + Z-Scores (for full cohorts)
|
||||||
*
|
*
|
||||||
* Structure (grouped cohorts):
|
* Structure (grouped cohorts):
|
||||||
* - Realized: Price + Ratio comparison across cohorts
|
* - Realized: Price + Ratio comparison across cohorts
|
||||||
* - Investor: Price + Ratio comparison across cohorts
|
* - Capitalized: Price + Ratio comparison across cohorts
|
||||||
*
|
*
|
||||||
* For cohorts WITHOUT full ratio patterns: basic Price/Ratio charts only (no Z-Scores)
|
* For cohorts WITHOUT full ratio patterns: basic Price/Ratio charts only (no Z-Scores)
|
||||||
*/
|
*/
|
||||||
@@ -34,7 +34,7 @@ export function createPricesSectionFull({ cohort, title }) {
|
|||||||
title: title("Realized Prices"),
|
title: title("Realized Prices"),
|
||||||
top: [
|
top: [
|
||||||
price({ series: tree.realized.price, name: "Realized", color: colors.realized }),
|
price({ series: tree.realized.price, name: "Realized", color: colors.realized }),
|
||||||
price({ series: tree.realized.investor.price, name: "Investor", color: colors.investor }),
|
price({ series: tree.realized.capitalized.price, name: "Capitalized", color: colors.capitalized }),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,12 +50,12 @@ export function createPricesSectionFull({ cohort, title }) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Investor",
|
name: "Capitalized",
|
||||||
tree: priceRatioPercentilesTree({
|
tree: priceRatioPercentilesTree({
|
||||||
pattern: tree.realized.investor.price,
|
pattern: tree.realized.capitalized.price,
|
||||||
title: title("Investor Price"),
|
title: title("Capitalized Price"),
|
||||||
ratioTitle: title("Investor Price Ratio"),
|
ratioTitle: title("Capitalized Price Ratio"),
|
||||||
legend: "Investor",
|
legend: "Capitalized",
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -150,20 +150,20 @@ export function createGroupedPricesSectionFull({ list, all, title }) {
|
|||||||
tree: [
|
tree: [
|
||||||
...groupedRealizedPriceItems(list, all, title),
|
...groupedRealizedPriceItems(list, all, title),
|
||||||
{
|
{
|
||||||
name: "Investor",
|
name: "Capitalized",
|
||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Price",
|
name: "Price",
|
||||||
title: title("Investor Price"),
|
title: title("Capitalized Price"),
|
||||||
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||||
price({ series: tree.realized.investor.price, name, color }),
|
price({ series: tree.realized.capitalized.price, name, color }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ratio",
|
name: "Ratio",
|
||||||
title: title("Investor Price Ratio"),
|
title: title("Capitalized Price Ratio"),
|
||||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||||
baseline({ series: tree.realized.investor.price.ratio, name, color, unit: Unit.ratio, base: 1 }),
|
baseline({ series: tree.realized.capitalized.price.ratio, name, color, unit: Unit.ratio, base: 1 }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
searchResultsElement,
|
searchResultsElement,
|
||||||
} from "../utils/elements.js";
|
} from "../utils/elements.js";
|
||||||
import { QuickMatch } from "../modules/quickmatch-js/0.4.1/src/index.js";
|
import { QuickMatch } from "../modules/quickmatch-js/0.4.1/src/index.js";
|
||||||
|
import { brk } from "../utils/client.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Options} options
|
* @param {Options} options
|
||||||
@@ -28,9 +29,67 @@ export function init(options) {
|
|||||||
if (li) li.dataset.highlight = "";
|
if (li) li.dataset.highlight = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HEX64_RE = /^[0-9a-f]{64}$/i;
|
||||||
|
const ADDR_RE = /^([13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-z0-9]{8,87})$/;
|
||||||
|
|
||||||
|
/** @param {string} label @param {string} href @param {Element | null} [before] */
|
||||||
|
function createResultLink(label, href, before) {
|
||||||
|
const li = window.document.createElement("li");
|
||||||
|
const a = window.document.createElement("a");
|
||||||
|
a.href = href;
|
||||||
|
a.textContent = label;
|
||||||
|
a.title = label;
|
||||||
|
if (href === window.location.pathname) setHighlight(li);
|
||||||
|
a.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setHighlight(li);
|
||||||
|
history.pushState(null, "", href);
|
||||||
|
options.resolveUrl();
|
||||||
|
});
|
||||||
|
li.append(a);
|
||||||
|
searchResultsElement.insertBefore(li, before ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {AbortController | undefined} */
|
||||||
|
let lookupController;
|
||||||
|
|
||||||
|
/** @param {string} needle @param {AbortSignal} signal */
|
||||||
|
async function lookup(needle, signal) {
|
||||||
|
/** @type {Array<[string, string]>} */
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
if (HEX64_RE.test(needle)) {
|
||||||
|
const [blockRes, txRes] = await Promise.allSettled([
|
||||||
|
brk.getBlock(needle, { signal }),
|
||||||
|
brk.getTx(needle, { signal }),
|
||||||
|
]);
|
||||||
|
if (signal.aborted) return;
|
||||||
|
if (blockRes.status === "fulfilled") results.push(["Block", `/block/${needle}`]);
|
||||||
|
if (txRes.status === "fulfilled") results.push(["Transaction", `/tx/${needle}`]);
|
||||||
|
} else if (ADDR_RE.test(needle)) {
|
||||||
|
try {
|
||||||
|
const { isvalid } = await brk.validateAddress(needle, { signal });
|
||||||
|
if (signal.aborted || !isvalid) return;
|
||||||
|
results.push(["Address", `/address/${needle}`]);
|
||||||
|
} catch { return; }
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const before = searchResultsElement.firstElementChild;
|
||||||
|
for (const [label, href] of results) {
|
||||||
|
createResultLink(`${label} ${needle}`, href, before);
|
||||||
|
}
|
||||||
|
// Remove "No results" placeholder if present
|
||||||
|
const last = searchResultsElement.lastElementChild;
|
||||||
|
if (last && !last.querySelector("a")) last.remove();
|
||||||
|
}
|
||||||
|
|
||||||
function inputEvent() {
|
function inputEvent() {
|
||||||
const needle = /** @type {string} */ (searchInput.value).trim();
|
const needle = /** @type {string} */ (searchInput.value).trim();
|
||||||
|
|
||||||
|
if (lookupController) lookupController.abort();
|
||||||
|
|
||||||
searchResultsElement.scrollTo({ top: 0 });
|
searchResultsElement.scrollTo({ top: 0 });
|
||||||
searchResultsElement.innerHTML = "";
|
searchResultsElement.innerHTML = "";
|
||||||
setHighlight();
|
setHighlight();
|
||||||
@@ -54,23 +113,13 @@ export function init(options) {
|
|||||||
["Transaction", `/tx/${num}`],
|
["Transaction", `/tx/${num}`],
|
||||||
];
|
];
|
||||||
for (const [label, href] of entries) {
|
for (const [label, href] of entries) {
|
||||||
const li = window.document.createElement("li");
|
createResultLink(`${label} #${num}`, href);
|
||||||
const a = window.document.createElement("a");
|
|
||||||
a.href = href;
|
|
||||||
a.textContent = `${label} #${num}`;
|
|
||||||
a.title = `${label} #${num}`;
|
|
||||||
if (href === window.location.pathname) setHighlight(li);
|
|
||||||
a.addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setHighlight(li);
|
|
||||||
history.pushState(null, "", href);
|
|
||||||
options.resolveUrl();
|
|
||||||
});
|
|
||||||
li.append(a);
|
|
||||||
searchResultsElement.appendChild(li);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookupController = new AbortController();
|
||||||
|
lookup(needle, lookupController.signal);
|
||||||
|
|
||||||
if (matches.length) {
|
if (matches.length) {
|
||||||
matches.forEach((title) => {
|
matches.forEach((title) => {
|
||||||
const option = titleToOption.get(title);
|
const option = titleToOption.get(title);
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ export const colors = {
|
|||||||
// Valuations
|
// Valuations
|
||||||
realized: palette.orange,
|
realized: palette.orange,
|
||||||
investor: palette.fuchsia,
|
investor: palette.fuchsia,
|
||||||
|
capitalized: palette.green,
|
||||||
thermo: palette.emerald,
|
thermo: palette.emerald,
|
||||||
trueMarketMean: palette.blue,
|
trueMarketMean: palette.blue,
|
||||||
vocdd: palette.purple,
|
vocdd: palette.purple,
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ nav {
|
|||||||
padding: 0.25rem 0;
|
padding: 0.25rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
color: var(--off-color);
|
color: var(--off-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -40,13 +40,16 @@
|
|||||||
.blocks {
|
.blocks {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
--gap: 0.8;
|
--min-gap: 0rem;
|
||||||
gap: calc(var(--cube) * var(--gap));
|
--max-gap: calc(var(--cube) * 6);
|
||||||
|
--min-dt: 0;
|
||||||
|
--max-dt: 10800;
|
||||||
margin-right: var(--cube);
|
margin-right: var(--cube);
|
||||||
margin-top: calc(var(--cube) * -0.25);
|
margin-top: calc(var(--cube) * -0.25);
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
--gap: 1.25;
|
--min-gap: 0rem;
|
||||||
|
--max-gap: calc(var(--cube) * 1.5);
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
height: 11.5rem;
|
height: 11.5rem;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
@@ -58,7 +61,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cube {
|
.cube {
|
||||||
margin-top: -0.375rem;
|
--t: pow(
|
||||||
|
clamp(
|
||||||
|
0,
|
||||||
|
(var(--dt, 600) - var(--min-dt)) / (var(--max-dt) - var(--min-dt)),
|
||||||
|
1
|
||||||
|
),
|
||||||
|
0.7
|
||||||
|
);
|
||||||
|
--block-gap: calc(
|
||||||
|
var(--min-gap) + var(--t) * (var(--max-gap) - var(--min-gap))
|
||||||
|
);
|
||||||
|
--empty-alpha: 0.5;
|
||||||
|
--face-step: 0.033;
|
||||||
|
--face-right-color: light-dark(
|
||||||
|
oklch(from var(--face-color) calc(l - var(--face-step) * 2) c h),
|
||||||
|
var(--face-color)
|
||||||
|
);
|
||||||
|
--face-left-color: light-dark(
|
||||||
|
oklch(from var(--face-color) calc(l - var(--face-step)) c h),
|
||||||
|
oklch(from var(--face-color) calc(l + var(--face-step)) c h)
|
||||||
|
);
|
||||||
|
--face-top-color: light-dark(
|
||||||
|
var(--face-color),
|
||||||
|
oklch(from var(--face-color) calc(l + var(--face-step) * 2) c h)
|
||||||
|
);
|
||||||
|
/*margin-top: -0.375rem;*/
|
||||||
margin-left: calc(var(--cube) * -0.25);
|
margin-left: calc(var(--cube) * -0.25);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -116,17 +144,40 @@
|
|||||||
width: var(--cube);
|
width: var(--cube);
|
||||||
height: var(--cube);
|
height: var(--cube);
|
||||||
padding: 0.1rem;
|
padding: 0.1rem;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-top {
|
||||||
|
backdrop-filter: none;
|
||||||
|
background-color: var(--face-top-color);
|
||||||
|
/*-webkit-mask-image: linear-gradient(transparent, black 0.5rem, black calc(100% - 0.5rem), transparent);
|
||||||
|
mask-image: linear-gradient(transparent, black 0.5rem, black calc(100% - 0.5rem), transparent);*/
|
||||||
|
transform: rotate(30deg) skew(-30deg)
|
||||||
|
translate(
|
||||||
|
calc(var(--cube) * (1.99 - var(--fill, 1))),
|
||||||
|
calc(var(--cube) * (0.599 - 0.864 * var(--fill, 1)))
|
||||||
|
)
|
||||||
|
scaleY(0.864);
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
background-color: oklch(from var(--face-color) calc(l - 0.05) c h);
|
background: linear-gradient(
|
||||||
|
to top,
|
||||||
|
var(--face-right-color) calc(var(--fill, 1) * 100%),
|
||||||
|
oklch(from var(--face-right-color) l c h / var(--empty-alpha))
|
||||||
|
calc(var(--fill, 1) * 100%)
|
||||||
|
);
|
||||||
transform: rotate(-30deg) skewX(-30deg)
|
transform: rotate(-30deg) skewX(-30deg)
|
||||||
translate(calc(var(--cube) * 1.3), calc(var(--cube) * 1.725))
|
translate(calc(var(--cube) * 1.3), calc(var(--cube) * 1.725))
|
||||||
scaleY(0.864);
|
scaleY(0.864);
|
||||||
}
|
}
|
||||||
|
|
||||||
.top {
|
.top {
|
||||||
background-color: oklch(from var(--face-color) calc(l + 0.05) c h);
|
--is-full: round(down, calc(var(--fill, 1) + 0.0025), 1);
|
||||||
|
background-color: oklch(
|
||||||
|
from var(--face-top-color) l c h /
|
||||||
|
calc(var(--empty-alpha) + var(--is-full) * (1 - var(--empty-alpha)))
|
||||||
|
);
|
||||||
transform: rotate(30deg) skew(-30deg)
|
transform: rotate(30deg) skew(-30deg)
|
||||||
translate(calc(var(--cube) * 0.99), calc(var(--cube) * -0.265))
|
translate(calc(var(--cube) * 0.99), calc(var(--cube) * -0.265))
|
||||||
scaleY(0.864);
|
scaleY(0.864);
|
||||||
@@ -143,7 +194,12 @@
|
|||||||
.left {
|
.left {
|
||||||
font-size: var(--font-size-xs);
|
font-size: var(--font-size-xs);
|
||||||
line-height: var(--line-height-xs);
|
line-height: var(--line-height-xs);
|
||||||
background-color: var(--face-color);
|
background: linear-gradient(
|
||||||
|
to top,
|
||||||
|
var(--face-left-color) calc(var(--fill, 1) * 100%),
|
||||||
|
oklch(from var(--face-left-color) l c h / var(--empty-alpha))
|
||||||
|
calc(var(--fill, 1) * 100%)
|
||||||
|
);
|
||||||
transform: rotate(30deg) skewX(30deg)
|
transform: rotate(30deg) skewX(30deg)
|
||||||
translate(calc(var(--cube) * 0.3), calc(var(--cube) * 0.6))
|
translate(calc(var(--cube) * 0.3), calc(var(--cube) * 0.6))
|
||||||
scaleY(0.864);
|
scaleY(0.864);
|
||||||
@@ -151,7 +207,9 @@
|
|||||||
|
|
||||||
&.skeleton {
|
&.skeleton {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
.face { color: transparent; }
|
.face {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fees {
|
.fees {
|
||||||
@@ -161,6 +219,35 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& + & {
|
||||||
|
margin-bottom: var(--block-gap);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: calc(var(--cube) * 1.75);
|
||||||
|
left: calc(var(--cube) * 1.12);
|
||||||
|
width: 1px;
|
||||||
|
height: var(--block-gap);
|
||||||
|
background: var(--border-color);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: var(--block-gap);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
bottom: auto;
|
||||||
|
left: auto;
|
||||||
|
right: calc(-1 * var(--block-gap));
|
||||||
|
top: 50%;
|
||||||
|
width: var(--block-gap);
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user