mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
computer: snapshot
This commit is contained in:
@@ -18,7 +18,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
super::metrics::{CohortMetrics, ImportConfig},
|
||||
super::metrics::{CohortMetrics, ImportConfig, SupplyMetrics},
|
||||
traits::{CohortVecs, DynCohortVecs},
|
||||
};
|
||||
|
||||
@@ -47,6 +47,9 @@ pub struct AddressCohortVecs {
|
||||
|
||||
impl AddressCohortVecs {
|
||||
/// Import address cohort from database.
|
||||
///
|
||||
/// `all_supply` is the supply metrics from the "all" cohort, used as global
|
||||
/// sources for `*_rel_to_market_cap` ratios. Pass `None` if not available.
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
filter: Filter,
|
||||
@@ -54,6 +57,7 @@ impl AddressCohortVecs {
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: Option<&Path>,
|
||||
all_supply: Option<&SupplyMetrics>,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
let full_name = filter.to_full_name(CohortContext::Address);
|
||||
@@ -73,7 +77,7 @@ impl AddressCohortVecs {
|
||||
state: states_path
|
||||
.map(|path| AddressCohortState::new(path, &full_name, compute_dollars)),
|
||||
|
||||
metrics: CohortMetrics::forced_import(&cfg)?,
|
||||
metrics: CohortMetrics::forced_import(&cfg, all_supply)?,
|
||||
|
||||
height_to_addr_count: EagerVec::forced_import(
|
||||
db,
|
||||
@@ -288,7 +292,6 @@ impl CohortVecs for AddressCohortVecs {
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
@@ -298,7 +301,6 @@ impl CohortVecs for AddressCohortVecs {
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
|
||||
@@ -13,7 +13,7 @@ use vecdb::{AnyStoredVec, Database, Exit, IterableVec};
|
||||
|
||||
use crate::{Indexes, indexes, price, stateful::DynCohortVecs};
|
||||
|
||||
use super::{AddressCohortVecs, CohortVecs};
|
||||
use super::{super::metrics::SupplyMetrics, AddressCohortVecs, CohortVecs};
|
||||
|
||||
const VERSION: Version = Version::new(0);
|
||||
|
||||
@@ -23,19 +23,23 @@ pub struct AddressCohorts(AddressGroups<AddressCohortVecs>);
|
||||
|
||||
impl AddressCohorts {
|
||||
/// Import all Address cohorts from database.
|
||||
///
|
||||
/// `all_supply` is the supply metrics from the UTXO "all" cohort, used as global
|
||||
/// sources for `*_rel_to_market_cap` ratios.
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
all_supply: Option<&SupplyMetrics>,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION + Version::ZERO;
|
||||
|
||||
// Helper to create a cohort - only amount_range cohorts have state
|
||||
let create = |filter: Filter, has_state: bool| -> Result<AddressCohortVecs> {
|
||||
let states_path = if has_state { Some(states_path) } else { None };
|
||||
AddressCohortVecs::forced_import(db, filter, v, indexes, price, states_path)
|
||||
AddressCohortVecs::forced_import(db, filter, v, indexes, price, states_path, all_supply)
|
||||
};
|
||||
|
||||
let full = |f: Filter| create(f, true);
|
||||
@@ -183,20 +187,18 @@ impl AddressCohorts {
|
||||
|
||||
/// Second phase of post-processing: compute relative metrics.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2<S, D, HM, DM>(
|
||||
pub fn compute_rest_part2<S, HM, DM>(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &S,
|
||||
dateindex_to_supply: &D,
|
||||
height_to_market_cap: Option<&HM>,
|
||||
dateindex_to_market_cap: Option<&DM>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: IterableVec<Height, Bitcoin> + Sync,
|
||||
D: IterableVec<DateIndex, Bitcoin> + Sync,
|
||||
HM: IterableVec<Height, Dollars> + Sync,
|
||||
DM: IterableVec<DateIndex, Dollars> + Sync,
|
||||
{
|
||||
@@ -206,7 +208,6 @@ impl AddressCohorts {
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
|
||||
@@ -61,7 +61,6 @@ pub trait CohortVecs: DynCohortVecs {
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
stateful::{CohortVecs, DynCohortVecs, states::UTXOCohortState},
|
||||
};
|
||||
|
||||
use super::super::metrics::{CohortMetrics, ImportConfig};
|
||||
use super::super::metrics::{CohortMetrics, ImportConfig, SupplyMetrics};
|
||||
|
||||
/// UTXO cohort with metrics and optional runtime state.
|
||||
#[derive(Clone, Traversable)]
|
||||
@@ -31,6 +31,10 @@ pub struct UTXOCohortVecs {
|
||||
|
||||
impl UTXOCohortVecs {
|
||||
/// Import UTXO cohort from database.
|
||||
///
|
||||
/// `all_supply` is the supply metrics from the "all" cohort, used as global
|
||||
/// sources for `*_rel_to_market_cap` ratios. Pass `None` for the "all" cohort itself.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
filter: Filter,
|
||||
@@ -39,6 +43,7 @@ impl UTXOCohortVecs {
|
||||
price: Option<&price::Vecs>,
|
||||
states_path: &Path,
|
||||
state_level: StateLevel,
|
||||
all_supply: Option<&SupplyMetrics>,
|
||||
) -> Result<Self> {
|
||||
let compute_dollars = price.is_some();
|
||||
let full_name = filter.to_full_name(CohortContext::Utxo);
|
||||
@@ -65,7 +70,7 @@ impl UTXOCohortVecs {
|
||||
None
|
||||
},
|
||||
|
||||
metrics: CohortMetrics::forced_import(&cfg)?,
|
||||
metrics: CohortMetrics::forced_import(&cfg, all_supply)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -233,7 +238,6 @@ impl CohortVecs for UTXOCohortVecs {
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
@@ -243,7 +247,6 @@ impl CohortVecs for UTXOCohortVecs {
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
|
||||
@@ -46,212 +46,256 @@ impl UTXOCohorts {
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION + Version::ZERO;
|
||||
|
||||
let create = |filter: Filter, state_level: StateLevel| -> Result<UTXOCohortVecs> {
|
||||
UTXOCohortVecs::forced_import(db, filter, v, indexes, price, states_path, state_level)
|
||||
};
|
||||
// Create "all" cohort first - it doesn't need global sources (it IS the global source)
|
||||
let all = UTXOCohortVecs::forced_import(
|
||||
db,
|
||||
Filter::All,
|
||||
version + VERSION + Version::ONE,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
StateLevel::PriceOnly,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let full = |f: Filter| create(f, StateLevel::Full);
|
||||
let none = |f: Filter| create(f, StateLevel::None);
|
||||
// Get reference to all's supply for other cohorts to use as global source
|
||||
let all_supply = Some(&all.metrics.supply);
|
||||
|
||||
Ok(Self(UTXOGroups {
|
||||
all: UTXOCohortVecs::forced_import(
|
||||
// Create all cohorts first (while borrowing all_supply), then assemble struct
|
||||
let term = ByTerm {
|
||||
short: UTXOCohortVecs::forced_import(
|
||||
db,
|
||||
Filter::All,
|
||||
version + VERSION + Version::ONE,
|
||||
Filter::Term(Term::Sth),
|
||||
v,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
StateLevel::PriceOnly,
|
||||
all_supply,
|
||||
)?,
|
||||
long: UTXOCohortVecs::forced_import(
|
||||
db,
|
||||
Filter::Term(Term::Lth),
|
||||
v,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
StateLevel::PriceOnly,
|
||||
all_supply,
|
||||
)?,
|
||||
};
|
||||
|
||||
term: ByTerm {
|
||||
short: create(Filter::Term(Term::Sth), StateLevel::PriceOnly)?,
|
||||
long: create(Filter::Term(Term::Lth), StateLevel::PriceOnly)?,
|
||||
},
|
||||
let full = |f: Filter| {
|
||||
UTXOCohortVecs::forced_import(
|
||||
db,
|
||||
f,
|
||||
v,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
StateLevel::Full,
|
||||
all_supply,
|
||||
)
|
||||
};
|
||||
let none = |f: Filter| {
|
||||
UTXOCohortVecs::forced_import(
|
||||
db,
|
||||
f,
|
||||
v,
|
||||
indexes,
|
||||
price,
|
||||
states_path,
|
||||
StateLevel::None,
|
||||
all_supply,
|
||||
)
|
||||
};
|
||||
|
||||
epoch: ByEpoch {
|
||||
_0: full(Filter::Epoch(HalvingEpoch::new(0)))?,
|
||||
_1: full(Filter::Epoch(HalvingEpoch::new(1)))?,
|
||||
_2: full(Filter::Epoch(HalvingEpoch::new(2)))?,
|
||||
_3: full(Filter::Epoch(HalvingEpoch::new(3)))?,
|
||||
_4: full(Filter::Epoch(HalvingEpoch::new(4)))?,
|
||||
},
|
||||
let epoch = ByEpoch {
|
||||
_0: full(Filter::Epoch(HalvingEpoch::new(0)))?,
|
||||
_1: full(Filter::Epoch(HalvingEpoch::new(1)))?,
|
||||
_2: full(Filter::Epoch(HalvingEpoch::new(2)))?,
|
||||
_3: full(Filter::Epoch(HalvingEpoch::new(3)))?,
|
||||
_4: full(Filter::Epoch(HalvingEpoch::new(4)))?,
|
||||
};
|
||||
|
||||
year: ByYear {
|
||||
_2009: full(Filter::Year(Year::new(2009)))?,
|
||||
_2010: full(Filter::Year(Year::new(2010)))?,
|
||||
_2011: full(Filter::Year(Year::new(2011)))?,
|
||||
_2012: full(Filter::Year(Year::new(2012)))?,
|
||||
_2013: full(Filter::Year(Year::new(2013)))?,
|
||||
_2014: full(Filter::Year(Year::new(2014)))?,
|
||||
_2015: full(Filter::Year(Year::new(2015)))?,
|
||||
_2016: full(Filter::Year(Year::new(2016)))?,
|
||||
_2017: full(Filter::Year(Year::new(2017)))?,
|
||||
_2018: full(Filter::Year(Year::new(2018)))?,
|
||||
_2019: full(Filter::Year(Year::new(2019)))?,
|
||||
_2020: full(Filter::Year(Year::new(2020)))?,
|
||||
_2021: full(Filter::Year(Year::new(2021)))?,
|
||||
_2022: full(Filter::Year(Year::new(2022)))?,
|
||||
_2023: full(Filter::Year(Year::new(2023)))?,
|
||||
_2024: full(Filter::Year(Year::new(2024)))?,
|
||||
_2025: full(Filter::Year(Year::new(2025)))?,
|
||||
_2026: full(Filter::Year(Year::new(2026)))?,
|
||||
},
|
||||
let year = ByYear {
|
||||
_2009: full(Filter::Year(Year::new(2009)))?,
|
||||
_2010: full(Filter::Year(Year::new(2010)))?,
|
||||
_2011: full(Filter::Year(Year::new(2011)))?,
|
||||
_2012: full(Filter::Year(Year::new(2012)))?,
|
||||
_2013: full(Filter::Year(Year::new(2013)))?,
|
||||
_2014: full(Filter::Year(Year::new(2014)))?,
|
||||
_2015: full(Filter::Year(Year::new(2015)))?,
|
||||
_2016: full(Filter::Year(Year::new(2016)))?,
|
||||
_2017: full(Filter::Year(Year::new(2017)))?,
|
||||
_2018: full(Filter::Year(Year::new(2018)))?,
|
||||
_2019: full(Filter::Year(Year::new(2019)))?,
|
||||
_2020: full(Filter::Year(Year::new(2020)))?,
|
||||
_2021: full(Filter::Year(Year::new(2021)))?,
|
||||
_2022: full(Filter::Year(Year::new(2022)))?,
|
||||
_2023: full(Filter::Year(Year::new(2023)))?,
|
||||
_2024: full(Filter::Year(Year::new(2024)))?,
|
||||
_2025: full(Filter::Year(Year::new(2025)))?,
|
||||
_2026: full(Filter::Year(Year::new(2026)))?,
|
||||
};
|
||||
|
||||
type_: BySpendableType {
|
||||
p2pk65: full(Filter::Type(OutputType::P2PK65))?,
|
||||
p2pk33: full(Filter::Type(OutputType::P2PK33))?,
|
||||
p2pkh: full(Filter::Type(OutputType::P2PKH))?,
|
||||
p2sh: full(Filter::Type(OutputType::P2SH))?,
|
||||
p2wpkh: full(Filter::Type(OutputType::P2WPKH))?,
|
||||
p2wsh: full(Filter::Type(OutputType::P2WSH))?,
|
||||
p2tr: full(Filter::Type(OutputType::P2TR))?,
|
||||
p2a: full(Filter::Type(OutputType::P2A))?,
|
||||
p2ms: full(Filter::Type(OutputType::P2MS))?,
|
||||
empty: full(Filter::Type(OutputType::Empty))?,
|
||||
unknown: full(Filter::Type(OutputType::Unknown))?,
|
||||
},
|
||||
let type_ = BySpendableType {
|
||||
p2pk65: full(Filter::Type(OutputType::P2PK65))?,
|
||||
p2pk33: full(Filter::Type(OutputType::P2PK33))?,
|
||||
p2pkh: full(Filter::Type(OutputType::P2PKH))?,
|
||||
p2sh: full(Filter::Type(OutputType::P2SH))?,
|
||||
p2wpkh: full(Filter::Type(OutputType::P2WPKH))?,
|
||||
p2wsh: full(Filter::Type(OutputType::P2WSH))?,
|
||||
p2tr: full(Filter::Type(OutputType::P2TR))?,
|
||||
p2a: full(Filter::Type(OutputType::P2A))?,
|
||||
p2ms: full(Filter::Type(OutputType::P2MS))?,
|
||||
empty: full(Filter::Type(OutputType::Empty))?,
|
||||
unknown: full(Filter::Type(OutputType::Unknown))?,
|
||||
};
|
||||
|
||||
max_age: ByMaxAge {
|
||||
_1w: none(Filter::Time(TimeFilter::LowerThan(DAYS_1W)))?,
|
||||
_1m: none(Filter::Time(TimeFilter::LowerThan(DAYS_1M)))?,
|
||||
_2m: none(Filter::Time(TimeFilter::LowerThan(DAYS_2M)))?,
|
||||
_3m: none(Filter::Time(TimeFilter::LowerThan(DAYS_3M)))?,
|
||||
_4m: none(Filter::Time(TimeFilter::LowerThan(DAYS_4M)))?,
|
||||
_5m: none(Filter::Time(TimeFilter::LowerThan(DAYS_5M)))?,
|
||||
_6m: none(Filter::Time(TimeFilter::LowerThan(DAYS_6M)))?,
|
||||
_1y: none(Filter::Time(TimeFilter::LowerThan(DAYS_1Y)))?,
|
||||
_2y: none(Filter::Time(TimeFilter::LowerThan(DAYS_2Y)))?,
|
||||
_3y: none(Filter::Time(TimeFilter::LowerThan(DAYS_3Y)))?,
|
||||
_4y: none(Filter::Time(TimeFilter::LowerThan(DAYS_4Y)))?,
|
||||
_5y: none(Filter::Time(TimeFilter::LowerThan(DAYS_5Y)))?,
|
||||
_6y: none(Filter::Time(TimeFilter::LowerThan(DAYS_6Y)))?,
|
||||
_7y: none(Filter::Time(TimeFilter::LowerThan(DAYS_7Y)))?,
|
||||
_8y: none(Filter::Time(TimeFilter::LowerThan(DAYS_8Y)))?,
|
||||
_10y: none(Filter::Time(TimeFilter::LowerThan(DAYS_10Y)))?,
|
||||
_12y: none(Filter::Time(TimeFilter::LowerThan(DAYS_12Y)))?,
|
||||
_15y: none(Filter::Time(TimeFilter::LowerThan(DAYS_15Y)))?,
|
||||
},
|
||||
let max_age = ByMaxAge {
|
||||
_1w: none(Filter::Time(TimeFilter::LowerThan(DAYS_1W)))?,
|
||||
_1m: none(Filter::Time(TimeFilter::LowerThan(DAYS_1M)))?,
|
||||
_2m: none(Filter::Time(TimeFilter::LowerThan(DAYS_2M)))?,
|
||||
_3m: none(Filter::Time(TimeFilter::LowerThan(DAYS_3M)))?,
|
||||
_4m: none(Filter::Time(TimeFilter::LowerThan(DAYS_4M)))?,
|
||||
_5m: none(Filter::Time(TimeFilter::LowerThan(DAYS_5M)))?,
|
||||
_6m: none(Filter::Time(TimeFilter::LowerThan(DAYS_6M)))?,
|
||||
_1y: none(Filter::Time(TimeFilter::LowerThan(DAYS_1Y)))?,
|
||||
_2y: none(Filter::Time(TimeFilter::LowerThan(DAYS_2Y)))?,
|
||||
_3y: none(Filter::Time(TimeFilter::LowerThan(DAYS_3Y)))?,
|
||||
_4y: none(Filter::Time(TimeFilter::LowerThan(DAYS_4Y)))?,
|
||||
_5y: none(Filter::Time(TimeFilter::LowerThan(DAYS_5Y)))?,
|
||||
_6y: none(Filter::Time(TimeFilter::LowerThan(DAYS_6Y)))?,
|
||||
_7y: none(Filter::Time(TimeFilter::LowerThan(DAYS_7Y)))?,
|
||||
_8y: none(Filter::Time(TimeFilter::LowerThan(DAYS_8Y)))?,
|
||||
_10y: none(Filter::Time(TimeFilter::LowerThan(DAYS_10Y)))?,
|
||||
_12y: none(Filter::Time(TimeFilter::LowerThan(DAYS_12Y)))?,
|
||||
_15y: none(Filter::Time(TimeFilter::LowerThan(DAYS_15Y)))?,
|
||||
};
|
||||
|
||||
min_age: ByMinAge {
|
||||
_1d: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1D)))?,
|
||||
_1w: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1W)))?,
|
||||
_1m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1M)))?,
|
||||
_2m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2M)))?,
|
||||
_3m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3M)))?,
|
||||
_4m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4M)))?,
|
||||
_5m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5M)))?,
|
||||
_6m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6M)))?,
|
||||
_1y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1Y)))?,
|
||||
_2y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2Y)))?,
|
||||
_3y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3Y)))?,
|
||||
_4y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4Y)))?,
|
||||
_5y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5Y)))?,
|
||||
_6y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6Y)))?,
|
||||
_7y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_7Y)))?,
|
||||
_8y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_8Y)))?,
|
||||
_10y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_10Y)))?,
|
||||
_12y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_12Y)))?,
|
||||
},
|
||||
let min_age = ByMinAge {
|
||||
_1d: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1D)))?,
|
||||
_1w: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1W)))?,
|
||||
_1m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1M)))?,
|
||||
_2m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2M)))?,
|
||||
_3m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3M)))?,
|
||||
_4m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4M)))?,
|
||||
_5m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5M)))?,
|
||||
_6m: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6M)))?,
|
||||
_1y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_1Y)))?,
|
||||
_2y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_2Y)))?,
|
||||
_3y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_3Y)))?,
|
||||
_4y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_4Y)))?,
|
||||
_5y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_5Y)))?,
|
||||
_6y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_6Y)))?,
|
||||
_7y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_7Y)))?,
|
||||
_8y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_8Y)))?,
|
||||
_10y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_10Y)))?,
|
||||
_12y: none(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_12Y)))?,
|
||||
};
|
||||
|
||||
age_range: ByAgeRange {
|
||||
up_to_1d: full(Filter::Time(TimeFilter::Range(0..DAYS_1D)))?,
|
||||
_1d_to_1w: full(Filter::Time(TimeFilter::Range(DAYS_1D..DAYS_1W)))?,
|
||||
_1w_to_1m: full(Filter::Time(TimeFilter::Range(DAYS_1W..DAYS_1M)))?,
|
||||
_1m_to_2m: full(Filter::Time(TimeFilter::Range(DAYS_1M..DAYS_2M)))?,
|
||||
_2m_to_3m: full(Filter::Time(TimeFilter::Range(DAYS_2M..DAYS_3M)))?,
|
||||
_3m_to_4m: full(Filter::Time(TimeFilter::Range(DAYS_3M..DAYS_4M)))?,
|
||||
_4m_to_5m: full(Filter::Time(TimeFilter::Range(DAYS_4M..DAYS_5M)))?,
|
||||
_5m_to_6m: full(Filter::Time(TimeFilter::Range(DAYS_5M..DAYS_6M)))?,
|
||||
_6m_to_1y: full(Filter::Time(TimeFilter::Range(DAYS_6M..DAYS_1Y)))?,
|
||||
_1y_to_2y: full(Filter::Time(TimeFilter::Range(DAYS_1Y..DAYS_2Y)))?,
|
||||
_2y_to_3y: full(Filter::Time(TimeFilter::Range(DAYS_2Y..DAYS_3Y)))?,
|
||||
_3y_to_4y: full(Filter::Time(TimeFilter::Range(DAYS_3Y..DAYS_4Y)))?,
|
||||
_4y_to_5y: full(Filter::Time(TimeFilter::Range(DAYS_4Y..DAYS_5Y)))?,
|
||||
_5y_to_6y: full(Filter::Time(TimeFilter::Range(DAYS_5Y..DAYS_6Y)))?,
|
||||
_6y_to_7y: full(Filter::Time(TimeFilter::Range(DAYS_6Y..DAYS_7Y)))?,
|
||||
_7y_to_8y: full(Filter::Time(TimeFilter::Range(DAYS_7Y..DAYS_8Y)))?,
|
||||
_8y_to_10y: full(Filter::Time(TimeFilter::Range(DAYS_8Y..DAYS_10Y)))?,
|
||||
_10y_to_12y: full(Filter::Time(TimeFilter::Range(DAYS_10Y..DAYS_12Y)))?,
|
||||
_12y_to_15y: full(Filter::Time(TimeFilter::Range(DAYS_12Y..DAYS_15Y)))?,
|
||||
from_15y: full(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_15Y)))?,
|
||||
},
|
||||
let age_range = ByAgeRange {
|
||||
up_to_1d: full(Filter::Time(TimeFilter::Range(0..DAYS_1D)))?,
|
||||
_1d_to_1w: full(Filter::Time(TimeFilter::Range(DAYS_1D..DAYS_1W)))?,
|
||||
_1w_to_1m: full(Filter::Time(TimeFilter::Range(DAYS_1W..DAYS_1M)))?,
|
||||
_1m_to_2m: full(Filter::Time(TimeFilter::Range(DAYS_1M..DAYS_2M)))?,
|
||||
_2m_to_3m: full(Filter::Time(TimeFilter::Range(DAYS_2M..DAYS_3M)))?,
|
||||
_3m_to_4m: full(Filter::Time(TimeFilter::Range(DAYS_3M..DAYS_4M)))?,
|
||||
_4m_to_5m: full(Filter::Time(TimeFilter::Range(DAYS_4M..DAYS_5M)))?,
|
||||
_5m_to_6m: full(Filter::Time(TimeFilter::Range(DAYS_5M..DAYS_6M)))?,
|
||||
_6m_to_1y: full(Filter::Time(TimeFilter::Range(DAYS_6M..DAYS_1Y)))?,
|
||||
_1y_to_2y: full(Filter::Time(TimeFilter::Range(DAYS_1Y..DAYS_2Y)))?,
|
||||
_2y_to_3y: full(Filter::Time(TimeFilter::Range(DAYS_2Y..DAYS_3Y)))?,
|
||||
_3y_to_4y: full(Filter::Time(TimeFilter::Range(DAYS_3Y..DAYS_4Y)))?,
|
||||
_4y_to_5y: full(Filter::Time(TimeFilter::Range(DAYS_4Y..DAYS_5Y)))?,
|
||||
_5y_to_6y: full(Filter::Time(TimeFilter::Range(DAYS_5Y..DAYS_6Y)))?,
|
||||
_6y_to_7y: full(Filter::Time(TimeFilter::Range(DAYS_6Y..DAYS_7Y)))?,
|
||||
_7y_to_8y: full(Filter::Time(TimeFilter::Range(DAYS_7Y..DAYS_8Y)))?,
|
||||
_8y_to_10y: full(Filter::Time(TimeFilter::Range(DAYS_8Y..DAYS_10Y)))?,
|
||||
_10y_to_12y: full(Filter::Time(TimeFilter::Range(DAYS_10Y..DAYS_12Y)))?,
|
||||
_12y_to_15y: full(Filter::Time(TimeFilter::Range(DAYS_12Y..DAYS_15Y)))?,
|
||||
from_15y: full(Filter::Time(TimeFilter::GreaterOrEqual(DAYS_15Y)))?,
|
||||
};
|
||||
|
||||
amount_range: ByAmountRange {
|
||||
_0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?,
|
||||
_1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?,
|
||||
_10sats_to_100sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10..Sats::_100,
|
||||
)))?,
|
||||
_100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100..Sats::_1K,
|
||||
)))?,
|
||||
_1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1K..Sats::_10K,
|
||||
)))?,
|
||||
_10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10K..Sats::_100K,
|
||||
)))?,
|
||||
_100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100K..Sats::_1M,
|
||||
)))?,
|
||||
_1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1M..Sats::_10M,
|
||||
)))?,
|
||||
_10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10M..Sats::_1BTC,
|
||||
)))?,
|
||||
_1btc_to_10btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1BTC..Sats::_10BTC,
|
||||
)))?,
|
||||
_10btc_to_100btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10BTC..Sats::_100BTC,
|
||||
)))?,
|
||||
_100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100BTC..Sats::_1K_BTC,
|
||||
)))?,
|
||||
_1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1K_BTC..Sats::_10K_BTC,
|
||||
)))?,
|
||||
_10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10K_BTC..Sats::_100K_BTC,
|
||||
)))?,
|
||||
_100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||
Sats::_100K_BTC,
|
||||
)))?,
|
||||
},
|
||||
let amount_range = ByAmountRange {
|
||||
_0sats: full(Filter::Amount(AmountFilter::LowerThan(Sats::_1)))?,
|
||||
_1sat_to_10sats: full(Filter::Amount(AmountFilter::Range(Sats::_1..Sats::_10)))?,
|
||||
_10sats_to_100sats: full(Filter::Amount(AmountFilter::Range(Sats::_10..Sats::_100)))?,
|
||||
_100sats_to_1k_sats: full(Filter::Amount(AmountFilter::Range(Sats::_100..Sats::_1K)))?,
|
||||
_1k_sats_to_10k_sats: full(Filter::Amount(AmountFilter::Range(Sats::_1K..Sats::_10K)))?,
|
||||
_10k_sats_to_100k_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10K..Sats::_100K,
|
||||
)))?,
|
||||
_100k_sats_to_1m_sats: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100K..Sats::_1M,
|
||||
)))?,
|
||||
_1m_sats_to_10m_sats: full(Filter::Amount(AmountFilter::Range(Sats::_1M..Sats::_10M)))?,
|
||||
_10m_sats_to_1btc: full(Filter::Amount(AmountFilter::Range(Sats::_10M..Sats::_1BTC)))?,
|
||||
_1btc_to_10btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1BTC..Sats::_10BTC,
|
||||
)))?,
|
||||
_10btc_to_100btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10BTC..Sats::_100BTC,
|
||||
)))?,
|
||||
_100btc_to_1k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_100BTC..Sats::_1K_BTC,
|
||||
)))?,
|
||||
_1k_btc_to_10k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_1K_BTC..Sats::_10K_BTC,
|
||||
)))?,
|
||||
_10k_btc_to_100k_btc: full(Filter::Amount(AmountFilter::Range(
|
||||
Sats::_10K_BTC..Sats::_100K_BTC,
|
||||
)))?,
|
||||
_100k_btc_or_more: full(Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||
Sats::_100K_BTC,
|
||||
)))?,
|
||||
};
|
||||
|
||||
lt_amount: ByLowerThanAmount {
|
||||
_10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?,
|
||||
_100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?,
|
||||
_1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?,
|
||||
_10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?,
|
||||
_100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?,
|
||||
_1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?,
|
||||
_10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?,
|
||||
_1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?,
|
||||
_10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?,
|
||||
_100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?,
|
||||
_1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?,
|
||||
_10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?,
|
||||
_100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?,
|
||||
},
|
||||
let lt_amount = ByLowerThanAmount {
|
||||
_10sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10)))?,
|
||||
_100sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100)))?,
|
||||
_1k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K)))?,
|
||||
_10k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K)))?,
|
||||
_100k_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K)))?,
|
||||
_1m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1M)))?,
|
||||
_10m_sats: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10M)))?,
|
||||
_1btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1BTC)))?,
|
||||
_10btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10BTC)))?,
|
||||
_100btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100BTC)))?,
|
||||
_1k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_1K_BTC)))?,
|
||||
_10k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_10K_BTC)))?,
|
||||
_100k_btc: none(Filter::Amount(AmountFilter::LowerThan(Sats::_100K_BTC)))?,
|
||||
};
|
||||
|
||||
ge_amount: ByGreatEqualAmount {
|
||||
_1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?,
|
||||
_10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?,
|
||||
_100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?,
|
||||
_1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?,
|
||||
_10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?,
|
||||
_100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?,
|
||||
_1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?,
|
||||
_10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?,
|
||||
_1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?,
|
||||
_10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?,
|
||||
_100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?,
|
||||
_1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?,
|
||||
_10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?,
|
||||
},
|
||||
let ge_amount = ByGreatEqualAmount {
|
||||
_1sat: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1)))?,
|
||||
_10sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10)))?,
|
||||
_100sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100)))?,
|
||||
_1k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K)))?,
|
||||
_10k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K)))?,
|
||||
_100k_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100K)))?,
|
||||
_1m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1M)))?,
|
||||
_10m_sats: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10M)))?,
|
||||
_1btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1BTC)))?,
|
||||
_10btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10BTC)))?,
|
||||
_100btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_100BTC)))?,
|
||||
_1k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_1K_BTC)))?,
|
||||
_10k_btc: none(Filter::Amount(AmountFilter::GreaterOrEqual(Sats::_10K_BTC)))?,
|
||||
};
|
||||
|
||||
Ok(Self(UTXOGroups {
|
||||
all,
|
||||
term,
|
||||
epoch,
|
||||
year,
|
||||
type_,
|
||||
max_age,
|
||||
min_age,
|
||||
age_range,
|
||||
amount_range,
|
||||
lt_amount,
|
||||
ge_amount,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -335,20 +379,18 @@ impl UTXOCohorts {
|
||||
|
||||
/// Second phase of post-processing: compute relative metrics.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2<S, D, HM, DM>(
|
||||
pub fn compute_rest_part2<S, HM, DM>(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &S,
|
||||
dateindex_to_supply: &D,
|
||||
height_to_market_cap: Option<&HM>,
|
||||
dateindex_to_market_cap: Option<&DM>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: IterableVec<Height, Bitcoin> + Sync,
|
||||
D: IterableVec<DateIndex, Bitcoin> + Sync,
|
||||
HM: IterableVec<Height, Dollars> + Sync,
|
||||
DM: IterableVec<DateIndex, Dollars> + Sync,
|
||||
{
|
||||
@@ -358,7 +400,6 @@ impl UTXOCohorts {
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
|
||||
@@ -49,21 +49,19 @@ pub fn compute_rest_part1(
|
||||
///
|
||||
/// Computes supply ratios, market cap ratios, etc. using total references.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2<S, D, HM, DM>(
|
||||
pub fn compute_rest_part2<S, HM, DM>(
|
||||
utxo_cohorts: &mut UTXOCohorts,
|
||||
address_cohorts: &mut AddressCohorts,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &S,
|
||||
dateindex_to_supply: &D,
|
||||
height_to_market_cap: Option<&HM>,
|
||||
dateindex_to_market_cap: Option<&DM>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: IterableVec<Height, Bitcoin> + Sync,
|
||||
D: IterableVec<DateIndex, Bitcoin> + Sync,
|
||||
HM: IterableVec<Height, Dollars> + Sync,
|
||||
DM: IterableVec<DateIndex, Dollars> + Sync,
|
||||
{
|
||||
@@ -74,7 +72,6 @@ where
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
@@ -85,7 +82,6 @@ where
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
|
||||
@@ -55,14 +55,6 @@ pub fn process_blocks(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!(
|
||||
"Processing blocks {} to {} (compute_dollars: {}, price_data: {})...",
|
||||
ctx.starting_height,
|
||||
ctx.last_height,
|
||||
ctx.compute_dollars,
|
||||
ctx.price.is_some()
|
||||
);
|
||||
|
||||
// References to vectors using correct field paths
|
||||
// From indexer.vecs:
|
||||
let height_to_first_txindex = &indexer.vecs.tx.height_to_first_txindex;
|
||||
|
||||
@@ -51,7 +51,10 @@ impl<'a> ComputeContext<'a> {
|
||||
|
||||
/// Get price at height (None if no price data or height out of range).
|
||||
pub fn price_at(&self, height: Height) -> Option<Dollars> {
|
||||
self.height_to_price.as_ref()?.get(height.to_usize()).copied()
|
||||
self.height_to_price
|
||||
.as_ref()?
|
||||
.get(height.to_usize())
|
||||
.copied()
|
||||
}
|
||||
|
||||
/// Get timestamp at height.
|
||||
|
||||
@@ -31,8 +31,6 @@ pub fn process_address_updates(
|
||||
empty_updates: AddressTypeToTypeIndexMap<EmptyAddressDataWithSource>,
|
||||
loaded_updates: AddressTypeToTypeIndexMap<LoadedAddressDataWithSource>,
|
||||
) -> Result<()> {
|
||||
info!("Processing address updates...");
|
||||
|
||||
let empty_result = process_empty_addresses(addresses_data, empty_updates)?;
|
||||
let loaded_result = process_loaded_addresses(addresses_data, loaded_updates)?;
|
||||
let all_updates = empty_result.merge(loaded_result);
|
||||
|
||||
@@ -50,7 +50,11 @@ pub struct CohortMetrics {
|
||||
|
||||
impl CohortMetrics {
|
||||
/// Import all metrics from database.
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
///
|
||||
/// `all_supply` is the supply metrics from the "all" cohort, used as global
|
||||
/// sources for `*_rel_to_market_cap` and `*_rel_to_circulating_supply` ratios.
|
||||
/// Pass `None` for the "all" cohort itself.
|
||||
pub fn forced_import(cfg: &ImportConfig, all_supply: Option<&SupplyMetrics>) -> Result<Self> {
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
@@ -61,7 +65,7 @@ impl CohortMetrics {
|
||||
|
||||
let relative = unrealized
|
||||
.as_ref()
|
||||
.map(|u| RelativeMetrics::forced_import(cfg, u, &supply))
|
||||
.map(|u| RelativeMetrics::forced_import(cfg, u, &supply, all_supply))
|
||||
.transpose()?;
|
||||
|
||||
Ok(Self {
|
||||
@@ -290,7 +294,6 @@ impl CohortMetrics {
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
@@ -307,20 +310,6 @@ impl CohortMetrics {
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(relative) = self.relative.as_mut() {
|
||||
relative.compute_rest_part2(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
&self.supply,
|
||||
self.unrealized.as_ref(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,70 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, StoredF32, StoredF64, Version};
|
||||
use vecdb::{
|
||||
EagerVec, Exit, ImportableVec, IterableCloneableVec, IterableVec, LazyVecFrom1, LazyVecFrom2,
|
||||
Negate, PcoVec,
|
||||
};
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, StoredF32, StoredF64, Version};
|
||||
use vecdb::{IterableCloneableVec, LazyVecFrom2};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{
|
||||
ComputedVecsFromDateIndex, ComputedVecsFromHeight, LazyVecsFrom2FromDateIndex,
|
||||
LazyVecsFromDateIndex, NegPercentageDollarsF32, NegRatio32, PercentageDollarsF32,
|
||||
PercentageSatsF64, Ratio32, Source, VecBuilderOptions,
|
||||
},
|
||||
indexes,
|
||||
use crate::grouped::{
|
||||
LazyVecsFrom2FromDateIndex, NegPercentageDollarsF32, NegRatio32, PercentageBtcF64,
|
||||
PercentageDollarsF32, PercentageSatsF64, Ratio32,
|
||||
};
|
||||
|
||||
use super::{ImportConfig, SupplyMetrics, UnrealizedMetrics};
|
||||
|
||||
/// Relative metrics comparing cohort values to global values.
|
||||
/// All `rel_to_` vecs are lazy - computed on-demand from their sources.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct RelativeMetrics {
|
||||
// === Supply Relative to Circulating Supply ===
|
||||
pub indexes_to_supply_rel_to_circulating_supply: Option<ComputedVecsFromHeight<StoredF64>>,
|
||||
// === Supply Relative to Circulating Supply (lazy from global supply) ===
|
||||
pub indexes_to_supply_rel_to_circulating_supply:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>>,
|
||||
|
||||
// === Supply in Profit/Loss Relative to Own Supply ===
|
||||
pub height_to_supply_in_profit_rel_to_own_supply: EagerVec<PcoVec<Height, StoredF64>>,
|
||||
pub height_to_supply_in_loss_rel_to_own_supply: EagerVec<PcoVec<Height, StoredF64>>,
|
||||
// === Supply in Profit/Loss Relative to Own Supply (lazy) ===
|
||||
pub height_to_supply_in_profit_rel_to_own_supply:
|
||||
LazyVecFrom2<Height, StoredF64, Height, Bitcoin, Height, Bitcoin>,
|
||||
pub height_to_supply_in_loss_rel_to_own_supply:
|
||||
LazyVecFrom2<Height, StoredF64, Height, Bitcoin, Height, Bitcoin>,
|
||||
pub indexes_to_supply_in_profit_rel_to_own_supply:
|
||||
LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>,
|
||||
pub indexes_to_supply_in_loss_rel_to_own_supply:
|
||||
LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>,
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply ===
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply (lazy from global supply) ===
|
||||
pub height_to_supply_in_profit_rel_to_circulating_supply:
|
||||
Option<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF64, Height, Bitcoin, Height, Bitcoin>>,
|
||||
pub height_to_supply_in_loss_rel_to_circulating_supply:
|
||||
Option<EagerVec<PcoVec<Height, StoredF64>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF64, Height, Bitcoin, Height, Bitcoin>>,
|
||||
pub indexes_to_supply_in_profit_rel_to_circulating_supply:
|
||||
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>>,
|
||||
pub indexes_to_supply_in_loss_rel_to_circulating_supply:
|
||||
Option<ComputedVecsFromDateIndex<StoredF64>>,
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>>,
|
||||
|
||||
// === Unrealized vs Market Cap ===
|
||||
pub height_to_unrealized_profit_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
pub height_to_unrealized_loss_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
// === Unrealized vs Market Cap (lazy from global market cap) ===
|
||||
pub height_to_unrealized_profit_rel_to_market_cap:
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_unrealized_loss_rel_to_market_cap:
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_market_cap:
|
||||
LazyVecFrom1<Height, StoredF32, Height, StoredF32>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_unrealized_loss_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_market_cap: LazyVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_market_cap:
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_market_cap:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_unrealized_loss_rel_to_market_cap:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_market_cap:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_market_cap:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
|
||||
// === Unrealized vs Own Market Cap (optional) ===
|
||||
// === Unrealized vs Own Market Cap (lazy) ===
|
||||
pub height_to_unrealized_profit_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<LazyVecFrom1<Height, StoredF32, Height, StoredF32>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_own_market_cap:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_unrealized_loss_rel_to_own_market_cap:
|
||||
@@ -71,7 +74,7 @@ pub struct RelativeMetrics {
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL (optional) ===
|
||||
// === Unrealized vs Own Total Unrealized PnL (lazy) ===
|
||||
pub height_to_unrealized_profit_rel_to_own_total_unrealized_pnl:
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
@@ -92,176 +95,66 @@ pub struct RelativeMetrics {
|
||||
|
||||
impl RelativeMetrics {
|
||||
/// Import relative metrics from database.
|
||||
///
|
||||
/// All `rel_to_` metrics are lazy - computed on-demand from their sources.
|
||||
/// `all_supply` provides global sources for `*_rel_to_market_cap` and `*_rel_to_circulating_supply`.
|
||||
pub fn forced_import(
|
||||
cfg: &ImportConfig,
|
||||
unrealized: &UnrealizedMetrics,
|
||||
supply: &SupplyMetrics,
|
||||
all_supply: Option<&SupplyMetrics>,
|
||||
) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let v1 = Version::ONE;
|
||||
let v2 = Version::new(2);
|
||||
let extended = cfg.extended();
|
||||
let compute_rel_to_all = cfg.compute_rel_to_all();
|
||||
let last = VecBuilderOptions::default().add_last();
|
||||
|
||||
// Create sources for lazy neg vecs
|
||||
let height_to_unrealized_loss_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>> =
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
)?;
|
||||
// Global sources from "all" cohort
|
||||
let global_supply_sats = all_supply.map(|s| &s.indexes_to_supply.sats);
|
||||
let global_supply_btc = all_supply.map(|s| &s.height_to_supply_value.bitcoin);
|
||||
let global_market_cap = all_supply.and_then(|s| s.indexes_to_supply.dollars.as_ref());
|
||||
let global_market_cap_height =
|
||||
all_supply.and_then(|s| s.height_to_supply_value.dollars.as_ref());
|
||||
|
||||
let height_to_neg_unrealized_loss_rel_to_market_cap = LazyVecFrom1::transformed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
height_to_unrealized_loss_rel_to_market_cap.boxed_clone(),
|
||||
);
|
||||
|
||||
let indexes_to_unrealized_loss_rel_to_market_cap =
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?;
|
||||
|
||||
let indexes_to_neg_unrealized_loss_rel_to_market_cap =
|
||||
LazyVecsFromDateIndex::from_computed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v1,
|
||||
indexes_to_unrealized_loss_rel_to_market_cap
|
||||
.dateindex
|
||||
.as_ref()
|
||||
.map(|v| v.boxed_clone()),
|
||||
&indexes_to_unrealized_loss_rel_to_market_cap,
|
||||
);
|
||||
|
||||
// Optional: own market cap vecs
|
||||
let height_to_unrealized_loss_rel_to_own_market_cap: Option<
|
||||
EagerVec<PcoVec<Height, StoredF32>>,
|
||||
> = (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let height_to_neg_unrealized_loss_rel_to_own_market_cap =
|
||||
height_to_unrealized_loss_rel_to_own_market_cap
|
||||
.as_ref()
|
||||
.map(|source| {
|
||||
LazyVecFrom1::transformed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
source.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
// Optional: own total unrealized pnl vecs (lazy from unrealized sources)
|
||||
let height_to_unrealized_profit_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_profit.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let height_to_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<NegRatio32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
unrealized.height_to_net_unrealized_pnl.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_profit,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<NegRatio32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_net_unrealized_pnl,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
// Own market cap source
|
||||
let own_market_cap = supply.indexes_to_supply.dollars.as_ref();
|
||||
let own_market_cap_height = supply.height_to_supply_value.dollars.as_ref();
|
||||
|
||||
Ok(Self {
|
||||
// === Supply Relative to Circulating Supply ===
|
||||
indexes_to_supply_rel_to_circulating_supply: compute_rel_to_all
|
||||
.then(|| {
|
||||
ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_rel_to_circulating_supply"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
// === Supply Relative to Circulating Supply (lazy from global supply) ===
|
||||
indexes_to_supply_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_sats.is_some())
|
||||
.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageSatsF64>(
|
||||
&cfg.name("supply_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
&supply.indexes_to_supply.sats,
|
||||
global_supply_sats.unwrap(),
|
||||
)
|
||||
}),
|
||||
|
||||
// === Supply in Profit/Loss Relative to Own Supply ===
|
||||
height_to_supply_in_profit_rel_to_own_supply: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
// === Supply in Profit/Loss Relative to Own Supply (lazy) ===
|
||||
height_to_supply_in_profit_rel_to_own_supply: LazyVecFrom2::transformed::<
|
||||
PercentageBtcF64,
|
||||
>(
|
||||
&cfg.name("supply_in_profit_rel_to_own_supply"),
|
||||
cfg.version + v1,
|
||||
)?,
|
||||
height_to_supply_in_loss_rel_to_own_supply: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
unrealized
|
||||
.height_to_supply_in_profit_value
|
||||
.bitcoin
|
||||
.boxed_clone(),
|
||||
supply.height_to_supply_value.bitcoin.boxed_clone(),
|
||||
),
|
||||
height_to_supply_in_loss_rel_to_own_supply: LazyVecFrom2::transformed::<PercentageBtcF64>(
|
||||
&cfg.name("supply_in_loss_rel_to_own_supply"),
|
||||
cfg.version + v1,
|
||||
)?,
|
||||
unrealized
|
||||
.height_to_supply_in_loss_value
|
||||
.bitcoin
|
||||
.boxed_clone(),
|
||||
supply.height_to_supply_value.bitcoin.boxed_clone(),
|
||||
),
|
||||
indexes_to_supply_in_profit_rel_to_own_supply:
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_profit_rel_to_own_supply"),
|
||||
@@ -278,412 +171,283 @@ impl RelativeMetrics {
|
||||
&supply.indexes_to_supply.sats,
|
||||
),
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply ===
|
||||
height_to_supply_in_profit_rel_to_circulating_supply: compute_rel_to_all
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_profit_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_supply_in_loss_rel_to_circulating_supply: compute_rel_to_all
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_supply_in_profit_rel_to_circulating_supply: compute_rel_to_all
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_profit_rel_to_circulating_supply"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_supply_in_loss_rel_to_circulating_supply: compute_rel_to_all
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply (lazy from global supply) ===
|
||||
height_to_supply_in_profit_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_btc.is_some())
|
||||
.then(|| {
|
||||
LazyVecFrom2::transformed::<PercentageBtcF64>(
|
||||
&cfg.name("supply_in_profit_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized
|
||||
.height_to_supply_in_profit_value
|
||||
.bitcoin
|
||||
.boxed_clone(),
|
||||
global_supply_btc.unwrap().boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_supply_in_loss_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_btc.is_some())
|
||||
.then(|| {
|
||||
LazyVecFrom2::transformed::<PercentageBtcF64>(
|
||||
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized
|
||||
.height_to_supply_in_loss_value
|
||||
.bitcoin
|
||||
.boxed_clone(),
|
||||
global_supply_btc.unwrap().boxed_clone(),
|
||||
)
|
||||
}),
|
||||
indexes_to_supply_in_profit_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_sats.is_some())
|
||||
.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_profit_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_supply_in_profit.sats,
|
||||
global_supply_sats.unwrap(),
|
||||
)
|
||||
}),
|
||||
indexes_to_supply_in_loss_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_sats.is_some())
|
||||
.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_supply_in_loss.sats,
|
||||
global_supply_sats.unwrap(),
|
||||
)
|
||||
}),
|
||||
|
||||
// === Unrealized vs Market Cap ===
|
||||
height_to_unrealized_profit_rel_to_market_cap: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_profit_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
height_to_unrealized_loss_rel_to_market_cap,
|
||||
height_to_neg_unrealized_loss_rel_to_market_cap,
|
||||
height_to_net_unrealized_pnl_rel_to_market_cap: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_unrealized_pnl_rel_to_market_cap"),
|
||||
cfg.version + v1,
|
||||
)?,
|
||||
indexes_to_unrealized_profit_rel_to_market_cap:
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
// === Unrealized vs Market Cap (lazy from global market cap) ===
|
||||
height_to_unrealized_profit_rel_to_market_cap: global_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_profit_rel_to_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?,
|
||||
indexes_to_unrealized_loss_rel_to_market_cap,
|
||||
indexes_to_neg_unrealized_loss_rel_to_market_cap,
|
||||
indexes_to_net_unrealized_pnl_rel_to_market_cap:
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_profit.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_unrealized_loss_rel_to_market_cap: global_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_neg_unrealized_loss_rel_to_market_cap: global_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<NegPercentageDollarsF32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_net_unrealized_pnl_rel_to_market_cap: global_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<PercentageDollarsF32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?,
|
||||
unrealized.height_to_net_unrealized_pnl.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_profit_rel_to_market_cap: global_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_profit_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_profit,
|
||||
mc,
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_loss_rel_to_market_cap: global_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
mc,
|
||||
)
|
||||
}),
|
||||
indexes_to_neg_unrealized_loss_rel_to_market_cap: global_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<NegPercentageDollarsF32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
mc,
|
||||
)
|
||||
}),
|
||||
indexes_to_net_unrealized_pnl_rel_to_market_cap: global_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_net_unrealized_pnl,
|
||||
mc,
|
||||
)
|
||||
}),
|
||||
|
||||
// === Unrealized vs Own Market Cap (optional) ===
|
||||
// === Unrealized vs Own Market Cap (lazy, optional) ===
|
||||
height_to_unrealized_profit_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
own_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
unrealized.height_to_unrealized_profit.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_unrealized_loss_rel_to_own_market_cap,
|
||||
height_to_neg_unrealized_loss_rel_to_own_market_cap,
|
||||
.flatten(),
|
||||
height_to_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
own_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
height_to_neg_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
own_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<NegPercentageDollarsF32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
height_to_net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
)
|
||||
own_market_cap_height.map(|mc| {
|
||||
LazyVecFrom2::transformed::<PercentageDollarsF32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
unrealized.height_to_net_unrealized_pnl.boxed_clone(),
|
||||
mc.boxed_clone(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?,
|
||||
.flatten(),
|
||||
indexes_to_unrealized_profit_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
supply
|
||||
.indexes_to_supply
|
||||
.dollars
|
||||
.as_ref()
|
||||
.map(|supply_dollars| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_profit,
|
||||
supply_dollars,
|
||||
)
|
||||
})
|
||||
own_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_profit,
|
||||
mc,
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
indexes_to_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
supply
|
||||
.indexes_to_supply
|
||||
.dollars
|
||||
.as_ref()
|
||||
.map(|supply_dollars| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
supply_dollars,
|
||||
)
|
||||
})
|
||||
own_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
mc,
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
supply
|
||||
.indexes_to_supply
|
||||
.dollars
|
||||
.as_ref()
|
||||
.map(|supply_dollars| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<NegPercentageDollarsF32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
supply_dollars,
|
||||
)
|
||||
})
|
||||
own_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<NegPercentageDollarsF32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
mc,
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
supply
|
||||
.indexes_to_supply
|
||||
.dollars
|
||||
.as_ref()
|
||||
.map(|supply_dollars| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_net_unrealized_pnl,
|
||||
supply_dollars,
|
||||
)
|
||||
})
|
||||
own_market_cap.map(|mc| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
&unrealized.indexes_to_net_unrealized_pnl,
|
||||
mc,
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL (optional) ===
|
||||
height_to_unrealized_profit_rel_to_own_total_unrealized_pnl,
|
||||
height_to_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl,
|
||||
// === Unrealized vs Own Total Unrealized PnL (lazy, optional) ===
|
||||
height_to_unrealized_profit_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_profit.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_unrealized_loss_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecFrom2::transformed::<NegRatio32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
unrealized.height_to_net_unrealized_pnl.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_profit,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
}),
|
||||
indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
}),
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<NegRatio32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
}),
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_net_unrealized_pnl,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
/// Second phase of computed metrics (ratios, relative values).
|
||||
///
|
||||
/// This computes percentage ratios comparing cohort metrics to global metrics:
|
||||
/// - Supply relative to circulating supply
|
||||
/// - Supply in profit/loss relative to own supply and circulating supply
|
||||
/// - Unrealized profit/loss relative to market cap, total unrealized
|
||||
///
|
||||
/// See `stateful/common/compute.rs` lines 800-1200 for the full original implementation.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
supply: &SupplyMetrics,
|
||||
unrealized: Option<&super::UnrealizedMetrics>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// === Supply Relative to Circulating Supply ===
|
||||
if let Some(v) = self.indexes_to_supply_rel_to_circulating_supply.as_mut() {
|
||||
v.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&supply.height_to_supply_value.bitcoin,
|
||||
height_to_supply,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// === Supply in Profit/Loss Relative to Own Supply ===
|
||||
// Note: indexes_to_* versions are now lazy (LazyVecsFrom2FromDateIndex)
|
||||
if let Some(unrealized) = unrealized {
|
||||
self.height_to_supply_in_profit_rel_to_own_supply
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_supply_in_profit_value.bitcoin,
|
||||
&supply.height_to_supply_value.bitcoin,
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_supply_in_loss_rel_to_own_supply
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_supply_in_loss_value.bitcoin,
|
||||
&supply.height_to_supply_value.bitcoin,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply ===
|
||||
if let (Some(unrealized), Some(v)) = (
|
||||
unrealized,
|
||||
self.height_to_supply_in_profit_rel_to_circulating_supply
|
||||
.as_mut(),
|
||||
) {
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_supply_in_profit_value.bitcoin,
|
||||
height_to_supply,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let (Some(unrealized), Some(v)) = (
|
||||
unrealized,
|
||||
self.height_to_supply_in_loss_rel_to_circulating_supply
|
||||
.as_mut(),
|
||||
) {
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_supply_in_loss_value.bitcoin,
|
||||
height_to_supply,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// === Unrealized vs Market Cap ===
|
||||
if let (Some(unrealized), Some(height_to_mc)) = (unrealized, height_to_market_cap) {
|
||||
self.height_to_unrealized_profit_rel_to_market_cap
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_unrealized_profit,
|
||||
height_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_unrealized_loss_rel_to_market_cap
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_unrealized_loss,
|
||||
height_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_net_unrealized_pnl,
|
||||
height_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(dateindex_to_mc) = dateindex_to_market_cap
|
||||
&& let Some(unrealized) = unrealized
|
||||
{
|
||||
self.indexes_to_unrealized_profit_rel_to_market_cap
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
&unrealized.dateindex_to_unrealized_profit,
|
||||
dateindex_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.indexes_to_unrealized_loss_rel_to_market_cap
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
&unrealized.dateindex_to_unrealized_loss,
|
||||
dateindex_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(dateindex_to_mc) = dateindex_to_market_cap
|
||||
&& let Some(unrealized) = unrealized
|
||||
&& let Some(dateindex_vec) = unrealized.indexes_to_net_unrealized_pnl.dateindex.as_ref()
|
||||
{
|
||||
self.indexes_to_net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_vec,
|
||||
dateindex_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply (indexes) ===
|
||||
if let Some(v) = self
|
||||
.indexes_to_supply_in_profit_rel_to_circulating_supply
|
||||
.as_mut()
|
||||
&& let Some(unrealized) = unrealized
|
||||
&& let Some(dateindex_vec) = unrealized
|
||||
.indexes_to_supply_in_profit
|
||||
.bitcoin
|
||||
.dateindex
|
||||
.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_vec,
|
||||
dateindex_to_supply,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(v) = self
|
||||
.indexes_to_supply_in_loss_rel_to_circulating_supply
|
||||
.as_mut()
|
||||
&& let Some(unrealized) = unrealized
|
||||
&& let Some(dateindex_vec) = unrealized
|
||||
.indexes_to_supply_in_loss
|
||||
.bitcoin
|
||||
.dateindex
|
||||
.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_vec,
|
||||
dateindex_to_supply,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// === Unrealized vs Own Market Cap ===
|
||||
// own_market_cap = supply_value.dollars
|
||||
// Note: indexes_to_* versions are now lazy (LazyVecsFrom2FromDateIndex)
|
||||
if let Some(unrealized) = unrealized {
|
||||
if let Some(v) = self
|
||||
.height_to_unrealized_profit_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
&& let Some(supply_dollars) = supply.height_to_supply_value.dollars.as_ref()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_unrealized_profit,
|
||||
supply_dollars,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_unrealized_loss_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
&& let Some(supply_dollars) = supply.height_to_supply_value.dollars.as_ref()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_unrealized_loss,
|
||||
supply_dollars,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_net_unrealized_pnl_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
&& let Some(supply_dollars) = supply.height_to_supply_value.dollars.as_ref()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_net_unrealized_pnl,
|
||||
supply_dollars,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ mod range_map;
|
||||
mod states;
|
||||
mod vecs;
|
||||
|
||||
use states::*;
|
||||
pub use range_map::RangeMap;
|
||||
pub use vecs::Vecs;
|
||||
|
||||
|
||||
@@ -89,6 +89,16 @@ impl Vecs {
|
||||
|
||||
let utxo_cohorts = UTXOCohorts::forced_import(&db, version, indexes, price, &states_path)?;
|
||||
|
||||
// Create address cohorts with reference to utxo "all" cohort's supply for global ratios
|
||||
let address_cohorts = AddressCohorts::forced_import(
|
||||
&db,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
&states_path,
|
||||
Some(&utxo_cohorts.all.metrics.supply),
|
||||
)?;
|
||||
|
||||
// Create address data BytesVecs first so we can also use them for identity mappings
|
||||
let loadedaddressindex_to_loadedaddressdata = BytesVec::forced_import_with(
|
||||
vecdb::ImportOptions::new(&db, "loadedaddressdata", v0)
|
||||
@@ -212,14 +222,7 @@ impl Vecs {
|
||||
)?,
|
||||
|
||||
utxo_cohorts,
|
||||
|
||||
address_cohorts: AddressCohorts::forced_import(
|
||||
&db,
|
||||
version,
|
||||
indexes,
|
||||
price,
|
||||
&states_path,
|
||||
)?,
|
||||
address_cohorts,
|
||||
|
||||
any_address_indexes: AnyAddressIndexesVecs::forced_import(&db, v0)?,
|
||||
addresses_data: AddressesDataVecs {
|
||||
@@ -347,10 +350,6 @@ impl Vecs {
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!(
|
||||
"State recovery: resumed from checkpoint at height {}",
|
||||
recovered_height
|
||||
);
|
||||
(recovered_height, chain_state)
|
||||
};
|
||||
|
||||
@@ -453,16 +452,6 @@ impl Vecs {
|
||||
.bitcoin
|
||||
.clone();
|
||||
|
||||
let dateindex_to_supply = self
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.indexes_to_supply
|
||||
.bitcoin
|
||||
.dateindex
|
||||
.clone();
|
||||
|
||||
let height_to_market_cap = self.height_to_market_cap.clone();
|
||||
|
||||
let dateindex_to_market_cap = self
|
||||
@@ -470,7 +459,6 @@ impl Vecs {
|
||||
.as_ref()
|
||||
.map(|v| v.dateindex.u().clone());
|
||||
|
||||
let dateindex_to_supply_ref = dateindex_to_supply.u();
|
||||
let height_to_market_cap_ref = height_to_market_cap.as_ref();
|
||||
let dateindex_to_market_cap_ref = dateindex_to_market_cap.as_ref();
|
||||
|
||||
@@ -481,7 +469,6 @@ impl Vecs {
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply_ref,
|
||||
height_to_market_cap_ref,
|
||||
dateindex_to_market_cap_ref,
|
||||
exit,
|
||||
|
||||
Reference in New Issue
Block a user