mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,7 @@ impl Vecs {
|
||||
vec.compute_subtract(
|
||||
starting_indexes.height,
|
||||
&self.coinblocks_created.height,
|
||||
&all_metrics.activity.coinblocks_destroyed.height,
|
||||
&all_metrics.activity.coinblocks_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -42,7 +42,7 @@ impl Vecs {
|
||||
|
||||
self.liveliness.height.compute_divide(
|
||||
starting_indexes.height,
|
||||
&all_metrics.activity.coinblocks_destroyed_cumulative.height,
|
||||
&all_metrics.activity.coinblocks_destroyed.cumulative.height,
|
||||
&self.coinblocks_created.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -18,7 +18,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let all_metrics = &distribution.utxo_cohorts.all.metrics;
|
||||
let realized_cap_cents = &all_metrics.realized.cap_cents.height;
|
||||
let realized_cap_cents = &all_metrics.realized.cap.cents.height;
|
||||
let circulating_supply = &all_metrics.supply.total.btc.height;
|
||||
|
||||
self.thermo_cap.cents.height.compute_transform(
|
||||
|
||||
@@ -28,7 +28,7 @@ impl Vecs {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
&coinblocks_destroyed.height,
|
||||
&coinblocks_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -64,7 +64,7 @@ impl Vecs {
|
||||
vec.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
&coindays_destroyed.height,
|
||||
&coindays_destroyed.raw.height,
|
||||
circulating_supply,
|
||||
|(i, price, cdd, supply, _): (_, Dollars, StoredF64, Bitcoin, _)| {
|
||||
let supply_f64 = f64::from(supply);
|
||||
|
||||
@@ -157,7 +157,6 @@ impl DynCohortVecs for AddressCohortVecs {
|
||||
self.addr_count
|
||||
.height
|
||||
.validate_computed_version_or_reset(base_version)?;
|
||||
self.metrics.validate_computed_versions(base_version)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -170,18 +169,9 @@ impl DynCohortVecs for AddressCohortVecs {
|
||||
self.addr_count
|
||||
.height
|
||||
.truncate_push(height, state.addr_count.into())?;
|
||||
self.metrics
|
||||
.supply
|
||||
.truncate_push(height, state.inner.supply.value)?;
|
||||
self.metrics
|
||||
.outputs
|
||||
.truncate_push(height, state.inner.supply.utxo_count)?;
|
||||
self.metrics
|
||||
.activity
|
||||
.truncate_push(height, state.inner.sent)?;
|
||||
self.metrics
|
||||
.realized
|
||||
.truncate_push(height, &state.inner.realized)?;
|
||||
self.metrics.supply.truncate_push(height, &state.inner)?;
|
||||
self.metrics.outputs.truncate_push(height, &state.inner)?;
|
||||
self.metrics.realized.truncate_push(height, &state.inner)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -189,17 +179,10 @@ impl DynCohortVecs for AddressCohortVecs {
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
_height: Height,
|
||||
_height_price: Cents,
|
||||
_is_day_boundary: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.inner.apply_pending();
|
||||
let unrealized_state = state.inner.compute_unrealized_state(height_price);
|
||||
self.metrics
|
||||
.unrealized
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,10 @@ use crate::{
|
||||
distribution::{
|
||||
DynCohortVecs,
|
||||
metrics::{
|
||||
AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase, CoreCohortMetrics,
|
||||
ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig,
|
||||
MinimalCohortMetrics, ProfitabilityMetrics, RealizedFullAccum, SupplyMetrics,
|
||||
AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase,
|
||||
CoreCohortMetrics, ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig,
|
||||
MinimalCohortMetrics, ProfitabilityMetrics, RealizedFullAccum, SupplyFull,
|
||||
TypeCohortMetrics,
|
||||
},
|
||||
state::UTXOCohortState,
|
||||
},
|
||||
@@ -47,7 +48,7 @@ pub struct UTXOCohorts<M: StorageMode = Rw> {
|
||||
pub amount_range: ByAmountRange<UTXOCohortVecs<MinimalCohortMetrics<M>>>,
|
||||
pub lt_amount: ByLowerThanAmount<UTXOCohortVecs<MinimalCohortMetrics<M>>>,
|
||||
#[traversable(rename = "type")]
|
||||
pub type_: BySpendableType<UTXOCohortVecs<MinimalCohortMetrics<M>>>,
|
||||
pub type_: BySpendableType<UTXOCohortVecs<TypeCohortMetrics<M>>>,
|
||||
pub profitability: ProfitabilityMetrics<M>,
|
||||
pub matured: ByAgeRange<AmountPerBlock<M>>,
|
||||
#[traversable(skip)]
|
||||
@@ -81,7 +82,7 @@ impl UTXOCohorts<Rw> {
|
||||
version: v + Version::ONE,
|
||||
indexes,
|
||||
};
|
||||
let all_supply = SupplyMetrics::forced_import(&all_cfg)?;
|
||||
let all_supply = SupplyFull::forced_import(&all_cfg)?;
|
||||
|
||||
// Phase 2: Import separate (stateful) cohorts.
|
||||
|
||||
@@ -144,7 +145,25 @@ impl UTXOCohorts<Rw> {
|
||||
};
|
||||
|
||||
let amount_range = ByAmountRange::try_new(&minimal_separate)?;
|
||||
let type_ = BySpendableType::try_new(&minimal_separate)?;
|
||||
|
||||
let type_separate =
|
||||
|f: Filter, name: &'static str| -> Result<UTXOCohortVecs<TypeCohortMetrics>> {
|
||||
let full_name = CohortContext::Utxo.full_name(&f, name);
|
||||
let cfg = ImportConfig {
|
||||
db,
|
||||
filter: &f,
|
||||
full_name: &full_name,
|
||||
version: v,
|
||||
indexes,
|
||||
};
|
||||
let state = Some(Box::new(UTXOCohortState::new(states_path, &full_name)));
|
||||
Ok(UTXOCohortVecs::new(
|
||||
state,
|
||||
TypeCohortMetrics::forced_import(&cfg)?,
|
||||
))
|
||||
};
|
||||
|
||||
let type_ = BySpendableType::try_new(&type_separate)?;
|
||||
|
||||
// Phase 3: Import "all" cohort with pre-imported supply.
|
||||
let all = UTXOCohortVecs::new(
|
||||
@@ -208,7 +227,6 @@ impl UTXOCohorts<Rw> {
|
||||
// min_age: CoreCohortMetrics (no state, aggregates from age_range)
|
||||
let min_age = ByMinAge::try_new(&core_no_state)?;
|
||||
|
||||
// MinimalCohortMetrics without state (for aggregate amount cohorts)
|
||||
let minimal_no_state =
|
||||
|f: Filter, name: &'static str| -> Result<UTXOCohortVecs<MinimalCohortMetrics>> {
|
||||
let full_name = CohortContext::Utxo.full_name(&f, name);
|
||||
@@ -424,7 +442,8 @@ impl UTXOCohorts<Rw> {
|
||||
.try_for_each(|vecs| {
|
||||
let sources =
|
||||
filter_minimal_sources_from(amr.iter(), Some(&vecs.metrics.filter));
|
||||
vecs.metrics.compute_from_sources(si, &sources, exit)
|
||||
vecs.metrics
|
||||
.compute_from_sources(si, &sources, exit)
|
||||
})
|
||||
}),
|
||||
];
|
||||
@@ -507,7 +526,10 @@ impl UTXOCohorts<Rw> {
|
||||
.up_to_1h
|
||||
.metrics
|
||||
.realized
|
||||
.minimal
|
||||
.sopr
|
||||
.value_created
|
||||
.raw
|
||||
.height
|
||||
.read_only_clone();
|
||||
let up_to_1h_value_destroyed = self
|
||||
@@ -515,7 +537,10 @@ impl UTXOCohorts<Rw> {
|
||||
.up_to_1h
|
||||
.metrics
|
||||
.realized
|
||||
.minimal
|
||||
.sopr
|
||||
.value_destroyed
|
||||
.raw
|
||||
.height
|
||||
.read_only_clone();
|
||||
|
||||
@@ -746,12 +771,6 @@ impl UTXOCohorts<Rw> {
|
||||
for v in self.max_age.iter_mut() {
|
||||
v.metrics.validate_computed_versions(base_version)?;
|
||||
}
|
||||
for v in self.ge_amount.iter_mut() {
|
||||
v.metrics.validate_computed_versions(base_version)?;
|
||||
}
|
||||
for v in self.lt_amount.iter_mut() {
|
||||
v.metrics.validate_computed_versions(base_version)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
use brk_cohort::{Filter, Filtered};
|
||||
use brk_error::Result;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
|
||||
use crate::{blocks, distribution::{cohorts::traits::DynCohortVecs, metrics::CoreCohortMetrics}, prices};
|
||||
|
||||
use super::UTXOCohortVecs;
|
||||
|
||||
impl Filtered for UTXOCohortVecs<CoreCohortMetrics> {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.metrics.filter
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for UTXOCohortVecs<CoreCohortMetrics> {
|
||||
fn min_stateful_height_len(&self) -> usize {
|
||||
self.metrics.min_stateful_height_len()
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.reset_state_impl();
|
||||
}
|
||||
|
||||
impl_import_state!();
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.metrics.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.state_starting_height.is_some_and(|h| h > height) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(state) = self.state.as_ref() {
|
||||
self.metrics.supply.truncate_push(height, state)?;
|
||||
self.metrics.outputs.truncate_push(height, state)?;
|
||||
self.metrics.activity.truncate_push(height, state)?;
|
||||
self.metrics.realized.truncate_push(height, state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
_is_day_boundary: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.apply_pending();
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.metrics
|
||||
.unrealized
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics
|
||||
.compute_rest_part1(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn write_state(&mut self, height: Height, cleanup: bool) -> Result<()> {
|
||||
self.write_state_impl(height, cleanup)
|
||||
}
|
||||
|
||||
fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||
self.reset_cost_basis_impl()
|
||||
}
|
||||
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.reset_iteration_impl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use brk_cohort::{Filter, Filtered};
|
||||
use brk_error::Result;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::{cohorts::traits::DynCohortVecs, metrics::MinimalCohortMetrics},
|
||||
prices,
|
||||
};
|
||||
|
||||
use super::UTXOCohortVecs;
|
||||
|
||||
impl Filtered for UTXOCohortVecs<MinimalCohortMetrics> {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.metrics.filter
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for UTXOCohortVecs<MinimalCohortMetrics> {
|
||||
fn min_stateful_height_len(&self) -> usize {
|
||||
self.metrics.min_stateful_height_len()
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.reset_state_impl();
|
||||
}
|
||||
|
||||
impl_import_state!();
|
||||
|
||||
fn validate_computed_versions(&mut self, _base_version: Version) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.state_starting_height.is_some_and(|h| h > height) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(state) = self.state.as_ref() {
|
||||
self.metrics.supply.truncate_push(height, state)?;
|
||||
self.metrics.outputs.truncate_push(height, state)?;
|
||||
self.metrics.realized.truncate_push(height, state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
_height: Height,
|
||||
_height_price: Cents,
|
||||
_is_day_boundary: bool,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics
|
||||
.compute_rest_part1(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn write_state(&mut self, height: Height, cleanup: bool) -> Result<()> {
|
||||
self.write_state_impl(height, cleanup)
|
||||
}
|
||||
|
||||
fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||
self.reset_cost_basis_impl()
|
||||
}
|
||||
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.reset_iteration_impl();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,63 @@
|
||||
macro_rules! impl_import_state {
|
||||
() => {
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
if let Some(mut prev_height) = starting_height.decremented() {
|
||||
prev_height = state.import_at_or_before(prev_height)?;
|
||||
|
||||
state.supply.value = self
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.sats
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap();
|
||||
state.supply.utxo_count = *self
|
||||
.metrics
|
||||
.outputs
|
||||
.utxo_count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap();
|
||||
|
||||
state.restore_realized_cap();
|
||||
|
||||
let result = prev_height.incremented();
|
||||
self.state_starting_height = Some(result);
|
||||
Ok(result)
|
||||
} else {
|
||||
self.state_starting_height = Some(Height::ZERO);
|
||||
Ok(Height::ZERO)
|
||||
}
|
||||
} else {
|
||||
self.state_starting_height = Some(starting_height);
|
||||
Ok(starting_height)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod core;
|
||||
mod minimal;
|
||||
mod r#type;
|
||||
|
||||
use brk_cohort::{Filter, Filtered};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
|
||||
use crate::{blocks, distribution::state::UTXOCohortState, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
CohortMetricsBase, CohortMetricsState, CoreCohortMetrics, MinimalCohortMetrics,
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::{
|
||||
cohorts::traits::DynCohortVecs,
|
||||
metrics::{CohortMetricsBase, CohortMetricsState},
|
||||
state::UTXOCohortState,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
use super::super::traits::DynCohortVecs;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct UTXOCohortVecs<M: CohortMetricsState> {
|
||||
#[traversable(skip)]
|
||||
@@ -24,8 +70,6 @@ pub struct UTXOCohortVecs<M: CohortMetricsState> {
|
||||
pub metrics: M,
|
||||
}
|
||||
|
||||
// --- Shared state helpers (identical across all DynCohortVecs impls) ---
|
||||
|
||||
impl<M: CohortMetricsState> UTXOCohortVecs<M> {
|
||||
pub(crate) fn new(state: Option<Box<UTXOCohortState<M::Realized>>>, metrics: M) -> Self {
|
||||
Self {
|
||||
@@ -172,213 +216,3 @@ impl<M: CohortMetricsBase + Traversable> DynCohortVecs for UTXOCohortVecs<M> {
|
||||
self.reset_iteration_impl();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Shared import_state for non-blanket impls (direct field access) ---
|
||||
|
||||
macro_rules! impl_import_state {
|
||||
() => {
|
||||
fn import_state(&mut self, starting_height: Height) -> Result<Height> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
if let Some(mut prev_height) = starting_height.decremented() {
|
||||
prev_height = state.import_at_or_before(prev_height)?;
|
||||
|
||||
state.supply.value = self
|
||||
.metrics
|
||||
.supply
|
||||
.total
|
||||
.sats
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap();
|
||||
state.supply.utxo_count = *self
|
||||
.metrics
|
||||
.outputs
|
||||
.utxo_count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap();
|
||||
|
||||
state.restore_realized_cap();
|
||||
|
||||
let result = prev_height.incremented();
|
||||
self.state_starting_height = Some(result);
|
||||
Ok(result)
|
||||
} else {
|
||||
self.state_starting_height = Some(Height::ZERO);
|
||||
Ok(Height::ZERO)
|
||||
}
|
||||
} else {
|
||||
self.state_starting_height = Some(starting_height);
|
||||
Ok(starting_height)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --- MinimalCohortMetrics: uses MinimalRealizedState ---
|
||||
|
||||
impl Filtered for UTXOCohortVecs<MinimalCohortMetrics> {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.metrics.filter
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for UTXOCohortVecs<MinimalCohortMetrics> {
|
||||
fn min_stateful_height_len(&self) -> usize {
|
||||
self.metrics.min_stateful_height_len()
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.reset_state_impl();
|
||||
}
|
||||
|
||||
impl_import_state!();
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.metrics.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.state_starting_height.is_some_and(|h| h > height) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(state) = self.state.as_ref() {
|
||||
self.metrics
|
||||
.supply
|
||||
.truncate_push(height, state.supply.value)?;
|
||||
self.metrics
|
||||
.outputs
|
||||
.truncate_push(height, state.supply.utxo_count)?;
|
||||
self.metrics.activity.truncate_push(height, state.sent)?;
|
||||
self.metrics
|
||||
.realized
|
||||
.truncate_push(height, &state.realized)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
_is_day_boundary: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.apply_pending();
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.metrics
|
||||
.unrealized
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics
|
||||
.compute_rest_part1(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn write_state(&mut self, height: Height, cleanup: bool) -> Result<()> {
|
||||
self.write_state_impl(height, cleanup)
|
||||
}
|
||||
|
||||
fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||
self.reset_cost_basis_impl()
|
||||
}
|
||||
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.reset_iteration_impl();
|
||||
}
|
||||
}
|
||||
|
||||
// --- CoreCohortMetrics: uses CoreRealizedState ---
|
||||
|
||||
impl Filtered for UTXOCohortVecs<CoreCohortMetrics> {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.metrics.filter
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for UTXOCohortVecs<CoreCohortMetrics> {
|
||||
fn min_stateful_height_len(&self) -> usize {
|
||||
self.metrics.min_stateful_height_len()
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.reset_state_impl();
|
||||
}
|
||||
|
||||
impl_import_state!();
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.metrics.validate_computed_versions(base_version)
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.state_starting_height.is_some_and(|h| h > height) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(state) = self.state.as_ref() {
|
||||
self.metrics
|
||||
.supply
|
||||
.truncate_push(height, state.supply.value)?;
|
||||
self.metrics
|
||||
.outputs
|
||||
.truncate_push(height, state.supply.utxo_count)?;
|
||||
self.metrics.activity.truncate_push(height, state.sent)?;
|
||||
self.metrics
|
||||
.realized
|
||||
.truncate_push(height, &state.realized)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
_is_day_boundary: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.apply_pending();
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.metrics
|
||||
.unrealized
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics
|
||||
.compute_rest_part1(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn write_state(&mut self, height: Height, cleanup: bool) -> Result<()> {
|
||||
self.write_state_impl(height, cleanup)
|
||||
}
|
||||
|
||||
fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||
self.reset_cost_basis_impl()
|
||||
}
|
||||
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.reset_iteration_impl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use brk_cohort::{Filter, Filtered};
|
||||
use brk_error::Result;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use vecdb::{Exit, ReadableVec};
|
||||
|
||||
use crate::{blocks, distribution::cohorts::traits::DynCohortVecs, distribution::metrics::TypeCohortMetrics, prices};
|
||||
|
||||
use super::UTXOCohortVecs;
|
||||
|
||||
impl Filtered for UTXOCohortVecs<TypeCohortMetrics> {
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.metrics.filter
|
||||
}
|
||||
}
|
||||
|
||||
impl DynCohortVecs for UTXOCohortVecs<TypeCohortMetrics> {
|
||||
fn min_stateful_height_len(&self) -> usize {
|
||||
self.metrics.min_stateful_height_len()
|
||||
}
|
||||
|
||||
fn reset_state_starting_height(&mut self) {
|
||||
self.reset_state_impl();
|
||||
}
|
||||
|
||||
impl_import_state!();
|
||||
|
||||
fn validate_computed_versions(&mut self, _base_version: Version) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height) -> Result<()> {
|
||||
if self.state_starting_height.is_some_and(|h| h > height) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(state) = self.state.as_ref() {
|
||||
self.metrics.supply.truncate_push(height, state)?;
|
||||
self.metrics.outputs.truncate_push(height, state)?;
|
||||
self.metrics.realized.truncate_push(height, state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
_is_day_boundary: bool,
|
||||
) -> Result<()> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
state.apply_pending();
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.metrics
|
||||
.unrealized
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics
|
||||
.compute_rest_part1(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
|
||||
fn write_state(&mut self, height: Height, cleanup: bool) -> Result<()> {
|
||||
self.write_state_impl(height, cleanup)
|
||||
}
|
||||
|
||||
fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||
self.reset_cost_basis_impl()
|
||||
}
|
||||
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.reset_iteration_impl();
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::internal::ComputedPerBlock;
|
||||
|
||||
use crate::{blocks, distribution::metrics::ImportConfig};
|
||||
|
||||
use super::ActivityCore;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct ActivityBase<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub core: ActivityCore<M>,
|
||||
|
||||
pub coinblocks_destroyed: ComputedPerBlock<StoredF64, M>,
|
||||
pub coindays_destroyed: ComputedPerBlock<StoredF64, M>,
|
||||
}
|
||||
|
||||
impl ActivityBase {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v1 = Version::ONE;
|
||||
Ok(Self {
|
||||
core: ActivityCore::forced_import(cfg)?,
|
||||
coinblocks_destroyed: cfg.import("coinblocks_destroyed", v1)?,
|
||||
coindays_destroyed: cfg.import("coindays_destroyed", v1)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.core
|
||||
.min_len()
|
||||
.min(self.coinblocks_destroyed.height.len())
|
||||
.min(self.coindays_destroyed.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
sent: Sats,
|
||||
satblocks_destroyed: Sats,
|
||||
satdays_destroyed: Sats,
|
||||
) -> Result<()> {
|
||||
self.core.truncate_push(height, sent)?;
|
||||
self.coinblocks_destroyed.height.truncate_push(
|
||||
height,
|
||||
StoredF64::from(Bitcoin::from(satblocks_destroyed)),
|
||||
)?;
|
||||
self.coindays_destroyed.height.truncate_push(
|
||||
height,
|
||||
StoredF64::from(Bitcoin::from(satdays_destroyed)),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.core.sent.height as &mut dyn AnyStoredVec,
|
||||
&mut self.coinblocks_destroyed.height as &mut dyn AnyStoredVec,
|
||||
&mut self.coindays_destroyed.height as &mut dyn AnyStoredVec,
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn validate_computed_versions(&mut self, _base_version: Version) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let core_refs: Vec<&ActivityCore> = others.iter().map(|o| &o.core).collect();
|
||||
self.core
|
||||
.compute_from_stateful(starting_indexes, &core_refs, exit)?;
|
||||
|
||||
sum_others!(self, starting_indexes, others, exit; coinblocks_destroyed.height);
|
||||
sum_others!(self, starting_indexes, others, exit; coindays_destroyed.height);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.core
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, Sats, Version};
|
||||
use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF64, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RollingWindow24h};
|
||||
|
||||
use crate::{blocks, distribution::metrics::ImportConfig};
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::{metrics::ImportConfig, state::{CohortState, RealizedOps}},
|
||||
internal::PerBlockWithSum24h,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ActivityCore<M: StorageMode = Rw> {
|
||||
pub sent: ComputedPerBlock<Sats, M>,
|
||||
pub sent_sum: RollingWindow24h<Sats, M>,
|
||||
pub sent: PerBlockWithSum24h<Sats, M>,
|
||||
pub coindays_destroyed: PerBlockWithSum24h<StoredF64, M>,
|
||||
}
|
||||
|
||||
impl ActivityCore {
|
||||
@@ -18,21 +20,36 @@ impl ActivityCore {
|
||||
let v1 = Version::ONE;
|
||||
Ok(Self {
|
||||
sent: cfg.import("sent", v1)?,
|
||||
sent_sum: cfg.import("sent", v1)?,
|
||||
coindays_destroyed: cfg.import("coindays_destroyed", v1)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.sent.height.len()
|
||||
self.sent
|
||||
.raw
|
||||
.height
|
||||
.len()
|
||||
.min(self.coindays_destroyed.raw.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, sent: Sats) -> Result<()> {
|
||||
self.sent.height.truncate_push(height, sent)?;
|
||||
pub(crate) fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
state: &CohortState<impl RealizedOps>,
|
||||
) -> Result<()> {
|
||||
self.sent.raw.height.truncate_push(height, state.sent)?;
|
||||
self.coindays_destroyed.raw.height.truncate_push(
|
||||
height,
|
||||
StoredF64::from(Bitcoin::from(state.satdays_destroyed)),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![&mut self.sent.height as &mut dyn AnyStoredVec]
|
||||
vec![
|
||||
&mut self.sent.raw.height as &mut dyn AnyStoredVec,
|
||||
&mut self.coindays_destroyed.raw.height,
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn validate_computed_versions(&mut self, _base_version: Version) -> Result<()> {
|
||||
@@ -45,14 +62,17 @@ impl ActivityCore {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sent.height.compute_sum_of_others(
|
||||
self.sent.raw.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.sent.height)
|
||||
.map(|v| &v.sent.raw.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
sum_others!(self, starting_indexes, others, exit; coindays_destroyed.raw.height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -62,10 +82,16 @@ impl ActivityCore {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sent_sum.compute_rolling_sum(
|
||||
self.sent.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sent.height,
|
||||
&self.sent.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
self.coindays_destroyed.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.coindays_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -1,47 +1,90 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Indexes, Sats, StoredF64, Version};
|
||||
use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF32, StoredF64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, Rw, StorageMode};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RollingWindows, RollingWindowsFrom1w};
|
||||
use crate::internal::{ComputedPerBlock, RollingWindowsFrom1w};
|
||||
|
||||
use crate::{blocks, distribution::metrics::ImportConfig};
|
||||
use crate::{blocks, distribution::{metrics::ImportConfig, state::{CohortState, RealizedOps}}};
|
||||
|
||||
use super::ActivityBase;
|
||||
use super::ActivityCore;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ActivityCoinblocks<M: StorageMode = Rw> {
|
||||
pub raw: ComputedPerBlock<StoredF64, M>,
|
||||
pub cumulative: ComputedPerBlock<StoredF64, M>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct ActivityFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: ActivityBase<M>,
|
||||
pub inner: ActivityCore<M>,
|
||||
|
||||
pub coinblocks_destroyed_cumulative: ComputedPerBlock<StoredF64, M>,
|
||||
pub coinblocks_destroyed: ActivityCoinblocks<M>,
|
||||
|
||||
#[traversable(wrap = "coindays_destroyed", rename = "cumulative")]
|
||||
pub coindays_destroyed_cumulative: ComputedPerBlock<StoredF64, M>,
|
||||
pub coindays_destroyed_sum: RollingWindows<StoredF64, M>,
|
||||
#[traversable(wrap = "coindays_destroyed", rename = "sum")]
|
||||
pub coindays_destroyed_sum: RollingWindowsFrom1w<StoredF64, M>,
|
||||
|
||||
#[traversable(rename = "sent_sum")]
|
||||
#[traversable(wrap = "sent", rename = "sum")]
|
||||
pub sent_sum_extended: RollingWindowsFrom1w<Sats, M>,
|
||||
|
||||
pub dormancy: ComputedPerBlock<StoredF32, M>,
|
||||
pub velocity: ComputedPerBlock<StoredF32, M>,
|
||||
}
|
||||
|
||||
impl ActivityFull {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v1 = Version::ONE;
|
||||
Ok(Self {
|
||||
inner: ActivityBase::forced_import(cfg)?,
|
||||
coinblocks_destroyed_cumulative: cfg
|
||||
.import("coinblocks_destroyed_cumulative", v1)?,
|
||||
inner: ActivityCore::forced_import(cfg)?,
|
||||
coinblocks_destroyed: ActivityCoinblocks {
|
||||
raw: cfg.import("coinblocks_destroyed", v1)?,
|
||||
cumulative: cfg.import("coinblocks_destroyed_cumulative", v1)?,
|
||||
},
|
||||
coindays_destroyed_cumulative: cfg.import("coindays_destroyed_cumulative", v1)?,
|
||||
coindays_destroyed_sum: cfg.import("coindays_destroyed", v1)?,
|
||||
sent_sum_extended: cfg.import("sent", v1)?,
|
||||
dormancy: cfg.import("dormancy", v1)?,
|
||||
velocity: cfg.import("velocity", v1)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn full_min_len(&self) -> usize {
|
||||
self.inner
|
||||
.min_len()
|
||||
.min(self.coinblocks_destroyed.raw.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn full_truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
state: &CohortState<impl RealizedOps>,
|
||||
) -> Result<()> {
|
||||
self.inner.truncate_push(height, state)?;
|
||||
self.coinblocks_destroyed.raw.height.truncate_push(
|
||||
height,
|
||||
StoredF64::from(Bitcoin::from(state.satblocks_destroyed)),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.inner.collect_vecs_mut();
|
||||
vecs.push(&mut self.coinblocks_destroyed.raw.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.dormancy.height);
|
||||
vecs.push(&mut self.velocity.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&ActivityBase],
|
||||
others: &[&ActivityCore],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner
|
||||
@@ -57,11 +100,12 @@ impl ActivityFull {
|
||||
self.inner
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
|
||||
self.coinblocks_destroyed_cumulative
|
||||
self.coinblocks_destroyed
|
||||
.cumulative
|
||||
.height
|
||||
.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.inner.coinblocks_destroyed.height,
|
||||
&self.coinblocks_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -69,7 +113,7 @@ impl ActivityFull {
|
||||
.height
|
||||
.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.inner.coindays_destroyed.height,
|
||||
&self.inner.coindays_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -77,14 +121,53 @@ impl ActivityFull {
|
||||
self.coindays_destroyed_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.inner.coindays_destroyed.height,
|
||||
&self.inner.coindays_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.sent_sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.inner.core.sent.height,
|
||||
&self.inner.sent.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part2(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.dormancy.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.inner.coindays_destroyed.raw.height,
|
||||
&self.inner.sent.raw.height,
|
||||
|(i, cdd, sent_sats, ..)| {
|
||||
let sent_btc = f64::from(Bitcoin::from(sent_sats));
|
||||
if sent_btc == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
} else {
|
||||
(i, StoredF32::from((f64::from(cdd) / sent_btc) as f32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.velocity.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.inner.sent.raw.height,
|
||||
supply_total_sats,
|
||||
|(i, sent_sats, supply_sats, ..)| {
|
||||
let supply = supply_sats.as_u128() as f64;
|
||||
if supply == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
} else {
|
||||
(i, StoredF32::from((sent_sats.as_u128() as f64 / supply) as f32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
mod base;
|
||||
mod core;
|
||||
mod full;
|
||||
|
||||
pub use base::ActivityBase;
|
||||
pub use self::core::ActivityCore;
|
||||
pub use full::ActivityFull;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_types::{Height, Indexes, Sats, Version};
|
||||
use brk_types::{Height, Indexes, Version};
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::blocks;
|
||||
use crate::{blocks, distribution::state::{CohortState, RealizedOps}};
|
||||
|
||||
pub trait ActivityLike: Send + Sync {
|
||||
fn as_base(&self) -> &ActivityBase;
|
||||
fn as_base_mut(&mut self) -> &mut ActivityBase;
|
||||
fn as_core(&self) -> &ActivityCore;
|
||||
fn as_core_mut(&mut self) -> &mut ActivityCore;
|
||||
fn min_len(&self) -> usize;
|
||||
fn truncate_push(
|
||||
fn truncate_push<R: RealizedOps>(
|
||||
&mut self,
|
||||
height: Height,
|
||||
sent: Sats,
|
||||
satblocks_destroyed: Sats,
|
||||
satdays_destroyed: Sats,
|
||||
state: &CohortState<R>,
|
||||
) -> Result<()>;
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&ActivityBase],
|
||||
others: &[&ActivityCore],
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
fn compute_rest_part1(
|
||||
@@ -38,17 +34,17 @@ pub trait ActivityLike: Send + Sync {
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl ActivityLike for ActivityBase {
|
||||
fn as_base(&self) -> &ActivityBase { self }
|
||||
fn as_base_mut(&mut self) -> &mut ActivityBase { self }
|
||||
impl ActivityLike for ActivityCore {
|
||||
fn as_core(&self) -> &ActivityCore { self }
|
||||
fn as_core_mut(&mut self) -> &mut ActivityCore { self }
|
||||
fn min_len(&self) -> usize { self.min_len() }
|
||||
fn truncate_push(&mut self, height: Height, sent: Sats, satblocks_destroyed: Sats, satdays_destroyed: Sats) -> Result<()> {
|
||||
self.truncate_push(height, sent, satblocks_destroyed, satdays_destroyed)
|
||||
fn truncate_push<R: RealizedOps>(&mut self, height: Height, state: &CohortState<R>) -> Result<()> {
|
||||
self.truncate_push(height, state)
|
||||
}
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.validate_computed_versions(base_version)
|
||||
}
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityBase], exit: &Exit) -> Result<()> {
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
|
||||
self.compute_from_stateful(starting_indexes, others, exit)
|
||||
}
|
||||
fn compute_rest_part1(&mut self, blocks: &blocks::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
@@ -57,16 +53,16 @@ impl ActivityLike for ActivityBase {
|
||||
}
|
||||
|
||||
impl ActivityLike for ActivityFull {
|
||||
fn as_base(&self) -> &ActivityBase { &self.inner }
|
||||
fn as_base_mut(&mut self) -> &mut ActivityBase { &mut self.inner }
|
||||
fn min_len(&self) -> usize { self.inner.min_len() }
|
||||
fn truncate_push(&mut self, height: Height, sent: Sats, satblocks_destroyed: Sats, satdays_destroyed: Sats) -> Result<()> {
|
||||
self.inner.truncate_push(height, sent, satblocks_destroyed, satdays_destroyed)
|
||||
fn as_core(&self) -> &ActivityCore { &self.inner }
|
||||
fn as_core_mut(&mut self) -> &mut ActivityCore { &mut self.inner }
|
||||
fn min_len(&self) -> usize { self.full_min_len() }
|
||||
fn truncate_push<R: RealizedOps>(&mut self, height: Height, state: &CohortState<R>) -> Result<()> {
|
||||
self.full_truncate_push(height, state)
|
||||
}
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.inner.validate_computed_versions(base_version)
|
||||
}
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityBase], exit: &Exit) -> Result<()> {
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
|
||||
self.compute_from_stateful(starting_indexes, others, exit)
|
||||
}
|
||||
fn compute_rest_part1(&mut self, blocks: &blocks::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Bitcoin, Cents, Dollars, Height, Indexes, Sats, SatsSigned, StoredF32, StoredI64, StoredU64,
|
||||
Cents, Dollars, Height, Indexes, Sats, SatsSigned, StoredI64, StoredU64,
|
||||
Version,
|
||||
};
|
||||
use vecdb::AnyStoredVec;
|
||||
@@ -10,11 +10,11 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RollingDeltaExcept1m};
|
||||
use crate::internal::RollingDeltaExcept1m;
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityFull, CohortMetricsBase, CostBasis, ImportConfig, OutputsMetrics,
|
||||
RealizedAdjusted, RealizedFull, RelativeForAll, SupplyMetrics, UnrealizedFull,
|
||||
ActivityFull, CohortMetricsBase, CostBasis, ImportConfig, OutputsFull,
|
||||
AdjustedSopr, RealizedFull, RelativeForAll, SupplyFull, UnrealizedFull,
|
||||
};
|
||||
|
||||
/// All-cohort metrics: extended realized + adjusted (as composable add-on),
|
||||
@@ -24,16 +24,15 @@ use crate::distribution::metrics::{
|
||||
pub struct AllCohortMetrics<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
pub supply: Box<SupplyMetrics<M>>,
|
||||
pub outputs: Box<OutputsMetrics<M>>,
|
||||
pub supply: Box<SupplyFull<M>>,
|
||||
pub outputs: Box<OutputsFull<M>>,
|
||||
pub activity: Box<ActivityFull<M>>,
|
||||
pub realized: Box<RealizedFull<M>>,
|
||||
pub cost_basis: Box<CostBasis<M>>,
|
||||
pub unrealized: Box<UnrealizedFull<M>>,
|
||||
pub adjusted: Box<RealizedAdjusted<M>>,
|
||||
#[traversable(wrap = "realized/sopr", rename = "adjusted")]
|
||||
pub asopr: Box<AdjustedSopr<M>>,
|
||||
pub relative: Box<RelativeForAll<M>>,
|
||||
pub dormancy: ComputedPerBlock<StoredF32, M>,
|
||||
pub velocity: ComputedPerBlock<StoredF32, M>,
|
||||
|
||||
#[traversable(wrap = "supply", rename = "delta")]
|
||||
pub supply_delta_extended: RollingDeltaExcept1m<Sats, SatsSigned, M>,
|
||||
@@ -73,8 +72,6 @@ impl CohortMetricsBase for AllCohortMetrics {
|
||||
vecs.extend(self.realized.collect_vecs_mut());
|
||||
vecs.extend(self.cost_basis.collect_vecs_mut());
|
||||
vecs.extend(self.unrealized.collect_vecs_mut());
|
||||
vecs.push(&mut self.dormancy.height);
|
||||
vecs.push(&mut self.velocity.height);
|
||||
vecs
|
||||
}
|
||||
}
|
||||
@@ -86,26 +83,24 @@ impl AllCohortMetrics {
|
||||
/// reference for relative metric lazy vecs in other cohorts.
|
||||
pub(crate) fn forced_import_with_supply(
|
||||
cfg: &ImportConfig,
|
||||
supply: SupplyMetrics,
|
||||
supply: SupplyFull,
|
||||
) -> Result<Self> {
|
||||
let unrealized = UnrealizedFull::forced_import(cfg)?;
|
||||
let realized = RealizedFull::forced_import(cfg)?;
|
||||
let adjusted = RealizedAdjusted::forced_import(cfg)?;
|
||||
let asopr = AdjustedSopr::forced_import(cfg)?;
|
||||
|
||||
let relative = RelativeForAll::forced_import(cfg)?;
|
||||
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: Box::new(supply),
|
||||
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsFull::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityFull::forced_import(cfg)?),
|
||||
realized: Box::new(realized),
|
||||
cost_basis: Box::new(CostBasis::forced_import(cfg)?),
|
||||
unrealized: Box::new(unrealized),
|
||||
adjusted: Box::new(adjusted),
|
||||
asopr: Box::new(asopr),
|
||||
relative: Box::new(relative),
|
||||
dormancy: cfg.import("dormancy", Version::ONE)?,
|
||||
velocity: cfg.import("velocity", Version::ONE)?,
|
||||
supply_delta_extended: cfg.import("supply_delta", Version::ONE)?,
|
||||
utxo_count_delta_extended: cfg.import("utxo_count_delta", Version::ONE)?,
|
||||
})
|
||||
@@ -131,11 +126,11 @@ impl AllCohortMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.adjusted.compute_rest_part2(
|
||||
self.asopr.compute_rest_part2(
|
||||
blocks,
|
||||
starting_indexes,
|
||||
&self.realized.value_created.height,
|
||||
&self.realized.value_destroyed.height,
|
||||
&self.realized.minimal.sopr.value_created.raw.height,
|
||||
&self.realized.minimal.sopr.value_destroyed.raw.height,
|
||||
up_to_1h_value_created,
|
||||
up_to_1h_value_destroyed,
|
||||
exit,
|
||||
@@ -163,33 +158,9 @@ impl AllCohortMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dormancy.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.activity.coindays_destroyed.height,
|
||||
&self.activity.sent.height,
|
||||
|(i, cdd, sent_sats, ..)| {
|
||||
let sent_btc = f64::from(Bitcoin::from(sent_sats));
|
||||
if sent_btc == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
} else {
|
||||
(i, StoredF32::from((f64::from(cdd) / sent_btc) as f32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.velocity.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.activity.sent.height,
|
||||
self.activity.compute_rest_part2(
|
||||
starting_indexes,
|
||||
&self.supply.total.sats.height,
|
||||
|(i, sent_sats, supply_sats, ..)| {
|
||||
let supply = supply_sats.as_u128() as f64;
|
||||
if supply == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
} else {
|
||||
(i, StoredF32::from((sent_sats.as_u128() as f64 / supply) as f32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityBase, CohortMetricsBase, ImportConfig, OutputsMetrics, RealizedBase,
|
||||
RelativeToAll, SupplyMetrics, UnrealizedBase,
|
||||
ActivityCore, CohortMetricsBase, ImportConfig, OutputsFull, RealizedCore,
|
||||
RelativeToAll, SupplyFull, UnrealizedBase,
|
||||
};
|
||||
|
||||
/// Basic cohort metrics: no extensions, with relative (rel_to_all).
|
||||
@@ -17,17 +17,17 @@ use crate::distribution::metrics::{
|
||||
pub struct BasicCohortMetrics<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
pub supply: Box<SupplyMetrics<M>>,
|
||||
pub outputs: Box<OutputsMetrics<M>>,
|
||||
pub activity: Box<ActivityBase<M>>,
|
||||
pub realized: Box<RealizedBase<M>>,
|
||||
pub supply: Box<SupplyFull<M>>,
|
||||
pub outputs: Box<OutputsFull<M>>,
|
||||
pub activity: Box<ActivityCore<M>>,
|
||||
pub realized: Box<RealizedCore<M>>,
|
||||
pub unrealized: Box<UnrealizedBase<M>>,
|
||||
pub relative: Box<RelativeToAll<M>>,
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for BasicCohortMetrics {
|
||||
type ActivityVecs = ActivityBase;
|
||||
type RealizedVecs = RealizedBase;
|
||||
type ActivityVecs = ActivityCore;
|
||||
type RealizedVecs = RealizedCore;
|
||||
type UnrealizedVecs = UnrealizedBase;
|
||||
|
||||
impl_cohort_accessors!();
|
||||
@@ -45,17 +45,17 @@ impl CohortMetricsBase for BasicCohortMetrics {
|
||||
|
||||
impl BasicCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let supply = SupplyFull::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedCore::forced_import(cfg)?;
|
||||
|
||||
let relative = RelativeToAll::forced_import(cfg)?;
|
||||
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: Box::new(supply),
|
||||
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityBase::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsFull::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityCore::forced_import(cfg)?),
|
||||
realized: Box::new(realized),
|
||||
unrealized: Box::new(unrealized),
|
||||
relative: Box::new(relative),
|
||||
|
||||
@@ -7,16 +7,16 @@ use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityCore, CohortMetricsBase, RealizedCore, ImportConfig, OutputsMetrics,
|
||||
RelativeToAll, SupplyMetrics, UnrealizedCore,
|
||||
ActivityCore, CohortMetricsBase, RealizedCore, ImportConfig, OutputsFull,
|
||||
RelativeToAll, SupplyFull, UnrealizedCore,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct CoreCohortMetrics<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
pub supply: Box<SupplyMetrics<M>>,
|
||||
pub outputs: Box<OutputsMetrics<M>>,
|
||||
pub supply: Box<SupplyFull<M>>,
|
||||
pub outputs: Box<OutputsFull<M>>,
|
||||
pub activity: Box<ActivityCore<M>>,
|
||||
pub realized: Box<RealizedCore<M>>,
|
||||
pub unrealized: Box<UnrealizedCore<M>>,
|
||||
@@ -27,8 +27,8 @@ impl CoreCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: Box::new(SupplyMetrics::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
|
||||
supply: Box::new(SupplyFull::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsFull::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityCore::forced_import(cfg)?),
|
||||
realized: Box::new(RealizedCore::forced_import(cfg)?),
|
||||
unrealized: Box::new(UnrealizedCore::forced_import(cfg)?),
|
||||
@@ -80,12 +80,12 @@ impl CoreCohortMetrics {
|
||||
)?;
|
||||
self.activity.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.activity_base().core).collect::<Vec<_>>(),
|
||||
&others.iter().map(|v| v.activity_core()).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.realized.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.realized_base().core).collect::<Vec<_>>(),
|
||||
&others.iter().map(|v| v.realized_core()).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized.compute_from_stateful(
|
||||
@@ -117,7 +117,7 @@ impl CoreCohortMetrics {
|
||||
self.realized
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
|
||||
self.unrealized.compute_rest(prices, starting_indexes, exit)?;
|
||||
self.unrealized.compute_rest(blocks, prices, starting_indexes, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Bitcoin, Dollars, Height, Indexes, Sats, SatsSigned, StoredF32, StoredI64, StoredU64, Version,
|
||||
Dollars, Height, Indexes, Sats, SatsSigned, StoredI64, StoredU64, Version,
|
||||
};
|
||||
use vecdb::AnyStoredVec;
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RollingDeltaExcept1m};
|
||||
use crate::internal::RollingDeltaExcept1m;
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityFull, CohortMetricsBase, CostBasis, ImportConfig, OutputsMetrics,
|
||||
RealizedFull, RelativeWithExtended, SupplyMetrics, UnrealizedFull,
|
||||
ActivityFull, CohortMetricsBase, CostBasis, ImportConfig, OutputsFull,
|
||||
RealizedFull, RelativeWithExtended, SupplyFull, UnrealizedFull,
|
||||
};
|
||||
|
||||
/// Cohort metrics with extended realized + extended cost basis (no adjusted).
|
||||
@@ -22,15 +22,13 @@ use crate::distribution::metrics::{
|
||||
pub struct ExtendedCohortMetrics<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
pub supply: Box<SupplyMetrics<M>>,
|
||||
pub outputs: Box<OutputsMetrics<M>>,
|
||||
pub supply: Box<SupplyFull<M>>,
|
||||
pub outputs: Box<OutputsFull<M>>,
|
||||
pub activity: Box<ActivityFull<M>>,
|
||||
pub realized: Box<RealizedFull<M>>,
|
||||
pub cost_basis: Box<CostBasis<M>>,
|
||||
pub unrealized: Box<UnrealizedFull<M>>,
|
||||
pub relative: Box<RelativeWithExtended<M>>,
|
||||
pub dormancy: ComputedPerBlock<StoredF32, M>,
|
||||
pub velocity: ComputedPerBlock<StoredF32, M>,
|
||||
|
||||
#[traversable(wrap = "supply", rename = "delta")]
|
||||
pub supply_delta_extended: RollingDeltaExcept1m<Sats, SatsSigned, M>,
|
||||
@@ -70,15 +68,13 @@ impl CohortMetricsBase for ExtendedCohortMetrics {
|
||||
vecs.extend(self.realized.collect_vecs_mut());
|
||||
vecs.extend(self.cost_basis.collect_vecs_mut());
|
||||
vecs.extend(self.unrealized.collect_vecs_mut());
|
||||
vecs.push(&mut self.dormancy.height);
|
||||
vecs.push(&mut self.velocity.height);
|
||||
vecs
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendedCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let supply = SupplyFull::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedFull::forced_import(cfg)?;
|
||||
let realized = RealizedFull::forced_import(cfg)?;
|
||||
|
||||
@@ -87,14 +83,12 @@ impl ExtendedCohortMetrics {
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: Box::new(supply),
|
||||
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsFull::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityFull::forced_import(cfg)?),
|
||||
realized: Box::new(realized),
|
||||
cost_basis: Box::new(CostBasis::forced_import(cfg)?),
|
||||
unrealized: Box::new(unrealized),
|
||||
relative: Box::new(relative),
|
||||
dormancy: cfg.import("dormancy", Version::ONE)?,
|
||||
velocity: cfg.import("velocity", Version::ONE)?,
|
||||
supply_delta_extended: cfg.import("supply_delta", Version::ONE)?,
|
||||
utxo_count_delta_extended: cfg.import("utxo_count_delta", Version::ONE)?,
|
||||
})
|
||||
@@ -142,33 +136,9 @@ impl ExtendedCohortMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dormancy.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.activity.coindays_destroyed.height,
|
||||
&self.activity.sent.height,
|
||||
|(i, cdd, sent_sats, ..)| {
|
||||
let sent_btc = f64::from(Bitcoin::from(sent_sats));
|
||||
if sent_btc == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
} else {
|
||||
(i, StoredF32::from((f64::from(cdd) / sent_btc) as f32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.velocity.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.activity.sent.height,
|
||||
self.activity.compute_rest_part2(
|
||||
starting_indexes,
|
||||
&self.supply.total.sats.height,
|
||||
|(i, sent_sats, supply_sats, ..)| {
|
||||
let supply = supply_sats.as_u128() as f64;
|
||||
if supply == 0.0 {
|
||||
(i, StoredF32::from(0.0f32))
|
||||
} else {
|
||||
(i, StoredF32::from((sent_sats.as_u128() as f64 / supply) as f32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityFull, CohortMetricsBase, ImportConfig, RealizedAdjusted,
|
||||
ActivityFull, CohortMetricsBase, ImportConfig, AdjustedSopr,
|
||||
RealizedFull, UnrealizedFull,
|
||||
};
|
||||
|
||||
@@ -22,7 +22,8 @@ pub struct ExtendedAdjustedCohortMetrics<M: StorageMode = Rw> {
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: ExtendedCohortMetrics<M>,
|
||||
pub adjusted: Box<RealizedAdjusted<M>>,
|
||||
#[traversable(wrap = "realized/sopr", rename = "adjusted")]
|
||||
pub asopr: Box<AdjustedSopr<M>>,
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
|
||||
@@ -48,10 +49,10 @@ impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
|
||||
impl ExtendedAdjustedCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let inner = ExtendedCohortMetrics::forced_import(cfg)?;
|
||||
let adjusted = RealizedAdjusted::forced_import(cfg)?;
|
||||
let asopr = AdjustedSopr::forced_import(cfg)?;
|
||||
Ok(Self {
|
||||
inner,
|
||||
adjusted: Box::new(adjusted),
|
||||
asopr: Box::new(asopr),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -76,11 +77,11 @@ impl ExtendedAdjustedCohortMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.adjusted.compute_rest_part2(
|
||||
self.asopr.compute_rest_part2(
|
||||
blocks,
|
||||
starting_indexes,
|
||||
&self.inner.realized.value_created.height,
|
||||
&self.inner.realized.value_destroyed.height,
|
||||
&self.inner.realized.minimal.sopr.value_created.raw.height,
|
||||
&self.inner.realized.minimal.sopr.value_destroyed.raw.height,
|
||||
up_to_1h_value_created,
|
||||
up_to_1h_value_destroyed,
|
||||
exit,
|
||||
|
||||
@@ -1,41 +1,35 @@
|
||||
use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Indexes, Version};
|
||||
use brk_types::Indexes;
|
||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::unrealized::UnrealizedMinimal;
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityCore, ImportConfig, OutputsMetrics, RealizedMinimal, SupplyMetrics,
|
||||
ImportConfig, OutputsBase, RealizedMinimal, SupplyBase,
|
||||
};
|
||||
|
||||
/// MinimalCohortMetrics: supply, outputs, sent+ema, realized cap/price/mvrv/profit/loss,
|
||||
/// supply in profit/loss.
|
||||
/// MinimalCohortMetrics: supply, outputs, realized cap/price/mvrv/profit/loss + value_created/destroyed.
|
||||
///
|
||||
/// Used for type_, amount, and address cohorts.
|
||||
/// Used for amount_range cohorts.
|
||||
/// Does NOT implement CohortMetricsBase — standalone, not aggregatable via trait.
|
||||
#[derive(Traversable)]
|
||||
pub struct MinimalCohortMetrics<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
pub supply: Box<SupplyMetrics<M>>,
|
||||
pub outputs: Box<OutputsMetrics<M>>,
|
||||
pub activity: Box<ActivityCore<M>>,
|
||||
pub supply: Box<SupplyBase<M>>,
|
||||
pub outputs: Box<OutputsBase<M>>,
|
||||
pub realized: Box<RealizedMinimal<M>>,
|
||||
pub unrealized: Box<UnrealizedMinimal<M>>,
|
||||
}
|
||||
|
||||
impl MinimalCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: Box::new(SupplyMetrics::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityCore::forced_import(cfg)?),
|
||||
supply: Box::new(SupplyBase::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsBase::forced_import(cfg)?),
|
||||
realized: Box::new(RealizedMinimal::forced_import(cfg)?),
|
||||
unrealized: Box::new(UnrealizedMinimal::forced_import(cfg)?),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,23 +37,14 @@ impl MinimalCohortMetrics {
|
||||
self.supply
|
||||
.min_len()
|
||||
.min(self.outputs.min_len())
|
||||
.min(self.activity.min_len())
|
||||
.min(self.realized.min_stateful_height_len())
|
||||
.min(self.unrealized.min_stateful_height_len())
|
||||
}
|
||||
|
||||
pub(crate) fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.supply.validate_computed_versions(base_version)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs: Vec<&mut dyn AnyStoredVec> = Vec::new();
|
||||
vecs.extend(self.supply.collect_vecs_mut());
|
||||
vecs.extend(self.outputs.collect_vecs_mut());
|
||||
vecs.extend(self.activity.collect_vecs_mut());
|
||||
vecs.extend(self.realized.collect_vecs_mut());
|
||||
vecs.extend(self.unrealized.collect_vecs_mut());
|
||||
vecs
|
||||
}
|
||||
|
||||
@@ -83,14 +68,6 @@ impl MinimalCohortMetrics {
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.activity.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| v.activity.as_ref())
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.realized.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others
|
||||
@@ -99,14 +76,6 @@ impl MinimalCohortMetrics {
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized.compute_from_sources(
|
||||
starting_indexes,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| v.unrealized.as_ref())
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -118,15 +87,8 @@ impl MinimalCohortMetrics {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply.compute(prices, starting_indexes.height, exit)?;
|
||||
self.supply
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
self.outputs.compute_rest(blocks, starting_indexes, exit)?;
|
||||
self.activity
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
self.realized
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
self.unrealized
|
||||
.compute_rest(prices, starting_indexes.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ mod core;
|
||||
mod extended;
|
||||
mod extended_adjusted;
|
||||
mod minimal;
|
||||
mod r#type;
|
||||
|
||||
pub use all::AllCohortMetrics;
|
||||
pub use basic::BasicCohortMetrics;
|
||||
@@ -11,3 +12,4 @@ pub use core::CoreCohortMetrics;
|
||||
pub use extended::ExtendedCohortMetrics;
|
||||
pub use extended_adjusted::ExtendedAdjustedCohortMetrics;
|
||||
pub use minimal::MinimalCohortMetrics;
|
||||
pub use r#type::TypeCohortMetrics;
|
||||
|
||||
83
crates/brk_computer/src/distribution/metrics/cohort/type.rs
Normal file
83
crates/brk_computer/src/distribution/metrics/cohort/type.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Indexes;
|
||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ImportConfig, OutputsBase, RealizedMinimal, SupplyBase, UnrealizedBasic,
|
||||
};
|
||||
|
||||
/// TypeCohortMetrics: supply(base), outputs(base), realized(minimal), unrealized(basic).
|
||||
///
|
||||
/// Used for type_ cohorts (p2pkh, p2sh, etc.).
|
||||
#[derive(Traversable)]
|
||||
pub struct TypeCohortMetrics<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub filter: Filter,
|
||||
pub supply: Box<SupplyBase<M>>,
|
||||
pub outputs: Box<OutputsBase<M>>,
|
||||
pub realized: Box<RealizedMinimal<M>>,
|
||||
pub unrealized: Box<UnrealizedBasic<M>>,
|
||||
}
|
||||
|
||||
impl TypeCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: Box::new(SupplyBase::forced_import(cfg)?),
|
||||
outputs: Box::new(OutputsBase::forced_import(cfg)?),
|
||||
realized: Box::new(RealizedMinimal::forced_import(cfg)?),
|
||||
unrealized: Box::new(UnrealizedBasic::forced_import(cfg)?),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.supply
|
||||
.min_len()
|
||||
.min(self.outputs.min_len())
|
||||
.min(self.realized.min_stateful_height_len())
|
||||
.min(self.unrealized.min_stateful_height_len())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs: Vec<&mut dyn AnyStoredVec> = Vec::new();
|
||||
vecs.extend(self.supply.collect_vecs_mut());
|
||||
vecs.extend(self.outputs.collect_vecs_mut());
|
||||
vecs.extend(self.realized.collect_vecs_mut());
|
||||
vecs.extend(self.unrealized.collect_vecs_mut());
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply.compute(prices, starting_indexes.height, exit)?;
|
||||
self.realized
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
self.unrealized
|
||||
.compute_rest(blocks, prices, starting_indexes.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part2(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.realized.compute_rest_part2(
|
||||
prices,
|
||||
starting_indexes,
|
||||
&self.supply.total.btc.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,13 @@ use vecdb::{BytesVec, BytesVecValue, Database, ImportableVec};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
AmountPerBlock, AmountPerBlockCumulative, CentsType, ComputedPerBlock,
|
||||
ComputedPerBlockCumulative, ComputedPerBlockCumulativeSum, RatioPerBlock,
|
||||
AmountPerBlock, AmountPerBlockCumulative, AmountPerBlockWithSum24h, CentsType, ComputedPerBlock,
|
||||
ComputedPerBlockCumulative, ComputedPerBlockCumulativeSum, FiatPerBlockWithSum24h,
|
||||
PerBlockWithSum24h, RatioPerBlock, RollingWindow24hAmountPerBlock,
|
||||
RollingWindow24hFiatPerBlock, RollingWindow24hPerBlock,
|
||||
FiatPerBlock, FiatRollingDelta1m, FiatRollingDeltaExcept1m, NumericValue,
|
||||
PercentPerBlock, PercentRollingWindows, Price, RollingDelta1m, RollingDeltaExcept1m,
|
||||
RollingWindow24h, RollingWindows, RollingWindowsFrom1w,
|
||||
RollingWindows, RollingWindowsFrom1w,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -37,7 +39,9 @@ macro_rules! impl_config_import {
|
||||
impl_config_import!(
|
||||
AmountPerBlock,
|
||||
AmountPerBlockCumulative,
|
||||
RatioPerBlock,
|
||||
RollingWindow24hAmountPerBlock,
|
||||
RatioPerBlock<BasisPoints32>,
|
||||
RatioPerBlock<BasisPointsSigned32>,
|
||||
PercentPerBlock<BasisPoints16>,
|
||||
PercentPerBlock<BasisPoints32>,
|
||||
PercentPerBlock<BasisPointsSigned32>,
|
||||
@@ -66,11 +70,40 @@ impl<T: NumericValue + JsonSchema> ConfigImport for RollingWindows<T> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
}
|
||||
}
|
||||
impl<T: NumericValue + JsonSchema> ConfigImport for RollingWindow24h<T> {
|
||||
impl<T: NumericValue + JsonSchema> ConfigImport for RollingWindow24hPerBlock<T> {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
}
|
||||
}
|
||||
impl<T: NumericValue + JsonSchema> ConfigImport for PerBlockWithSum24h<T> {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
raw: ComputedPerBlock::config_import(cfg, suffix, offset)?,
|
||||
sum: RollingWindow24hPerBlock::config_import(cfg, suffix, offset)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl ConfigImport for AmountPerBlockWithSum24h {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
raw: AmountPerBlock::config_import(cfg, suffix, offset)?,
|
||||
sum: RollingWindow24hAmountPerBlock::config_import(cfg, suffix, offset)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<C: CentsType> ConfigImport for RollingWindow24hFiatPerBlock<C> {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
}
|
||||
}
|
||||
impl<C: CentsType> ConfigImport for FiatPerBlockWithSum24h<C> {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
raw: FiatPerBlock::config_import(cfg, suffix, offset)?,
|
||||
sum: RollingWindow24hFiatPerBlock::config_import(cfg, suffix, offset)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<T: NumericValue + JsonSchema> ConfigImport for RollingWindowsFrom1w<T> {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
|
||||
@@ -19,10 +19,10 @@ mod activity;
|
||||
macro_rules! impl_cohort_accessors {
|
||||
() => {
|
||||
fn filter(&self) -> &brk_cohort::Filter { &self.filter }
|
||||
fn supply(&self) -> &$crate::distribution::metrics::SupplyMetrics { &self.supply }
|
||||
fn supply_mut(&mut self) -> &mut $crate::distribution::metrics::SupplyMetrics { &mut self.supply }
|
||||
fn outputs(&self) -> &$crate::distribution::metrics::OutputsMetrics { &self.outputs }
|
||||
fn outputs_mut(&mut self) -> &mut $crate::distribution::metrics::OutputsMetrics { &mut self.outputs }
|
||||
fn supply(&self) -> &$crate::distribution::metrics::SupplyFull { &self.supply }
|
||||
fn supply_mut(&mut self) -> &mut $crate::distribution::metrics::SupplyFull { &mut self.supply }
|
||||
fn outputs(&self) -> &$crate::distribution::metrics::OutputsFull { &self.outputs }
|
||||
fn outputs_mut(&mut self) -> &mut $crate::distribution::metrics::OutputsFull { &mut self.outputs }
|
||||
fn activity(&self) -> &Self::ActivityVecs { &self.activity }
|
||||
fn activity_mut(&mut self) -> &mut Self::ActivityVecs { &mut self.activity }
|
||||
fn realized(&self) -> &Self::RealizedVecs { &self.realized }
|
||||
@@ -42,24 +42,24 @@ mod relative;
|
||||
mod supply;
|
||||
mod unrealized;
|
||||
|
||||
pub use activity::{ActivityBase, ActivityCore, ActivityFull, ActivityLike};
|
||||
pub use activity::{ActivityCore, ActivityFull, ActivityLike};
|
||||
pub use cohort::{
|
||||
AllCohortMetrics, BasicCohortMetrics, CoreCohortMetrics, ExtendedAdjustedCohortMetrics,
|
||||
ExtendedCohortMetrics, MinimalCohortMetrics,
|
||||
AllCohortMetrics, BasicCohortMetrics, CoreCohortMetrics,
|
||||
ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, MinimalCohortMetrics, TypeCohortMetrics,
|
||||
};
|
||||
pub use config::ImportConfig;
|
||||
pub use cost_basis::CostBasis;
|
||||
pub use profitability::ProfitabilityMetrics;
|
||||
pub use outputs::OutputsMetrics;
|
||||
pub use outputs::{OutputsBase, OutputsFull};
|
||||
pub use realized::{
|
||||
RealizedAdjusted, RealizedBase, RealizedCore, RealizedFull, RealizedFullAccum, RealizedLike,
|
||||
AdjustedSopr, RealizedCore, RealizedFull, RealizedFullAccum, RealizedLike,
|
||||
RealizedMinimal,
|
||||
};
|
||||
pub use relative::{
|
||||
RelativeForAll, RelativeToAll, RelativeWithExtended,
|
||||
};
|
||||
pub use supply::SupplyMetrics;
|
||||
pub use unrealized::{UnrealizedBase, UnrealizedCore, UnrealizedFull, UnrealizedLike};
|
||||
pub use supply::{SupplyBase, SupplyFull};
|
||||
pub use unrealized::{UnrealizedBase, UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike};
|
||||
|
||||
use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
@@ -72,6 +72,9 @@ pub trait CohortMetricsState {
|
||||
type Realized: RealizedOps;
|
||||
}
|
||||
|
||||
impl<M: StorageMode> CohortMetricsState for TypeCohortMetrics<M> {
|
||||
type Realized = MinimalRealizedState;
|
||||
}
|
||||
impl<M: StorageMode> CohortMetricsState for MinimalCohortMetrics<M> {
|
||||
type Realized = MinimalRealizedState;
|
||||
}
|
||||
@@ -97,10 +100,10 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
type UnrealizedVecs: UnrealizedLike;
|
||||
|
||||
fn filter(&self) -> &Filter;
|
||||
fn supply(&self) -> &SupplyMetrics;
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics;
|
||||
fn outputs(&self) -> &OutputsMetrics;
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics;
|
||||
fn supply(&self) -> &SupplyFull;
|
||||
fn supply_mut(&mut self) -> &mut SupplyFull;
|
||||
fn outputs(&self) -> &OutputsFull;
|
||||
fn outputs_mut(&mut self) -> &mut OutputsFull;
|
||||
fn activity(&self) -> &Self::ActivityVecs;
|
||||
fn activity_mut(&mut self) -> &mut Self::ActivityVecs;
|
||||
fn realized(&self) -> &Self::RealizedVecs;
|
||||
@@ -108,13 +111,13 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
fn unrealized(&self) -> &Self::UnrealizedVecs;
|
||||
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs;
|
||||
|
||||
/// Convenience: access activity as `&ActivityBase` (via `ActivityLike::as_base`).
|
||||
fn activity_base(&self) -> &ActivityBase { self.activity().as_base() }
|
||||
fn activity_base_mut(&mut self) -> &mut ActivityBase { self.activity_mut().as_base_mut() }
|
||||
/// Convenience: access activity as `&ActivityCore` (via `ActivityLike::as_core`).
|
||||
fn activity_core(&self) -> &ActivityCore { self.activity().as_core() }
|
||||
fn activity_core_mut(&mut self) -> &mut ActivityCore { self.activity_mut().as_core_mut() }
|
||||
|
||||
/// Convenience: access realized as `&RealizedBase` (via `RealizedLike::as_base`).
|
||||
fn realized_base(&self) -> &RealizedBase { self.realized().as_base() }
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase { self.realized_mut().as_base_mut() }
|
||||
/// Convenience: access realized as `&RealizedCore` (via `RealizedLike::as_core`).
|
||||
fn realized_core(&self) -> &RealizedCore { self.realized().as_core() }
|
||||
fn realized_core_mut(&mut self) -> &mut RealizedCore { self.realized_mut().as_core_mut() }
|
||||
|
||||
/// Convenience: access unrealized as `&UnrealizedBase` (via `UnrealizedLike::as_base`).
|
||||
fn unrealized_base(&self) -> &UnrealizedBase { self.unrealized().as_base() }
|
||||
@@ -152,18 +155,10 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
}
|
||||
|
||||
fn truncate_push(&mut self, height: Height, state: &CohortState<RealizedState>) -> Result<()> {
|
||||
self.supply_mut()
|
||||
.truncate_push(height, state.supply.value)?;
|
||||
self.outputs_mut()
|
||||
.truncate_push(height, state.supply.utxo_count)?;
|
||||
self.activity_mut().truncate_push(
|
||||
height,
|
||||
state.sent,
|
||||
state.satblocks_destroyed,
|
||||
state.satdays_destroyed,
|
||||
)?;
|
||||
self.realized_mut()
|
||||
.truncate_push(height, &state.realized)?;
|
||||
self.supply_mut().truncate_push(height, state)?;
|
||||
self.outputs_mut().truncate_push(height, state)?;
|
||||
self.activity_mut().truncate_push(height, state)?;
|
||||
self.realized_mut().truncate_push(height, state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -188,7 +183,7 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
|
||||
self.unrealized_mut()
|
||||
.compute_rest(prices, starting_indexes, exit)?;
|
||||
.compute_rest(blocks, prices, starting_indexes, exit)?;
|
||||
|
||||
self.unrealized_mut()
|
||||
.compute_net_sentiment_height(starting_indexes, exit)?;
|
||||
@@ -215,12 +210,12 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
)?;
|
||||
self.activity_mut().compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| v.activity_base()).collect::<Vec<_>>(),
|
||||
&others.iter().map(|v| v.activity_core()).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.realized_mut().compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| v.realized_base()).collect::<Vec<_>>(),
|
||||
&others.iter().map(|v| v.realized_core()).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_base_mut().compute_from_stateful(
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, StoredI64, StoredU64, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{blocks, internal::{ComputedPerBlock, RollingDelta1m}};
|
||||
|
||||
use super::ImportConfig;
|
||||
|
||||
/// Output metrics for a cohort.
|
||||
#[derive(Traversable)]
|
||||
pub struct OutputsMetrics<M: StorageMode = Rw> {
|
||||
pub utxo_count: ComputedPerBlock<StoredU64, M>,
|
||||
pub utxo_count_delta: RollingDelta1m<StoredU64, StoredI64, M>,
|
||||
}
|
||||
|
||||
impl OutputsMetrics {
|
||||
/// Import output metrics from database.
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
utxo_count: cfg.import("utxo_count", Version::ZERO)?,
|
||||
utxo_count_delta: cfg.import("utxo_count_delta", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get minimum length across height-indexed vectors.
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.utxo_count.height.len()
|
||||
}
|
||||
|
||||
/// Push utxo count to height-indexed vector.
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, utxo_count: u64) -> Result<()> {
|
||||
self.utxo_count
|
||||
.height
|
||||
.truncate_push(height, StoredU64::from(utxo_count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![&mut self.utxo_count.height as &mut dyn AnyStoredVec]
|
||||
}
|
||||
|
||||
/// Compute aggregate values from separate cohorts.
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo_count.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.utxo_count.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute derived metrics.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo_count_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
&self.utxo_count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
54
crates/brk_computer/src/distribution/metrics/outputs/base.rs
Normal file
54
crates/brk_computer/src/distribution/metrics/outputs/base.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, StoredU64, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{distribution::state::{CohortState, RealizedOps}, internal::ComputedPerBlock};
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
/// Base output metrics: utxo_count only (1 stored vec).
|
||||
#[derive(Traversable)]
|
||||
pub struct OutputsBase<M: StorageMode = Rw> {
|
||||
pub utxo_count: ComputedPerBlock<StoredU64, M>,
|
||||
}
|
||||
|
||||
impl OutputsBase {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
utxo_count: cfg.import("utxo_count", Version::ZERO)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.utxo_count.height.len()
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &CohortState<impl RealizedOps>) -> Result<()> {
|
||||
self.utxo_count
|
||||
.height
|
||||
.truncate_push(height, StoredU64::from(state.supply.utxo_count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![&mut self.utxo_count.height as &mut dyn AnyStoredVec]
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo_count.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.utxo_count.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
64
crates/brk_computer/src/distribution/metrics/outputs/full.rs
Normal file
64
crates/brk_computer/src/distribution/metrics/outputs/full.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Indexes, StoredI64, StoredU64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, internal::RollingDelta1m};
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::OutputsBase;
|
||||
|
||||
/// Full output metrics: utxo_count + delta (3 stored vecs).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct OutputsFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub base: OutputsBase<M>,
|
||||
|
||||
pub utxo_count_delta: RollingDelta1m<StoredU64, StoredI64, M>,
|
||||
}
|
||||
|
||||
impl OutputsFull {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let base = OutputsBase::forced_import(cfg)?;
|
||||
let utxo_count_delta = cfg.import("utxo_count_delta", Version::ONE)?;
|
||||
|
||||
Ok(Self {
|
||||
base,
|
||||
utxo_count_delta,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
self.base.collect_vecs_mut()
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let base_refs: Vec<&OutputsBase> = others.iter().map(|o| &o.base).collect();
|
||||
self.base.compute_from_stateful(starting_indexes, &base_refs, exit)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo_count_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
&self.base.utxo_count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod base;
|
||||
mod full;
|
||||
|
||||
pub use base::OutputsBase;
|
||||
pub use full::OutputsFull;
|
||||
@@ -11,22 +11,22 @@ use crate::{
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedAdjusted<M: StorageMode = Rw> {
|
||||
pub struct AdjustedSopr<M: StorageMode = Rw> {
|
||||
pub value_created: ComputedPerBlock<Cents, M>,
|
||||
pub value_destroyed: ComputedPerBlock<Cents, M>,
|
||||
pub value_created_sum: RollingWindows<Cents, M>,
|
||||
pub value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
pub sopr: RollingWindows<StoredF64, M>,
|
||||
pub ratio: RollingWindows<StoredF64, M>,
|
||||
}
|
||||
|
||||
impl RealizedAdjusted {
|
||||
impl AdjustedSopr {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(RealizedAdjusted {
|
||||
Ok(Self {
|
||||
value_created: cfg.import("adjusted_value_created", Version::ZERO)?,
|
||||
value_destroyed: cfg.import("adjusted_value_destroyed", Version::ZERO)?,
|
||||
value_created_sum: cfg.import("adjusted_value_created", Version::ONE)?,
|
||||
value_destroyed_sum: cfg.import("adjusted_value_destroyed", Version::ONE)?,
|
||||
sopr: cfg.import("adjusted_sopr", Version::ONE)?,
|
||||
ratio: cfg.import("adjusted_sopr", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ impl RealizedAdjusted {
|
||||
|
||||
// SOPR ratios from rolling sums
|
||||
for ((sopr, vc), vd) in self
|
||||
.sopr
|
||||
.ratio
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.value_created_sum.as_array())
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::state::RealizedOps,
|
||||
internal::{ComputedPerBlock, RollingWindow24h},
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::RealizedCore;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct RealizedBase<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub core: RealizedCore<M>,
|
||||
|
||||
pub sent_in_profit: ComputedPerBlock<Sats, M>,
|
||||
pub sent_in_loss: ComputedPerBlock<Sats, M>,
|
||||
|
||||
pub sent_in_profit_sum: RollingWindow24h<Sats, M>,
|
||||
pub sent_in_loss_sum: RollingWindow24h<Sats, M>,
|
||||
}
|
||||
|
||||
impl RealizedBase {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v1 = Version::ONE;
|
||||
|
||||
Ok(Self {
|
||||
core: RealizedCore::forced_import(cfg)?,
|
||||
sent_in_profit: cfg.import("sent_in_profit", v1)?,
|
||||
sent_in_loss: cfg.import("sent_in_loss", v1)?,
|
||||
sent_in_profit_sum: cfg.import("sent_in_profit", v1)?,
|
||||
sent_in_loss_sum: cfg.import("sent_in_loss", v1)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.core
|
||||
.min_stateful_height_len()
|
||||
.min(self.sent_in_profit.height.len())
|
||||
.min(self.sent_in_loss.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &impl RealizedOps) -> Result<()> {
|
||||
self.core.truncate_push(height, state)?;
|
||||
self.sent_in_profit
|
||||
.height
|
||||
.truncate_push(height, state.sent_in_profit())?;
|
||||
self.sent_in_loss
|
||||
.height
|
||||
.truncate_push(height, state.sent_in_loss())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.core.collect_vecs_mut();
|
||||
vecs.push(&mut self.sent_in_profit.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.sent_in_loss.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let core_refs: Vec<&RealizedCore> = others.iter().map(|o| &o.core).collect();
|
||||
self.core
|
||||
.compute_from_stateful(starting_indexes, &core_refs, exit)?;
|
||||
|
||||
sum_others!(self, starting_indexes, others, exit; sent_in_profit.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sent_in_loss.height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.core.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
self.sent_in_profit_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sent_in_profit.height,
|
||||
exit,
|
||||
)?;
|
||||
self.sent_in_loss_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sent_in_loss.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,11 @@ use vecdb::{
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::state::RealizedOps,
|
||||
distribution::state::{CohortState, RealizedOps},
|
||||
internal::{
|
||||
ComputedPerBlock, FiatRollingDelta1m, LazyPerBlock, NegCentsUnsignedToDollars,
|
||||
RatioCents64, RollingWindow24h,
|
||||
AmountPerBlockWithSum24h, ComputedPerBlock, FiatRollingDelta1m, LazyPerBlock,
|
||||
NegCentsUnsignedToDollars, PerBlockWithSum24h, RatioCents64,
|
||||
RollingWindow24hPerBlock,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
@@ -20,6 +21,17 @@ use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::RealizedMinimal;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedSoprCore<M: StorageMode = Rw> {
|
||||
pub ratio: RollingWindow24hPerBlock<StoredF64, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedSentCore<M: StorageMode = Rw> {
|
||||
pub in_profit: AmountPerBlockWithSum24h<M>,
|
||||
pub in_loss: AmountPerBlockWithSum24h<M>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct RealizedCore<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
@@ -27,17 +39,19 @@ pub struct RealizedCore<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub minimal: RealizedMinimal<M>,
|
||||
|
||||
#[traversable(wrap = "profit", rename = "cumulative")]
|
||||
pub profit_cumulative: ComputedPerBlock<Cents, M>,
|
||||
#[traversable(wrap = "loss", rename = "cumulative")]
|
||||
pub loss_cumulative: ComputedPerBlock<Cents, M>,
|
||||
|
||||
#[traversable(wrap = "cap", rename = "delta")]
|
||||
pub cap_delta: FiatRollingDelta1m<Cents, CentsSigned, M>,
|
||||
|
||||
#[traversable(wrap = "loss", rename = "neg")]
|
||||
pub neg_loss: LazyPerBlock<Dollars, Cents>,
|
||||
pub net_pnl: ComputedPerBlock<CentsSigned, M>,
|
||||
pub net_pnl_sum: RollingWindow24h<CentsSigned, M>,
|
||||
|
||||
pub value_created: ComputedPerBlock<Cents, M>,
|
||||
pub value_destroyed: ComputedPerBlock<Cents, M>,
|
||||
pub value_created_sum: RollingWindow24h<Cents, M>,
|
||||
pub value_destroyed_sum: RollingWindow24h<Cents, M>,
|
||||
pub sopr: RollingWindow24h<StoredF64, M>,
|
||||
pub net_pnl: PerBlockWithSum24h<CentsSigned, M>,
|
||||
pub sopr: RealizedSoprCore<M>,
|
||||
pub sent: RealizedSentCore<M>,
|
||||
}
|
||||
|
||||
impl RealizedCore {
|
||||
@@ -50,55 +64,57 @@ impl RealizedCore {
|
||||
let neg_realized_loss = LazyPerBlock::from_height_source::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_realized_loss"),
|
||||
cfg.version + Version::ONE,
|
||||
minimal.loss.height.read_only_boxed_clone(),
|
||||
minimal.loss.raw.cents.height.read_only_boxed_clone(),
|
||||
cfg.indexes,
|
||||
);
|
||||
|
||||
let net_realized_pnl = cfg.import("net_realized_pnl", v1)?;
|
||||
let net_realized_pnl_sum = cfg.import("net_realized_pnl", v1)?;
|
||||
|
||||
let value_created = cfg.import("value_created", v0)?;
|
||||
let value_destroyed = cfg.import("value_destroyed", v0)?;
|
||||
let value_created_sum = cfg.import("value_created", v1)?;
|
||||
let value_destroyed_sum = cfg.import("value_destroyed", v1)?;
|
||||
let sopr = cfg.import("sopr", v1)?;
|
||||
|
||||
Ok(Self {
|
||||
minimal,
|
||||
profit_cumulative: cfg.import("realized_profit_cumulative", v0)?,
|
||||
loss_cumulative: cfg.import("realized_loss_cumulative", v0)?,
|
||||
cap_delta: cfg.import("realized_cap_delta", v1)?,
|
||||
neg_loss: neg_realized_loss,
|
||||
net_pnl: net_realized_pnl,
|
||||
net_pnl_sum: net_realized_pnl_sum,
|
||||
value_created,
|
||||
value_destroyed,
|
||||
value_created_sum,
|
||||
value_destroyed_sum,
|
||||
sopr,
|
||||
net_pnl: cfg.import("net_realized_pnl", v1)?,
|
||||
sopr: RealizedSoprCore {
|
||||
ratio: cfg.import("sopr", v1)?,
|
||||
},
|
||||
sent: RealizedSentCore {
|
||||
in_profit: cfg.import("sent_in_profit", v1)?,
|
||||
in_loss: cfg.import("sent_in_loss", v1)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.minimal
|
||||
.min_stateful_height_len()
|
||||
.min(self.value_created.height.len())
|
||||
.min(self.value_destroyed.height.len())
|
||||
.min(self.sent.in_profit.raw.sats.height.len())
|
||||
.min(self.sent.in_loss.raw.sats.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &impl RealizedOps) -> Result<()> {
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &CohortState<impl RealizedOps>) -> Result<()> {
|
||||
self.minimal.truncate_push(height, state)?;
|
||||
self.value_created
|
||||
self.sent
|
||||
.in_profit
|
||||
.raw
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, state.value_created())?;
|
||||
self.value_destroyed
|
||||
.truncate_push(height, state.realized.sent_in_profit())?;
|
||||
self.sent
|
||||
.in_loss
|
||||
.raw
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, state.value_destroyed())?;
|
||||
.truncate_push(height, state.realized.sent_in_loss())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.minimal.collect_vecs_mut();
|
||||
vecs.push(&mut self.value_created.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.value_destroyed.height);
|
||||
vecs.push(&mut self.sent.in_profit.raw.sats.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.sent.in_profit.raw.cents.height);
|
||||
vecs.push(&mut self.sent.in_loss.raw.sats.height);
|
||||
vecs.push(&mut self.sent.in_loss.raw.cents.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
@@ -112,8 +128,10 @@ impl RealizedCore {
|
||||
self.minimal
|
||||
.compute_from_stateful(starting_indexes, &minimal_refs, exit)?;
|
||||
|
||||
sum_others!(self, starting_indexes, others, exit; value_created.height);
|
||||
sum_others!(self, starting_indexes, others, exit; value_destroyed.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sent.in_profit.raw.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sent.in_profit.raw.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sent.in_loss.raw.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sent.in_loss.raw.cents.height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -127,10 +145,21 @@ impl RealizedCore {
|
||||
self.minimal
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
|
||||
self.net_pnl.height.compute_transform2(
|
||||
self.profit_cumulative.height.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.minimal.profit.height,
|
||||
&self.minimal.loss.height,
|
||||
&self.minimal.profit.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss_cumulative.height.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.minimal.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.net_pnl.raw.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.minimal.profit.raw.cents.height,
|
||||
&self.minimal.loss.raw.cents.height,
|
||||
|(i, profit, loss, ..)| {
|
||||
(
|
||||
i,
|
||||
@@ -157,36 +186,48 @@ impl RealizedCore {
|
||||
self.cap_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
&self.minimal.cap_cents.height,
|
||||
&self.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.net_pnl_sum.compute_rolling_sum(
|
||||
self.net_pnl.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.net_pnl.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.value_created_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.value_created.height,
|
||||
exit,
|
||||
)?;
|
||||
self.value_destroyed_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.value_destroyed.height,
|
||||
&self.net_pnl.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.sopr
|
||||
.ratio
|
||||
._24h
|
||||
.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height,
|
||||
&self.value_created_sum._24h.height,
|
||||
&self.value_destroyed_sum._24h.height,
|
||||
&self.minimal.sopr.value_created.sum._24h.height,
|
||||
&self.minimal.sopr.value_destroyed.sum._24h.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.sent
|
||||
.in_profit
|
||||
.raw
|
||||
.compute(prices, starting_indexes.height, exit)?;
|
||||
self.sent
|
||||
.in_loss
|
||||
.raw
|
||||
.compute(prices, starting_indexes.height, exit)?;
|
||||
|
||||
self.sent.in_profit.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sent.in_profit.raw.sats.height,
|
||||
&self.sent.in_profit.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.sent.in_loss.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sent.in_loss.raw.sats.height,
|
||||
&self.sent.in_loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use vecdb::{
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::state::RealizedState,
|
||||
distribution::state::{CohortState, RealizedState},
|
||||
internal::{
|
||||
CentsUnsignedToDollars, ComputedPerBlock, ComputedPerBlockCumulative, FiatPerBlock,
|
||||
FiatRollingDelta1m, FiatRollingDeltaExcept1m, LazyPerBlock, PercentPerBlock,
|
||||
@@ -25,86 +25,118 @@ use crate::{
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::RealizedBase;
|
||||
use super::RealizedCore;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedProfit<M: StorageMode = Rw> {
|
||||
pub rel_to_rcap: PercentPerBlock<BasisPoints32, M>,
|
||||
pub value_created: ComputedPerBlock<Cents, M>,
|
||||
pub value_destroyed: ComputedPerBlock<Cents, M>,
|
||||
pub value_created_sum: RollingWindows<Cents, M>,
|
||||
pub value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
pub flow: LazyPerBlock<Dollars, Cents>,
|
||||
#[traversable(rename = "sum")]
|
||||
pub sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedLoss<M: StorageMode = Rw> {
|
||||
pub rel_to_rcap: PercentPerBlock<BasisPoints32, M>,
|
||||
pub value_created: ComputedPerBlock<Cents, M>,
|
||||
pub value_destroyed: ComputedPerBlock<Cents, M>,
|
||||
pub value_created_sum: RollingWindows<Cents, M>,
|
||||
pub value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
pub capitulation_flow: LazyPerBlock<Dollars, Cents>,
|
||||
#[traversable(rename = "sum")]
|
||||
pub sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedGrossPnl<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub value: FiatPerBlock<Cents, M>,
|
||||
pub sum: RollingWindows<Cents, M>,
|
||||
pub sell_side_risk_ratio: PercentRollingWindows<BasisPoints32, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedNetPnl<M: StorageMode = Rw> {
|
||||
pub rel_to_rcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub cumulative: ComputedPerBlock<CentsSigned, M>,
|
||||
#[traversable(rename = "sum")]
|
||||
pub sum_extended: RollingWindowsFrom1w<CentsSigned, M>,
|
||||
pub delta: FiatRollingDelta1m<CentsSigned, CentsSigned, M>,
|
||||
#[traversable(rename = "delta")]
|
||||
pub delta_extended: FiatRollingDeltaExcept1m<CentsSigned, CentsSigned, M>,
|
||||
pub change_1m_rel_to_rcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub change_1m_rel_to_mcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedSopr<M: StorageMode = Rw> {
|
||||
#[traversable(rename = "value_created_sum")]
|
||||
pub value_created_sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
#[traversable(rename = "value_destroyed_sum")]
|
||||
pub value_destroyed_sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
#[traversable(rename = "ratio")]
|
||||
pub ratio_extended: RollingWindowsFrom1w<StoredF64, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedSentFull<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "in_profit", rename = "sum")]
|
||||
pub in_profit_sum_extended: RollingWindowsFrom1w<Sats, M>,
|
||||
#[traversable(wrap = "in_loss", rename = "sum")]
|
||||
pub in_loss_sum_extended: RollingWindowsFrom1w<Sats, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedPeakRegret<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub value: ComputedPerBlockCumulative<Cents, M>,
|
||||
pub rel_to_rcap: PercentPerBlock<BasisPoints32, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedInvestor<M: StorageMode = Rw> {
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_ratio: RatioPerBlock<BasisPoints32, M>,
|
||||
pub lower_price_band: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub upper_price_band: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub cap_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub price_ratio_percentiles: RatioPerBlockPercentiles<M>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct RealizedFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub base: RealizedBase<M>,
|
||||
pub core: RealizedCore<M>,
|
||||
|
||||
pub gross_pnl: FiatPerBlock<Cents, M>,
|
||||
pub profit: RealizedProfit<M>,
|
||||
pub loss: RealizedLoss<M>,
|
||||
pub gross_pnl: RealizedGrossPnl<M>,
|
||||
pub net_pnl: RealizedNetPnl<M>,
|
||||
pub sopr: RealizedSopr<M>,
|
||||
pub sent: RealizedSentFull<M>,
|
||||
pub peak_regret: RealizedPeakRegret<M>,
|
||||
pub investor: RealizedInvestor<M>,
|
||||
|
||||
pub profit_rel_to_rcap: PercentPerBlock<BasisPoints32, M>,
|
||||
pub loss_rel_to_rcap: PercentPerBlock<BasisPoints32, M>,
|
||||
pub net_pnl_rel_to_rcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
|
||||
pub profit_value_created: ComputedPerBlock<Cents, M>,
|
||||
pub profit_value_destroyed: ComputedPerBlock<Cents, M>,
|
||||
pub loss_value_created: ComputedPerBlock<Cents, M>,
|
||||
pub loss_value_destroyed: ComputedPerBlock<Cents, M>,
|
||||
|
||||
pub profit_value_created_sum: RollingWindows<Cents, M>,
|
||||
pub profit_value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
pub loss_value_created_sum: RollingWindows<Cents, M>,
|
||||
pub loss_value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
|
||||
pub capitulation_flow: LazyPerBlock<Dollars, Cents>,
|
||||
pub profit_flow: LazyPerBlock<Dollars, Cents>,
|
||||
|
||||
pub gross_pnl_sum: RollingWindows<Cents, M>,
|
||||
|
||||
pub net_pnl_cumulative: ComputedPerBlock<CentsSigned, M>,
|
||||
#[traversable(rename = "net_pnl_sum")]
|
||||
pub net_pnl_sum_extended: RollingWindowsFrom1w<CentsSigned, M>,
|
||||
|
||||
pub net_pnl_delta: FiatRollingDelta1m<CentsSigned, CentsSigned, M>,
|
||||
#[traversable(rename = "net_pnl_delta")]
|
||||
pub net_pnl_delta_extended: FiatRollingDeltaExcept1m<CentsSigned, CentsSigned, M>,
|
||||
pub net_pnl_change_1m_rel_to_rcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub net_pnl_change_1m_rel_to_mcap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
|
||||
#[traversable(rename = "cap_delta")]
|
||||
pub cap_delta_extended: FiatRollingDeltaExcept1m<Cents, CentsSigned, M>,
|
||||
|
||||
pub investor_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub investor_price_ratio: RatioPerBlock<M>,
|
||||
|
||||
pub lower_price_band: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub upper_price_band: Price<ComputedPerBlock<Cents, M>>,
|
||||
|
||||
pub cap_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub investor_cap_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
|
||||
pub sell_side_risk_ratio: PercentRollingWindows<BasisPoints32, M>,
|
||||
|
||||
pub peak_regret: ComputedPerBlockCumulative<Cents, M>,
|
||||
pub peak_regret_rel_to_rcap: PercentPerBlock<BasisPoints32, M>,
|
||||
|
||||
pub cap_rel_to_own_mcap: PercentPerBlock<BasisPoints32, M>,
|
||||
|
||||
#[traversable(rename = "profit_sum")]
|
||||
pub profit_sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
#[traversable(rename = "loss_sum")]
|
||||
pub loss_sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
pub profit_to_loss_ratio: RollingWindows<StoredF64, M>,
|
||||
|
||||
#[traversable(rename = "value_created_sum")]
|
||||
pub value_created_sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
#[traversable(rename = "value_destroyed_sum")]
|
||||
pub value_destroyed_sum_extended: RollingWindowsFrom1w<Cents, M>,
|
||||
#[traversable(rename = "sopr")]
|
||||
pub sopr_extended: RollingWindowsFrom1w<StoredF64, M>,
|
||||
#[traversable(wrap = "cap", rename = "delta")]
|
||||
pub cap_delta_extended: FiatRollingDeltaExcept1m<Cents, CentsSigned, M>,
|
||||
|
||||
#[traversable(rename = "sent_in_profit_sum")]
|
||||
pub sent_in_profit_sum_extended: RollingWindowsFrom1w<Sats, M>,
|
||||
#[traversable(rename = "sent_in_loss_sum")]
|
||||
pub sent_in_loss_sum_extended: RollingWindowsFrom1w<Sats, M>,
|
||||
#[traversable(wrap = "cap", rename = "raw")]
|
||||
pub cap_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
#[traversable(wrap = "cap", rename = "rel_to_own_mcap")]
|
||||
pub cap_rel_to_own_mcap: PercentPerBlock<BasisPoints32, M>,
|
||||
|
||||
#[traversable(wrap = "price_ratio", rename = "percentiles")]
|
||||
pub price_ratio_percentiles: RatioPerBlockPercentiles<M>,
|
||||
#[traversable(wrap = "price_ratio", rename = "std_dev")]
|
||||
pub price_ratio_std_dev: RatioPerBlockStdDevBands<M>,
|
||||
pub investor_price_ratio_percentiles: RatioPerBlockPercentiles<M>,
|
||||
}
|
||||
|
||||
impl RealizedFull {
|
||||
@@ -112,111 +144,120 @@ impl RealizedFull {
|
||||
let v0 = Version::ZERO;
|
||||
let v1 = Version::ONE;
|
||||
|
||||
let base = RealizedBase::forced_import(cfg)?;
|
||||
let core = RealizedCore::forced_import(cfg)?;
|
||||
|
||||
let gross_pnl = cfg.import("realized_gross_pnl", v0)?;
|
||||
|
||||
let profit_value_created = cfg.import("profit_value_created", v0)?;
|
||||
// Profit
|
||||
let profit_value_destroyed: ComputedPerBlock<Cents> =
|
||||
cfg.import("profit_value_destroyed", v0)?;
|
||||
let loss_value_created = cfg.import("loss_value_created", v0)?;
|
||||
let loss_value_destroyed: ComputedPerBlock<Cents> =
|
||||
cfg.import("loss_value_destroyed", v0)?;
|
||||
|
||||
let profit_value_created_sum = cfg.import("profit_value_created", v1)?;
|
||||
let profit_value_destroyed_sum = cfg.import("profit_value_destroyed", v1)?;
|
||||
let loss_value_created_sum = cfg.import("loss_value_created", v1)?;
|
||||
let loss_value_destroyed_sum = cfg.import("loss_value_destroyed", v1)?;
|
||||
|
||||
let capitulation_flow = LazyPerBlock::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("capitulation_flow"),
|
||||
cfg.version,
|
||||
loss_value_destroyed.height.read_only_boxed_clone(),
|
||||
&loss_value_destroyed,
|
||||
);
|
||||
let profit_flow = LazyPerBlock::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("profit_flow"),
|
||||
cfg.version,
|
||||
profit_value_destroyed.height.read_only_boxed_clone(),
|
||||
&profit_value_destroyed,
|
||||
);
|
||||
let profit = RealizedProfit {
|
||||
rel_to_rcap: cfg.import("realized_profit_rel_to_realized_cap", Version::new(2))?,
|
||||
value_created: cfg.import("profit_value_created", v0)?,
|
||||
value_destroyed: profit_value_destroyed,
|
||||
value_created_sum: cfg.import("profit_value_created", v1)?,
|
||||
value_destroyed_sum: cfg.import("profit_value_destroyed", v1)?,
|
||||
flow: profit_flow,
|
||||
sum_extended: cfg.import("realized_profit", v1)?,
|
||||
};
|
||||
|
||||
let gross_pnl_sum = cfg.import("gross_pnl_sum", Version::ONE)?;
|
||||
// Loss
|
||||
let loss_value_destroyed: ComputedPerBlock<Cents> =
|
||||
cfg.import("loss_value_destroyed", v0)?;
|
||||
let capitulation_flow = LazyPerBlock::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("capitulation_flow"),
|
||||
cfg.version,
|
||||
loss_value_destroyed.height.read_only_boxed_clone(),
|
||||
&loss_value_destroyed,
|
||||
);
|
||||
let loss = RealizedLoss {
|
||||
rel_to_rcap: cfg.import("realized_loss_rel_to_realized_cap", Version::new(2))?,
|
||||
value_created: cfg.import("loss_value_created", v0)?,
|
||||
value_destroyed: loss_value_destroyed,
|
||||
value_created_sum: cfg.import("loss_value_created", v1)?,
|
||||
value_destroyed_sum: cfg.import("loss_value_destroyed", v1)?,
|
||||
capitulation_flow,
|
||||
sum_extended: cfg.import("realized_loss", v1)?,
|
||||
};
|
||||
|
||||
let investor_price = cfg.import("investor_price", v0)?;
|
||||
let investor_price_ratio = cfg.import("investor_price", v0)?;
|
||||
let lower_price_band = cfg.import("lower_price_band", v0)?;
|
||||
let upper_price_band = cfg.import("upper_price_band", v0)?;
|
||||
// Gross PnL
|
||||
let gross_pnl = RealizedGrossPnl {
|
||||
value: cfg.import("realized_gross_pnl", v0)?,
|
||||
sum: cfg.import("gross_pnl_sum", v1)?,
|
||||
sell_side_risk_ratio: cfg.import("sell_side_risk_ratio", Version::new(2))?,
|
||||
};
|
||||
|
||||
let cap_raw = cfg.import("cap_raw", v0)?;
|
||||
let investor_cap_raw = cfg.import("investor_cap_raw", v0)?;
|
||||
// Net PnL
|
||||
let net_pnl = RealizedNetPnl {
|
||||
rel_to_rcap: cfg
|
||||
.import("net_realized_pnl_rel_to_realized_cap", Version::new(2))?,
|
||||
cumulative: cfg.import("net_realized_pnl_cumulative", v1)?,
|
||||
sum_extended: cfg.import("net_realized_pnl", v1)?,
|
||||
delta: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
delta_extended: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
change_1m_rel_to_rcap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_realized_cap", Version::new(4))?,
|
||||
change_1m_rel_to_mcap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_market_cap", Version::new(4))?,
|
||||
};
|
||||
|
||||
let sell_side_risk_ratio = cfg.import("sell_side_risk_ratio", Version::new(2))?;
|
||||
// SOPR
|
||||
let sopr = RealizedSopr {
|
||||
value_created_sum_extended: cfg.import("value_created", v1)?,
|
||||
value_destroyed_sum_extended: cfg.import("value_destroyed", v1)?,
|
||||
ratio_extended: cfg.import("sopr", v1)?,
|
||||
};
|
||||
|
||||
let peak_regret = cfg.import("realized_peak_regret", Version::new(2))?;
|
||||
let peak_regret_rel_to_realized_cap =
|
||||
cfg.import("realized_peak_regret_rel_to_realized_cap", Version::new(2))?;
|
||||
// Sent
|
||||
let sent = RealizedSentFull {
|
||||
in_profit_sum_extended: cfg.import("sent_in_profit", v1)?,
|
||||
in_loss_sum_extended: cfg.import("sent_in_loss", v1)?,
|
||||
};
|
||||
|
||||
// Peak regret
|
||||
let peak_regret = RealizedPeakRegret {
|
||||
value: cfg.import("realized_peak_regret", Version::new(2))?,
|
||||
rel_to_rcap: cfg
|
||||
.import("realized_peak_regret_rel_to_realized_cap", Version::new(2))?,
|
||||
};
|
||||
|
||||
// Investor
|
||||
let investor = RealizedInvestor {
|
||||
price: cfg.import("investor_price", v0)?,
|
||||
price_ratio: cfg.import("investor_price", v0)?,
|
||||
lower_price_band: cfg.import("lower_price_band", v0)?,
|
||||
upper_price_band: cfg.import("upper_price_band", v0)?,
|
||||
cap_raw: cfg.import("investor_cap_raw", v0)?,
|
||||
price_ratio_percentiles: RatioPerBlockPercentiles::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("investor_price"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
};
|
||||
|
||||
// Price ratio stats
|
||||
let realized_price_name = cfg.name("realized_price");
|
||||
let realized_price_version = cfg.version + v1;
|
||||
let investor_price_name = cfg.name("investor_price");
|
||||
let investor_price_version = cfg.version;
|
||||
|
||||
let realized_profit_rel_to_realized_cap =
|
||||
cfg.import("realized_profit_rel_to_realized_cap", Version::new(2))?;
|
||||
let realized_loss_rel_to_realized_cap =
|
||||
cfg.import("realized_loss_rel_to_realized_cap", Version::new(2))?;
|
||||
let net_realized_pnl_rel_to_realized_cap =
|
||||
cfg.import("net_realized_pnl_rel_to_realized_cap", Version::new(2))?;
|
||||
|
||||
let value_created_sum_extended = cfg.import("value_created", v1)?;
|
||||
let value_destroyed_sum_extended = cfg.import("value_destroyed", v1)?;
|
||||
let sopr_extended = cfg.import("sopr", v1)?;
|
||||
|
||||
Ok(Self {
|
||||
base,
|
||||
core,
|
||||
profit,
|
||||
loss,
|
||||
gross_pnl,
|
||||
profit_rel_to_rcap: realized_profit_rel_to_realized_cap,
|
||||
loss_rel_to_rcap: realized_loss_rel_to_realized_cap,
|
||||
net_pnl_rel_to_rcap: net_realized_pnl_rel_to_realized_cap,
|
||||
profit_value_created,
|
||||
profit_value_destroyed,
|
||||
loss_value_created,
|
||||
loss_value_destroyed,
|
||||
profit_value_created_sum,
|
||||
profit_value_destroyed_sum,
|
||||
loss_value_created_sum,
|
||||
loss_value_destroyed_sum,
|
||||
capitulation_flow,
|
||||
profit_flow,
|
||||
gross_pnl_sum,
|
||||
net_pnl_cumulative: cfg.import("net_realized_pnl_cumulative", Version::ONE)?,
|
||||
net_pnl_sum_extended: cfg.import("net_realized_pnl", Version::ONE)?,
|
||||
net_pnl_delta: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
net_pnl_delta_extended: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
net_pnl_change_1m_rel_to_rcap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_realized_cap", Version::new(4))?,
|
||||
net_pnl_change_1m_rel_to_mcap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_market_cap", Version::new(4))?,
|
||||
cap_delta_extended: cfg.import("realized_cap_delta", Version::new(5))?,
|
||||
investor_price,
|
||||
investor_price_ratio,
|
||||
lower_price_band,
|
||||
upper_price_band,
|
||||
cap_raw,
|
||||
investor_cap_raw,
|
||||
sell_side_risk_ratio,
|
||||
net_pnl,
|
||||
sopr,
|
||||
sent,
|
||||
peak_regret,
|
||||
peak_regret_rel_to_rcap: peak_regret_rel_to_realized_cap,
|
||||
cap_rel_to_own_mcap: cfg.import("realized_cap_rel_to_own_market_cap", v1)?,
|
||||
profit_sum_extended: cfg.import("realized_profit", v1)?,
|
||||
loss_sum_extended: cfg.import("realized_loss", v1)?,
|
||||
investor,
|
||||
profit_to_loss_ratio: cfg.import("realized_profit_to_loss_ratio", v1)?,
|
||||
value_created_sum_extended,
|
||||
value_destroyed_sum_extended,
|
||||
sopr_extended,
|
||||
sent_in_profit_sum_extended: cfg.import("sent_in_profit", v1)?,
|
||||
sent_in_loss_sum_extended: cfg.import("sent_in_loss", v1)?,
|
||||
cap_delta_extended: cfg.import("realized_cap_delta", Version::new(5))?,
|
||||
cap_raw: cfg.import("cap_raw", v0)?,
|
||||
cap_rel_to_own_mcap: cfg.import("realized_cap_rel_to_own_market_cap", v1)?,
|
||||
price_ratio_percentiles: RatioPerBlockPercentiles::forced_import(
|
||||
cfg.db,
|
||||
&realized_price_name,
|
||||
@@ -229,76 +270,82 @@ impl RealizedFull {
|
||||
realized_price_version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
investor_price_ratio_percentiles: RatioPerBlockPercentiles::forced_import(
|
||||
cfg.db,
|
||||
&investor_price_name,
|
||||
investor_price_version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.base
|
||||
self.core
|
||||
.min_stateful_height_len()
|
||||
.min(self.profit_value_created.height.len())
|
||||
.min(self.profit_value_destroyed.height.len())
|
||||
.min(self.loss_value_created.height.len())
|
||||
.min(self.loss_value_destroyed.height.len())
|
||||
.min(self.investor_price.cents.height.len())
|
||||
.min(self.profit.value_created.height.len())
|
||||
.min(self.profit.value_destroyed.height.len())
|
||||
.min(self.loss.value_created.height.len())
|
||||
.min(self.loss.value_destroyed.height.len())
|
||||
.min(self.investor.price.cents.height.len())
|
||||
.min(self.cap_raw.len())
|
||||
.min(self.investor_cap_raw.len())
|
||||
.min(self.peak_regret.height.len())
|
||||
.min(self.investor.cap_raw.len())
|
||||
.min(self.peak_regret.value.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &RealizedState) -> Result<()> {
|
||||
self.base.truncate_push(height, state)?;
|
||||
self.profit_value_created
|
||||
pub(crate) fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
state: &CohortState<RealizedState>,
|
||||
) -> Result<()> {
|
||||
self.core.truncate_push(height, state)?;
|
||||
self.profit
|
||||
.value_created
|
||||
.height
|
||||
.truncate_push(height, state.profit_value_created())?;
|
||||
self.profit_value_destroyed
|
||||
.truncate_push(height, state.realized.profit_value_created())?;
|
||||
self.profit
|
||||
.value_destroyed
|
||||
.height
|
||||
.truncate_push(height, state.profit_value_destroyed())?;
|
||||
self.loss_value_created
|
||||
.truncate_push(height, state.realized.profit_value_destroyed())?;
|
||||
self.loss
|
||||
.value_created
|
||||
.height
|
||||
.truncate_push(height, state.loss_value_created())?;
|
||||
self.loss_value_destroyed
|
||||
.truncate_push(height, state.realized.loss_value_created())?;
|
||||
self.loss
|
||||
.value_destroyed
|
||||
.height
|
||||
.truncate_push(height, state.loss_value_destroyed())?;
|
||||
self.investor_price
|
||||
.truncate_push(height, state.realized.loss_value_destroyed())?;
|
||||
self.investor
|
||||
.price
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, state.investor_price())?;
|
||||
self.cap_raw.truncate_push(height, state.cap_raw())?;
|
||||
self.investor_cap_raw
|
||||
.truncate_push(height, state.investor_cap_raw())?;
|
||||
.truncate_push(height, state.realized.investor_price())?;
|
||||
self.cap_raw
|
||||
.truncate_push(height, state.realized.cap_raw())?;
|
||||
self.investor
|
||||
.cap_raw
|
||||
.truncate_push(height, state.realized.investor_cap_raw())?;
|
||||
self.peak_regret
|
||||
.value
|
||||
.height
|
||||
.truncate_push(height, state.peak_regret())?;
|
||||
.truncate_push(height, state.realized.peak_regret())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.base.collect_vecs_mut();
|
||||
vecs.push(&mut self.profit_value_created.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.profit_value_destroyed.height);
|
||||
vecs.push(&mut self.loss_value_created.height);
|
||||
vecs.push(&mut self.loss_value_destroyed.height);
|
||||
vecs.push(&mut self.investor_price.cents.height);
|
||||
let mut vecs = self.core.collect_vecs_mut();
|
||||
vecs.push(&mut self.profit.value_created.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.profit.value_destroyed.height);
|
||||
vecs.push(&mut self.loss.value_created.height);
|
||||
vecs.push(&mut self.loss.value_destroyed.height);
|
||||
vecs.push(&mut self.investor.price.cents.height);
|
||||
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.investor_cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.peak_regret.height);
|
||||
vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.peak_regret.value.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&RealizedBase],
|
||||
others: &[&RealizedCore],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.base
|
||||
self.core
|
||||
.compute_from_stateful(starting_indexes, others, exit)?;
|
||||
|
||||
Ok(())
|
||||
@@ -309,20 +356,26 @@ impl RealizedFull {
|
||||
accum: &RealizedFullAccum,
|
||||
height: Height,
|
||||
) -> Result<()> {
|
||||
self.profit_value_created
|
||||
self.profit
|
||||
.value_created
|
||||
.height
|
||||
.truncate_push(height, accum.profit_value_created)?;
|
||||
self.profit_value_destroyed
|
||||
self.profit
|
||||
.value_destroyed
|
||||
.height
|
||||
.truncate_push(height, accum.profit_value_destroyed)?;
|
||||
self.loss_value_created
|
||||
self.loss
|
||||
.value_created
|
||||
.height
|
||||
.truncate_push(height, accum.loss_value_created)?;
|
||||
self.loss_value_destroyed
|
||||
self.loss
|
||||
.value_destroyed
|
||||
.height
|
||||
.truncate_push(height, accum.loss_value_destroyed)?;
|
||||
self.cap_raw.truncate_push(height, accum.cap_raw)?;
|
||||
self.investor_cap_raw
|
||||
self.cap_raw
|
||||
.truncate_push(height, accum.cap_raw)?;
|
||||
self.investor
|
||||
.cap_raw
|
||||
.truncate_push(height, accum.investor_cap_raw)?;
|
||||
|
||||
let investor_price = {
|
||||
@@ -333,12 +386,14 @@ impl RealizedFull {
|
||||
Cents::new((accum.investor_cap_raw / cap) as u64)
|
||||
}
|
||||
};
|
||||
self.investor_price
|
||||
self.investor
|
||||
.price
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, investor_price)?;
|
||||
|
||||
self.peak_regret
|
||||
.value
|
||||
.height
|
||||
.truncate_push(height, accum.peak_regret)?;
|
||||
|
||||
@@ -351,16 +406,17 @@ impl RealizedFull {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.base
|
||||
self.core
|
||||
.compute_rest_part1(blocks, starting_indexes, exit)?;
|
||||
|
||||
self.net_pnl_cumulative.height.compute_cumulative(
|
||||
self.net_pnl.cumulative.height.compute_cumulative(
|
||||
starting_indexes.height,
|
||||
&self.base.core.net_pnl.height,
|
||||
&self.core.net_pnl.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.peak_regret
|
||||
.value
|
||||
.compute_rest(starting_indexes.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -374,7 +430,7 @@ impl RealizedFull {
|
||||
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.base.core.compute_rest_part2(
|
||||
self.core.compute_rest_part2(
|
||||
blocks,
|
||||
prices,
|
||||
starting_indexes,
|
||||
@@ -384,33 +440,36 @@ impl RealizedFull {
|
||||
|
||||
let window_starts = blocks.lookback.window_starts();
|
||||
|
||||
// Extended rolling sum (1w, 1m, 1y) for net_realized_pnl
|
||||
self.net_pnl_sum_extended.compute_rolling_sum(
|
||||
// Net PnL rolling sums (1w, 1m, 1y)
|
||||
self.net_pnl.sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.net_pnl.height,
|
||||
&self.core.net_pnl.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Extended rolling windows (1w, 1m, 1y) for value_created/destroyed/sopr
|
||||
self.value_created_sum_extended.compute_rolling_sum(
|
||||
// SOPR: value created/destroyed rolling sums and ratios
|
||||
self.sopr.value_created_sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.value_created.height,
|
||||
&self.core.minimal.sopr.value_created.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
self.value_destroyed_sum_extended.compute_rolling_sum(
|
||||
self.sopr
|
||||
.value_destroyed_sum_extended
|
||||
.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.value_destroyed.height,
|
||||
&self.core.minimal.sopr.value_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
for ((sopr, vc), vd) in self
|
||||
.sopr_extended
|
||||
.sopr
|
||||
.ratio_extended
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.value_created_sum_extended.as_array())
|
||||
.zip(self.value_destroyed_sum_extended.as_array())
|
||||
.zip(self.sopr.value_created_sum_extended.as_array())
|
||||
.zip(self.sopr.value_destroyed_sum_extended.as_array())
|
||||
{
|
||||
sopr.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height,
|
||||
@@ -420,108 +479,113 @@ impl RealizedFull {
|
||||
)?;
|
||||
}
|
||||
|
||||
// Realized P/L rel to realized cap
|
||||
self.profit_rel_to_rcap
|
||||
// Profit/loss/net_pnl rel to realized cap
|
||||
self.profit
|
||||
.rel_to_rcap
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp32>(
|
||||
starting_indexes.height,
|
||||
&self.base.core.minimal.profit.height,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.core.minimal.profit.raw.cents.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss_rel_to_rcap
|
||||
self.loss
|
||||
.rel_to_rcap
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp32>(
|
||||
starting_indexes.height,
|
||||
&self.base.core.minimal.loss.height,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.core.minimal.loss.raw.cents.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_rel_to_rcap
|
||||
self.net_pnl
|
||||
.rel_to_rcap
|
||||
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.base.core.net_pnl.height,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.core.net_pnl.raw.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Sent in profit/loss extended rolling sums (1w, 1m, 1y)
|
||||
self.sent_in_profit_sum_extended.compute_rolling_sum(
|
||||
// Sent rolling sums (1w, 1m, 1y)
|
||||
self.sent.in_profit_sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.sent_in_profit.height,
|
||||
&self.core.sent.in_profit.raw.sats.height,
|
||||
exit,
|
||||
)?;
|
||||
self.sent_in_loss_sum_extended.compute_rolling_sum(
|
||||
self.sent.in_loss_sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.sent_in_loss.height,
|
||||
&self.core.sent.in_loss.raw.sats.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Profit/loss value created/destroyed rolling sums
|
||||
self.profit_value_created_sum.compute_rolling_sum(
|
||||
self.profit.value_created_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.profit_value_created.height,
|
||||
&self.profit.value_created.height,
|
||||
exit,
|
||||
)?;
|
||||
self.profit_value_destroyed_sum.compute_rolling_sum(
|
||||
self.profit.value_destroyed_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.profit_value_destroyed.height,
|
||||
&self.profit.value_destroyed.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss_value_created_sum.compute_rolling_sum(
|
||||
self.loss.value_created_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.loss_value_created.height,
|
||||
&self.loss.value_created.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss_value_destroyed_sum.compute_rolling_sum(
|
||||
self.loss.value_destroyed_sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.loss_value_destroyed.height,
|
||||
&self.loss.value_destroyed.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Gross PnL
|
||||
self.gross_pnl.cents.height.compute_add(
|
||||
self.gross_pnl.value.cents.height.compute_add(
|
||||
starting_indexes.height,
|
||||
&self.base.core.minimal.profit.height,
|
||||
&self.base.core.minimal.loss.height,
|
||||
&self.core.minimal.profit.raw.cents.height,
|
||||
&self.core.minimal.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.gross_pnl_sum.compute_rolling_sum(
|
||||
self.gross_pnl.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.gross_pnl.cents.height,
|
||||
&self.gross_pnl.value.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Net PnL delta (1m base + 24h/1w/1y extended)
|
||||
self.net_pnl_delta.compute(
|
||||
self.net_pnl.delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
&self.net_pnl_cumulative.height,
|
||||
&self.net_pnl.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_delta_extended.compute(
|
||||
self.net_pnl.delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.net_pnl_cumulative.height,
|
||||
&self.net_pnl.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_change_1m_rel_to_rcap
|
||||
self.net_pnl
|
||||
.change_1m_rel_to_rcap
|
||||
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.net_pnl_delta.change_1m.cents.height,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.net_pnl.delta.change_1m.cents.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_change_1m_rel_to_mcap
|
||||
self.net_pnl
|
||||
.change_1m_rel_to_mcap
|
||||
.compute_binary::<CentsSigned, Dollars, RatioCentsSignedDollarsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.net_pnl_delta.change_1m.cents.height,
|
||||
&self.net_pnl.delta.change_1m.cents.height,
|
||||
height_to_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
@@ -530,31 +594,36 @@ impl RealizedFull {
|
||||
self.cap_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Peak regret
|
||||
self.peak_regret_rel_to_rcap
|
||||
// Peak regret rel to rcap
|
||||
self.peak_regret
|
||||
.rel_to_rcap
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp32>(
|
||||
starting_indexes.height,
|
||||
&self.peak_regret.height,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.peak_regret.value.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Investor price ratio and price bands
|
||||
self.investor_price_ratio.compute_ratio(
|
||||
// Investor price ratio and bands
|
||||
self.investor.price_ratio.compute_ratio(
|
||||
starting_indexes,
|
||||
&prices.price.cents.height,
|
||||
&self.investor_price.cents.height,
|
||||
&self.investor.price.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.lower_price_band.cents.height.compute_transform2(
|
||||
self.investor
|
||||
.lower_price_band
|
||||
.cents
|
||||
.height
|
||||
.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.base.core.minimal.price.cents.height,
|
||||
&self.investor_price.cents.height,
|
||||
&self.core.minimal.price.cents.height,
|
||||
&self.investor.price.cents.height,
|
||||
|(i, rp, ip, ..)| {
|
||||
let rp = rp.as_u128();
|
||||
let ip = ip.as_u128();
|
||||
@@ -567,10 +636,14 @@ impl RealizedFull {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.upper_price_band.cents.height.compute_transform2(
|
||||
self.investor
|
||||
.upper_price_band
|
||||
.cents
|
||||
.height
|
||||
.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.investor_price.cents.height,
|
||||
&self.base.core.minimal.price.cents.height,
|
||||
&self.investor.price.cents.height,
|
||||
&self.core.minimal.price.cents.height,
|
||||
|(i, ip, rp, ..)| {
|
||||
let ip = ip.as_u128();
|
||||
let rp = rp.as_u128();
|
||||
@@ -585,30 +658,31 @@ impl RealizedFull {
|
||||
|
||||
// Sell-side risk ratios
|
||||
for (ssrr, rv) in self
|
||||
.gross_pnl
|
||||
.sell_side_risk_ratio
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.gross_pnl_sum.as_array())
|
||||
.zip(self.gross_pnl.sum.as_array())
|
||||
{
|
||||
ssrr.compute_binary::<Cents, Cents, RatioCentsBp32>(
|
||||
starting_indexes.height,
|
||||
&rv.height,
|
||||
&self.base.core.minimal.cap_cents.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Extended: realized profit/loss rolling sums (1w, 1m, 1y)
|
||||
self.profit_sum_extended.compute_rolling_sum(
|
||||
// Profit/loss sum extended (1w, 1m, 1y)
|
||||
self.profit.sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.minimal.profit.height,
|
||||
&self.core.minimal.profit.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss_sum_extended.compute_rolling_sum(
|
||||
self.loss.sum_extended.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.minimal.loss.height,
|
||||
&self.core.minimal.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -616,7 +690,7 @@ impl RealizedFull {
|
||||
self.cap_rel_to_own_mcap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
&self.base.core.minimal.cap.height,
|
||||
&self.core.minimal.cap.usd.height,
|
||||
height_to_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
@@ -626,16 +700,16 @@ impl RealizedFull {
|
||||
._24h
|
||||
.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height,
|
||||
&self.base.core.minimal.profit_sum._24h.height,
|
||||
&self.base.core.minimal.loss_sum._24h.height,
|
||||
&self.core.minimal.profit.sum._24h.cents.height,
|
||||
&self.core.minimal.loss.sum._24h.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
for ((ratio, profit), loss) in self
|
||||
.profit_to_loss_ratio
|
||||
.as_mut_array_from_1w()
|
||||
.into_iter()
|
||||
.zip(self.profit_sum_extended.as_array())
|
||||
.zip(self.loss_sum_extended.as_array())
|
||||
.zip(self.profit.sum_extended.as_array())
|
||||
.zip(self.loss.sum_extended.as_array())
|
||||
{
|
||||
ratio.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height,
|
||||
@@ -645,29 +719,30 @@ impl RealizedFull {
|
||||
)?;
|
||||
}
|
||||
|
||||
// Price ratio: percentiles and std dev bands
|
||||
self.price_ratio_percentiles.compute(
|
||||
blocks,
|
||||
starting_indexes,
|
||||
exit,
|
||||
&self.base.core.minimal.price_ratio.ratio.height,
|
||||
&self.base.core.minimal.price.cents.height,
|
||||
&self.core.minimal.price_ratio.ratio.height,
|
||||
&self.core.minimal.price.cents.height,
|
||||
)?;
|
||||
|
||||
self.price_ratio_std_dev.compute(
|
||||
blocks,
|
||||
starting_indexes,
|
||||
exit,
|
||||
&self.base.core.minimal.price_ratio.ratio.height,
|
||||
&self.base.core.minimal.price.cents.height,
|
||||
&self.core.minimal.price_ratio.ratio.height,
|
||||
&self.core.minimal.price.cents.height,
|
||||
)?;
|
||||
|
||||
// Investor price: percentiles
|
||||
let investor_price = &self.investor_price.cents.height;
|
||||
self.investor_price_ratio_percentiles.compute(
|
||||
// Investor price ratio: percentiles
|
||||
let investor_price = &self.investor.price.cents.height;
|
||||
self.investor.price_ratio_percentiles.compute(
|
||||
blocks,
|
||||
starting_indexes,
|
||||
exit,
|
||||
&self.investor_price_ratio.ratio.height,
|
||||
&self.investor.price_ratio.ratio.height,
|
||||
investor_price,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
BasisPoints32, Bitcoin, Cents, Dollars, Height, Indexes, Sats, StoredF32, Version,
|
||||
BasisPoints32, BasisPointsSigned32, Bitcoin, Cents, Height, Indexes, Sats, StoredF32,
|
||||
Version,
|
||||
};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, WritableVec,
|
||||
AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::state::RealizedOps,
|
||||
distribution::state::{CohortState, RealizedOps},
|
||||
internal::{
|
||||
CentsUnsignedToDollars, ComputedPerBlock, ComputedPerBlockCumulative, Identity,
|
||||
LazyPerBlock, Price, RatioPerBlock, RollingWindow24h,
|
||||
ComputedPerBlock, FiatPerBlock, FiatPerBlockWithSum24h, Identity, LazyPerBlock,
|
||||
PerBlockWithSum24h, Price, RatioPerBlock,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
@@ -20,77 +21,92 @@ use crate::{
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedMinimal<M: StorageMode = Rw> {
|
||||
pub cap_cents: ComputedPerBlock<Cents, M>,
|
||||
pub profit: ComputedPerBlockCumulative<Cents, M>,
|
||||
pub loss: ComputedPerBlockCumulative<Cents, M>,
|
||||
pub cap: LazyPerBlock<Dollars, Cents>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_ratio: RatioPerBlock<M>,
|
||||
pub mvrv: LazyPerBlock<StoredF32>,
|
||||
pub struct RealizedSoprMinimal<M: StorageMode = Rw> {
|
||||
pub value_created: PerBlockWithSum24h<Cents, M>,
|
||||
pub value_destroyed: PerBlockWithSum24h<Cents, M>,
|
||||
}
|
||||
|
||||
pub profit_sum: RollingWindow24h<Cents, M>,
|
||||
pub loss_sum: RollingWindow24h<Cents, M>,
|
||||
/// Minimal realized metrics: cap (fiat), profit/loss (fiat + 24h sum),
|
||||
/// price, mvrv, nupl, sopr (value_created/destroyed with 24h sums).
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedMinimal<M: StorageMode = Rw> {
|
||||
pub cap: FiatPerBlock<Cents, M>,
|
||||
pub profit: FiatPerBlockWithSum24h<Cents, M>,
|
||||
pub loss: FiatPerBlockWithSum24h<Cents, M>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_ratio: RatioPerBlock<BasisPoints32, M>,
|
||||
pub mvrv: LazyPerBlock<StoredF32>,
|
||||
pub nupl: RatioPerBlock<BasisPointsSigned32, M>,
|
||||
|
||||
pub sopr: RealizedSoprMinimal<M>,
|
||||
}
|
||||
|
||||
impl RealizedMinimal {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let realized_cap_cents: ComputedPerBlock<Cents> =
|
||||
cfg.import("realized_cap_cents", Version::ZERO)?;
|
||||
let realized_cap = LazyPerBlock::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("realized_cap"),
|
||||
cfg.version,
|
||||
realized_cap_cents.height.read_only_boxed_clone(),
|
||||
&realized_cap_cents,
|
||||
);
|
||||
let v1 = Version::ONE;
|
||||
|
||||
let realized_profit = cfg.import("realized_profit", Version::ZERO)?;
|
||||
let realized_loss = cfg.import("realized_loss", Version::ZERO)?;
|
||||
let cap: FiatPerBlock<Cents> = cfg.import("realized_cap", Version::ZERO)?;
|
||||
|
||||
let realized_price = cfg.import("realized_price", Version::ONE)?;
|
||||
let realized_price_ratio: RatioPerBlock = cfg.import("realized_price", Version::ONE)?;
|
||||
let realized_price = cfg.import("realized_price", v1)?;
|
||||
let realized_price_ratio: RatioPerBlock = cfg.import("realized_price", v1)?;
|
||||
let mvrv = LazyPerBlock::from_lazy::<Identity<StoredF32>, BasisPoints32>(
|
||||
&cfg.name("mvrv"),
|
||||
cfg.version,
|
||||
&realized_price_ratio.ratio,
|
||||
);
|
||||
|
||||
let realized_profit_sum = cfg.import("realized_profit", Version::ONE)?;
|
||||
let realized_loss_sum = cfg.import("realized_loss", Version::ONE)?;
|
||||
let nupl = cfg.import("nupl", v1)?;
|
||||
|
||||
Ok(Self {
|
||||
cap_cents: realized_cap_cents,
|
||||
profit: realized_profit,
|
||||
loss: realized_loss,
|
||||
cap: realized_cap,
|
||||
cap,
|
||||
profit: cfg.import("realized_profit", v1)?,
|
||||
loss: cfg.import("realized_loss", v1)?,
|
||||
price: realized_price,
|
||||
price_ratio: realized_price_ratio,
|
||||
mvrv,
|
||||
profit_sum: realized_profit_sum,
|
||||
loss_sum: realized_loss_sum,
|
||||
nupl,
|
||||
sopr: RealizedSoprMinimal {
|
||||
value_created: cfg.import("value_created", v1)?,
|
||||
value_destroyed: cfg.import("value_destroyed", v1)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.cap_cents
|
||||
self.cap
|
||||
.cents
|
||||
.height
|
||||
.len()
|
||||
.min(self.profit.height.len())
|
||||
.min(self.loss.height.len())
|
||||
.min(self.profit.raw.cents.height.len())
|
||||
.min(self.loss.raw.cents.height.len())
|
||||
.min(self.sopr.value_created.raw.height.len())
|
||||
.min(self.sopr.value_destroyed.raw.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &impl RealizedOps) -> Result<()> {
|
||||
self.cap_cents.height.truncate_push(height, state.cap())?;
|
||||
self.profit.height.truncate_push(height, state.profit())?;
|
||||
self.loss.height.truncate_push(height, state.loss())?;
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &CohortState<impl RealizedOps>) -> Result<()> {
|
||||
self.cap.cents.height.truncate_push(height, state.realized.cap())?;
|
||||
self.profit.raw.cents.height.truncate_push(height, state.realized.profit())?;
|
||||
self.loss.raw.cents.height.truncate_push(height, state.realized.loss())?;
|
||||
self.sopr
|
||||
.value_created
|
||||
.raw
|
||||
.height
|
||||
.truncate_push(height, state.realized.value_created())?;
|
||||
self.sopr
|
||||
.value_destroyed
|
||||
.raw
|
||||
.height
|
||||
.truncate_push(height, state.realized.value_destroyed())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.cap_cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.profit.height,
|
||||
&mut self.loss.height,
|
||||
&mut self.cap.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.profit.raw.cents.height,
|
||||
&mut self.loss.raw.cents.height,
|
||||
&mut self.sopr.value_created.raw.height,
|
||||
&mut self.sopr.value_destroyed.raw.height,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -100,9 +116,11 @@ impl RealizedMinimal {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
sum_others!(self, starting_indexes, others, exit; cap_cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; profit.height);
|
||||
sum_others!(self, starting_indexes, others, exit; loss.height);
|
||||
sum_others!(self, starting_indexes, others, exit; cap.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; profit.raw.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; loss.raw.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sopr.value_created.raw.height);
|
||||
sum_others!(self, starting_indexes, others, exit; sopr.value_destroyed.raw.height);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -112,18 +130,28 @@ impl RealizedMinimal {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.profit.compute_rest(starting_indexes.height, exit)?;
|
||||
self.loss.compute_rest(starting_indexes.height, exit)?;
|
||||
self.profit_sum.compute_rolling_sum(
|
||||
self.profit.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.profit.height,
|
||||
&self.profit.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss_sum.compute_rolling_sum(
|
||||
self.loss.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.loss.height,
|
||||
&self.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.sopr.value_created.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sopr.value_created.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
self.sopr.value_destroyed.sum.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.sopr.value_destroyed.raw.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
@@ -138,7 +166,7 @@ impl RealizedMinimal {
|
||||
) -> Result<()> {
|
||||
self.price.cents.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.cap_cents.height,
|
||||
&self.cap.cents.height,
|
||||
height_to_supply,
|
||||
|(i, cap_cents, supply, ..)| {
|
||||
let cap = cap_cents.as_u128();
|
||||
@@ -159,6 +187,23 @@ impl RealizedMinimal {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.nupl.bps.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&prices.price.cents.height,
|
||||
&self.price.cents.height,
|
||||
|(i, price, realized_price, ..)| {
|
||||
let p = price.as_u128();
|
||||
if p == 0 {
|
||||
(i, BasisPointsSigned32::ZERO)
|
||||
} else {
|
||||
let rp = realized_price.as_u128();
|
||||
let nupl_bps = ((p as i128 - rp as i128) * 10000) / p as i128;
|
||||
(i, BasisPointsSigned32::from(nupl_bps as i32))
|
||||
}
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
mod adjusted;
|
||||
mod base;
|
||||
mod core;
|
||||
mod full;
|
||||
mod minimal;
|
||||
|
||||
pub use adjusted::RealizedAdjusted;
|
||||
pub use base::RealizedBase;
|
||||
pub use adjusted::AdjustedSopr;
|
||||
pub use self::core::RealizedCore;
|
||||
pub use full::{RealizedFull, RealizedFullAccum};
|
||||
pub use minimal::RealizedMinimal;
|
||||
@@ -14,53 +12,53 @@ use brk_error::Result;
|
||||
use brk_types::{Height, Indexes};
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::{blocks, distribution::state::RealizedState};
|
||||
use crate::{blocks, distribution::state::{CohortState, RealizedState}};
|
||||
|
||||
/// Polymorphic dispatch for realized metric types.
|
||||
///
|
||||
/// Both `RealizedBase` and `RealizedFull` have the same inherent methods
|
||||
/// Both `RealizedCore` and `RealizedFull` have the same inherent methods
|
||||
/// but with different behavior (Full checks/pushes more fields).
|
||||
/// This trait enables `CohortMetricsBase` to dispatch correctly via associated type.
|
||||
pub trait RealizedLike: Send + Sync {
|
||||
fn as_base(&self) -> &RealizedBase;
|
||||
fn as_base_mut(&mut self) -> &mut RealizedBase;
|
||||
fn as_core(&self) -> &RealizedCore;
|
||||
fn as_core_mut(&mut self) -> &mut RealizedCore;
|
||||
fn min_stateful_height_len(&self) -> usize;
|
||||
fn truncate_push(&mut self, height: Height, state: &RealizedState) -> Result<()>;
|
||||
fn truncate_push(&mut self, height: Height, state: &CohortState<RealizedState>) -> Result<()>;
|
||||
fn compute_rest_part1(&mut self, blocks: &blocks::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
|
||||
fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&RealizedBase],
|
||||
others: &[&RealizedCore],
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl RealizedLike for RealizedBase {
|
||||
fn as_base(&self) -> &RealizedBase { self }
|
||||
fn as_base_mut(&mut self) -> &mut RealizedBase { self }
|
||||
impl RealizedLike for RealizedCore {
|
||||
fn as_core(&self) -> &RealizedCore { self }
|
||||
fn as_core_mut(&mut self) -> &mut RealizedCore { self }
|
||||
fn min_stateful_height_len(&self) -> usize { self.min_stateful_height_len() }
|
||||
fn truncate_push(&mut self, height: Height, state: &RealizedState) -> Result<()> {
|
||||
fn truncate_push(&mut self, height: Height, state: &CohortState<RealizedState>) -> Result<()> {
|
||||
self.truncate_push(height, state)
|
||||
}
|
||||
fn compute_rest_part1(&mut self, blocks: &blocks::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
self.compute_rest_part1(blocks, starting_indexes, exit)
|
||||
}
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedBase], exit: &Exit) -> Result<()> {
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
|
||||
self.compute_from_stateful(starting_indexes, others, exit)
|
||||
}
|
||||
}
|
||||
|
||||
impl RealizedLike for RealizedFull {
|
||||
fn as_base(&self) -> &RealizedBase { &self.base }
|
||||
fn as_base_mut(&mut self) -> &mut RealizedBase { &mut self.base }
|
||||
fn as_core(&self) -> &RealizedCore { &self.core }
|
||||
fn as_core_mut(&mut self) -> &mut RealizedCore { &mut self.core }
|
||||
fn min_stateful_height_len(&self) -> usize { self.min_stateful_height_len() }
|
||||
fn truncate_push(&mut self, height: Height, state: &RealizedState) -> Result<()> {
|
||||
fn truncate_push(&mut self, height: Height, state: &CohortState<RealizedState>) -> Result<()> {
|
||||
self.truncate_push(height, state)
|
||||
}
|
||||
fn compute_rest_part1(&mut self, blocks: &blocks::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
self.compute_rest_part1(blocks, starting_indexes, exit)
|
||||
}
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedBase], exit: &Exit) -> Result<()> {
|
||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
|
||||
self.compute_from_stateful(starting_indexes, others, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,11 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedCore};
|
||||
/// Extended relative metrics for own market cap (extended && rel_to_all).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeExtendedOwnMarketCap<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_own_market_cap")]
|
||||
pub unrealized_profit_rel_to_own_market_cap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_own_market_cap")]
|
||||
pub unrealized_loss_rel_to_own_market_cap: PercentPerBlock<BasisPoints32, M>,
|
||||
#[traversable(wrap = "unrealized/net_pnl", rename = "rel_to_own_market_cap")]
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
@@ -39,14 +42,14 @@ impl RelativeExtendedOwnMarketCap {
|
||||
self.unrealized_profit_rel_to_own_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.profit.usd.height,
|
||||
&unrealized.profit.raw.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_own_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
max_from,
|
||||
&unrealized.loss.usd.height,
|
||||
&unrealized.loss.raw.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -10,8 +10,11 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedCore};
|
||||
/// Extended relative metrics for own total unrealized PnL (extended only).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeExtendedOwnPnl<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_own_gross_pnl")]
|
||||
pub unrealized_profit_rel_to_own_gross_pnl: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_own_gross_pnl")]
|
||||
pub unrealized_loss_rel_to_own_gross_pnl: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/net_pnl", rename = "rel_to_own_gross_pnl")]
|
||||
pub net_unrealized_pnl_rel_to_own_gross_pnl: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
@@ -39,14 +42,14 @@ impl RelativeExtendedOwnPnl {
|
||||
self.unrealized_profit_rel_to_own_gross_pnl
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.profit.usd.height,
|
||||
&unrealized.profit.raw.usd.height,
|
||||
gross_pnl_usd,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_own_gross_pnl
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.loss.usd.height,
|
||||
&unrealized.loss.raw.usd.height,
|
||||
gross_pnl_usd,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -1,44 +1,31 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, BasisPointsSigned32, Dollars, Height, Sats, StoredF32, Version};
|
||||
use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
use brk_types::{BasisPoints16, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{
|
||||
Bps32ToFloat, LazyPerBlock, PercentPerBlock, RatioDollarsBp16, RatioDollarsBps32, RatioSatsBp16,
|
||||
use crate::{
|
||||
distribution::metrics::{ImportConfig, UnrealizedCore},
|
||||
internal::{PercentPerBlock, RatioDollarsBp16, RatioSatsBp16},
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedCore};
|
||||
|
||||
/// Full relative metrics (sth/lth/all tier).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeFull<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "supply/in_profit", rename = "rel_to_own_supply")]
|
||||
pub supply_in_profit_rel_to_own_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_loss", rename = "rel_to_own_supply")]
|
||||
pub supply_in_loss_rel_to_own_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
|
||||
#[traversable(wrap = "unrealized/profit", rename = "rel_to_market_cap")]
|
||||
pub unrealized_profit_rel_to_market_cap: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "unrealized/loss", rename = "rel_to_market_cap")]
|
||||
pub unrealized_loss_rel_to_market_cap: PercentPerBlock<BasisPoints16, M>,
|
||||
pub net_unrealized_pnl_rel_to_market_cap: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub nupl: LazyPerBlock<StoredF32, BasisPointsSigned32>,
|
||||
}
|
||||
|
||||
impl RelativeFull {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v1 = Version::ONE;
|
||||
let v2 = Version::new(2);
|
||||
let v3 = Version::new(3);
|
||||
|
||||
let net_unrealized_pnl_rel_to_market_cap: PercentPerBlock<BasisPointsSigned32> =
|
||||
cfg.import("net_unrealized_pnl_rel_to_market_cap", v3)?;
|
||||
|
||||
let nupl = LazyPerBlock::from_computed::<Bps32ToFloat>(
|
||||
&cfg.name("nupl"),
|
||||
cfg.version + v3,
|
||||
net_unrealized_pnl_rel_to_market_cap
|
||||
.bps
|
||||
.height
|
||||
.read_only_boxed_clone(),
|
||||
&net_unrealized_pnl_rel_to_market_cap.bps,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
supply_in_profit_rel_to_own_supply: cfg
|
||||
@@ -48,8 +35,6 @@ impl RelativeFull {
|
||||
.import("unrealized_profit_rel_to_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_market_cap: cfg
|
||||
.import("unrealized_loss_rel_to_market_cap", v2)?,
|
||||
net_unrealized_pnl_rel_to_market_cap,
|
||||
nupl,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,21 +64,14 @@ impl RelativeFull {
|
||||
self.unrealized_profit_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.profit.usd.height,
|
||||
&unrealized.profit.raw.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from,
|
||||
&unrealized.loss.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBps32>(
|
||||
max_from,
|
||||
&unrealized.net_pnl.usd.height,
|
||||
&unrealized.loss.raw.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -10,8 +10,11 @@ use crate::distribution::metrics::ImportConfig;
|
||||
/// Relative-to-all metrics (not present for the "all" cohort itself).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeToAll<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "supply", rename = "rel_to_circulating_supply")]
|
||||
pub supply_rel_to_circulating_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_profit", rename = "rel_to_circulating_supply")]
|
||||
pub supply_in_profit_rel_to_circulating_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
#[traversable(wrap = "supply/in_loss", rename = "rel_to_circulating_supply")]
|
||||
pub supply_in_loss_rel_to_circulating_supply: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, Sats, SatsSigned, Version};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
use brk_types::{Height, Indexes, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{distribution::state::{CohortState, RealizedOps}, prices};
|
||||
|
||||
use crate::internal::{
|
||||
AmountPerBlock, HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin,
|
||||
LazyAmountPerBlock, RollingDelta1m,
|
||||
LazyAmountPerBlock,
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
/// Supply metrics for a cohort.
|
||||
/// Base supply metrics: total supply only (2 stored vecs).
|
||||
#[derive(Traversable)]
|
||||
pub struct SupplyMetrics<M: StorageMode = Rw> {
|
||||
pub struct SupplyBase<M: StorageMode = Rw> {
|
||||
pub total: AmountPerBlock<M>,
|
||||
pub halved: LazyAmountPerBlock,
|
||||
pub delta: RollingDelta1m<Sats, SatsSigned, M>,
|
||||
}
|
||||
|
||||
impl SupplyMetrics {
|
||||
/// Import supply metrics from database.
|
||||
impl SupplyBase {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = cfg.import("supply", Version::ZERO)?;
|
||||
|
||||
@@ -32,23 +30,18 @@ impl SupplyMetrics {
|
||||
HalveDollars,
|
||||
>(&cfg.name("supply_halved"), &supply, cfg.version);
|
||||
|
||||
let delta = cfg.import("supply_delta", Version::ONE)?;
|
||||
|
||||
Ok(Self {
|
||||
total: supply,
|
||||
halved: supply_halved,
|
||||
delta,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get minimum length across height-indexed vectors.
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.total.sats.height.len()
|
||||
}
|
||||
|
||||
/// Push supply state values to height-indexed vectors.
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, supply: Sats) -> Result<()> {
|
||||
self.total.sats.height.truncate_push(height, supply)?;
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &CohortState<impl RealizedOps>) -> Result<()> {
|
||||
self.total.sats.height.truncate_push(height, state.supply.value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -59,7 +52,6 @@ impl SupplyMetrics {
|
||||
]
|
||||
}
|
||||
|
||||
/// Eagerly compute USD height values from sats × price.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
@@ -69,13 +61,6 @@ impl SupplyMetrics {
|
||||
self.total.compute(prices, max_from, exit)
|
||||
}
|
||||
|
||||
/// Validate computed versions against base version.
|
||||
pub(crate) fn validate_computed_versions(&mut self, _base_version: Version) -> Result<()> {
|
||||
// Validation logic for computed vecs
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute aggregate values from separate cohorts.
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
@@ -92,19 +77,4 @@ impl SupplyMetrics {
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute derived vecs from existing height data.
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
&self.total.sats.height,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
63
crates/brk_computer/src/distribution/metrics/supply/full.rs
Normal file
63
crates/brk_computer/src/distribution/metrics/supply/full.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Indexes, Sats, SatsSigned, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, internal::RollingDelta1m};
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::SupplyBase;
|
||||
|
||||
/// Full supply metrics: total + delta (4 stored vecs).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct SupplyFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub base: SupplyBase<M>,
|
||||
|
||||
pub delta: RollingDelta1m<Sats, SatsSigned, M>,
|
||||
}
|
||||
|
||||
impl SupplyFull {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let base = SupplyBase::forced_import(cfg)?;
|
||||
let delta = cfg.import("supply_delta", Version::ONE)?;
|
||||
|
||||
Ok(Self { base, delta })
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
self.base.collect_vecs_mut()
|
||||
}
|
||||
|
||||
pub(crate) fn validate_computed_versions(&mut self, _base_version: Version) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let base_refs: Vec<&SupplyBase> = others.iter().map(|o| &o.base).collect();
|
||||
self.base.compute_from_stateful(starting_indexes, &base_refs, exit)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
&self.base.total.sats.height,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod base;
|
||||
mod full;
|
||||
|
||||
pub use base::SupplyBase;
|
||||
pub use full::SupplyFull;
|
||||
@@ -4,7 +4,7 @@ use brk_types::{CentsSats, CentsSquaredSats, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{distribution::{metrics::ImportConfig, state::UnrealizedState}, prices};
|
||||
use crate::{blocks, distribution::{metrics::ImportConfig, state::UnrealizedState}, prices};
|
||||
|
||||
use super::UnrealizedCore;
|
||||
|
||||
@@ -15,9 +15,13 @@ pub struct UnrealizedBase<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub core: UnrealizedCore<M>,
|
||||
|
||||
#[traversable(wrap = "invested_capital/in_profit", rename = "raw")]
|
||||
pub invested_capital_in_profit_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
#[traversable(wrap = "invested_capital/in_loss", rename = "raw")]
|
||||
pub invested_capital_in_loss_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
#[traversable(wrap = "investor_cap/in_profit", rename = "raw")]
|
||||
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
#[traversable(wrap = "investor_cap/in_loss", rename = "raw")]
|
||||
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
}
|
||||
|
||||
@@ -161,11 +165,12 @@ impl UnrealizedBase {
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.core.compute_rest(prices, starting_indexes, exit)?;
|
||||
self.core.compute_rest(blocks, prices, starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
107
crates/brk_computer/src/distribution/metrics/unrealized/basic.rs
Normal file
107
crates/brk_computer/src/distribution/metrics/unrealized/basic.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::{metrics::ImportConfig, state::UnrealizedState},
|
||||
internal::FiatPerBlockWithSum24h,
|
||||
prices,
|
||||
};
|
||||
|
||||
use super::UnrealizedMinimal;
|
||||
|
||||
/// Basic unrealized metrics: supply in profit/loss + unrealized profit/loss (fiat + 24h sums).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct UnrealizedBasic<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub minimal: UnrealizedMinimal<M>,
|
||||
|
||||
pub profit: FiatPerBlockWithSum24h<Cents, M>,
|
||||
pub loss: FiatPerBlockWithSum24h<Cents, M>,
|
||||
}
|
||||
|
||||
impl UnrealizedBasic {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v1 = Version::ONE;
|
||||
let minimal = UnrealizedMinimal::forced_import(cfg)?;
|
||||
|
||||
Ok(Self {
|
||||
minimal,
|
||||
profit: cfg.import("unrealized_profit", v1)?,
|
||||
loss: cfg.import("unrealized_loss", v1)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.minimal
|
||||
.min_stateful_height_len()
|
||||
.min(self.profit.raw.cents.height.len())
|
||||
.min(self.loss.raw.cents.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()> {
|
||||
self.minimal.truncate_push(height, state)?;
|
||||
self.profit
|
||||
.raw
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, state.unrealized_profit)?;
|
||||
self.loss
|
||||
.raw
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, state.unrealized_loss)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.minimal.collect_vecs_mut();
|
||||
vecs.push(&mut self.profit.raw.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.loss.raw.cents.height);
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_sources(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let minimal_refs: Vec<&UnrealizedMinimal> = others.iter().map(|o| &o.minimal).collect();
|
||||
self.minimal
|
||||
.compute_from_sources(starting_indexes, &minimal_refs, exit)?;
|
||||
sum_others!(self, starting_indexes, others, exit; profit.raw.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; loss.raw.cents.height);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.minimal.compute_rest(prices, max_from, exit)?;
|
||||
|
||||
self.profit.sum.compute_rolling_sum(
|
||||
max_from,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.profit.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.loss.sum.compute_rolling_sum(
|
||||
max_from,
|
||||
&blocks.lookback.height_24h_ago,
|
||||
&self.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, CentsSigned, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableCloneableVec, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{AnyStoredVec, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::{
|
||||
metrics::{ImportConfig, unrealized::UnrealizedMinimal},
|
||||
metrics::ImportConfig,
|
||||
state::UnrealizedState,
|
||||
},
|
||||
internal::{CentsSubtractToCentsSigned, FiatPerBlock, LazyPerBlock, NegCentsUnsignedToDollars},
|
||||
@@ -15,51 +16,42 @@ use crate::{
|
||||
|
||||
use brk_types::Dollars;
|
||||
|
||||
use super::UnrealizedBasic;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct UnrealizedCore<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub minimal: UnrealizedMinimal<M>,
|
||||
pub basic: UnrealizedBasic<M>,
|
||||
|
||||
pub profit: FiatPerBlock<Cents, M>,
|
||||
pub loss: FiatPerBlock<Cents, M>,
|
||||
#[traversable(wrap = "loss", rename = "neg")]
|
||||
pub neg_loss: LazyPerBlock<Dollars, Cents>,
|
||||
pub net_pnl: FiatPerBlock<CentsSigned, M>,
|
||||
}
|
||||
|
||||
impl UnrealizedCore {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
|
||||
let minimal = UnrealizedMinimal::forced_import(cfg)?;
|
||||
|
||||
let unrealized_profit = cfg.import("unrealized_profit", v0)?;
|
||||
let unrealized_loss: FiatPerBlock<Cents> = cfg.import("unrealized_loss", v0)?;
|
||||
let basic = UnrealizedBasic::forced_import(cfg)?;
|
||||
|
||||
let neg_unrealized_loss = LazyPerBlock::from_computed::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version,
|
||||
unrealized_loss.cents.height.read_only_boxed_clone(),
|
||||
&unrealized_loss.cents,
|
||||
basic.loss.raw.cents.height.read_only_boxed_clone(),
|
||||
&basic.loss.raw.cents,
|
||||
);
|
||||
|
||||
let net_unrealized_pnl = cfg.import("net_unrealized_pnl", v0)?;
|
||||
let net_unrealized_pnl = cfg.import("net_unrealized_pnl", Version::ZERO)?;
|
||||
|
||||
Ok(Self {
|
||||
minimal,
|
||||
profit: unrealized_profit,
|
||||
loss: unrealized_loss,
|
||||
basic,
|
||||
neg_loss: neg_unrealized_loss,
|
||||
net_pnl: net_unrealized_pnl,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.minimal
|
||||
.min_stateful_height_len()
|
||||
.min(self.profit.cents.height.len())
|
||||
.min(self.loss.cents.height.len())
|
||||
self.basic.min_stateful_height_len()
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
@@ -67,24 +59,12 @@ impl UnrealizedCore {
|
||||
height: Height,
|
||||
height_state: &UnrealizedState,
|
||||
) -> Result<()> {
|
||||
self.minimal.truncate_push(height, height_state)?;
|
||||
self.profit
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.unrealized_profit)?;
|
||||
self.loss
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.unrealized_loss)?;
|
||||
|
||||
self.basic.truncate_push(height, height_state)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.minimal.collect_vecs_mut();
|
||||
vecs.push(&mut self.profit.cents.height);
|
||||
vecs.push(&mut self.loss.cents.height);
|
||||
vecs
|
||||
self.basic.collect_vecs_mut()
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
@@ -93,33 +73,30 @@ impl UnrealizedCore {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let minimal_refs: Vec<&UnrealizedMinimal> = others.iter().map(|o| &o.minimal).collect();
|
||||
self.minimal
|
||||
.compute_from_sources(starting_indexes, &minimal_refs, exit)?;
|
||||
|
||||
sum_others!(self, starting_indexes, others, exit; profit.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; loss.cents.height);
|
||||
|
||||
let basic_refs: Vec<&UnrealizedBasic> = others.iter().map(|o| &o.basic).collect();
|
||||
self.basic
|
||||
.compute_from_sources(starting_indexes, &basic_refs, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute derived metrics from stored values.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.minimal
|
||||
.compute_rest(prices, starting_indexes.height, exit)?;
|
||||
self.basic
|
||||
.compute_rest(blocks, prices, starting_indexes.height, exit)?;
|
||||
|
||||
self.net_pnl
|
||||
.cents
|
||||
.height
|
||||
.compute_binary::<Cents, Cents, CentsSubtractToCentsSigned>(
|
||||
starting_indexes.height,
|
||||
&self.profit.cents.height,
|
||||
&self.loss.cents.height,
|
||||
&self.basic.profit.raw.cents.height,
|
||||
&self.basic.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -6,10 +6,17 @@ use vecdb::{AnyStoredVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::distribution::state::UnrealizedState;
|
||||
use crate::internal::{CentsSubtractToCentsSigned, FiatPerBlock};
|
||||
use crate::{distribution::metrics::ImportConfig, prices};
|
||||
use crate::{blocks, distribution::metrics::ImportConfig, prices};
|
||||
|
||||
use super::UnrealizedBase;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct UnrealizedSentiment<M: StorageMode = Rw> {
|
||||
pub pain_index: FiatPerBlock<Cents, M>,
|
||||
pub greed_index: FiatPerBlock<Cents, M>,
|
||||
pub net: FiatPerBlock<CentsSigned, M>,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct UnrealizedFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
@@ -18,12 +25,13 @@ pub struct UnrealizedFull<M: StorageMode = Rw> {
|
||||
pub inner: UnrealizedBase<M>,
|
||||
|
||||
pub gross_pnl: FiatPerBlock<Cents, M>,
|
||||
|
||||
#[traversable(wrap = "invested_capital", rename = "in_profit")]
|
||||
pub invested_capital_in_profit: FiatPerBlock<Cents, M>,
|
||||
#[traversable(wrap = "invested_capital", rename = "in_loss")]
|
||||
pub invested_capital_in_loss: FiatPerBlock<Cents, M>,
|
||||
|
||||
pub pain_index: FiatPerBlock<Cents, M>,
|
||||
pub greed_index: FiatPerBlock<Cents, M>,
|
||||
pub net_sentiment: FiatPerBlock<CentsSigned, M>,
|
||||
pub sentiment: UnrealizedSentiment<M>,
|
||||
}
|
||||
|
||||
impl UnrealizedFull {
|
||||
@@ -35,18 +43,18 @@ impl UnrealizedFull {
|
||||
let invested_capital_in_profit = cfg.import("invested_capital_in_profit", v0)?;
|
||||
let invested_capital_in_loss = cfg.import("invested_capital_in_loss", v0)?;
|
||||
|
||||
let pain_index = cfg.import("pain_index", v0)?;
|
||||
let greed_index = cfg.import("greed_index", v0)?;
|
||||
let net_sentiment = cfg.import("net_sentiment", Version::ONE)?;
|
||||
let sentiment = UnrealizedSentiment {
|
||||
pain_index: cfg.import("pain_index", v0)?,
|
||||
greed_index: cfg.import("greed_index", v0)?,
|
||||
net: cfg.import("net_sentiment", Version::ONE)?,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
gross_pnl,
|
||||
invested_capital_in_profit,
|
||||
invested_capital_in_loss,
|
||||
pain_index,
|
||||
greed_index,
|
||||
net_sentiment,
|
||||
sentiment,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,24 +80,25 @@ impl UnrealizedFull {
|
||||
vecs.push(&mut self.gross_pnl.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_profit.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_loss.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.pain_index.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.greed_index.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.net_sentiment.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.sentiment.pain_index.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.sentiment.greed_index.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.sentiment.net.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest_all(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.inner.compute_rest(prices, starting_indexes, exit)?;
|
||||
self.inner.compute_rest(blocks, prices, starting_indexes, exit)?;
|
||||
|
||||
self.gross_pnl.cents.height.compute_add(
|
||||
starting_indexes.height,
|
||||
&self.inner.core.profit.cents.height,
|
||||
&self.inner.core.loss.cents.height,
|
||||
&self.inner.core.basic.profit.raw.cents.height,
|
||||
&self.inner.core.basic.loss.raw.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -123,7 +132,7 @@ impl UnrealizedFull {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.pain_index.cents.height.compute_transform3(
|
||||
self.sentiment.pain_index.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.inner.investor_cap_in_loss_raw,
|
||||
&self.inner.invested_capital_in_loss_raw,
|
||||
@@ -139,7 +148,7 @@ impl UnrealizedFull {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.greed_index.cents.height.compute_transform3(
|
||||
self.sentiment.greed_index.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.inner.investor_cap_in_profit_raw,
|
||||
&self.inner.invested_capital_in_profit_raw,
|
||||
@@ -163,13 +172,14 @@ impl UnrealizedFull {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.net_sentiment
|
||||
self.sentiment
|
||||
.net
|
||||
.cents
|
||||
.height
|
||||
.compute_binary::<Cents, Cents, CentsSubtractToCentsSigned>(
|
||||
starting_indexes.height,
|
||||
&self.greed_index.cents.height,
|
||||
&self.pain_index.cents.height,
|
||||
&self.sentiment.greed_index.cents.height,
|
||||
&self.sentiment.pain_index.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -12,15 +12,18 @@ use crate::distribution::{metrics::ImportConfig, state::UnrealizedState};
|
||||
/// Minimal unrealized metrics: supply in profit/loss only.
|
||||
#[derive(Traversable)]
|
||||
pub struct UnrealizedMinimal<M: StorageMode = Rw> {
|
||||
#[traversable(wrap = "profit", rename = "supply")]
|
||||
pub supply_in_profit: AmountPerBlock<M>,
|
||||
#[traversable(wrap = "loss", rename = "supply")]
|
||||
pub supply_in_loss: AmountPerBlock<M>,
|
||||
}
|
||||
|
||||
impl UnrealizedMinimal {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
Ok(Self {
|
||||
supply_in_profit: cfg.import("supply_in_profit", Version::ZERO)?,
|
||||
supply_in_loss: cfg.import("supply_in_loss", Version::ZERO)?,
|
||||
supply_in_profit: cfg.import("supply_in_profit", v0)?,
|
||||
supply_in_loss: cfg.import("supply_in_loss", v0)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,9 +50,9 @@ impl UnrealizedMinimal {
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.supply_in_profit.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_profit.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_loss.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_loss.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_profit.cents.height,
|
||||
&mut self.supply_in_loss.sats.height,
|
||||
&mut self.supply_in_loss.cents.height,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
mod base;
|
||||
mod basic;
|
||||
mod core;
|
||||
mod full;
|
||||
mod minimal;
|
||||
|
||||
pub use self::core::UnrealizedCore;
|
||||
pub use base::UnrealizedBase;
|
||||
pub use basic::UnrealizedBasic;
|
||||
pub use full::UnrealizedFull;
|
||||
pub use minimal::UnrealizedMinimal;
|
||||
|
||||
@@ -12,7 +14,7 @@ use brk_error::Result;
|
||||
use brk_types::{Height, Indexes};
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::{distribution::state::UnrealizedState, prices};
|
||||
use crate::{blocks, distribution::state::UnrealizedState, prices};
|
||||
|
||||
pub trait UnrealizedLike: Send + Sync {
|
||||
fn as_base(&self) -> &UnrealizedBase;
|
||||
@@ -21,6 +23,7 @@ pub trait UnrealizedLike: Send + Sync {
|
||||
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()>;
|
||||
fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
@@ -47,11 +50,12 @@ impl UnrealizedLike for UnrealizedBase {
|
||||
}
|
||||
fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_rest(prices, starting_indexes, exit)
|
||||
self.compute_rest(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
fn compute_net_sentiment_height(
|
||||
&mut self,
|
||||
@@ -77,11 +81,12 @@ impl UnrealizedLike for UnrealizedFull {
|
||||
}
|
||||
fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_rest_all(prices, starting_indexes, exit)
|
||||
self.compute_rest_all(blocks, prices, starting_indexes, exit)
|
||||
}
|
||||
fn compute_net_sentiment_height(
|
||||
&mut self,
|
||||
|
||||
@@ -38,13 +38,15 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static {
|
||||
);
|
||||
}
|
||||
|
||||
/// Minimal realized state: only cap, profit, loss.
|
||||
/// Minimal realized state: cap, profit, loss, value_created/destroyed.
|
||||
/// Used by MinimalCohortMetrics cohorts (amount_range, type_, address — ~135 separate cohorts).
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MinimalRealizedState {
|
||||
cap_raw: u128,
|
||||
profit_raw: u128,
|
||||
loss_raw: u128,
|
||||
value_created_raw: u128,
|
||||
value_destroyed_raw: u128,
|
||||
}
|
||||
|
||||
impl RealizedOps for MinimalRealizedState {
|
||||
@@ -72,6 +74,22 @@ impl RealizedOps for MinimalRealizedState {
|
||||
Cents::new((self.loss_raw / Sats::ONE_BTC_U128) as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_created(&self) -> Cents {
|
||||
if self.value_created_raw == 0 {
|
||||
return Cents::ZERO;
|
||||
}
|
||||
Cents::new((self.value_created_raw / Sats::ONE_BTC_U128) as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_destroyed(&self) -> Cents {
|
||||
if self.value_destroyed_raw == 0 {
|
||||
return Cents::ZERO;
|
||||
}
|
||||
Cents::new((self.value_destroyed_raw / Sats::ONE_BTC_U128) as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_cap_raw(&mut self, cap_raw: CentsSats) {
|
||||
self.cap_raw = cap_raw.inner();
|
||||
@@ -84,6 +102,8 @@ impl RealizedOps for MinimalRealizedState {
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.profit_raw = 0;
|
||||
self.loss_raw = 0;
|
||||
self.value_created_raw = 0;
|
||||
self.value_destroyed_raw = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -124,16 +144,16 @@ impl RealizedOps for MinimalRealizedState {
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
self.cap_raw -= prev_ps.as_u128();
|
||||
self.value_created_raw += current_ps.as_u128();
|
||||
self.value_destroyed_raw += prev_ps.as_u128();
|
||||
}
|
||||
}
|
||||
|
||||
/// Core realized state: cap, profit, loss + value_created/destroyed for SOPR + sent tracking.
|
||||
/// Core realized state: extends Minimal with sent_in_profit/loss tracking.
|
||||
/// Used by CoreCohortMetrics cohorts (epoch, class, max_age, min_age — ~59 separate cohorts).
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct CoreRealizedState {
|
||||
minimal: MinimalRealizedState,
|
||||
value_created_raw: u128,
|
||||
value_destroyed_raw: u128,
|
||||
sent_in_profit: Sats,
|
||||
sent_in_loss: Sats,
|
||||
}
|
||||
@@ -156,18 +176,12 @@ impl RealizedOps for CoreRealizedState {
|
||||
|
||||
#[inline]
|
||||
fn value_created(&self) -> Cents {
|
||||
if self.value_created_raw == 0 {
|
||||
return Cents::ZERO;
|
||||
}
|
||||
Cents::new((self.value_created_raw / Sats::ONE_BTC_U128) as u64)
|
||||
self.minimal.value_created()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_destroyed(&self) -> Cents {
|
||||
if self.value_destroyed_raw == 0 {
|
||||
return Cents::ZERO;
|
||||
}
|
||||
Cents::new((self.value_destroyed_raw / Sats::ONE_BTC_U128) as u64)
|
||||
self.minimal.value_destroyed()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -191,8 +205,6 @@ impl RealizedOps for CoreRealizedState {
|
||||
#[inline]
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
self.minimal.reset_single_iteration_values();
|
||||
self.value_created_raw = 0;
|
||||
self.value_destroyed_raw = 0;
|
||||
self.sent_in_profit = Sats::ZERO;
|
||||
self.sent_in_loss = Sats::ZERO;
|
||||
}
|
||||
@@ -223,8 +235,6 @@ impl RealizedOps for CoreRealizedState {
|
||||
) {
|
||||
self.minimal
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
||||
self.value_created_raw += current_ps.as_u128();
|
||||
self.value_destroyed_raw += prev_ps.as_u128();
|
||||
match current_ps.cmp(&prev_ps) {
|
||||
Ordering::Greater | Ordering::Equal => {
|
||||
self.sent_in_profit += sats;
|
||||
|
||||
@@ -60,9 +60,9 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
/// Windowed change + growth rate for addr_count, global + per-type
|
||||
pub delta: DeltaVecs<M>,
|
||||
|
||||
pub fundedaddressindex:
|
||||
pub funded_address_index:
|
||||
LazyVecFrom1<FundedAddressIndex, FundedAddressIndex, FundedAddressIndex, FundedAddressData>,
|
||||
pub emptyaddressindex:
|
||||
pub empty_address_index:
|
||||
LazyVecFrom1<EmptyAddressIndex, EmptyAddressIndex, EmptyAddressIndex, EmptyAddressData>,
|
||||
|
||||
/// In-memory block state for UTXO processing. Persisted via supply_state.
|
||||
@@ -115,14 +115,14 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
// Identity mappings for traversable
|
||||
let fundedaddressindex = LazyVecFrom1::init(
|
||||
"fundedaddressindex",
|
||||
let funded_address_index = LazyVecFrom1::init(
|
||||
"funded_address_index",
|
||||
version,
|
||||
fundedaddressindex_to_fundedaddressdata.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
);
|
||||
let emptyaddressindex = LazyVecFrom1::init(
|
||||
"emptyaddressindex",
|
||||
let empty_address_index = LazyVecFrom1::init(
|
||||
"empty_address_index",
|
||||
version,
|
||||
emptyaddressindex_to_emptyaddressdata.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
@@ -164,8 +164,8 @@ impl Vecs {
|
||||
funded: fundedaddressindex_to_fundedaddressdata,
|
||||
empty: emptyaddressindex_to_emptyaddressdata,
|
||||
},
|
||||
fundedaddressindex,
|
||||
emptyaddressindex,
|
||||
funded_address_index,
|
||||
empty_address_index,
|
||||
|
||||
chain_state: Vec::new(),
|
||||
txindex_to_height: RangeMap::default(),
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "difficultyepoch", version)?,
|
||||
identity: EagerVec::forced_import(db, "epoch", version)?,
|
||||
first_height: EagerVec::forced_import(db, "first_height", version)?,
|
||||
height_count: EagerVec::forced_import(db, "height_count", version)?,
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "halvingepoch", version)?,
|
||||
identity: EagerVec::forced_import(db, "halving", version)?,
|
||||
first_height: EagerVec::forced_import(db, "first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Vecs {
|
||||
hour12: EagerVec::forced_import(db, "hour12", version)?,
|
||||
day1: EagerVec::forced_import(db, "day1", version)?,
|
||||
day3: EagerVec::forced_import(db, "day3", version)?,
|
||||
epoch: EagerVec::forced_import(db, "difficulty", version)?,
|
||||
epoch: EagerVec::forced_import(db, "epoch", version)?,
|
||||
halving: EagerVec::forced_import(db, "halving", version)?,
|
||||
week1: EagerVec::forced_import(db, "week1", version)?,
|
||||
month1: EagerVec::forced_import(db, "month1", version)?,
|
||||
|
||||
@@ -106,19 +106,19 @@ where
|
||||
&mut *p75_out,
|
||||
&mut *p90_out,
|
||||
] {
|
||||
v.checked_push_at(i, zero)?;
|
||||
v.truncate_push_at(i, zero)?;
|
||||
}
|
||||
} else {
|
||||
average_out.checked_push_at(i, T::from(window.average()))?;
|
||||
min_out.checked_push_at(i, T::from(window.min()))?;
|
||||
max_out.checked_push_at(i, T::from(window.max()))?;
|
||||
average_out.truncate_push_at(i, T::from(window.average()))?;
|
||||
min_out.truncate_push_at(i, T::from(window.min()))?;
|
||||
max_out.truncate_push_at(i, T::from(window.max()))?;
|
||||
let [p10, p25, p50, p75, p90] =
|
||||
window.percentiles(&[0.10, 0.25, 0.50, 0.75, 0.90]);
|
||||
p10_out.checked_push_at(i, T::from(p10))?;
|
||||
p25_out.checked_push_at(i, T::from(p25))?;
|
||||
median_out.checked_push_at(i, T::from(p50))?;
|
||||
p75_out.checked_push_at(i, T::from(p75))?;
|
||||
p90_out.checked_push_at(i, T::from(p90))?;
|
||||
p10_out.truncate_push_at(i, T::from(p10))?;
|
||||
p25_out.truncate_push_at(i, T::from(p25))?;
|
||||
median_out.truncate_push_at(i, T::from(p50))?;
|
||||
p75_out.truncate_push_at(i, T::from(p75))?;
|
||||
p90_out.truncate_push_at(i, T::from(p90))?;
|
||||
}
|
||||
|
||||
if average_out.batch_limit_reached() {
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
mod distribution_stats;
|
||||
mod per_resolution;
|
||||
mod window_24h;
|
||||
mod windows;
|
||||
mod windows_except_1m;
|
||||
mod windows_from_1w;
|
||||
|
||||
pub use distribution_stats::*;
|
||||
pub use per_resolution::*;
|
||||
pub use window_24h::*;
|
||||
pub use windows::*;
|
||||
pub use windows_except_1m::*;
|
||||
pub use windows_from_1w::*;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
/// Generic single-24h-window container.
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingWindow24h<Inner> {
|
||||
pub _24h: Inner,
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct WindowsExcept1m<A> {
|
||||
pub _24h: A,
|
||||
pub _1w: A,
|
||||
pub _1y: A,
|
||||
}
|
||||
|
||||
impl<A> WindowsExcept1m<A> {
|
||||
pub const SUFFIXES: [&'static str; 3] = ["24h", "1w", "1y"];
|
||||
|
||||
pub fn try_from_fn<E>(
|
||||
mut f: impl FnMut(&str) -> std::result::Result<A, E>,
|
||||
) -> std::result::Result<Self, E> {
|
||||
Ok(Self {
|
||||
_24h: f(Self::SUFFIXES[0])?,
|
||||
_1w: f(Self::SUFFIXES[1])?,
|
||||
_1y: f(Self::SUFFIXES[2])?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> [&A; 3] {
|
||||
[&self._24h, &self._1w, &self._1y]
|
||||
}
|
||||
|
||||
pub fn as_mut_array(&mut self) -> [&mut A; 3] {
|
||||
[&mut self._24h, &mut self._1w, &mut self._1y]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct WindowsFrom1w<A> {
|
||||
pub _1w: A,
|
||||
pub _1m: A,
|
||||
pub _1y: A,
|
||||
}
|
||||
|
||||
impl<A> WindowsFrom1w<A> {
|
||||
pub const SUFFIXES: [&'static str; 3] = ["1w", "1m", "1y"];
|
||||
|
||||
pub fn try_from_fn<E>(
|
||||
mut f: impl FnMut(&str) -> std::result::Result<A, E>,
|
||||
) -> std::result::Result<Self, E> {
|
||||
Ok(Self {
|
||||
_1w: f(Self::SUFFIXES[0])?,
|
||||
_1m: f(Self::SUFFIXES[1])?,
|
||||
_1y: f(Self::SUFFIXES[2])?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> [&A; 3] {
|
||||
[&self._1w, &self._1m, &self._1y]
|
||||
}
|
||||
|
||||
pub fn as_mut_array(&mut self) -> [&mut A; 3] {
|
||||
[&mut self._1w, &mut self._1m, &mut self._1y]
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ mod rolling;
|
||||
mod rolling_full;
|
||||
mod rolling_sum;
|
||||
mod windows;
|
||||
mod with_sum_24h;
|
||||
|
||||
pub use base::*;
|
||||
pub use cumulative::*;
|
||||
@@ -19,3 +20,4 @@ pub use rolling::*;
|
||||
pub use rolling_full::*;
|
||||
pub use rolling_sum::*;
|
||||
pub use windows::*;
|
||||
pub use with_sum_24h::*;
|
||||
|
||||
@@ -6,9 +6,43 @@ use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{AmountPerBlock, WindowStarts, Windows},
|
||||
internal::{AmountPerBlock, RollingWindow24h, WindowStarts, Windows},
|
||||
};
|
||||
|
||||
/// Single 24h rolling sum as amount (sats + btc + cents + usd).
|
||||
///
|
||||
/// Tree: `_24h.sats.height`, `_24h.btc.height`, etc.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingWindow24hAmountPerBlock<M: StorageMode = Rw>(
|
||||
pub RollingWindow24h<AmountPerBlock<M>>,
|
||||
);
|
||||
|
||||
impl RollingWindow24hAmountPerBlock {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(RollingWindow24h {
|
||||
_24h: AmountPerBlock::forced_import(db, &format!("{name}_24h"), version, indexes)?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_24h_ago: &impl ReadableVec<Height, Height>,
|
||||
sats_source: &impl ReadableVec<Height, Sats>,
|
||||
cents_source: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self._24h
|
||||
.compute_rolling_sum(max_from, height_24h_ago, sats_source, cents_source, exit)
|
||||
}
|
||||
}
|
||||
|
||||
/// Rolling sum only, window-first then unit.
|
||||
///
|
||||
/// Tree: `_24h.sats.height`, `_24h.btc.height`, etc.
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//! AmountPerBlockWithSum24h - AmountPerBlock raw + RollingWindow24hAmountPerBlock sum.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{AmountPerBlock, RollingWindow24hAmountPerBlock};
|
||||
|
||||
/// Amount per-block value (sats + cents) with 24h rolling sum (also amount).
|
||||
#[derive(Traversable)]
|
||||
pub struct AmountPerBlockWithSum24h<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub raw: AmountPerBlock<M>,
|
||||
pub sum: RollingWindow24hAmountPerBlock<M>,
|
||||
}
|
||||
@@ -19,7 +19,7 @@ use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedPerBlock, NumericValue, PercentPerBlock, PercentRollingWindows,
|
||||
RollingWindows, WindowStarts,
|
||||
RollingWindows, WindowStarts, WindowsExcept1m,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -168,7 +168,9 @@ where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
#[traversable(wrap = "change", rename = "1m")]
|
||||
pub change_1m: ComputedPerBlock<C, M>,
|
||||
#[traversable(wrap = "rate", rename = "1m")]
|
||||
pub rate_1m: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
@@ -227,14 +229,8 @@ where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
#[traversable(rename = "24h")]
|
||||
pub change_24h: ComputedPerBlock<C, M>,
|
||||
pub change_1w: ComputedPerBlock<C, M>,
|
||||
pub change_1y: ComputedPerBlock<C, M>,
|
||||
#[traversable(rename = "24h")]
|
||||
pub rate_24h: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub rate_1w: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub rate_1y: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub change: WindowsExcept1m<ComputedPerBlock<C, M>>,
|
||||
pub rate: WindowsExcept1m<PercentPerBlock<BasisPointsSigned32, M>>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
@@ -250,42 +246,22 @@ where
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change_24h: ComputedPerBlock::forced_import(
|
||||
change: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_24h"),
|
||||
&format!("{name}_change_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1w: ComputedPerBlock::forced_import(
|
||||
)
|
||||
})?,
|
||||
rate: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1w"),
|
||||
&format!("{name}_rate_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1y: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1y"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_24h: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_24h"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_1w: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_1w"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_1y: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_1y"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
)
|
||||
})?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -297,8 +273,8 @@ where
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let changes = [&mut self.change_24h, &mut self.change_1w, &mut self.change_1y];
|
||||
let rates = [&mut self.rate_24h, &mut self.rate_1w, &mut self.rate_1y];
|
||||
let changes = self.change.as_mut_array();
|
||||
let rates = self.rate.as_mut_array();
|
||||
let starts = [windows._24h, windows._1w, windows._1y];
|
||||
|
||||
for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) {
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
CentsType, FiatPerBlock, NumericValue, PercentPerBlock, PercentRollingWindows,
|
||||
WindowStarts,
|
||||
Windows, WindowStarts, WindowsExcept1m,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -24,7 +24,9 @@ where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
#[traversable(wrap = "change", rename = "1m")]
|
||||
pub change_1m: FiatPerBlock<C, M>,
|
||||
#[traversable(wrap = "rate", rename = "1m")]
|
||||
pub rate_1m: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
@@ -82,14 +84,8 @@ where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
#[traversable(rename = "24h")]
|
||||
pub change_24h: FiatPerBlock<C, M>,
|
||||
pub change_1w: FiatPerBlock<C, M>,
|
||||
pub change_1y: FiatPerBlock<C, M>,
|
||||
#[traversable(rename = "24h")]
|
||||
pub rate_24h: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub rate_1w: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub rate_1y: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub change: WindowsExcept1m<FiatPerBlock<C, M>>,
|
||||
pub rate: WindowsExcept1m<PercentPerBlock<BasisPointsSigned32, M>>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
@@ -105,42 +101,22 @@ where
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change_24h: FiatPerBlock::forced_import(
|
||||
change: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_24h"),
|
||||
&format!("{name}_change_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1w: FiatPerBlock::forced_import(
|
||||
)
|
||||
})?,
|
||||
rate: WindowsExcept1m::try_from_fn(|suffix| {
|
||||
PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1w"),
|
||||
&format!("{name}_rate_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1y: FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1y"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_24h: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_24h"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_1w: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_1w"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate_1y: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate_1y"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
)
|
||||
})?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -152,12 +128,8 @@ where
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let changes: [&mut FiatPerBlock<C>; 3] = [
|
||||
&mut self.change_24h,
|
||||
&mut self.change_1w,
|
||||
&mut self.change_1y,
|
||||
];
|
||||
let rates = [&mut self.rate_24h, &mut self.rate_1w, &mut self.rate_1y];
|
||||
let changes = self.change.as_mut_array();
|
||||
let rates = self.rate.as_mut_array();
|
||||
let starts = [windows._24h, windows._1w, windows._1y];
|
||||
|
||||
for ((change_w, rate_w), starts) in changes.into_iter().zip(rates).zip(starts) {
|
||||
@@ -181,10 +153,7 @@ where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: CentsType,
|
||||
{
|
||||
pub change_24h: FiatPerBlock<C, M>,
|
||||
pub change_1w: FiatPerBlock<C, M>,
|
||||
pub change_1m: FiatPerBlock<C, M>,
|
||||
pub change_1y: FiatPerBlock<C, M>,
|
||||
pub change: Windows<FiatPerBlock<C, M>>,
|
||||
pub rate: PercentRollingWindows<BasisPointsSigned32, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
@@ -201,30 +170,14 @@ where
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change_24h: FiatPerBlock::forced_import(
|
||||
change: Windows::try_from_fn(|suffix| {
|
||||
FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_24h"),
|
||||
&format!("{name}_change_{suffix}"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1w: FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1w"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1m: FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
change_1y: FiatPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1y"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
)
|
||||
})?,
|
||||
rate: PercentRollingWindows::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate"),
|
||||
@@ -242,12 +195,7 @@ where
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let changes: [&mut FiatPerBlock<C>; 4] = [
|
||||
&mut self.change_24h,
|
||||
&mut self.change_1w,
|
||||
&mut self.change_1m,
|
||||
&mut self.change_1y,
|
||||
];
|
||||
let changes = self.change.as_mut_array();
|
||||
let rates = self.rate.0.as_mut_array();
|
||||
let starts = windows.as_array();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ mod fiat_delta;
|
||||
mod full;
|
||||
mod rolling_average;
|
||||
mod sum;
|
||||
mod with_sum_24h;
|
||||
|
||||
pub use aggregated::*;
|
||||
pub use base::*;
|
||||
@@ -23,3 +24,4 @@ pub use fiat_delta::*;
|
||||
pub use full::*;
|
||||
pub use rolling_average::*;
|
||||
pub use sum::*;
|
||||
pub use with_sum_24h::*;
|
||||
|
||||
@@ -90,12 +90,7 @@ where
|
||||
break;
|
||||
}
|
||||
let target = S1I::max_from(I::from(i), source_len);
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
if let Some(v) = cursor.next() {
|
||||
f(v);
|
||||
}
|
||||
} else if let Some(v) = source.collect_one_at(target) {
|
||||
if let Some(v) = cursor.get(target) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//! PerBlockWithSum24h - ComputedPerBlock + RollingWindow24hPerBlock rolling sum.
|
||||
//!
|
||||
//! Generic building block for metrics that store a per-block value
|
||||
//! plus its 24h rolling sum. Used across activity and realized metrics.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, ComputedVecValue, RollingWindow24hPerBlock};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct PerBlockWithSum24h<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub raw: ComputedPerBlock<T, M>,
|
||||
pub sum: RollingWindow24hPerBlock<T, M>,
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod base;
|
||||
mod lazy;
|
||||
mod with_sum_24h;
|
||||
|
||||
pub use base::*;
|
||||
pub use lazy::*;
|
||||
pub use with_sum_24h::*;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//! FiatPerBlockWithSum24h - FiatPerBlock raw + RollingWindow24hFiatPerBlock sum.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CentsType, FiatPerBlock, RollingWindow24h},
|
||||
};
|
||||
|
||||
/// Single 24h rolling window backed by FiatPerBlock (cents + lazy usd).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingWindow24hFiatPerBlock<C: CentsType, M: StorageMode = Rw>(
|
||||
pub RollingWindow24h<FiatPerBlock<C, M>>,
|
||||
);
|
||||
|
||||
impl<C: CentsType> RollingWindow24hFiatPerBlock<C> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(RollingWindow24h {
|
||||
_24h: FiatPerBlock::forced_import(db, &format!("{name}_24h"), version, indexes)?,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
height_24h_ago: &impl ReadableVec<Height, Height>,
|
||||
source: &impl ReadableVec<Height, C>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
C: Default + SubAssign,
|
||||
{
|
||||
self._24h
|
||||
.cents
|
||||
.height
|
||||
.compute_rolling_sum(max_from, height_24h_ago, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Fiat per-block value (cents + usd) with 24h rolling sum (also fiat).
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatPerBlockWithSum24h<C: CentsType, M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub raw: FiatPerBlock<C, M>,
|
||||
pub sum: RollingWindow24hFiatPerBlock<C, M>,
|
||||
}
|
||||
@@ -5,18 +5,18 @@ use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{Bp32ToFloat, ComputedPerBlock, LazyPerBlock},
|
||||
internal::{BpsType, ComputedPerBlock, LazyPerBlock},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RatioPerBlock<M: StorageMode = Rw> {
|
||||
pub bps: ComputedPerBlock<BasisPoints32, M>,
|
||||
pub ratio: LazyPerBlock<StoredF32, BasisPoints32>,
|
||||
pub struct RatioPerBlock<B: BpsType = BasisPoints32, M: StorageMode = Rw> {
|
||||
pub bps: ComputedPerBlock<B, M>,
|
||||
pub ratio: LazyPerBlock<StoredF32, B>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO;
|
||||
|
||||
impl RatioPerBlock {
|
||||
impl<B: BpsType> RatioPerBlock<B> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
@@ -36,7 +36,7 @@ impl RatioPerBlock {
|
||||
|
||||
let bps = ComputedPerBlock::forced_import(db, &format!("{name}_bps"), v, indexes)?;
|
||||
|
||||
let ratio = LazyPerBlock::from_computed::<Bp32ToFloat>(
|
||||
let ratio = LazyPerBlock::from_computed::<B::ToRatio>(
|
||||
name,
|
||||
v,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
@@ -45,7 +45,9 @@ impl RatioPerBlock {
|
||||
|
||||
Ok(Self { bps, ratio })
|
||||
}
|
||||
}
|
||||
|
||||
impl RatioPerBlock<BasisPoints32> {
|
||||
pub(crate) fn compute_ratio(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use brk_types::{BasisPoints32, Cents, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct RatioPerBlockExtended<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub base: RatioPerBlock<M>,
|
||||
pub base: RatioPerBlock<BasisPoints32, M>,
|
||||
#[traversable(flatten)]
|
||||
pub percentiles: RatioPerBlockPercentiles<M>,
|
||||
}
|
||||
|
||||
@@ -13,22 +13,23 @@ use crate::{
|
||||
|
||||
use super::{super::ComputedPerBlock, RatioPerBlock};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RatioBand<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub ratio: RatioPerBlock<BasisPoints32, M>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RatioPerBlockPercentiles<M: StorageMode = Rw> {
|
||||
pub ratio_sma_1w: RatioPerBlock<M>,
|
||||
pub ratio_sma_1m: RatioPerBlock<M>,
|
||||
pub ratio_pct99: RatioPerBlock<M>,
|
||||
pub ratio_pct98: RatioPerBlock<M>,
|
||||
pub ratio_pct95: RatioPerBlock<M>,
|
||||
pub ratio_pct5: RatioPerBlock<M>,
|
||||
pub ratio_pct2: RatioPerBlock<M>,
|
||||
pub ratio_pct1: RatioPerBlock<M>,
|
||||
pub ratio_pct99_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub ratio_pct98_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub ratio_pct95_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub ratio_pct5_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub ratio_pct2_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub ratio_pct1_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub sma_1w: RatioPerBlock<BasisPoints32, M>,
|
||||
pub sma_1m: RatioPerBlock<BasisPoints32, M>,
|
||||
pub pct99: RatioBand<M>,
|
||||
pub pct98: RatioBand<M>,
|
||||
pub pct95: RatioBand<M>,
|
||||
pub pct5: RatioBand<M>,
|
||||
pub pct2: RatioBand<M>,
|
||||
pub pct1: RatioBand<M>,
|
||||
|
||||
#[traversable(skip)]
|
||||
expanding_pct: ExpandingPercentiles,
|
||||
@@ -62,21 +63,24 @@ impl RatioPerBlockPercentiles {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! import_band {
|
||||
($suffix:expr) => {
|
||||
RatioBand {
|
||||
ratio: import_ratio!($suffix),
|
||||
price: import_price!($suffix),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
ratio_sma_1w: import_ratio!("ratio_sma_1w"),
|
||||
ratio_sma_1m: import_ratio!("ratio_sma_1m"),
|
||||
ratio_pct99: import_ratio!("ratio_pct99"),
|
||||
ratio_pct98: import_ratio!("ratio_pct98"),
|
||||
ratio_pct95: import_ratio!("ratio_pct95"),
|
||||
ratio_pct5: import_ratio!("ratio_pct5"),
|
||||
ratio_pct2: import_ratio!("ratio_pct2"),
|
||||
ratio_pct1: import_ratio!("ratio_pct1"),
|
||||
ratio_pct99_price: import_price!("ratio_pct99"),
|
||||
ratio_pct98_price: import_price!("ratio_pct98"),
|
||||
ratio_pct95_price: import_price!("ratio_pct95"),
|
||||
ratio_pct5_price: import_price!("ratio_pct5"),
|
||||
ratio_pct2_price: import_price!("ratio_pct2"),
|
||||
ratio_pct1_price: import_price!("ratio_pct1"),
|
||||
sma_1w: import_ratio!("ratio_sma_1w"),
|
||||
sma_1m: import_ratio!("ratio_sma_1m"),
|
||||
pct99: import_band!("ratio_pct99"),
|
||||
pct98: import_band!("ratio_pct98"),
|
||||
pct95: import_band!("ratio_pct95"),
|
||||
pct5: import_band!("ratio_pct5"),
|
||||
pct2: import_band!("ratio_pct2"),
|
||||
pct1: import_band!("ratio_pct1"),
|
||||
expanding_pct: ExpandingPercentiles::default(),
|
||||
})
|
||||
}
|
||||
@@ -89,14 +93,14 @@ impl RatioPerBlockPercentiles {
|
||||
ratio_source: &impl ReadableVec<Height, StoredF32>,
|
||||
metric_price: &impl ReadableVec<Height, Cents>,
|
||||
) -> Result<()> {
|
||||
self.ratio_sma_1w.bps.height.compute_rolling_average(
|
||||
self.sma_1w.bps.height.compute_rolling_average(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1w_ago,
|
||||
ratio_source,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.ratio_sma_1m.bps.height.compute_rolling_average(
|
||||
self.sma_1m.bps.height.compute_rolling_average(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
ratio_source,
|
||||
@@ -131,12 +135,12 @@ impl RatioPerBlockPercentiles {
|
||||
|
||||
let new_ratios = ratio_source.collect_range_at(start, ratio_len);
|
||||
let mut pct_vecs: [&mut EagerVec<PcoVec<Height, BasisPoints32>>; 6] = [
|
||||
&mut self.ratio_pct1.bps.height,
|
||||
&mut self.ratio_pct2.bps.height,
|
||||
&mut self.ratio_pct5.bps.height,
|
||||
&mut self.ratio_pct95.bps.height,
|
||||
&mut self.ratio_pct98.bps.height,
|
||||
&mut self.ratio_pct99.bps.height,
|
||||
&mut self.pct1.ratio.bps.height,
|
||||
&mut self.pct2.ratio.bps.height,
|
||||
&mut self.pct5.ratio.bps.height,
|
||||
&mut self.pct95.ratio.bps.height,
|
||||
&mut self.pct98.ratio.bps.height,
|
||||
&mut self.pct99.ratio.bps.height,
|
||||
];
|
||||
const PCTS: [f64; 6] = [0.01, 0.02, 0.05, 0.95, 0.98, 0.99];
|
||||
let mut out = [0u32; 6];
|
||||
@@ -158,24 +162,25 @@ impl RatioPerBlockPercentiles {
|
||||
|
||||
// Cents bands
|
||||
macro_rules! compute_band {
|
||||
($usd_field:ident, $band_source:expr) => {
|
||||
self.$usd_field
|
||||
($band:ident) => {
|
||||
self.$band
|
||||
.price
|
||||
.cents
|
||||
.compute_binary::<Cents, BasisPoints32, PriceTimesRatioBp32Cents>(
|
||||
starting_indexes.height,
|
||||
metric_price,
|
||||
$band_source,
|
||||
&self.$band.ratio.bps.height,
|
||||
exit,
|
||||
)?;
|
||||
};
|
||||
}
|
||||
|
||||
compute_band!(ratio_pct99_price, &self.ratio_pct99.bps.height);
|
||||
compute_band!(ratio_pct98_price, &self.ratio_pct98.bps.height);
|
||||
compute_band!(ratio_pct95_price, &self.ratio_pct95.bps.height);
|
||||
compute_band!(ratio_pct5_price, &self.ratio_pct5.bps.height);
|
||||
compute_band!(ratio_pct2_price, &self.ratio_pct2.bps.height);
|
||||
compute_band!(ratio_pct1_price, &self.ratio_pct1.bps.height);
|
||||
compute_band!(pct99);
|
||||
compute_band!(pct98);
|
||||
compute_band!(pct95);
|
||||
compute_band!(pct5);
|
||||
compute_band!(pct2);
|
||||
compute_band!(pct1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -184,12 +189,12 @@ impl RatioPerBlockPercentiles {
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = &mut EagerVec<PcoVec<Height, BasisPoints32>>> {
|
||||
[
|
||||
&mut self.ratio_pct1.bps.height,
|
||||
&mut self.ratio_pct2.bps.height,
|
||||
&mut self.ratio_pct5.bps.height,
|
||||
&mut self.ratio_pct95.bps.height,
|
||||
&mut self.ratio_pct98.bps.height,
|
||||
&mut self.ratio_pct99.bps.height,
|
||||
&mut self.pct1.ratio.bps.height,
|
||||
&mut self.pct2.ratio.bps.height,
|
||||
&mut self.pct5.ratio.bps.height,
|
||||
&mut self.pct95.ratio.bps.height,
|
||||
&mut self.pct98.ratio.bps.height,
|
||||
&mut self.pct99.ratio.bps.height,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, Version};
|
||||
use brk_types::{BasisPoints32, Cents, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct PriceWithRatioPerBlock<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub inner: RatioPerBlock<M>,
|
||||
pub inner: RatioPerBlock<BasisPoints32, M>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ use crate::{blocks, indexes, internal::StdDevPerBlockExtended};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RatioPerBlockStdDevBands<M: StorageMode = Rw> {
|
||||
pub ratio_sd: StdDevPerBlockExtended<M>,
|
||||
pub ratio_sd_4y: StdDevPerBlockExtended<M>,
|
||||
pub ratio_sd_2y: StdDevPerBlockExtended<M>,
|
||||
pub ratio_sd_1y: StdDevPerBlockExtended<M>,
|
||||
pub all: StdDevPerBlockExtended<M>,
|
||||
pub _4y: StdDevPerBlockExtended<M>,
|
||||
pub _2y: StdDevPerBlockExtended<M>,
|
||||
pub _1y: StdDevPerBlockExtended<M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::new(4);
|
||||
@@ -38,10 +38,10 @@ impl RatioPerBlockStdDevBands {
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
ratio_sd: import_sd!("ratio", "", usize::MAX),
|
||||
ratio_sd_1y: import_sd!("ratio", "1y", 365),
|
||||
ratio_sd_2y: import_sd!("ratio", "2y", 2 * 365),
|
||||
ratio_sd_4y: import_sd!("ratio", "4y", 4 * 365),
|
||||
all: import_sd!("ratio", "", usize::MAX),
|
||||
_1y: import_sd!("ratio", "1y", 365),
|
||||
_2y: import_sd!("ratio", "2y", 2 * 365),
|
||||
_4y: import_sd!("ratio", "4y", 4 * 365),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ impl RatioPerBlockStdDevBands {
|
||||
metric_price: &impl ReadableVec<Height, Cents>,
|
||||
) -> Result<()> {
|
||||
for sd in [
|
||||
&mut self.ratio_sd,
|
||||
&mut self.ratio_sd_4y,
|
||||
&mut self.ratio_sd_2y,
|
||||
&mut self.ratio_sd_1y,
|
||||
&mut self.all,
|
||||
&mut self._4y,
|
||||
&mut self._2y,
|
||||
&mut self._1y,
|
||||
] {
|
||||
sd.compute_all(blocks, starting_indexes, exit, ratio_source)?;
|
||||
sd.compute_cents_bands(starting_indexes, metric_price, exit)?;
|
||||
|
||||
@@ -15,7 +15,7 @@ use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, ComputedVecValue, NumericValue, Windows},
|
||||
internal::{ComputedPerBlock, ComputedVecValue, NumericValue, RollingWindow24h, Windows, WindowsFrom1w},
|
||||
};
|
||||
|
||||
/// Rolling window start heights — the 4 height-ago vecs (24h, 1w, 1m, 1y).
|
||||
@@ -61,16 +61,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Single 24h rolling window (1 stored vec).
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingWindow24h<T, M: StorageMode = Rw>
|
||||
/// Single 24h rolling window backed by ComputedPerBlock (1 stored vec).
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingWindow24hPerBlock<T, M: StorageMode = Rw>(
|
||||
pub RollingWindow24h<ComputedPerBlock<T, M>>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub _24h: ComputedPerBlock<T, M>,
|
||||
}
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema;
|
||||
|
||||
impl<T> RollingWindow24h<T>
|
||||
impl<T> RollingWindow24hPerBlock<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
@@ -80,14 +80,14 @@ where
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
Ok(Self(RollingWindow24h {
|
||||
_24h: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_24h"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
@@ -108,15 +108,11 @@ where
|
||||
}
|
||||
|
||||
/// Extended rolling windows: 1w + 1m + 1y (3 stored vecs).
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingWindowsFrom1w<T, M: StorageMode = Rw>
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingWindowsFrom1w<T, M: StorageMode = Rw>(pub WindowsFrom1w<ComputedPerBlock<T, M>>)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub _1w: ComputedPerBlock<T, M>,
|
||||
pub _1m: ComputedPerBlock<T, M>,
|
||||
pub _1y: ComputedPerBlock<T, M>,
|
||||
}
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema;
|
||||
|
||||
impl<T> RollingWindowsFrom1w<T>
|
||||
where
|
||||
@@ -128,34 +124,9 @@ where
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
_1w: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_1w"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_1m: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_1y: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{name}_1y"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_array(&self) -> [&ComputedPerBlock<T>; 3] {
|
||||
[&self._1w, &self._1m, &self._1y]
|
||||
}
|
||||
|
||||
pub fn as_mut_array(&mut self) -> [&mut ComputedPerBlock<T>; 3] {
|
||||
[&mut self._1w, &mut self._1m, &mut self._1y]
|
||||
Ok(Self(WindowsFrom1w::try_from_fn(|suffix| {
|
||||
ComputedPerBlock::forced_import(db, &format!("{name}_{suffix}"), version, indexes)
|
||||
})?))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_sum(
|
||||
@@ -168,15 +139,11 @@ where
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
self._1w
|
||||
.height
|
||||
.compute_rolling_sum(max_from, windows._1w, source, exit)?;
|
||||
self._1m
|
||||
.height
|
||||
.compute_rolling_sum(max_from, windows._1m, source, exit)?;
|
||||
self._1y
|
||||
.height
|
||||
.compute_rolling_sum(max_from, windows._1y, source, exit)?;
|
||||
let starts = [windows._1w, windows._1m, windows._1y];
|
||||
for (w, starts) in self.0.as_mut_array().into_iter().zip(starts) {
|
||||
w.height
|
||||
.compute_rolling_sum(max_from, starts, source, exit)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,31 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, StoredF32, Version};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex,
|
||||
WritableVec,
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, Ident, PcoVec, ReadableCloneableVec,
|
||||
ReadableVec, Rw, StorageMode, VecIndex, WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
blocks, indexes,
|
||||
internal::{ComputedPerBlock, Price, PriceTimesRatioCents},
|
||||
internal::{ComputedPerBlock, LazyPerBlock, Price, PriceTimesRatioCents},
|
||||
};
|
||||
|
||||
use super::StdDevPerBlock;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct StdDevBand<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub value: ComputedPerBlock<StoredF32, M>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct LazyStdDevBand<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub value: LazyPerBlock<StoredF32>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct StdDevPerBlockExtended<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
@@ -20,32 +34,19 @@ pub struct StdDevPerBlockExtended<M: StorageMode = Rw> {
|
||||
|
||||
pub zscore: ComputedPerBlock<StoredF32, M>,
|
||||
|
||||
pub p0_5sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub p1sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub p1_5sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub p2sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub p2_5sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub p3sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub m0_5sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub m1sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub m1_5sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub m2sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub m2_5sd: ComputedPerBlock<StoredF32, M>,
|
||||
pub m3sd: ComputedPerBlock<StoredF32, M>,
|
||||
|
||||
pub _0sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub p0_5sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub p1sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub p1_5sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub p2sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub p2_5sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub p3sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub m0_5sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub m1sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub m1_5sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub m2sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub m2_5sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub m3sd_price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub _0sd: LazyStdDevBand<M>,
|
||||
pub p0_5sd: StdDevBand<M>,
|
||||
pub p1sd: StdDevBand<M>,
|
||||
pub p1_5sd: StdDevBand<M>,
|
||||
pub p2sd: StdDevBand<M>,
|
||||
pub p2_5sd: StdDevBand<M>,
|
||||
pub p3sd: StdDevBand<M>,
|
||||
pub m0_5sd: StdDevBand<M>,
|
||||
pub m1sd: StdDevBand<M>,
|
||||
pub m1_5sd: StdDevBand<M>,
|
||||
pub m2sd: StdDevBand<M>,
|
||||
pub m2_5sd: StdDevBand<M>,
|
||||
pub m3sd: StdDevBand<M>,
|
||||
}
|
||||
|
||||
impl StdDevPerBlockExtended {
|
||||
@@ -77,41 +78,50 @@ impl StdDevPerBlockExtended {
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
base: StdDevPerBlock::forced_import(
|
||||
macro_rules! import_band {
|
||||
($suffix:expr) => {
|
||||
StdDevBand {
|
||||
value: import!($suffix),
|
||||
price: import_price!($suffix),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let base = StdDevPerBlock::forced_import(
|
||||
db,
|
||||
name,
|
||||
period,
|
||||
days,
|
||||
parent_version,
|
||||
indexes,
|
||||
)?,
|
||||
)?;
|
||||
|
||||
let _0sd = LazyStdDevBand {
|
||||
value: LazyPerBlock::from_computed::<Ident>(
|
||||
&format!("{name}_0sd{p}"),
|
||||
version,
|
||||
base.sma.height.read_only_boxed_clone(),
|
||||
&base.sma,
|
||||
),
|
||||
price: import_price!("0sd"),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
base,
|
||||
zscore: import!("zscore"),
|
||||
p0_5sd: import!("p0_5sd"),
|
||||
p1sd: import!("p1sd"),
|
||||
p1_5sd: import!("p1_5sd"),
|
||||
p2sd: import!("p2sd"),
|
||||
p2_5sd: import!("p2_5sd"),
|
||||
p3sd: import!("p3sd"),
|
||||
m0_5sd: import!("m0_5sd"),
|
||||
m1sd: import!("m1sd"),
|
||||
m1_5sd: import!("m1_5sd"),
|
||||
m2sd: import!("m2sd"),
|
||||
m2_5sd: import!("m2_5sd"),
|
||||
m3sd: import!("m3sd"),
|
||||
_0sd_price: import_price!("0sd"),
|
||||
p0_5sd_price: import_price!("p0_5sd"),
|
||||
p1sd_price: import_price!("p1sd"),
|
||||
p1_5sd_price: import_price!("p1_5sd"),
|
||||
p2sd_price: import_price!("p2sd"),
|
||||
p2_5sd_price: import_price!("p2_5sd"),
|
||||
p3sd_price: import_price!("p3sd"),
|
||||
m0_5sd_price: import_price!("m0_5sd"),
|
||||
m1sd_price: import_price!("m1sd"),
|
||||
m1_5sd_price: import_price!("m1_5sd"),
|
||||
m2sd_price: import_price!("m2sd"),
|
||||
m2_5sd_price: import_price!("m2_5sd"),
|
||||
m3sd_price: import_price!("m3sd"),
|
||||
_0sd,
|
||||
p0_5sd: import_band!("p0_5sd"),
|
||||
p1sd: import_band!("p1sd"),
|
||||
p1_5sd: import_band!("p1_5sd"),
|
||||
p2sd: import_band!("p2sd"),
|
||||
p2_5sd: import_band!("p2_5sd"),
|
||||
p3sd: import_band!("p3sd"),
|
||||
m0_5sd: import_band!("m0_5sd"),
|
||||
m1sd: import_band!("m1sd"),
|
||||
m1_5sd: import_band!("m1_5sd"),
|
||||
m2sd: import_band!("m2sd"),
|
||||
m2_5sd: import_band!("m2_5sd"),
|
||||
m3sd: import_band!("m3sd"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -214,9 +224,9 @@ impl StdDevPerBlockExtended {
|
||||
metric_price: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
macro_rules! compute_band {
|
||||
($usd_field:ident, $band_source:expr) => {
|
||||
self.$usd_field
|
||||
macro_rules! compute_band_price {
|
||||
($price:expr, $band_source:expr) => {
|
||||
$price
|
||||
.cents
|
||||
.compute_binary::<Cents, StoredF32, PriceTimesRatioCents>(
|
||||
starting_indexes.height,
|
||||
@@ -227,19 +237,19 @@ impl StdDevPerBlockExtended {
|
||||
};
|
||||
}
|
||||
|
||||
compute_band!(_0sd_price, &self.base.sma.height);
|
||||
compute_band!(p0_5sd_price, &self.p0_5sd.height);
|
||||
compute_band!(p1sd_price, &self.p1sd.height);
|
||||
compute_band!(p1_5sd_price, &self.p1_5sd.height);
|
||||
compute_band!(p2sd_price, &self.p2sd.height);
|
||||
compute_band!(p2_5sd_price, &self.p2_5sd.height);
|
||||
compute_band!(p3sd_price, &self.p3sd.height);
|
||||
compute_band!(m0_5sd_price, &self.m0_5sd.height);
|
||||
compute_band!(m1sd_price, &self.m1sd.height);
|
||||
compute_band!(m1_5sd_price, &self.m1_5sd.height);
|
||||
compute_band!(m2sd_price, &self.m2sd.height);
|
||||
compute_band!(m2_5sd_price, &self.m2_5sd.height);
|
||||
compute_band!(m3sd_price, &self.m3sd.height);
|
||||
compute_band_price!(&mut self._0sd.price, &self.base.sma.height);
|
||||
compute_band_price!(&mut self.p0_5sd.price, &self.p0_5sd.value.height);
|
||||
compute_band_price!(&mut self.p1sd.price, &self.p1sd.value.height);
|
||||
compute_band_price!(&mut self.p1_5sd.price, &self.p1_5sd.value.height);
|
||||
compute_band_price!(&mut self.p2sd.price, &self.p2sd.value.height);
|
||||
compute_band_price!(&mut self.p2_5sd.price, &self.p2_5sd.value.height);
|
||||
compute_band_price!(&mut self.p3sd.price, &self.p3sd.value.height);
|
||||
compute_band_price!(&mut self.m0_5sd.price, &self.m0_5sd.value.height);
|
||||
compute_band_price!(&mut self.m1sd.price, &self.m1sd.value.height);
|
||||
compute_band_price!(&mut self.m1_5sd.price, &self.m1_5sd.value.height);
|
||||
compute_band_price!(&mut self.m2sd.price, &self.m2sd.value.height);
|
||||
compute_band_price!(&mut self.m2_5sd.price, &self.m2_5sd.value.height);
|
||||
compute_band_price!(&mut self.m3sd.price, &self.m3sd.value.height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -248,18 +258,18 @@ impl StdDevPerBlockExtended {
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = &mut EagerVec<PcoVec<Height, StoredF32>>> {
|
||||
[
|
||||
&mut self.p0_5sd.height,
|
||||
&mut self.p1sd.height,
|
||||
&mut self.p1_5sd.height,
|
||||
&mut self.p2sd.height,
|
||||
&mut self.p2_5sd.height,
|
||||
&mut self.p3sd.height,
|
||||
&mut self.m0_5sd.height,
|
||||
&mut self.m1sd.height,
|
||||
&mut self.m1_5sd.height,
|
||||
&mut self.m2sd.height,
|
||||
&mut self.m2_5sd.height,
|
||||
&mut self.m3sd.height,
|
||||
&mut self.p0_5sd.value.height,
|
||||
&mut self.p1sd.value.height,
|
||||
&mut self.p1_5sd.value.height,
|
||||
&mut self.p2sd.value.height,
|
||||
&mut self.p2_5sd.value.height,
|
||||
&mut self.p3sd.value.height,
|
||||
&mut self.m0_5sd.value.height,
|
||||
&mut self.m1sd.value.height,
|
||||
&mut self.m1_5sd.value.height,
|
||||
&mut self.m2sd.value.height,
|
||||
&mut self.m2_5sd.value.height,
|
||||
&mut self.m3sd.value.height,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
@@ -13,16 +13,16 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.price_ath.cents.height.compute_all_time_high(
|
||||
self.price.cents.height.compute_all_time_high(
|
||||
starting_indexes.height,
|
||||
&prices.price.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let mut ath_ts: Option<Timestamp> = None;
|
||||
self.days_since_price_ath.height.compute_transform3(
|
||||
self.days_since.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.price_ath.cents.height,
|
||||
&self.price.cents.height,
|
||||
&prices.price.cents.height,
|
||||
&blocks.time.timestamp_monotonic,
|
||||
|(i, ath, price, ts, slf)| {
|
||||
@@ -47,9 +47,9 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
let mut prev = None;
|
||||
self.max_days_between_price_ath.height.compute_transform(
|
||||
self.max_days_between.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.days_since_price_ath.height,
|
||||
&self.days_since.height,
|
||||
|(i, days, slf)| {
|
||||
if prev.is_none() {
|
||||
let i = i.to_usize();
|
||||
@@ -66,10 +66,10 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.price_drawdown.compute_drawdown(
|
||||
self.drawdown.compute_drawdown(
|
||||
starting_indexes.height,
|
||||
&prices.price.cents.height,
|
||||
&self.price_ath.cents.height,
|
||||
&self.price.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -18,35 +18,35 @@ impl Vecs {
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let price_ath = Price::forced_import(db, "price_ath", v, indexes)?;
|
||||
let price = Price::forced_import(db, "price_ath", v, indexes)?;
|
||||
|
||||
let max_days_between_price_ath =
|
||||
let max_days_between =
|
||||
ComputedPerBlock::forced_import(db, "max_days_between_price_ath", v, indexes)?;
|
||||
|
||||
let max_years_between_price_ath = DerivedResolutions::from_computed::<DaysToYears>(
|
||||
let max_years_between = DerivedResolutions::from_computed::<DaysToYears>(
|
||||
"max_years_between_price_ath",
|
||||
v,
|
||||
&max_days_between_price_ath,
|
||||
&max_days_between,
|
||||
);
|
||||
|
||||
let days_since_price_ath =
|
||||
let days_since =
|
||||
ComputedPerBlock::forced_import(db, "days_since_price_ath", v, indexes)?;
|
||||
|
||||
let years_since_price_ath = DerivedResolutions::from_computed::<DaysToYears>(
|
||||
let years_since = DerivedResolutions::from_computed::<DaysToYears>(
|
||||
"years_since_price_ath",
|
||||
v,
|
||||
&days_since_price_ath,
|
||||
&days_since,
|
||||
);
|
||||
|
||||
let price_drawdown = PercentPerBlock::forced_import(db, "price_drawdown", v, indexes)?;
|
||||
let drawdown = PercentPerBlock::forced_import(db, "price_drawdown", v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
price_ath,
|
||||
price_drawdown,
|
||||
days_since_price_ath,
|
||||
years_since_price_ath,
|
||||
max_days_between_price_ath,
|
||||
max_years_between_price_ath,
|
||||
price,
|
||||
drawdown,
|
||||
days_since,
|
||||
years_since,
|
||||
max_days_between,
|
||||
max_years_between,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ use crate::internal::{ComputedPerBlock, DerivedResolutions, PercentPerBlock, Pri
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub price_ath: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_drawdown: PercentPerBlock<BasisPointsSigned16, M>,
|
||||
pub days_since_price_ath: ComputedPerBlock<StoredF32, M>,
|
||||
pub years_since_price_ath: DerivedResolutions<StoredF32, StoredF32>,
|
||||
pub max_days_between_price_ath: ComputedPerBlock<StoredF32, M>,
|
||||
pub max_years_between_price_ath: DerivedResolutions<StoredF32, StoredF32>,
|
||||
pub price: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub drawdown: PercentPerBlock<BasisPointsSigned16, M>,
|
||||
pub days_since: ComputedPerBlock<StoredF32, M>,
|
||||
pub years_since: DerivedResolutions<StoredF32, StoredF32>,
|
||||
pub max_days_between: ComputedPerBlock<StoredF32, M>,
|
||||
pub max_years_between: DerivedResolutions<StoredF32, StoredF32>,
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ impl Vecs {
|
||||
self.stoch_k.bps.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
price,
|
||||
&range.price_min_2w.usd.height,
|
||||
&range.price_max_2w.usd.height,
|
||||
&range.min._2w.usd.height,
|
||||
&range.max._2w.usd.height,
|
||||
|(h, close, low, high, ..)| {
|
||||
let range = *high - *low;
|
||||
let stoch = if range == 0.0 {
|
||||
@@ -127,8 +127,8 @@ impl Vecs {
|
||||
.bps
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp32>(
|
||||
starting_indexes.height,
|
||||
&moving_average.price_sma_111d.price.usd.height,
|
||||
&moving_average.price_sma_350d_x2.usd.height,
|
||||
&moving_average.sma._111d.price.usd.height,
|
||||
&moving_average.sma._350d_x2.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, StoredF32};
|
||||
use brk_types::{BasisPoints16, BasisPoints32, StoredF32};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, RatioPerBlock, PercentPerBlock, Windows};
|
||||
@@ -29,15 +29,15 @@ pub struct MacdChain<M: StorageMode = Rw> {
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub puell_multiple: RatioPerBlock<M>,
|
||||
pub nvt: RatioPerBlock<M>,
|
||||
pub puell_multiple: RatioPerBlock<BasisPoints32, M>,
|
||||
pub nvt: RatioPerBlock<BasisPoints32, M>,
|
||||
|
||||
pub rsi: Windows<RsiChain<M>>,
|
||||
|
||||
pub stoch_k: PercentPerBlock<BasisPoints16, M>,
|
||||
pub stoch_d: PercentPerBlock<BasisPoints16, M>,
|
||||
|
||||
pub pi_cycle: RatioPerBlock<M>,
|
||||
pub pi_cycle: RatioPerBlock<BasisPoints32, M>,
|
||||
|
||||
pub macd: Windows<MacdChain<M>>,
|
||||
|
||||
|
||||
@@ -16,22 +16,22 @@ impl Vecs {
|
||||
let close = &prices.price.cents.height;
|
||||
|
||||
for (sma, period) in [
|
||||
(&mut self.price_sma_1w, 7),
|
||||
(&mut self.price_sma_8d, 8),
|
||||
(&mut self.price_sma_13d, 13),
|
||||
(&mut self.price_sma_21d, 21),
|
||||
(&mut self.price_sma_1m, 30),
|
||||
(&mut self.price_sma_34d, 34),
|
||||
(&mut self.price_sma_55d, 55),
|
||||
(&mut self.price_sma_89d, 89),
|
||||
(&mut self.price_sma_111d, 111),
|
||||
(&mut self.price_sma_144d, 144),
|
||||
(&mut self.price_sma_200d, 200),
|
||||
(&mut self.price_sma_350d, 350),
|
||||
(&mut self.price_sma_1y, 365),
|
||||
(&mut self.price_sma_2y, 2 * 365),
|
||||
(&mut self.price_sma_200w, 200 * 7),
|
||||
(&mut self.price_sma_4y, 4 * 365),
|
||||
(&mut self.sma._1w, 7),
|
||||
(&mut self.sma._8d, 8),
|
||||
(&mut self.sma._13d, 13),
|
||||
(&mut self.sma._21d, 21),
|
||||
(&mut self.sma._1m, 30),
|
||||
(&mut self.sma._34d, 34),
|
||||
(&mut self.sma._55d, 55),
|
||||
(&mut self.sma._89d, 89),
|
||||
(&mut self.sma._111d, 111),
|
||||
(&mut self.sma._144d, 144),
|
||||
(&mut self.sma._200d, 200),
|
||||
(&mut self.sma._350d, 350),
|
||||
(&mut self.sma._1y, 365),
|
||||
(&mut self.sma._2y, 2 * 365),
|
||||
(&mut self.sma._200w, 200 * 7),
|
||||
(&mut self.sma._4y, 4 * 365),
|
||||
] {
|
||||
let window_starts = blocks.lookback.start_vec(period);
|
||||
sma.compute_all(prices, starting_indexes, exit, |v| {
|
||||
@@ -41,22 +41,22 @@ impl Vecs {
|
||||
}
|
||||
|
||||
for (ema, period) in [
|
||||
(&mut self.price_ema_1w, 7),
|
||||
(&mut self.price_ema_8d, 8),
|
||||
(&mut self.price_ema_12d, 12),
|
||||
(&mut self.price_ema_13d, 13),
|
||||
(&mut self.price_ema_21d, 21),
|
||||
(&mut self.price_ema_26d, 26),
|
||||
(&mut self.price_ema_1m, 30),
|
||||
(&mut self.price_ema_34d, 34),
|
||||
(&mut self.price_ema_55d, 55),
|
||||
(&mut self.price_ema_89d, 89),
|
||||
(&mut self.price_ema_144d, 144),
|
||||
(&mut self.price_ema_200d, 200),
|
||||
(&mut self.price_ema_1y, 365),
|
||||
(&mut self.price_ema_2y, 2 * 365),
|
||||
(&mut self.price_ema_200w, 200 * 7),
|
||||
(&mut self.price_ema_4y, 4 * 365),
|
||||
(&mut self.ema._1w, 7),
|
||||
(&mut self.ema._8d, 8),
|
||||
(&mut self.ema._12d, 12),
|
||||
(&mut self.ema._13d, 13),
|
||||
(&mut self.ema._21d, 21),
|
||||
(&mut self.ema._26d, 26),
|
||||
(&mut self.ema._1m, 30),
|
||||
(&mut self.ema._34d, 34),
|
||||
(&mut self.ema._55d, 55),
|
||||
(&mut self.ema._89d, 89),
|
||||
(&mut self.ema._144d, 144),
|
||||
(&mut self.ema._200d, 200),
|
||||
(&mut self.ema._1y, 365),
|
||||
(&mut self.ema._2y, 2 * 365),
|
||||
(&mut self.ema._200w, 200 * 7),
|
||||
(&mut self.ema._4y, 4 * 365),
|
||||
] {
|
||||
let window_starts = blocks.lookback.start_vec(period);
|
||||
ema.compute_all(prices, starting_indexes, exit, |v| {
|
||||
|
||||
@@ -2,10 +2,13 @@ use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use super::{
|
||||
vecs::{EmaVecs, SmaVecs},
|
||||
Vecs,
|
||||
};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CentsTimesTenths, PriceWithRatioPerBlock, Price},
|
||||
internal::{CentsTimesTenths, Price, PriceWithRatioPerBlock},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
@@ -16,72 +19,73 @@ impl Vecs {
|
||||
) -> Result<Self> {
|
||||
macro_rules! import {
|
||||
($name:expr) => {
|
||||
PriceWithRatioPerBlock::forced_import(
|
||||
db, $name, version, indexes,
|
||||
)?
|
||||
PriceWithRatioPerBlock::forced_import(db, $name, version, indexes)?
|
||||
};
|
||||
}
|
||||
|
||||
let price_sma_200d = import!("price_sma_200d");
|
||||
let price_sma_350d = import!("price_sma_350d");
|
||||
let sma_200d = import!("price_sma_200d");
|
||||
let sma_350d = import!("price_sma_350d");
|
||||
|
||||
let price_sma_200d_source = &price_sma_200d.price.cents;
|
||||
let price_sma_200d_x2_4 = Price::from_cents_source::<CentsTimesTenths<24>>(
|
||||
let price_sma_200d_source = &sma_200d.price.cents;
|
||||
let _200d_x2_4 = Price::from_cents_source::<CentsTimesTenths<24>>(
|
||||
"price_sma_200d_x2_4",
|
||||
version,
|
||||
price_sma_200d_source,
|
||||
);
|
||||
let price_sma_200d_x0_8 = Price::from_cents_source::<CentsTimesTenths<8>>(
|
||||
let _200d_x0_8 = Price::from_cents_source::<CentsTimesTenths<8>>(
|
||||
"price_sma_200d_x0_8",
|
||||
version,
|
||||
price_sma_200d_source,
|
||||
);
|
||||
|
||||
let price_sma_350d_source = &price_sma_350d.price.cents;
|
||||
let price_sma_350d_x2 = Price::from_cents_source::<CentsTimesTenths<20>>(
|
||||
let price_sma_350d_source = &sma_350d.price.cents;
|
||||
let _350d_x2 = Price::from_cents_source::<CentsTimesTenths<20>>(
|
||||
"price_sma_350d_x2",
|
||||
version,
|
||||
price_sma_350d_source,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
price_sma_1w: import!("price_sma_1w"),
|
||||
price_sma_8d: import!("price_sma_8d"),
|
||||
price_sma_13d: import!("price_sma_13d"),
|
||||
price_sma_21d: import!("price_sma_21d"),
|
||||
price_sma_1m: import!("price_sma_1m"),
|
||||
price_sma_34d: import!("price_sma_34d"),
|
||||
price_sma_55d: import!("price_sma_55d"),
|
||||
price_sma_89d: import!("price_sma_89d"),
|
||||
price_sma_111d: import!("price_sma_111d"),
|
||||
price_sma_144d: import!("price_sma_144d"),
|
||||
price_sma_200d,
|
||||
price_sma_350d,
|
||||
price_sma_1y: import!("price_sma_1y"),
|
||||
price_sma_2y: import!("price_sma_2y"),
|
||||
price_sma_200w: import!("price_sma_200w"),
|
||||
price_sma_4y: import!("price_sma_4y"),
|
||||
let sma = SmaVecs {
|
||||
_1w: import!("price_sma_1w"),
|
||||
_8d: import!("price_sma_8d"),
|
||||
_13d: import!("price_sma_13d"),
|
||||
_21d: import!("price_sma_21d"),
|
||||
_1m: import!("price_sma_1m"),
|
||||
_34d: import!("price_sma_34d"),
|
||||
_55d: import!("price_sma_55d"),
|
||||
_89d: import!("price_sma_89d"),
|
||||
_111d: import!("price_sma_111d"),
|
||||
_144d: import!("price_sma_144d"),
|
||||
_200d: sma_200d,
|
||||
_350d: sma_350d,
|
||||
_1y: import!("price_sma_1y"),
|
||||
_2y: import!("price_sma_2y"),
|
||||
_200w: import!("price_sma_200w"),
|
||||
_4y: import!("price_sma_4y"),
|
||||
_200d_x2_4,
|
||||
_200d_x0_8,
|
||||
_350d_x2,
|
||||
};
|
||||
|
||||
price_ema_1w: import!("price_ema_1w"),
|
||||
price_ema_8d: import!("price_ema_8d"),
|
||||
price_ema_12d: import!("price_ema_12d"),
|
||||
price_ema_13d: import!("price_ema_13d"),
|
||||
price_ema_21d: import!("price_ema_21d"),
|
||||
price_ema_26d: import!("price_ema_26d"),
|
||||
price_ema_1m: import!("price_ema_1m"),
|
||||
price_ema_34d: import!("price_ema_34d"),
|
||||
price_ema_55d: import!("price_ema_55d"),
|
||||
price_ema_89d: import!("price_ema_89d"),
|
||||
price_ema_144d: import!("price_ema_144d"),
|
||||
price_ema_200d: import!("price_ema_200d"),
|
||||
price_ema_1y: import!("price_ema_1y"),
|
||||
price_ema_2y: import!("price_ema_2y"),
|
||||
price_ema_200w: import!("price_ema_200w"),
|
||||
price_ema_4y: import!("price_ema_4y"),
|
||||
let ema = EmaVecs {
|
||||
_1w: import!("price_ema_1w"),
|
||||
_8d: import!("price_ema_8d"),
|
||||
_12d: import!("price_ema_12d"),
|
||||
_13d: import!("price_ema_13d"),
|
||||
_21d: import!("price_ema_21d"),
|
||||
_26d: import!("price_ema_26d"),
|
||||
_1m: import!("price_ema_1m"),
|
||||
_34d: import!("price_ema_34d"),
|
||||
_55d: import!("price_ema_55d"),
|
||||
_89d: import!("price_ema_89d"),
|
||||
_144d: import!("price_ema_144d"),
|
||||
_200d: import!("price_ema_200d"),
|
||||
_1y: import!("price_ema_1y"),
|
||||
_2y: import!("price_ema_2y"),
|
||||
_200w: import!("price_ema_200w"),
|
||||
_4y: import!("price_ema_4y"),
|
||||
};
|
||||
|
||||
price_sma_200d_x2_4,
|
||||
price_sma_200d_x0_8,
|
||||
price_sma_350d_x2,
|
||||
})
|
||||
Ok(Self { sma, ema })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,44 +2,53 @@ use brk_traversable::Traversable;
|
||||
use brk_types::Cents;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PriceWithRatioPerBlock, LazyPerBlock, Price};
|
||||
use crate::internal::{LazyPerBlock, Price, PriceWithRatioPerBlock};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct SmaVecs<M: StorageMode = Rw> {
|
||||
pub _1w: PriceWithRatioPerBlock<M>,
|
||||
pub _8d: PriceWithRatioPerBlock<M>,
|
||||
pub _13d: PriceWithRatioPerBlock<M>,
|
||||
pub _21d: PriceWithRatioPerBlock<M>,
|
||||
pub _1m: PriceWithRatioPerBlock<M>,
|
||||
pub _34d: PriceWithRatioPerBlock<M>,
|
||||
pub _55d: PriceWithRatioPerBlock<M>,
|
||||
pub _89d: PriceWithRatioPerBlock<M>,
|
||||
pub _111d: PriceWithRatioPerBlock<M>,
|
||||
pub _144d: PriceWithRatioPerBlock<M>,
|
||||
pub _200d: PriceWithRatioPerBlock<M>,
|
||||
pub _350d: PriceWithRatioPerBlock<M>,
|
||||
pub _1y: PriceWithRatioPerBlock<M>,
|
||||
pub _2y: PriceWithRatioPerBlock<M>,
|
||||
pub _200w: PriceWithRatioPerBlock<M>,
|
||||
pub _4y: PriceWithRatioPerBlock<M>,
|
||||
pub _200d_x2_4: Price<LazyPerBlock<Cents, Cents>>,
|
||||
pub _200d_x0_8: Price<LazyPerBlock<Cents, Cents>>,
|
||||
pub _350d_x2: Price<LazyPerBlock<Cents, Cents>>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct EmaVecs<M: StorageMode = Rw> {
|
||||
pub _1w: PriceWithRatioPerBlock<M>,
|
||||
pub _8d: PriceWithRatioPerBlock<M>,
|
||||
pub _12d: PriceWithRatioPerBlock<M>,
|
||||
pub _13d: PriceWithRatioPerBlock<M>,
|
||||
pub _21d: PriceWithRatioPerBlock<M>,
|
||||
pub _26d: PriceWithRatioPerBlock<M>,
|
||||
pub _1m: PriceWithRatioPerBlock<M>,
|
||||
pub _34d: PriceWithRatioPerBlock<M>,
|
||||
pub _55d: PriceWithRatioPerBlock<M>,
|
||||
pub _89d: PriceWithRatioPerBlock<M>,
|
||||
pub _144d: PriceWithRatioPerBlock<M>,
|
||||
pub _200d: PriceWithRatioPerBlock<M>,
|
||||
pub _1y: PriceWithRatioPerBlock<M>,
|
||||
pub _2y: PriceWithRatioPerBlock<M>,
|
||||
pub _200w: PriceWithRatioPerBlock<M>,
|
||||
pub _4y: PriceWithRatioPerBlock<M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub price_sma_1w: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_8d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_13d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_21d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_1m: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_34d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_55d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_89d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_111d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_144d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_200d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_350d: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_1y: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_2y: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_200w: PriceWithRatioPerBlock<M>,
|
||||
pub price_sma_4y: PriceWithRatioPerBlock<M>,
|
||||
|
||||
pub price_ema_1w: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_8d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_12d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_13d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_21d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_26d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_1m: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_34d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_55d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_89d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_144d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_200d: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_1y: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_2y: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_200w: PriceWithRatioPerBlock<M>,
|
||||
pub price_ema_4y: PriceWithRatioPerBlock<M>,
|
||||
|
||||
pub price_sma_200d_x2_4: Price<LazyPerBlock<Cents, Cents>>,
|
||||
pub price_sma_200d_x0_8: Price<LazyPerBlock<Cents, Cents>>,
|
||||
pub price_sma_350d_x2: Price<LazyPerBlock<Cents, Cents>>,
|
||||
pub sma: SmaVecs<M>,
|
||||
pub ema: EmaVecs<M>,
|
||||
}
|
||||
|
||||
@@ -17,23 +17,23 @@ impl Vecs {
|
||||
|
||||
for (min_vec, max_vec, starts) in [
|
||||
(
|
||||
&mut self.price_min_1w.cents.height,
|
||||
&mut self.price_max_1w.cents.height,
|
||||
&mut self.min._1w.cents.height,
|
||||
&mut self.max._1w.cents.height,
|
||||
&blocks.lookback.height_1w_ago,
|
||||
),
|
||||
(
|
||||
&mut self.price_min_2w.cents.height,
|
||||
&mut self.price_max_2w.cents.height,
|
||||
&mut self.min._2w.cents.height,
|
||||
&mut self.max._2w.cents.height,
|
||||
&blocks.lookback.height_2w_ago,
|
||||
),
|
||||
(
|
||||
&mut self.price_min_1m.cents.height,
|
||||
&mut self.price_max_1m.cents.height,
|
||||
&mut self.min._1m.cents.height,
|
||||
&mut self.max._1m.cents.height,
|
||||
&blocks.lookback.height_1m_ago,
|
||||
),
|
||||
(
|
||||
&mut self.price_min_1y.cents.height,
|
||||
&mut self.price_max_1y.cents.height,
|
||||
&mut self.min._1y.cents.height,
|
||||
&mut self.max._1y.cents.height,
|
||||
&blocks.lookback.height_1y_ago,
|
||||
),
|
||||
] {
|
||||
@@ -53,7 +53,7 @@ impl Vecs {
|
||||
|
||||
// True range at block level: |price[h] - price[h-1]|
|
||||
let mut prev_price = None;
|
||||
self.price_true_range.height.compute_transform(
|
||||
self.true_range.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
price,
|
||||
|(h, current, ..)| {
|
||||
@@ -73,21 +73,21 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
// 2w rolling sum of true range
|
||||
self.price_true_range_sum_2w.height.compute_rolling_sum(
|
||||
self.true_range_sum_2w.height.compute_rolling_sum(
|
||||
starting_indexes.height,
|
||||
&blocks.lookback.height_2w_ago,
|
||||
&self.price_true_range.height,
|
||||
&self.true_range.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.price_choppiness_index_2w
|
||||
self.choppiness_index_2w
|
||||
.bps
|
||||
.height
|
||||
.compute_transform4(
|
||||
starting_indexes.height,
|
||||
&self.price_true_range_sum_2w.height,
|
||||
&self.price_max_2w.cents.height,
|
||||
&self.price_min_2w.cents.height,
|
||||
&self.true_range_sum_2w.height,
|
||||
&self.max._2w.cents.height,
|
||||
&self.min._2w.cents.height,
|
||||
&blocks.lookback.height_2w_ago,
|
||||
|(h, tr_sum, max, min, window_start, ..)| {
|
||||
let range = f64::from(max) - f64::from(min);
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use super::{vecs::PriceMinMaxVecs, Vecs};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, PercentPerBlock, Price},
|
||||
@@ -17,27 +17,31 @@ impl Vecs {
|
||||
let v1 = Version::ONE;
|
||||
|
||||
Ok(Self {
|
||||
price_min_1w: Price::forced_import(db, "price_min_1w", version + v1, indexes)?,
|
||||
price_max_1w: Price::forced_import(db, "price_max_1w", version + v1, indexes)?,
|
||||
price_min_2w: Price::forced_import(db, "price_min_2w", version + v1, indexes)?,
|
||||
price_max_2w: Price::forced_import(db, "price_max_2w", version + v1, indexes)?,
|
||||
price_min_1m: Price::forced_import(db, "price_min_1m", version + v1, indexes)?,
|
||||
price_max_1m: Price::forced_import(db, "price_max_1m", version + v1, indexes)?,
|
||||
price_min_1y: Price::forced_import(db, "price_min_1y", version + v1, indexes)?,
|
||||
price_max_1y: Price::forced_import(db, "price_max_1y", version + v1, indexes)?,
|
||||
price_true_range: ComputedPerBlock::forced_import(
|
||||
min: PriceMinMaxVecs {
|
||||
_1w: Price::forced_import(db, "price_min_1w", version + v1, indexes)?,
|
||||
_2w: Price::forced_import(db, "price_min_2w", version + v1, indexes)?,
|
||||
_1m: Price::forced_import(db, "price_min_1m", version + v1, indexes)?,
|
||||
_1y: Price::forced_import(db, "price_min_1y", version + v1, indexes)?,
|
||||
},
|
||||
max: PriceMinMaxVecs {
|
||||
_1w: Price::forced_import(db, "price_max_1w", version + v1, indexes)?,
|
||||
_2w: Price::forced_import(db, "price_max_2w", version + v1, indexes)?,
|
||||
_1m: Price::forced_import(db, "price_max_1m", version + v1, indexes)?,
|
||||
_1y: Price::forced_import(db, "price_max_1y", version + v1, indexes)?,
|
||||
},
|
||||
true_range: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"price_true_range",
|
||||
version + v1,
|
||||
indexes,
|
||||
)?,
|
||||
price_true_range_sum_2w: ComputedPerBlock::forced_import(
|
||||
true_range_sum_2w: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"price_true_range_sum_2w",
|
||||
version + v1,
|
||||
indexes,
|
||||
)?,
|
||||
price_choppiness_index_2w: PercentPerBlock::forced_import(
|
||||
choppiness_index_2w: PercentPerBlock::forced_import(
|
||||
db,
|
||||
"price_choppiness_index_2w",
|
||||
version + v1,
|
||||
|
||||
@@ -3,17 +3,20 @@ use brk_types::{BasisPoints16, Cents, StoredF32};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, PercentPerBlock, Price};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct PriceMinMaxVecs<M: StorageMode = Rw> {
|
||||
pub _1w: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub _2w: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub _1m: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub _1y: Price<ComputedPerBlock<Cents, M>>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub price_min_1w: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_max_1w: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_min_2w: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_max_2w: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_min_1m: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_max_1m: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_min_1y: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_max_1y: Price<ComputedPerBlock<Cents, M>>,
|
||||
pub price_true_range: ComputedPerBlock<StoredF32, M>,
|
||||
pub price_true_range_sum_2w: ComputedPerBlock<StoredF32, M>,
|
||||
pub price_choppiness_index_2w: PercentPerBlock<BasisPoints16, M>,
|
||||
pub min: PriceMinMaxVecs<M>,
|
||||
pub max: PriceMinMaxVecs<M>,
|
||||
pub true_range: ComputedPerBlock<StoredF32, M>,
|
||||
pub true_range_sum_2w: ComputedPerBlock<StoredF32, M>,
|
||||
pub choppiness_index_2w: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ impl Vecs {
|
||||
let _24h_price_return_ratio = &self.price_return._24h.ratio.height;
|
||||
|
||||
for sd in [
|
||||
&mut self.price_return_24h_sd_1w,
|
||||
&mut self.price_return_24h_sd_1m,
|
||||
&mut self.price_return_24h_sd_1y,
|
||||
&mut self.price_return_24h_sd._1w,
|
||||
&mut self.price_return_24h_sd._1m,
|
||||
&mut self.price_return_24h_sd._1y,
|
||||
] {
|
||||
sd.compute_all(blocks, starting_indexes, exit, _24h_price_return_ratio)?;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::super::lookback::ByLookbackPeriod;
|
||||
use super::Vecs;
|
||||
use super::{vecs::PriceReturn24hSdVecs, Vecs};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{StdDevPerBlock, PercentPerBlock},
|
||||
@@ -27,37 +27,37 @@ impl Vecs {
|
||||
PercentPerBlock::forced_import(db, &format!("price_cagr_{name}"), version, indexes)
|
||||
})?;
|
||||
|
||||
let price_return_24h_sd_1w = StdDevPerBlock::forced_import(
|
||||
let price_return_24h_sd = PriceReturn24hSdVecs {
|
||||
_1w: StdDevPerBlock::forced_import(
|
||||
db,
|
||||
"price_return_24h",
|
||||
"1w",
|
||||
7,
|
||||
version + v1,
|
||||
indexes,
|
||||
)?;
|
||||
let price_return_24h_sd_1m = StdDevPerBlock::forced_import(
|
||||
)?,
|
||||
_1m: StdDevPerBlock::forced_import(
|
||||
db,
|
||||
"price_return_24h",
|
||||
"1m",
|
||||
30,
|
||||
version + v1,
|
||||
indexes,
|
||||
)?;
|
||||
let price_return_24h_sd_1y = StdDevPerBlock::forced_import(
|
||||
)?,
|
||||
_1y: StdDevPerBlock::forced_import(
|
||||
db,
|
||||
"price_return_24h",
|
||||
"1y",
|
||||
365,
|
||||
version + v1,
|
||||
indexes,
|
||||
)?;
|
||||
)?,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
price_return,
|
||||
price_cagr,
|
||||
price_return_24h_sd_1w,
|
||||
price_return_24h_sd_1m,
|
||||
price_return_24h_sd_1y,
|
||||
price_return_24h_sd,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ use brk_types::BasisPointsSigned32;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
internal::{StdDevPerBlock, PercentPerBlock},
|
||||
internal::{PercentPerBlock, StdDevPerBlock},
|
||||
market::{dca::ByDcaCagr, lookback::ByLookbackPeriod},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct PriceReturn24hSdVecs<M: StorageMode = Rw> {
|
||||
pub _1w: StdDevPerBlock<M>,
|
||||
pub _1m: StdDevPerBlock<M>,
|
||||
pub _1y: StdDevPerBlock<M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub price_return: ByLookbackPeriod<PercentPerBlock<BasisPointsSigned32, M>>,
|
||||
|
||||
// CAGR (computed from returns, 2y+ only)
|
||||
pub price_cagr: ByDcaCagr<PercentPerBlock<BasisPointsSigned32, M>>,
|
||||
|
||||
// Returns standard deviation (computed from 24h returns)
|
||||
pub price_return_24h_sd_1w: StdDevPerBlock<M>,
|
||||
pub price_return_24h_sd_1m: StdDevPerBlock<M>,
|
||||
pub price_return_24h_sd_1y: StdDevPerBlock<M>,
|
||||
|
||||
pub price_return_24h_sd: PriceReturn24hSdVecs<M>,
|
||||
}
|
||||
|
||||
@@ -10,43 +10,42 @@ impl Vecs {
|
||||
pub(crate) fn forced_import(version: Version, returns: &returns::Vecs) -> Result<Self> {
|
||||
let v2 = Version::TWO;
|
||||
|
||||
let price_volatility_1w = LazyPerBlock::from_computed::<TimesSqrt<Days7>>(
|
||||
let _1w = LazyPerBlock::from_computed::<TimesSqrt<Days7>>(
|
||||
"price_volatility_1w",
|
||||
version + v2,
|
||||
returns
|
||||
.price_return_24h_sd_1w
|
||||
.price_return_24h_sd
|
||||
._1w
|
||||
.sd
|
||||
.height
|
||||
.read_only_boxed_clone(),
|
||||
&returns.price_return_24h_sd_1w.sd,
|
||||
&returns.price_return_24h_sd._1w.sd,
|
||||
);
|
||||
|
||||
let price_volatility_1m = LazyPerBlock::from_computed::<TimesSqrt<Days30>>(
|
||||
let _1m = LazyPerBlock::from_computed::<TimesSqrt<Days30>>(
|
||||
"price_volatility_1m",
|
||||
version + v2,
|
||||
returns
|
||||
.price_return_24h_sd_1m
|
||||
.price_return_24h_sd
|
||||
._1m
|
||||
.sd
|
||||
.height
|
||||
.read_only_boxed_clone(),
|
||||
&returns.price_return_24h_sd_1m.sd,
|
||||
&returns.price_return_24h_sd._1m.sd,
|
||||
);
|
||||
|
||||
let price_volatility_1y = LazyPerBlock::from_computed::<TimesSqrt<Days365>>(
|
||||
let _1y = LazyPerBlock::from_computed::<TimesSqrt<Days365>>(
|
||||
"price_volatility_1y",
|
||||
version + v2,
|
||||
returns
|
||||
.price_return_24h_sd_1y
|
||||
.price_return_24h_sd
|
||||
._1y
|
||||
.sd
|
||||
.height
|
||||
.read_only_boxed_clone(),
|
||||
&returns.price_return_24h_sd_1y.sd,
|
||||
&returns.price_return_24h_sd._1y.sd,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
price_volatility_1w,
|
||||
price_volatility_1m,
|
||||
price_volatility_1y,
|
||||
})
|
||||
Ok(Self { _1w, _1m, _1y })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::internal::LazyPerBlock;
|
||||
use brk_types::StoredF32;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub price_volatility_1w: LazyPerBlock<StoredF32>,
|
||||
pub price_volatility_1m: LazyPerBlock<StoredF32>,
|
||||
pub price_volatility_1y: LazyPerBlock<StoredF32>,
|
||||
pub _1w: LazyPerBlock<StoredF32>,
|
||||
pub _1m: LazyPerBlock<StoredF32>,
|
||||
pub _1y: LazyPerBlock<StoredF32>,
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ impl Vecs {
|
||||
|
||||
let hash_rate = &self.hash_rate.height;
|
||||
for (sma, window) in [
|
||||
(&mut self.hash_rate_sma_1w.height, &lookback.height_1w_ago),
|
||||
(&mut self.hash_rate_sma_1m.height, &lookback.height_1m_ago),
|
||||
(&mut self.hash_rate_sma_2m.height, &lookback.height_2m_ago),
|
||||
(&mut self.hash_rate_sma_1y.height, &lookback.height_1y_ago),
|
||||
(&mut self.hash_rate_sma._1w.height, &lookback.height_1w_ago),
|
||||
(&mut self.hash_rate_sma._1m.height, &lookback.height_1m_ago),
|
||||
(&mut self.hash_rate_sma._2m.height, &lookback.height_2m_ago),
|
||||
(&mut self.hash_rate_sma._1y.height, &lookback.height_1y_ago),
|
||||
] {
|
||||
sma.compute_rolling_average(starting_indexes.height, window, hash_rate, exit)?;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.hash_price_ths.height.compute_transform2(
|
||||
self.hash_price.ths.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
coinbase_usd_24h_sum,
|
||||
&self.hash_rate.height,
|
||||
@@ -75,14 +75,14 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.hash_price_phs.height.compute_transform(
|
||||
self.hash_price.phs.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.hash_price_ths.height,
|
||||
&self.hash_price.ths.height,
|
||||
|(i, price, ..)| (i, (*price * 1000.0).into()),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.hash_value_ths.height.compute_transform2(
|
||||
self.hash_value.ths.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
coinbase_sats_24h_sum,
|
||||
&self.hash_rate.height,
|
||||
@@ -98,47 +98,49 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.hash_value_phs.height.compute_transform(
|
||||
self.hash_value.phs.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.hash_value_ths.height,
|
||||
&self.hash_value.ths.height,
|
||||
|(i, value, ..)| (i, (*value * 1000.0).into()),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
for (min_vec, src_vec) in [
|
||||
(
|
||||
&mut self.hash_price_ths_min.height,
|
||||
&self.hash_price_ths.height,
|
||||
&mut self.hash_price.ths_min.height,
|
||||
&self.hash_price.ths.height,
|
||||
),
|
||||
(
|
||||
&mut self.hash_price_phs_min.height,
|
||||
&self.hash_price_phs.height,
|
||||
&mut self.hash_price.phs_min.height,
|
||||
&self.hash_price.phs.height,
|
||||
),
|
||||
(
|
||||
&mut self.hash_value_ths_min.height,
|
||||
&self.hash_value_ths.height,
|
||||
&mut self.hash_value.ths_min.height,
|
||||
&self.hash_value.ths.height,
|
||||
),
|
||||
(
|
||||
&mut self.hash_value_phs_min.height,
|
||||
&self.hash_value_phs.height,
|
||||
&mut self.hash_value.phs_min.height,
|
||||
&self.hash_value.phs.height,
|
||||
),
|
||||
] {
|
||||
min_vec.compute_all_time_low_(starting_indexes.height, src_vec, exit, true)?;
|
||||
}
|
||||
|
||||
self.hash_price_rebound
|
||||
self.hash_price
|
||||
.rebound
|
||||
.compute_binary::<StoredF32, StoredF32, RatioDiffF32Bps32>(
|
||||
starting_indexes.height,
|
||||
&self.hash_price_phs.height,
|
||||
&self.hash_price_phs_min.height,
|
||||
&self.hash_price.phs.height,
|
||||
&self.hash_price.phs_min.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.hash_value_rebound
|
||||
self.hash_value
|
||||
.rebound
|
||||
.compute_binary::<StoredF32, StoredF32, RatioDiffF32Bps32>(
|
||||
starting_indexes.height,
|
||||
&self.hash_value_phs.height,
|
||||
&self.hash_value_phs_min.height,
|
||||
&self.hash_value.phs.height,
|
||||
&self.hash_value.phs_min.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
use vecdb::Database;
|
||||
|
||||
use super::Vecs;
|
||||
use super::{
|
||||
vecs::{HashPriceValueVecs, HashRateSmaVecs},
|
||||
Vecs,
|
||||
};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedPerBlock, PercentPerBlock},
|
||||
@@ -19,30 +22,32 @@ impl Vecs {
|
||||
|
||||
Ok(Self {
|
||||
hash_rate: ComputedPerBlock::forced_import(db, "hash_rate", version + v5, indexes)?,
|
||||
hash_rate_sma_1w: ComputedPerBlock::forced_import(
|
||||
hash_rate_sma: HashRateSmaVecs {
|
||||
_1w: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_rate_sma_1w",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_rate_sma_1m: ComputedPerBlock::forced_import(
|
||||
_1m: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_rate_sma_1m",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_rate_sma_2m: ComputedPerBlock::forced_import(
|
||||
_2m: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_rate_sma_2m",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_rate_sma_1y: ComputedPerBlock::forced_import(
|
||||
_1y: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_rate_sma_1y",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
},
|
||||
hash_rate_ath: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_rate_ath",
|
||||
@@ -55,66 +60,70 @@ impl Vecs {
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
hash_price_ths: ComputedPerBlock::forced_import(
|
||||
hash_price: HashPriceValueVecs {
|
||||
ths: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_price_ths",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_price_ths_min: ComputedPerBlock::forced_import(
|
||||
ths_min: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_price_ths_min",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_price_phs: ComputedPerBlock::forced_import(
|
||||
phs: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_price_phs",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_price_phs_min: ComputedPerBlock::forced_import(
|
||||
phs_min: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_price_phs_min",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_price_rebound: PercentPerBlock::forced_import(
|
||||
rebound: PercentPerBlock::forced_import(
|
||||
db,
|
||||
"hash_price_rebound",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_value_ths: ComputedPerBlock::forced_import(
|
||||
},
|
||||
hash_value: HashPriceValueVecs {
|
||||
ths: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_value_ths",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_value_ths_min: ComputedPerBlock::forced_import(
|
||||
ths_min: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_value_ths_min",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_value_phs: ComputedPerBlock::forced_import(
|
||||
phs: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_value_phs",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_value_phs_min: ComputedPerBlock::forced_import(
|
||||
phs_min: ComputedPerBlock::forced_import(
|
||||
db,
|
||||
"hash_value_phs_min",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
hash_value_rebound: PercentPerBlock::forced_import(
|
||||
rebound: PercentPerBlock::forced_import(
|
||||
db,
|
||||
"hash_value_rebound",
|
||||
version + v4,
|
||||
indexes,
|
||||
)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,29 @@ use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedPerBlock, PercentPerBlock};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct HashRateSmaVecs<M: StorageMode = Rw> {
|
||||
pub _1w: ComputedPerBlock<StoredF64, M>,
|
||||
pub _1m: ComputedPerBlock<StoredF64, M>,
|
||||
pub _2m: ComputedPerBlock<StoredF64, M>,
|
||||
pub _1y: ComputedPerBlock<StoredF64, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct HashPriceValueVecs<M: StorageMode = Rw> {
|
||||
pub ths: ComputedPerBlock<StoredF32, M>,
|
||||
pub ths_min: ComputedPerBlock<StoredF32, M>,
|
||||
pub phs: ComputedPerBlock<StoredF32, M>,
|
||||
pub phs_min: ComputedPerBlock<StoredF32, M>,
|
||||
pub rebound: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub hash_rate: ComputedPerBlock<StoredF64, M>,
|
||||
pub hash_rate_sma_1w: ComputedPerBlock<StoredF64, M>,
|
||||
pub hash_rate_sma_1m: ComputedPerBlock<StoredF64, M>,
|
||||
pub hash_rate_sma_2m: ComputedPerBlock<StoredF64, M>,
|
||||
pub hash_rate_sma_1y: ComputedPerBlock<StoredF64, M>,
|
||||
pub hash_rate_sma: HashRateSmaVecs<M>,
|
||||
pub hash_rate_ath: ComputedPerBlock<StoredF64, M>,
|
||||
pub hash_rate_drawdown: PercentPerBlock<BasisPointsSigned16, M>,
|
||||
pub hash_price_ths: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_price_ths_min: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_price_phs: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_price_phs_min: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_price_rebound: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub hash_value_ths: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_value_ths_min: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_value_phs: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_value_phs_min: ComputedPerBlock<StoredF32, M>,
|
||||
pub hash_value_rebound: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub hash_price: HashPriceValueVecs<M>,
|
||||
pub hash_value: HashPriceValueVecs<M>,
|
||||
}
|
||||
|
||||
@@ -61,10 +61,10 @@ impl Vecs {
|
||||
|
||||
// 24h, 1w, 1y from extended; 1m from core delta
|
||||
let rcr_rates = [
|
||||
&all_realized.cap_delta_extended.rate_24h.bps.height,
|
||||
&all_realized.cap_delta_extended.rate_1w.bps.height,
|
||||
&all_realized.cap_delta_extended.rate._24h.bps.height,
|
||||
&all_realized.cap_delta_extended.rate._1w.bps.height,
|
||||
&all_realized.cap_delta.rate_1m.bps.height,
|
||||
&all_realized.cap_delta_extended.rate_1y.bps.height,
|
||||
&all_realized.cap_delta_extended.rate._1y.bps.height,
|
||||
];
|
||||
|
||||
for i in 0..4 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use std::{io, path::PathBuf, result, time};
|
||||
use std::{fmt, io, path::PathBuf, result, time};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -127,13 +127,10 @@ pub enum Error {
|
||||
AuthFailed,
|
||||
|
||||
// Metric-specific errors
|
||||
#[error("'{metric}' not found{}", suggestion.as_ref().map(|s| format!(", did you mean '{s}'?")).unwrap_or_default())]
|
||||
MetricNotFound {
|
||||
metric: String,
|
||||
suggestion: Option<String>,
|
||||
},
|
||||
#[error("{0}")]
|
||||
MetricNotFound(MetricNotFound),
|
||||
|
||||
#[error("'{metric}' doesn't support the requested index. Supported indexes: {supported}")]
|
||||
#[error("'{metric}' doesn't support the requested index. Try: {supported}")]
|
||||
MetricUnsupportedIndex { metric: String, supported: String },
|
||||
|
||||
#[error("No metrics specified")]
|
||||
@@ -226,3 +223,46 @@ fn is_io_error_permanent(e: &std::io::Error) -> bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetricNotFound {
|
||||
pub metric: String,
|
||||
pub suggestions: Vec<String>,
|
||||
pub total_matches: usize,
|
||||
}
|
||||
|
||||
impl MetricNotFound {
|
||||
pub fn new(metric: String, all_matches: Vec<String>) -> Self {
|
||||
let total_matches = all_matches.len();
|
||||
let suggestions = all_matches.into_iter().take(3).collect();
|
||||
Self {
|
||||
metric,
|
||||
suggestions,
|
||||
total_matches,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for MetricNotFound {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "'{}' not found", self.metric)?;
|
||||
|
||||
if self.suggestions.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let quoted: Vec<_> = self.suggestions.iter().map(|s| format!("'{s}'")).collect();
|
||||
write!(f, ", did you mean {}?", quoted.join(", "))?;
|
||||
|
||||
let remaining = self.total_matches.saturating_sub(self.suggestions.len());
|
||||
if remaining > 0 {
|
||||
write!(
|
||||
f,
|
||||
" ({remaining} more — /api/metrics/search/{} for all)",
|
||||
self.metric
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ impl Query {
|
||||
}
|
||||
|
||||
pub fn blocks(&self, start_height: Option<Height>) -> Result<Vec<BlockInfo>> {
|
||||
let max_height = self.height();
|
||||
let max_height = self.indexed_height();
|
||||
|
||||
let start = start_height.unwrap_or(max_height);
|
||||
let start = start.min(max_height);
|
||||
|
||||
@@ -10,7 +10,7 @@ impl Query {
|
||||
let indexer = self.indexer();
|
||||
let computer = self.computer();
|
||||
|
||||
let max_height = self.height();
|
||||
let max_height = self.indexed_height();
|
||||
let max_height_usize: usize = max_height.into();
|
||||
|
||||
if max_height_usize == 0 {
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Query {
|
||||
fn block_txids_by_height(&self, height: Height) -> Result<Vec<Txid>> {
|
||||
let indexer = self.indexer();
|
||||
|
||||
let max_height = self.height();
|
||||
let max_height = self.indexed_height();
|
||||
if height > max_height {
|
||||
return Err(Error::OutOfRange("Block height out of range".into()));
|
||||
}
|
||||
@@ -55,7 +55,7 @@ impl Query {
|
||||
fn block_txs_by_height(&self, height: Height, start_index: usize) -> Result<Vec<Transaction>> {
|
||||
let indexer = self.indexer();
|
||||
|
||||
let max_height = self.height();
|
||||
let max_height = self.indexed_height();
|
||||
if height > max_height {
|
||||
return Err(Error::OutOfRange("Block height out of range".into()));
|
||||
}
|
||||
@@ -97,7 +97,7 @@ impl Query {
|
||||
fn block_txid_at_index_by_height(&self, height: Height, index: usize) -> Result<Txid> {
|
||||
let indexer = self.indexer();
|
||||
|
||||
let max_height = self.height();
|
||||
let max_height = self.indexed_height();
|
||||
if height > max_height {
|
||||
return Err(Error::OutOfRange("Block height out of range".into()));
|
||||
}
|
||||
|
||||
@@ -37,7 +37,10 @@ impl Query {
|
||||
.join(format!("utxo_{cohort}_cost_basis/by_date"));
|
||||
|
||||
if !dir.exists() {
|
||||
return Err(Error::NotFound(format!("Unknown cohort '{cohort}'")));
|
||||
let valid = self.cost_basis_cohorts().unwrap_or_default().join(", ");
|
||||
return Err(Error::NotFound(format!(
|
||||
"Unknown cohort '{cohort}'. Available: {valid}"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(dir)
|
||||
|
||||
@@ -27,21 +27,24 @@ impl Query {
|
||||
pub fn metric_not_found_error(&self, metric: &Metric) -> Error {
|
||||
// Check if metric exists but with different indexes
|
||||
if let Some(indexes) = self.vecs().metric_to_indexes(metric.clone()) {
|
||||
let index_list: Vec<_> = indexes.iter().map(|i| i.to_string()).collect();
|
||||
let supported = indexes
|
||||
.iter()
|
||||
.map(|i| format!("/api/metric/{metric}/{i}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
return Error::MetricUnsupportedIndex {
|
||||
metric: metric.to_string(),
|
||||
supported: index_list.join(", "),
|
||||
supported,
|
||||
};
|
||||
}
|
||||
|
||||
// Metric doesn't exist, suggest alternatives
|
||||
Error::MetricNotFound {
|
||||
metric: metric.to_string(),
|
||||
suggestion: self
|
||||
.match_metric(metric, Limit::MIN)
|
||||
.first()
|
||||
.map(|s| s.to_string()),
|
||||
}
|
||||
let matches = self
|
||||
.match_metric(metric, Limit::DEFAULT)
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
Error::MetricNotFound(brk_error::MetricNotFound::new(metric.to_string(), matches))
|
||||
}
|
||||
|
||||
pub(crate) fn columns_to_csv(
|
||||
@@ -334,4 +337,9 @@ impl ResolvedQuery {
|
||||
pub fn format(&self) -> Format {
|
||||
self.format
|
||||
}
|
||||
|
||||
pub fn csv_filename(&self) -> String {
|
||||
let names: Vec<_> = self.vecs.iter().map(|v| v.name()).collect();
|
||||
format!("{}-{}.csv", names.join("_"), self.index)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user