global: snapshot

This commit is contained in:
nym21
2025-05-29 10:39:58 +02:00
parent 99818924ee
commit cfc3081e8a
12 changed files with 228 additions and 151 deletions
@@ -0,0 +1,957 @@
use std::{fs, path::Path};
use brk_core::{
CheckedSub, DateIndex, Dollars, Height, Result, Sats, StoredF32, StoredUsize, Version,
};
use brk_exit::Exit;
use brk_indexer::Indexer;
use brk_state::{CohortState, RealizedState};
use brk_vec::{AnyCollectableVec, AnyVec, Compressed, Computation, EagerVec, VecIterator};
use crate::vecs::{
Indexes, fetched,
grouped::{
ComputedRatioVecsFromDateIndex, ComputedValueVecsFromHeight, ComputedVecsFromHeight,
StorableVecGeneatorOptions,
},
indexes,
};
const VERSION: Version = Version::ZERO;
pub struct Vecs {
starting_height: Height,
pub state: CohortState,
// Cumulative
pub height_to_realized_cap: Option<EagerVec<Height, Dollars>>,
pub height_to_supply: EagerVec<Height, Sats>,
pub height_to_utxo_count: EagerVec<Height, StoredUsize>,
// Single
pub height_to_realized_profit: Option<EagerVec<Height, Dollars>>,
pub height_to_realized_loss: Option<EagerVec<Height, Dollars>>,
pub height_to_value_created: Option<EagerVec<Height, Dollars>>,
pub height_to_adjusted_value_created: Option<EagerVec<Height, Dollars>>,
pub height_to_value_destroyed: Option<EagerVec<Height, Dollars>>,
pub height_to_adjusted_value_destroyed: Option<EagerVec<Height, Dollars>>,
pub dateindex_to_adjusted_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_realized_cap_30d_change: Option<EagerVec<DateIndex, Dollars>>,
pub dateindex_to_sell_side_risk_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub indexes_to_adjusted_value_created: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_adjusted_value_destroyed: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_negative_realized_loss: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_net_realized_profit_and_loss: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_realized_cap: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_realized_loss: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_realized_price: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_realized_price_extra: Option<ComputedRatioVecsFromDateIndex>,
pub indexes_to_realized_profit: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_realized_value: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_supply: ComputedValueVecsFromHeight,
pub indexes_to_utxo_count: ComputedVecsFromHeight<StoredUsize>,
pub indexes_to_value_created: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_value_destroyed: Option<ComputedVecsFromHeight<Dollars>>,
}
impl Vecs {
pub fn forced_import(
path: &Path,
cohort_name: Option<&str>,
_computation: Computation,
compressed: Compressed,
version: Version,
fetched: Option<&fetched::Vecs>,
) -> color_eyre::Result<Self> {
let compute_dollars = fetched.is_some();
fs::create_dir_all(path)?;
// let prefix = |s: &str| cohort_name.map_or(s.to_string(), |name| format!("{s}_{name}"));
let suffix = |s: &str| cohort_name.map_or(s.to_string(), |name| format!("{name}_{s}"));
let mut state = CohortState::default();
if compute_dollars {
state.realized = Some(RealizedState::NAN);
}
Ok(Self {
starting_height: Height::ZERO,
state,
height_to_realized_cap: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("realized_cap"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_realized_cap: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("realized_cap"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)
.unwrap()
}),
height_to_supply: EagerVec::forced_import(
path,
&suffix("supply"),
version + VERSION + Version::ZERO,
compressed,
)?,
indexes_to_supply: ComputedValueVecsFromHeight::forced_import(
path,
&suffix("supply"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
compute_dollars,
)?,
height_to_utxo_count: EagerVec::forced_import(
path,
&suffix("utxo_count"),
version + VERSION + Version::ZERO,
compressed,
)?,
indexes_to_utxo_count: ComputedVecsFromHeight::forced_import(
path,
&suffix("utxo_count"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)?,
indexes_to_realized_price: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("realized_price"),
true,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)
.unwrap()
}),
indexes_to_realized_price_extra: compute_dollars.then(|| {
ComputedRatioVecsFromDateIndex::forced_import(
path,
&suffix("realized_price"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_last(),
)
.unwrap()
}),
height_to_realized_profit: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("realized_profit"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_realized_profit: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("realized_profit"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
height_to_realized_loss: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("realized_loss"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_realized_loss: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("realized_loss"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
indexes_to_negative_realized_loss: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("negative_realized_loss"),
true,
version + VERSION + Version::ONE,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
height_to_value_created: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("value_created"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_value_created: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("value_created"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
indexes_to_realized_value: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("realized_value"),
true,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
height_to_adjusted_value_created: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("adjusted_value_created"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_adjusted_value_created: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("adjusted_value_created"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
height_to_value_destroyed: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("value_destroyed"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_value_destroyed: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("value_destroyed"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
height_to_adjusted_value_destroyed: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("adjusted_value_destroyed"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_adjusted_value_destroyed: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("adjusted_value_destroyed"),
false,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
dateindex_to_realized_cap_30d_change: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("realized_cap_30d_change"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
indexes_to_net_realized_profit_and_loss: compute_dollars.then(|| {
ComputedVecsFromHeight::forced_import(
path,
&suffix("net_realized_profit_and_loss"),
true,
version + VERSION + Version::ZERO,
compressed,
StorableVecGeneatorOptions::default().add_sum(),
)
.unwrap()
}),
dateindex_to_sell_side_risk_ratio: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("sell_side_risk_ratio"),
version + VERSION + Version::ONE,
compressed,
)
.unwrap()
}),
dateindex_to_spent_output_profit_ratio: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("spent_output_profit_ratio"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
dateindex_to_adjusted_spent_output_profit_ratio: compute_dollars.then(|| {
EagerVec::forced_import(
path,
&suffix("adjusted_spent_output_profit_ratio"),
version + VERSION + Version::ZERO,
compressed,
)
.unwrap()
}),
})
}
pub fn starting_height(&self) -> Height {
[
self.height_to_supply.len(),
self.height_to_utxo_count.len(),
self.height_to_realized_cap
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_realized_profit
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_realized_loss
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_value_created
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_adjusted_value_created
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_value_destroyed
.as_ref()
.map_or(usize::MAX, |v| v.len()),
self.height_to_adjusted_value_destroyed
.as_ref()
.map_or(usize::MAX, |v| v.len()),
]
.into_iter()
.map(Height::from)
.min()
.unwrap()
}
pub fn init(&mut self, starting_height: Height) {
if starting_height > self.starting_height() {
unreachable!()
}
self.starting_height = starting_height;
if let Some(prev_height) = starting_height.checked_sub(Height::new(1)) {
self.state.supply.value = self
.height_to_supply
.into_iter()
.unwrap_get_inner(prev_height);
self.state.supply.utxos = *self
.height_to_utxo_count
.into_iter()
.unwrap_get_inner(prev_height);
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
self.state.realized.as_mut().unwrap().cap = height_to_realized_cap
.into_iter()
.unwrap_get_inner(prev_height);
}
}
}
pub fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
self.height_to_supply
.validate_computed_version_or_reset_file(
base_version + self.height_to_supply.inner_version(),
)?;
self.height_to_utxo_count
.validate_computed_version_or_reset_file(
base_version + self.height_to_utxo_count.inner_version(),
)?;
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut().as_mut() {
height_to_realized_cap.validate_computed_version_or_reset_file(
base_version + height_to_realized_cap.inner_version(),
)?;
let height_to_realized_profit_inner_version = self
.height_to_realized_profit
.as_ref()
.unwrap()
.inner_version();
self.height_to_realized_profit
.as_mut()
.unwrap()
.validate_computed_version_or_reset_file(
base_version + height_to_realized_profit_inner_version,
)?;
let height_to_realized_loss_inner_version = self
.height_to_realized_loss
.as_ref()
.unwrap()
.inner_version();
self.height_to_realized_loss
.as_mut()
.unwrap()
.validate_computed_version_or_reset_file(
base_version + height_to_realized_loss_inner_version,
)?;
let height_to_value_created_inner_version = self
.height_to_value_created
.as_ref()
.unwrap()
.inner_version();
self.height_to_value_created
.as_mut()
.unwrap()
.validate_computed_version_or_reset_file(
base_version + height_to_value_created_inner_version,
)?;
let height_to_adjusted_value_created_inner_version = self
.height_to_adjusted_value_created
.as_ref()
.unwrap()
.inner_version();
self.height_to_adjusted_value_created
.as_mut()
.unwrap()
.validate_computed_version_or_reset_file(
base_version + height_to_adjusted_value_created_inner_version,
)?;
let height_to_value_destroyed_inner_version = self
.height_to_value_destroyed
.as_ref()
.unwrap()
.inner_version();
self.height_to_value_destroyed
.as_mut()
.unwrap()
.validate_computed_version_or_reset_file(
base_version + height_to_value_destroyed_inner_version,
)?;
let height_to_adjusted_value_destroyed_inner_version = self
.height_to_adjusted_value_destroyed
.as_ref()
.unwrap()
.inner_version();
self.height_to_adjusted_value_destroyed
.as_mut()
.unwrap()
.validate_computed_version_or_reset_file(
base_version + height_to_adjusted_value_destroyed_inner_version,
)?;
}
Ok(())
}
pub fn forced_pushed_at(&mut self, height: Height, exit: &Exit) -> Result<()> {
if self.starting_height > height {
return Ok(());
}
self.height_to_supply
.forced_push_at(height, self.state.supply.value, exit)?;
self.height_to_utxo_count.forced_push_at(
height,
StoredUsize::from(self.state.supply.utxos),
exit,
)?;
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
let realized = self.state.realized.as_ref().unwrap_or_else(|| {
dbg!((&self.state.realized, &self.state.supply));
panic!();
});
height_to_realized_cap.forced_push_at(height, realized.cap, exit)?;
self.height_to_realized_profit
.as_mut()
.unwrap()
.forced_push_at(height, realized.profit, exit)?;
self.height_to_realized_loss
.as_mut()
.unwrap()
.forced_push_at(height, realized.loss, exit)?;
self.height_to_value_created
.as_mut()
.unwrap()
.forced_push_at(height, realized.value_created, exit)?;
self.height_to_adjusted_value_created
.as_mut()
.unwrap()
.forced_push_at(height, realized.adj_value_created, exit)?;
self.height_to_value_destroyed
.as_mut()
.unwrap()
.forced_push_at(height, realized.value_destroyed, exit)?;
self.height_to_adjusted_value_destroyed
.as_mut()
.unwrap()
.forced_push_at(height, realized.adj_value_destroyed, exit)?;
}
Ok(())
}
pub fn safe_flush_height_vecs(&mut self, exit: &Exit) -> Result<()> {
self.height_to_supply.safe_flush(exit)?;
self.height_to_utxo_count.safe_flush(exit)?;
if let Some(height_to_realized_cap) = self.height_to_realized_cap.as_mut() {
height_to_realized_cap.safe_flush(exit)?;
self.height_to_realized_profit
.as_mut()
.unwrap()
.safe_flush(exit)?;
self.height_to_realized_loss
.as_mut()
.unwrap()
.safe_flush(exit)?;
self.height_to_value_created
.as_mut()
.unwrap()
.safe_flush(exit)?;
self.height_to_adjusted_value_created
.as_mut()
.unwrap()
.safe_flush(exit)?;
self.height_to_value_destroyed
.as_mut()
.unwrap()
.safe_flush(exit)?;
self.height_to_adjusted_value_destroyed
.as_mut()
.unwrap()
.safe_flush(exit)?;
}
Ok(())
}
pub fn compute_rest(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
fetched: Option<&fetched::Vecs>,
starting_indexes: &Indexes,
exit: &Exit,
) -> color_eyre::Result<()> {
self.indexes_to_supply.compute_rest(
indexer,
indexes,
fetched,
starting_indexes,
exit,
Some(&self.height_to_supply),
)?;
self.indexes_to_utxo_count.compute_rest(
indexes,
starting_indexes,
exit,
Some(&self.height_to_utxo_count),
)?;
if let Some(indexes_to_realized_cap) = self.indexes_to_realized_cap.as_mut() {
indexes_to_realized_cap.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_realized_cap.as_ref().unwrap()),
)?;
self.indexes_to_realized_price
.as_mut()
.unwrap()
.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
vec.compute_divide(
starting_indexes.height,
self.height_to_realized_cap.as_ref().unwrap(),
&**self.indexes_to_supply.bitcoin.height.as_ref().unwrap(),
exit,
)
},
)?;
self.indexes_to_realized_price_extra
.as_mut()
.unwrap()
.compute_rest(
indexer,
indexes,
fetched.as_ref().unwrap(),
starting_indexes,
exit,
Some(
self.indexes_to_realized_price
.as_ref()
.unwrap()
.dateindex
.unwrap_last(),
),
)?;
self.indexes_to_realized_profit
.as_mut()
.unwrap()
.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_realized_profit.as_ref().unwrap()),
)?;
self.indexes_to_realized_loss
.as_mut()
.unwrap()
.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_realized_loss.as_ref().unwrap()),
)?;
self.indexes_to_negative_realized_loss
.as_mut()
.unwrap()
.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
vec.compute_transform(
starting_indexes.height,
self.height_to_realized_loss.as_ref().unwrap(),
|(i, v, ..)| (i, v * -1_i64),
exit,
)
},
)?;
self.indexes_to_value_created
.as_mut()
.unwrap()
.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_value_created.as_ref().unwrap()),
)?;
self.indexes_to_adjusted_value_created
.as_mut()
.unwrap()
.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_adjusted_value_created.as_ref().unwrap()),
)?;
self.indexes_to_value_destroyed
.as_mut()
.unwrap()
.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_value_destroyed.as_ref().unwrap()),
)?;
self.indexes_to_adjusted_value_destroyed
.as_mut()
.unwrap()
.compute_rest(
indexes,
starting_indexes,
exit,
Some(self.height_to_adjusted_value_destroyed.as_ref().unwrap()),
)?;
self.dateindex_to_realized_cap_30d_change
.as_mut()
.unwrap()
.compute_change(
starting_indexes.dateindex,
self.indexes_to_realized_cap
.as_ref()
.unwrap()
.dateindex
.unwrap_last(),
30,
exit,
)?;
self.indexes_to_net_realized_profit_and_loss
.as_mut()
.unwrap()
.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
vec.compute_subtract(
starting_indexes.height,
self.height_to_realized_profit.as_ref().unwrap(),
self.height_to_realized_loss.as_ref().unwrap(),
exit,
)
},
)?;
self.indexes_to_realized_value
.as_mut()
.unwrap()
.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, _, starting_indexes, exit| {
vec.compute_add(
starting_indexes.height,
self.height_to_realized_profit.as_ref().unwrap(),
self.height_to_realized_loss.as_ref().unwrap(),
exit,
)
},
)?;
self.dateindex_to_spent_output_profit_ratio
.as_mut()
.unwrap()
.compute_divide(
starting_indexes.dateindex,
self.indexes_to_value_created
.as_ref()
.unwrap()
.dateindex
.unwrap_sum(),
self.indexes_to_value_destroyed
.as_ref()
.unwrap()
.dateindex
.unwrap_sum(),
exit,
)?;
self.dateindex_to_adjusted_spent_output_profit_ratio
.as_mut()
.unwrap()
.compute_divide(
starting_indexes.dateindex,
self.indexes_to_adjusted_value_created
.as_ref()
.unwrap()
.dateindex
.unwrap_sum(),
self.indexes_to_adjusted_value_destroyed
.as_ref()
.unwrap()
.dateindex
.unwrap_sum(),
exit,
)?;
self.dateindex_to_sell_side_risk_ratio
.as_mut()
.unwrap()
.compute_percentage(
starting_indexes.dateindex,
self.indexes_to_realized_value
.as_ref()
.unwrap()
.dateindex
.unwrap_sum(),
self.indexes_to_realized_cap
.as_ref()
.unwrap()
.dateindex
.unwrap_last(),
exit,
)?;
}
Ok(())
}
pub fn vecs(&self) -> Vec<&dyn AnyCollectableVec> {
[
vec![
&self.height_to_supply as &dyn AnyCollectableVec,
&self.height_to_utxo_count,
],
self.height_to_realized_cap
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_supply.vecs(),
self.indexes_to_utxo_count.vecs(),
self.indexes_to_realized_cap
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.indexes_to_realized_price
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.indexes_to_realized_value
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.indexes_to_realized_price_extra
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.height_to_realized_profit
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_realized_profit
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.height_to_realized_loss
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_realized_loss
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.indexes_to_negative_realized_loss
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.height_to_value_created
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_value_created
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.height_to_adjusted_value_created
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_adjusted_value_created
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.height_to_value_destroyed
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dateindex_to_spent_output_profit_ratio
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dateindex_to_adjusted_spent_output_profit_ratio
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_value_destroyed
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.height_to_adjusted_value_destroyed
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_adjusted_value_destroyed
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.dateindex_to_realized_cap_30d_change
.as_ref()
.map_or(vec![], |v| vec![v]),
self.indexes_to_net_realized_profit_and_loss
.as_ref()
.map_or(vec![], |v| v.vecs()),
self.dateindex_to_sell_side_risk_ratio
.as_ref()
.map_or(vec![], |v| vec![v]),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
}
}
impl Clone for Vecs {
fn clone(&self) -> Self {
Self {
starting_height: self.starting_height,
state: CohortState::default(),
height_to_realized_cap: self.height_to_realized_cap.clone(),
height_to_supply: self.height_to_supply.clone(),
height_to_utxo_count: self.height_to_utxo_count.clone(),
height_to_realized_profit: self.height_to_realized_profit.clone(),
height_to_realized_loss: self.height_to_realized_loss.clone(),
height_to_value_created: self.height_to_value_created.clone(),
height_to_adjusted_value_created: self.height_to_adjusted_value_created.clone(),
height_to_value_destroyed: self.height_to_value_destroyed.clone(),
height_to_adjusted_value_destroyed: self.height_to_adjusted_value_destroyed.clone(),
indexes_to_supply: self.indexes_to_supply.clone(),
indexes_to_utxo_count: self.indexes_to_utxo_count.clone(),
indexes_to_realized_cap: self.indexes_to_realized_cap.clone(),
indexes_to_realized_profit: self.indexes_to_realized_profit.clone(),
indexes_to_realized_loss: self.indexes_to_realized_loss.clone(),
indexes_to_negative_realized_loss: self.indexes_to_negative_realized_loss.clone(),
indexes_to_value_created: self.indexes_to_value_created.clone(),
indexes_to_adjusted_value_created: self.indexes_to_adjusted_value_created.clone(),
indexes_to_value_destroyed: self.indexes_to_value_destroyed.clone(),
indexes_to_adjusted_value_destroyed: self.indexes_to_adjusted_value_destroyed.clone(),
dateindex_to_realized_cap_30d_change: self.dateindex_to_realized_cap_30d_change.clone(),
indexes_to_realized_value: self.indexes_to_realized_value.clone(),
indexes_to_net_realized_profit_and_loss: self
.indexes_to_net_realized_profit_and_loss
.clone(),
indexes_to_realized_price: self.indexes_to_realized_price.clone(),
dateindex_to_sell_side_risk_ratio: self.dateindex_to_sell_side_risk_ratio.clone(),
indexes_to_realized_price_extra: self.indexes_to_realized_price_extra.clone(),
dateindex_to_spent_output_profit_ratio: self
.dateindex_to_spent_output_profit_ratio
.clone(),
dateindex_to_adjusted_spent_output_profit_ratio: self
.dateindex_to_adjusted_spent_output_profit_ratio
.clone(),
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,182 @@
use std::{collections::BTreeMap, ops::ControlFlow};
use brk_core::{CheckedSub, Dollars, HalvingEpoch, Height, Timestamp};
use brk_state::{BlockState, OutputFilter, Outputs, Transacted};
use brk_vec::StoredIndex;
use rayon::prelude::*;
use super::cohort;
pub trait OutputCohorts {
fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp);
fn send(&mut self, height_to_sent: BTreeMap<Height, Transacted>, chain_state: &[BlockState]);
fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>);
}
impl OutputCohorts for Outputs<(OutputFilter, cohort::Vecs)> {
fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp) {
if chain_state.is_empty() {
return;
}
let prev_timestamp = chain_state.last().unwrap().timestamp;
self.by_term
.as_mut_vec()
.into_par_iter()
.chain(self.by_up_to.as_mut_vec())
.chain(self.by_from.as_mut_vec())
.chain(self.by_range.as_mut_vec())
.for_each(|(filter, v)| {
let state = &mut v.state;
let mut check_days_old = |days_old: usize| -> bool {
match filter {
OutputFilter::From(from) => *from <= days_old,
OutputFilter::To(to) => *to > days_old,
OutputFilter::Range(range) => range.contains(&days_old),
OutputFilter::All
| OutputFilter::Epoch(_)
| OutputFilter::Size
| OutputFilter::Type(_) => unreachable!(),
}
};
let _ = chain_state
.iter()
.try_for_each(|block_state| -> ControlFlow<()> {
let prev_days_old = block_state
.timestamp
.difference_in_days_between(prev_timestamp);
let days_old = block_state.timestamp.difference_in_days_between(timestamp);
if prev_days_old == days_old {
return ControlFlow::Continue(());
}
let is = check_days_old(days_old);
let was = check_days_old(prev_days_old);
if is && !was {
state.increment(&block_state.supply, block_state.price);
} else if was && !is {
state.decrement(&block_state.supply, block_state.price);
}
ControlFlow::Continue(())
});
});
}
fn send(&mut self, height_to_sent: BTreeMap<Height, Transacted>, chain_state: &[BlockState]) {
let mut time_based_vecs = self
.by_term
.as_mut_vec()
.into_iter()
.chain(self.by_up_to.as_mut_vec())
.chain(self.by_from.as_mut_vec())
.chain(self.by_range.as_mut_vec())
.chain(self.by_epoch.as_mut_vec())
.collect::<Vec<_>>();
let last_timestamp = chain_state.last().unwrap().timestamp;
let current_price = chain_state.last().unwrap().price;
height_to_sent.into_iter().for_each(|(height, sent)| {
let block_state = chain_state.get(height.unwrap_to_usize()).unwrap();
let prev_price = block_state.price;
let days_old = block_state
.timestamp
.difference_in_days_between(last_timestamp);
let older_than_hour =
jiff::Timestamp::from(last_timestamp.checked_sub(block_state.timestamp).unwrap())
.as_second()
>= 60 * 60;
self.all.1.state.send(
&sent.spendable_supply,
current_price,
prev_price,
older_than_hour,
);
time_based_vecs
.iter_mut()
.filter(|(filter, _)| match filter {
OutputFilter::From(from) => *from <= days_old,
OutputFilter::To(to) => *to > days_old,
OutputFilter::Range(range) => range.contains(&days_old),
OutputFilter::Epoch(epoch) => *epoch == HalvingEpoch::from(height),
_ => unreachable!(),
})
.for_each(|(_, vecs)| {
vecs.state.send(
&sent.spendable_supply,
current_price,
prev_price,
older_than_hour,
);
});
sent.by_type.spendable.as_typed_vec().into_iter().for_each(
|(output_type, supply_state)| {
self.by_type.get_mut(output_type).1.state.send(
supply_state,
current_price,
prev_price,
older_than_hour,
)
},
);
sent.by_size.into_iter().for_each(|(group, supply_state)| {
self.by_size.get_mut(group).1.state.send(
&supply_state,
current_price,
prev_price,
older_than_hour,
);
});
});
}
fn receive(&mut self, received: Transacted, height: Height, price: Option<Dollars>) {
let supply_state = received.spendable_supply;
[
&mut self.all.1,
&mut self.by_term.short.1,
&mut self.by_epoch.mut_vec_from_height(height).1,
// Skip from and range as can't receive in the past
]
.into_iter()
.chain(self.by_up_to.as_mut_vec().map(|(_, v)| v))
.for_each(|v| {
v.state.receive(&supply_state, price);
});
self.by_type
.as_mut_vec()
.into_iter()
.for_each(|(filter, vecs)| {
let output_type = match filter {
OutputFilter::Type(output_type) => *output_type,
_ => unreachable!(),
};
vecs.state.receive(received.by_type.get(output_type), price)
});
received
.by_size
.into_iter()
.for_each(|(group, supply_state)| {
self.by_size
.get_mut(group)
.1
.state
.receive(&supply_state, price);
});
}
}