mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-29 19:58:10 -07:00
computer: snapshot
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{IterableCloneableVec, LazyVecFrom1, UnaryTransform, VecIndex};
|
||||
|
||||
use super::{ComputedVecValue, EagerVecsBuilder, LazyVecsBuilder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy transform version of `EagerVecsBuilder`.
|
||||
/// Each group is a `LazyVecFrom1` that transforms from the corresponding stored group.
|
||||
/// S1T is the source type, T is the output type (can be the same for transforms like negation).
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyTransformBuilder<I, T, S1T = T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub first: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub average: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub sum: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub max: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub pct90: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub pct75: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub median: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub pct25: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub pct10: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub min: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub last: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
pub cumulative: Option<Box<LazyVecFrom1<I, T, I, S1T>>>,
|
||||
}
|
||||
|
||||
impl<I, T, S1T> LazyTransformBuilder<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy transform from a stored `EagerVecsBuilder`.
|
||||
/// F is the transform type (e.g., `Negate`, `Halve`).
|
||||
pub fn from_eager<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &EagerVecsBuilder<I, S1T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
Self {
|
||||
first: source.first.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("first"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
average: source.average.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("avg"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
sum: source.sum.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("sum"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
max: source.max.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("max"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
pct90: source.pct90.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("pct90"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
pct75: source.pct75.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("pct75"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
median: source.median.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("median"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
pct25: source.pct25.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("pct25"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
pct10: source.pct10.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("pct10"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
min: source.min.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("min"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
last: source
|
||||
.last
|
||||
.as_ref()
|
||||
.map(|s| Box::new(LazyVecFrom1::transformed::<F>(name, v, s.boxed_clone()))),
|
||||
cumulative: source.cumulative.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("cumulative"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1T> LazyTransformBuilder<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy transform from a `LazyVecsBuilder`.
|
||||
/// Note: LazyVecsBuilder doesn't have percentiles, so those will be None.
|
||||
pub fn from_lazy<F: UnaryTransform<S1T, T>, S1I: VecIndex, S2T: ComputedVecValue>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &LazyVecsBuilder<I, S1T, S1I, S2T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
// Use same suffix pattern as EagerVecsBuilder
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
Self {
|
||||
first: source.first.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("first"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
average: source.average.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("avg"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
sum: source.sum.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("sum"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
max: source.max.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("max"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
pct90: None,
|
||||
pct75: None,
|
||||
median: None,
|
||||
pct25: None,
|
||||
pct10: None,
|
||||
min: source.min.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("min"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
last: source
|
||||
.last
|
||||
.as_ref()
|
||||
.map(|s| Box::new(LazyVecFrom1::transformed::<F>(name, v, s.boxed_clone()))),
|
||||
cumulative: source.cumulative.as_ref().map(|s| {
|
||||
Box::new(LazyVecFrom1::transformed::<F>(
|
||||
&suffix("cumulative"),
|
||||
v,
|
||||
s.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, IterableCloneableVec, LazyVecFrom2, VecIndex};
|
||||
|
||||
use super::{ComputedVecValue, EagerVecsBuilder, LazyVecsBuilder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy binary transform builder.
|
||||
/// Each group is a `LazyVecFrom2` that transforms from two corresponding stored groups.
|
||||
#[derive(Clone, Traversable)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct LazyTransform2Builder<I, T, S1T, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
pub first: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
pub average: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
pub sum: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
pub max: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
pub min: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
pub last: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
pub cumulative: Option<Box<LazyVecFrom2<I, T, I, S1T, I, S2T>>>,
|
||||
}
|
||||
|
||||
impl<I, T, S1T, S2T> LazyTransform2Builder<I, T, S1T, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy binary transform from two stored `EagerVecsBuilder`.
|
||||
pub fn from_eager<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &EagerVecsBuilder<I, S1T>,
|
||||
source2: &EagerVecsBuilder<I, S2T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
Self {
|
||||
first: source1
|
||||
.first
|
||||
.as_ref()
|
||||
.zip(source2.first.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("first"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
average: source1
|
||||
.average
|
||||
.as_ref()
|
||||
.zip(source2.average.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("avg"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
sum: source1
|
||||
.sum
|
||||
.as_ref()
|
||||
.zip(source2.sum.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("sum"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
max: source1
|
||||
.max
|
||||
.as_ref()
|
||||
.zip(source2.max.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("max"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
min: source1
|
||||
.min
|
||||
.as_ref()
|
||||
.zip(source2.min.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("min"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
last: source1
|
||||
.last
|
||||
.as_ref()
|
||||
.zip(source2.last.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
cumulative: source1
|
||||
.cumulative
|
||||
.as_ref()
|
||||
.zip(source2.cumulative.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("cumulative"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a lazy binary transform from two `LazyVecsBuilder`.
|
||||
pub fn from_lazy<
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1I: VecIndex,
|
||||
S1E: ComputedVecValue,
|
||||
S2I: VecIndex,
|
||||
S2E: ComputedVecValue,
|
||||
>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyVecsBuilder<I, S1T, S1I, S1E>,
|
||||
source2: &LazyVecsBuilder<I, S2T, S2I, S2E>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
let suffix = |s: &str| format!("{name}_{s}");
|
||||
Self {
|
||||
first: source1
|
||||
.first
|
||||
.as_ref()
|
||||
.zip(source2.first.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("first"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
average: source1
|
||||
.average
|
||||
.as_ref()
|
||||
.zip(source2.average.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("avg"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
sum: source1
|
||||
.sum
|
||||
.as_ref()
|
||||
.zip(source2.sum.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("sum"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
max: source1
|
||||
.max
|
||||
.as_ref()
|
||||
.zip(source2.max.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("max"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
min: source1
|
||||
.min
|
||||
.as_ref()
|
||||
.zip(source2.min.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("min"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
last: source1
|
||||
.last
|
||||
.as_ref()
|
||||
.zip(source2.last.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
cumulative: source1
|
||||
.cumulative
|
||||
.as_ref()
|
||||
.zip(source2.cumulative.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
Box::new(LazyVecFrom2::transformed::<F>(
|
||||
&suffix("cumulative"),
|
||||
v,
|
||||
s1.boxed_clone(),
|
||||
s2.boxed_clone(),
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex, YearIndex,
|
||||
@@ -0,0 +1,133 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyExportableVec, BinaryTransform, IterableCloneableVec, LazyVecFrom2};
|
||||
|
||||
use super::{ComputedVecValue, ComputedVecsFromDateIndex, LazyTransform2Builder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy binary transform from two `ComputedVecsFromDateIndex` sources.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecsFrom2FromDateIndex<T, S1T, S2T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
pub dateindex: Option<LazyVecFrom2<DateIndex, T, DateIndex, S1T, DateIndex, S2T>>,
|
||||
pub weekindex: LazyTransform2Builder<WeekIndex, T, S1T, S2T>,
|
||||
pub monthindex: LazyTransform2Builder<MonthIndex, T, S1T, S2T>,
|
||||
pub quarterindex: LazyTransform2Builder<QuarterIndex, T, S1T, S2T>,
|
||||
pub semesterindex: LazyTransform2Builder<SemesterIndex, T, S1T, S2T>,
|
||||
pub yearindex: LazyTransform2Builder<YearIndex, T, S1T, S2T>,
|
||||
pub decadeindex: LazyTransform2Builder<DecadeIndex, T, S1T, S2T>,
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> LazyVecsFrom2FromDateIndex<T, S1T, S2T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create from two `ComputedVecsFromDateIndex` sources.
|
||||
pub fn from_computed<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedVecsFromDateIndex<S1T>,
|
||||
source2: &ComputedVecsFromDateIndex<S2T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
dateindex: source1
|
||||
.dateindex
|
||||
.as_ref()
|
||||
.zip(source2.dateindex.as_ref())
|
||||
.map(|(s1, s2)| {
|
||||
LazyVecFrom2::transformed::<F>(name, v, s1.boxed_clone(), s2.boxed_clone())
|
||||
}),
|
||||
weekindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.weekindex,
|
||||
&source2.weekindex,
|
||||
),
|
||||
monthindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.monthindex,
|
||||
&source2.monthindex,
|
||||
),
|
||||
quarterindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.quarterindex,
|
||||
&source2.quarterindex,
|
||||
),
|
||||
semesterindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.semesterindex,
|
||||
&source2.semesterindex,
|
||||
),
|
||||
yearindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.yearindex,
|
||||
&source2.yearindex,
|
||||
),
|
||||
decadeindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.decadeindex,
|
||||
&source2.decadeindex,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> Traversable for LazyVecsFrom2FromDateIndex<T, S1T, S2T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
brk_traversable::TreeNode::Branch(
|
||||
[
|
||||
self.dateindex
|
||||
.as_ref()
|
||||
.map(|v| ("dateindex".to_string(), v.to_tree_node())),
|
||||
Some(("weekindex".to_string(), self.weekindex.to_tree_node())),
|
||||
Some(("monthindex".to_string(), self.monthindex.to_tree_node())),
|
||||
Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())),
|
||||
Some(("semesterindex".to_string(), self.semesterindex.to_tree_node())),
|
||||
Some(("yearindex".to_string(), self.yearindex.to_tree_node())),
|
||||
Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
.merge_branches()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
let mut iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
|
||||
Box::new(std::iter::empty());
|
||||
if let Some(ref v) = self.dateindex {
|
||||
iter = Box::new(iter.chain(v.iter_any_exportable()));
|
||||
}
|
||||
iter = Box::new(iter.chain(self.weekindex.iter_any_exportable()));
|
||||
iter = Box::new(iter.chain(self.monthindex.iter_any_exportable()));
|
||||
iter = Box::new(iter.chain(self.quarterindex.iter_any_exportable()));
|
||||
iter = Box::new(iter.chain(self.semesterindex.iter_any_exportable()));
|
||||
iter = Box::new(iter.chain(self.yearindex.iter_any_exportable()));
|
||||
iter = Box::new(iter.chain(self.decadeindex.iter_any_exportable()));
|
||||
iter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyExportableVec, IterableBoxedVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use super::{ComputedVecValue, ComputedVecsFromDateIndex, LazyTransformBuilder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Fully lazy version of `ComputedVecsFromDateIndex` where all vecs are lazy transforms.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecsFromDateIndex<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub dateindex: Option<LazyVecFrom1<DateIndex, T, DateIndex, S1T>>,
|
||||
pub weekindex: LazyTransformBuilder<WeekIndex, T, S1T>,
|
||||
pub monthindex: LazyTransformBuilder<MonthIndex, T, S1T>,
|
||||
pub quarterindex: LazyTransformBuilder<QuarterIndex, T, S1T>,
|
||||
pub semesterindex: LazyTransformBuilder<SemesterIndex, T, S1T>,
|
||||
pub yearindex: LazyTransformBuilder<YearIndex, T, S1T>,
|
||||
pub decadeindex: LazyTransformBuilder<DecadeIndex, T, S1T>,
|
||||
}
|
||||
|
||||
impl<T, S1T> LazyVecsFromDateIndex<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy transform from a stored `ComputedVecsFromDateIndex`.
|
||||
/// F is the transform type (e.g., `Negate`, `Halve`).
|
||||
pub fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
dateindex_source: Option<IterableBoxedVec<DateIndex, S1T>>,
|
||||
source: &ComputedVecsFromDateIndex<S1T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
dateindex: dateindex_source.map(|s| LazyVecFrom1::transformed::<F>(name, v, s)),
|
||||
weekindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.weekindex),
|
||||
monthindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.monthindex),
|
||||
quarterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.quarterindex),
|
||||
semesterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.semesterindex),
|
||||
yearindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.yearindex),
|
||||
decadeindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.decadeindex),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1T> Traversable for LazyVecsFromDateIndex<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
brk_traversable::TreeNode::Branch(
|
||||
[
|
||||
self.dateindex
|
||||
.as_ref()
|
||||
.map(|v| ("dateindex".to_string(), v.to_tree_node())),
|
||||
Some(("weekindex".to_string(), self.weekindex.to_tree_node())),
|
||||
Some(("monthindex".to_string(), self.monthindex.to_tree_node())),
|
||||
Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())),
|
||||
Some((
|
||||
"semesterindex".to_string(),
|
||||
self.semesterindex.to_tree_node(),
|
||||
)),
|
||||
Some(("yearindex".to_string(), self.yearindex.to_tree_node())),
|
||||
Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
.merge_branches()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
|
||||
Box::new(std::iter::empty());
|
||||
if let Some(ref dateindex) = self.dateindex {
|
||||
regular_iter = Box::new(regular_iter.chain(dateindex.iter_any_exportable()));
|
||||
}
|
||||
regular_iter = Box::new(regular_iter.chain(self.weekindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.monthindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.quarterindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.semesterindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.yearindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.decadeindex.iter_any_exportable()));
|
||||
regular_iter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
|
||||
Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyExportableVec, IterableBoxedVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use super::{ComputedVecValue, ComputedVecsFromHeight, LazyTransformBuilder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Fully lazy version of `ComputedVecsFromHeight` where all vecs are lazy transforms.
|
||||
/// Each index uses `LazyTransformBuilder` sourced from its corresponding stored groups.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecsFromHeight<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub height: LazyVecFrom1<Height, T, Height, S1T>,
|
||||
pub dateindex: LazyTransformBuilder<DateIndex, T, S1T>,
|
||||
pub weekindex: LazyTransformBuilder<WeekIndex, T, S1T>,
|
||||
pub difficultyepoch: LazyTransformBuilder<DifficultyEpoch, T, S1T>,
|
||||
pub monthindex: LazyTransformBuilder<MonthIndex, T, S1T>,
|
||||
pub quarterindex: LazyTransformBuilder<QuarterIndex, T, S1T>,
|
||||
pub semesterindex: LazyTransformBuilder<SemesterIndex, T, S1T>,
|
||||
pub yearindex: LazyTransformBuilder<YearIndex, T, S1T>,
|
||||
pub decadeindex: LazyTransformBuilder<DecadeIndex, T, S1T>,
|
||||
}
|
||||
|
||||
impl<T, S1T> LazyVecsFromHeight<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy transform from a stored `ComputedVecsFromHeight`.
|
||||
/// F is the transform type (e.g., `Negate`, `Halve`).
|
||||
pub fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: IterableBoxedVec<Height, S1T>,
|
||||
source: &ComputedVecsFromHeight<S1T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source),
|
||||
dateindex: LazyTransformBuilder::from_eager::<F>(name, v, &source.dateindex),
|
||||
weekindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.weekindex),
|
||||
difficultyepoch: LazyTransformBuilder::from_eager::<F>(name, v, &source.difficultyepoch),
|
||||
monthindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.monthindex),
|
||||
quarterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.quarterindex),
|
||||
semesterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.semesterindex),
|
||||
yearindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.yearindex),
|
||||
decadeindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.decadeindex),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1T> Traversable for LazyVecsFromHeight<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
brk_traversable::TreeNode::Branch(
|
||||
[
|
||||
Some(("height".to_string(), self.height.to_tree_node())),
|
||||
Some(("dateindex".to_string(), self.dateindex.to_tree_node())),
|
||||
Some(("weekindex".to_string(), self.weekindex.to_tree_node())),
|
||||
Some((
|
||||
"difficultyepoch".to_string(),
|
||||
self.difficultyepoch.to_tree_node(),
|
||||
)),
|
||||
Some(("monthindex".to_string(), self.monthindex.to_tree_node())),
|
||||
Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())),
|
||||
Some((
|
||||
"semesterindex".to_string(),
|
||||
self.semesterindex.to_tree_node(),
|
||||
)),
|
||||
Some(("yearindex".to_string(), self.yearindex.to_tree_node())),
|
||||
Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
.merge_branches()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
|
||||
Box::new(self.height.iter_any_exportable());
|
||||
regular_iter = Box::new(regular_iter.chain(self.dateindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.weekindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.difficultyepoch.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.monthindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.quarterindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.semesterindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.yearindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.decadeindex.iter_any_exportable()));
|
||||
regular_iter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DifficultyEpoch, Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyExportableVec, IterableBoxedVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use super::{ComputedVecValue, ComputedVecsFromHeightStrict, LazyTransformBuilder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Fully lazy version of `ComputedVecsFromHeightStrict` where all vecs are lazy transforms.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecsFromHeightStrict<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub height: LazyVecFrom1<Height, T, Height, S1T>,
|
||||
pub difficultyepoch: LazyTransformBuilder<DifficultyEpoch, T, S1T>,
|
||||
}
|
||||
|
||||
impl<T, S1T> LazyVecsFromHeightStrict<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy transform from a stored `ComputedVecsFromHeightStrict`.
|
||||
/// F is the transform type (e.g., `Negate`, `Halve`).
|
||||
pub fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: IterableBoxedVec<Height, S1T>,
|
||||
source: &ComputedVecsFromHeightStrict<S1T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source),
|
||||
difficultyepoch: LazyTransformBuilder::from_eager::<F>(name, v, &source.difficultyepoch),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1T> Traversable for LazyVecsFromHeightStrict<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
brk_traversable::TreeNode::Branch(
|
||||
[
|
||||
Some(("height".to_string(), self.height.to_tree_node())),
|
||||
Some((
|
||||
"difficultyepoch".to_string(),
|
||||
self.difficultyepoch.to_tree_node(),
|
||||
)),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
.merge_branches()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
|
||||
Box::new(self.height.iter_any_exportable());
|
||||
regular_iter = Box::new(regular_iter.chain(self.difficultyepoch.iter_any_exportable()));
|
||||
regular_iter
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
|
||||
TxIndex, Version, WeekIndex, YearIndex,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{AnyExportableVec, IterableBoxedVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use super::{ComputedVecValue, ComputedVecsFromTxindex, LazyTransformBuilder};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Fully lazy version of `ComputedVecsFromTxindex` where all vecs are lazy transforms.
|
||||
#[derive(Clone)]
|
||||
pub struct LazyVecsFromTxindex<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub txindex: Option<LazyVecFrom1<TxIndex, T, TxIndex, S1T>>,
|
||||
pub height: LazyTransformBuilder<Height, T, S1T>,
|
||||
pub dateindex: LazyTransformBuilder<DateIndex, T, S1T>,
|
||||
pub weekindex: LazyTransformBuilder<WeekIndex, T, S1T>,
|
||||
pub difficultyepoch: LazyTransformBuilder<DifficultyEpoch, T, S1T>,
|
||||
pub monthindex: LazyTransformBuilder<MonthIndex, T, S1T>,
|
||||
pub quarterindex: LazyTransformBuilder<QuarterIndex, T, S1T>,
|
||||
pub semesterindex: LazyTransformBuilder<SemesterIndex, T, S1T>,
|
||||
pub yearindex: LazyTransformBuilder<YearIndex, T, S1T>,
|
||||
pub decadeindex: LazyTransformBuilder<DecadeIndex, T, S1T>,
|
||||
}
|
||||
|
||||
impl<T, S1T> LazyVecsFromTxindex<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
/// Create a lazy transform from a stored `ComputedVecsFromTxindex`.
|
||||
/// F is the transform type (e.g., `Negate`, `Halve`).
|
||||
pub fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
txindex_source: Option<IterableBoxedVec<TxIndex, S1T>>,
|
||||
source: &ComputedVecsFromTxindex<S1T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
txindex: txindex_source.map(|s| LazyVecFrom1::transformed::<F>(name, v, s)),
|
||||
height: LazyTransformBuilder::from_eager::<F>(name, v, &source.height),
|
||||
dateindex: LazyTransformBuilder::from_eager::<F>(name, v, &source.dateindex),
|
||||
weekindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.weekindex),
|
||||
difficultyepoch: LazyTransformBuilder::from_eager::<F>(name, v, &source.difficultyepoch),
|
||||
monthindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.monthindex),
|
||||
quarterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.quarterindex),
|
||||
semesterindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.semesterindex),
|
||||
yearindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.yearindex),
|
||||
decadeindex: LazyTransformBuilder::from_lazy::<F, _, _>(name, v, &source.decadeindex),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1T> Traversable for LazyVecsFromTxindex<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
fn to_tree_node(&self) -> brk_traversable::TreeNode {
|
||||
brk_traversable::TreeNode::Branch(
|
||||
[
|
||||
self.txindex
|
||||
.as_ref()
|
||||
.map(|v| ("txindex".to_string(), v.to_tree_node())),
|
||||
Some(("height".to_string(), self.height.to_tree_node())),
|
||||
Some(("dateindex".to_string(), self.dateindex.to_tree_node())),
|
||||
Some((
|
||||
"difficultyepoch".to_string(),
|
||||
self.difficultyepoch.to_tree_node(),
|
||||
)),
|
||||
Some(("weekindex".to_string(), self.weekindex.to_tree_node())),
|
||||
Some(("monthindex".to_string(), self.monthindex.to_tree_node())),
|
||||
Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())),
|
||||
Some((
|
||||
"semesterindex".to_string(),
|
||||
self.semesterindex.to_tree_node(),
|
||||
)),
|
||||
Some(("yearindex".to_string(), self.yearindex.to_tree_node())),
|
||||
Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
.merge_branches()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
|
||||
let mut regular_iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
|
||||
Box::new(std::iter::empty());
|
||||
if let Some(ref txindex) = self.txindex {
|
||||
regular_iter = Box::new(regular_iter.chain(txindex.iter_any_exportable()));
|
||||
}
|
||||
regular_iter = Box::new(regular_iter.chain(self.height.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.dateindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.difficultyepoch.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.weekindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.monthindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.quarterindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.semesterindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.yearindex.iter_any_exportable()));
|
||||
regular_iter = Box::new(regular_iter.chain(self.decadeindex.iter_any_exportable()));
|
||||
regular_iter
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,22 @@
|
||||
mod builder_eager;
|
||||
mod builder_lazy;
|
||||
mod builder_transform;
|
||||
mod builder_transform2;
|
||||
mod computed;
|
||||
mod from_dateindex;
|
||||
mod from_height;
|
||||
mod from_height_strict;
|
||||
mod from_txindex;
|
||||
mod computed_from_dateindex;
|
||||
mod computed_from_height;
|
||||
mod computed_from_height_strict;
|
||||
mod computed_from_txindex;
|
||||
mod lazy2_from_dateindex;
|
||||
mod lazy_from_dateindex;
|
||||
mod lazy_from_height;
|
||||
// mod lazy_from_height_strict;
|
||||
// mod lazy_from_txindex;
|
||||
mod price_percentiles;
|
||||
mod ratio_from_dateindex;
|
||||
mod sd_from_dateindex;
|
||||
mod source;
|
||||
mod transforms;
|
||||
mod value_from_dateindex;
|
||||
mod value_from_height;
|
||||
mod value_from_txindex;
|
||||
@@ -16,15 +24,23 @@ mod value_height;
|
||||
|
||||
pub use builder_eager::*;
|
||||
pub use builder_lazy::*;
|
||||
pub use builder_transform::*;
|
||||
pub use builder_transform2::*;
|
||||
use computed::*;
|
||||
pub use from_dateindex::*;
|
||||
pub use from_height::*;
|
||||
pub use from_height_strict::*;
|
||||
pub use from_txindex::*;
|
||||
pub use computed_from_dateindex::*;
|
||||
pub use computed_from_height::*;
|
||||
pub use computed_from_height_strict::*;
|
||||
pub use computed_from_txindex::*;
|
||||
pub use lazy_from_dateindex::*;
|
||||
pub use lazy_from_height::*;
|
||||
pub use lazy2_from_dateindex::*;
|
||||
// pub use lazy_from_height_strict::*;
|
||||
// pub use lazy_from_txindex::*;
|
||||
pub use price_percentiles::*;
|
||||
pub use ratio_from_dateindex::*;
|
||||
pub use sd_from_dateindex::*;
|
||||
pub use source::*;
|
||||
pub use transforms::*;
|
||||
pub use value_from_dateindex::*;
|
||||
pub use value_from_height::*;
|
||||
pub use value_from_txindex::*;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
use brk_types::{Bitcoin, Dollars, Sats, StoredF32, StoredF64};
|
||||
use vecdb::{BinaryTransform, UnaryTransform};
|
||||
|
||||
/// (Dollars, Dollars) -> Dollars addition
|
||||
/// Used for computing total = profit + loss
|
||||
pub struct DollarsPlus;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, Dollars> for DollarsPlus {
|
||||
#[inline(always)]
|
||||
fn apply(lhs: Dollars, rhs: Dollars) -> Dollars {
|
||||
lhs + rhs
|
||||
}
|
||||
}
|
||||
|
||||
/// (Dollars, Dollars) -> Dollars subtraction
|
||||
/// Used for computing net = profit - loss
|
||||
pub struct DollarsMinus;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, Dollars> for DollarsMinus {
|
||||
#[inline(always)]
|
||||
fn apply(lhs: Dollars, rhs: Dollars) -> Dollars {
|
||||
lhs - rhs
|
||||
}
|
||||
}
|
||||
|
||||
/// (Dollars, Dollars) -> StoredF32 ratio
|
||||
/// Used for computing percentage ratios like profit/total, loss/total, etc.
|
||||
pub struct Ratio32;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, StoredF32> for Ratio32 {
|
||||
#[inline(always)]
|
||||
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
|
||||
StoredF32::from(numerator / denominator)
|
||||
}
|
||||
}
|
||||
|
||||
/// (Dollars, Dollars) -> -StoredF32 (negated ratio)
|
||||
/// Computes -(a/b) directly to avoid lazy-from-lazy chains.
|
||||
pub struct NegRatio32;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, StoredF32> for NegRatio32 {
|
||||
#[inline(always)]
|
||||
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
|
||||
-StoredF32::from(numerator / denominator)
|
||||
}
|
||||
}
|
||||
|
||||
// === Unary Transforms ===
|
||||
|
||||
/// Sats -> Bitcoin (divide by 1e8)
|
||||
pub struct SatsToBitcoin;
|
||||
|
||||
impl UnaryTransform<Sats, Bitcoin> for SatsToBitcoin {
|
||||
#[inline(always)]
|
||||
fn apply(sats: Sats) -> Bitcoin {
|
||||
Bitcoin::from(sats)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sats -> StoredF64 via Bitcoin (for coinblocks/coindays)
|
||||
pub struct SatsToStoredF64;
|
||||
|
||||
impl UnaryTransform<Sats, StoredF64> for SatsToStoredF64 {
|
||||
#[inline(always)]
|
||||
fn apply(sats: Sats) -> StoredF64 {
|
||||
StoredF64::from(Bitcoin::from(sats))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sats -> Sats/2 (for supply_half)
|
||||
pub struct HalveSats;
|
||||
|
||||
impl UnaryTransform<Sats, Sats> for HalveSats {
|
||||
#[inline(always)]
|
||||
fn apply(sats: Sats) -> Sats {
|
||||
sats / 2
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{CollectableVec, Database, EagerVec, Exit, ImportableVec, PcoVec};
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, IterableCloneableVec, LazyVecFrom1, PcoVec};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::Source,
|
||||
grouped::{SatsToBitcoin, Source},
|
||||
price,
|
||||
traits::{ComputeFromBitcoin, ComputeFromSats},
|
||||
traits::ComputeFromBitcoin,
|
||||
utils::OptionExt,
|
||||
};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct ComputedHeightValueVecs {
|
||||
pub sats: Option<EagerVec<PcoVec<Height, Sats>>>,
|
||||
pub bitcoin: EagerVec<PcoVec<Height, Bitcoin>>,
|
||||
pub bitcoin: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
|
||||
pub dollars: Option<EagerVec<PcoVec<Height, Dollars>>>,
|
||||
}
|
||||
|
||||
@@ -28,15 +28,29 @@ impl ComputedHeightValueVecs {
|
||||
version: Version,
|
||||
compute_dollars: bool,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sats: source.is_compute().then(|| {
|
||||
EagerVec::forced_import(db, name, version + VERSION + Version::ZERO).unwrap()
|
||||
}),
|
||||
bitcoin: EagerVec::forced_import(
|
||||
db,
|
||||
let sats = source
|
||||
.is_compute()
|
||||
.then(|| EagerVec::forced_import(db, name, version + VERSION + Version::ZERO).unwrap());
|
||||
|
||||
let bitcoin = match &source {
|
||||
Source::Compute => LazyVecFrom1::transformed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
version + VERSION + Version::ZERO,
|
||||
)?,
|
||||
sats.as_ref().unwrap().boxed_clone(),
|
||||
),
|
||||
Source::Vec(boxed) => LazyVecFrom1::transformed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
version + VERSION + Version::ZERO,
|
||||
boxed.clone(),
|
||||
),
|
||||
Source::None => {
|
||||
panic!("Source::None not supported for lazy bitcoin - use Source::Vec instead")
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
bitcoin,
|
||||
dollars: compute_dollars.then(|| {
|
||||
EagerVec::forced_import(
|
||||
db,
|
||||
@@ -60,8 +74,7 @@ impl ComputedHeightValueVecs {
|
||||
{
|
||||
compute(self.sats.um())?;
|
||||
|
||||
let height: Option<&PcoVec<Height, Sats>> = None;
|
||||
self.compute_rest(price, starting_indexes, exit, height)?;
|
||||
self.compute_rest(price, starting_indexes, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -71,27 +84,12 @@ impl ComputedHeightValueVecs {
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
height: Option<&impl CollectableVec<Height, Sats>>,
|
||||
) -> Result<()> {
|
||||
if let Some(height) = height {
|
||||
self.bitcoin
|
||||
.compute_from_sats(starting_indexes.height, height, exit)?;
|
||||
} else {
|
||||
self.bitcoin.compute_from_sats(
|
||||
starting_indexes.height,
|
||||
self.sats.u(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
let height_to_bitcoin = &self.bitcoin;
|
||||
let height_to_price_close = &price.u().chainindexes_to_price_close.height;
|
||||
|
||||
if let Some(dollars) = self.dollars.as_mut() {
|
||||
dollars.compute_from_bitcoin(
|
||||
starting_indexes.height,
|
||||
height_to_bitcoin,
|
||||
height_to_price_close,
|
||||
&self.bitcoin,
|
||||
&price.u().chainindexes_to_price_close.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,10 @@ impl Computer {
|
||||
|
||||
Ok((indexes, fetched, blks, txins, txouts))
|
||||
})?;
|
||||
info!("Imported indexes/fetched/blks/txins/txouts in {:?}", i.elapsed());
|
||||
info!(
|
||||
"Imported indexes/fetched/blks/txins/txouts in {:?}",
|
||||
i.elapsed()
|
||||
);
|
||||
|
||||
let i = Instant::now();
|
||||
let (price, constants, market) = thread::scope(|s| -> Result<_> {
|
||||
@@ -195,11 +198,11 @@ impl Computer {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(name) = entry.file_name().to_str() {
|
||||
if !EXPECTED_DBS.contains(&name) {
|
||||
info!("Removing obsolete database folder: {}", name);
|
||||
fs::remove_dir_all(entry.path())?;
|
||||
}
|
||||
if let Some(name) = entry.file_name().to_str()
|
||||
&& !EXPECTED_DBS.contains(&name)
|
||||
{
|
||||
info!("Removing obsolete database folder: {}", name);
|
||||
fs::remove_dir_all(entry.path())?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +265,8 @@ impl Computer {
|
||||
let txouts = scope.spawn(|| -> Result<()> {
|
||||
info!("Computing txouts...");
|
||||
let i = Instant::now();
|
||||
self.txouts.compute(indexer, &self.txins, &starting_indexes, exit)?;
|
||||
self.txouts
|
||||
.compute(indexer, &self.txins, &starting_indexes, exit)?;
|
||||
info!("Computed txouts in {:?}", i.elapsed());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
@@ -51,7 +51,6 @@ impl Vecs {
|
||||
vecs::Vecs::forced_import(
|
||||
&db,
|
||||
pool.slug,
|
||||
pools,
|
||||
version + Version::ZERO,
|
||||
indexes,
|
||||
price,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, PoolSlug, Pools, Sats, StoredF32, StoredU16, StoredU32};
|
||||
use brk_types::{Height, PoolSlug, Sats, StoredF32, StoredU16, StoredU32};
|
||||
use vecdb::{Database, Exit, GenericStoredVec, IterableVec, VecIndex, Version};
|
||||
|
||||
use crate::{
|
||||
@@ -37,7 +37,6 @@ impl Vecs {
|
||||
pub fn forced_import(
|
||||
db: &Database,
|
||||
slug: PoolSlug,
|
||||
_pools: &Pools,
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
|
||||
@@ -17,7 +17,10 @@ use crate::{
|
||||
stateful::states::AddressCohortState,
|
||||
};
|
||||
|
||||
use super::{super::metrics::{CohortMetrics, ImportConfig}, traits::{CohortVecs, DynCohortVecs}};
|
||||
use super::{
|
||||
super::metrics::{CohortMetrics, ImportConfig},
|
||||
traits::{CohortVecs, DynCohortVecs},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
@@ -288,8 +291,6 @@ impl CohortVecs for AddressCohortVecs {
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics.compute_rest_part2(
|
||||
@@ -300,8 +301,6 @@ impl CohortVecs for AddressCohortVecs {
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -183,7 +183,7 @@ impl AddressCohorts {
|
||||
|
||||
/// Second phase of post-processing: compute relative metrics.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2<S, D, HM, DM, HR, DR>(
|
||||
pub fn compute_rest_part2<S, D, HM, DM>(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
@@ -192,8 +192,6 @@ impl AddressCohorts {
|
||||
dateindex_to_supply: &D,
|
||||
height_to_market_cap: Option<&HM>,
|
||||
dateindex_to_market_cap: Option<&DM>,
|
||||
height_to_realized_cap: Option<&HR>,
|
||||
dateindex_to_realized_cap: Option<&DR>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
@@ -201,8 +199,6 @@ impl AddressCohorts {
|
||||
D: IterableVec<DateIndex, Bitcoin> + Sync,
|
||||
HM: IterableVec<Height, Dollars> + Sync,
|
||||
DM: IterableVec<DateIndex, Dollars> + Sync,
|
||||
HR: IterableVec<Height, Dollars> + Sync,
|
||||
DR: IterableVec<DateIndex, Dollars> + Sync,
|
||||
{
|
||||
self.0.par_iter_mut().try_for_each(|v| {
|
||||
v.compute_rest_part2(
|
||||
@@ -213,8 +209,6 @@ impl AddressCohorts {
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -64,8 +64,6 @@ pub trait CohortVecs: DynCohortVecs {
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
@@ -236,8 +236,6 @@ impl CohortVecs for UTXOCohortVecs {
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.metrics.compute_rest_part2(
|
||||
@@ -248,8 +246,6 @@ impl CohortVecs for UTXOCohortVecs {
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ impl UTXOCohorts {
|
||||
|
||||
/// Second phase of post-processing: compute relative metrics.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2<S, D, HM, DM, HR, DR>(
|
||||
pub fn compute_rest_part2<S, D, HM, DM>(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
@@ -344,8 +344,6 @@ impl UTXOCohorts {
|
||||
dateindex_to_supply: &D,
|
||||
height_to_market_cap: Option<&HM>,
|
||||
dateindex_to_market_cap: Option<&DM>,
|
||||
height_to_realized_cap: Option<&HR>,
|
||||
dateindex_to_realized_cap: Option<&DR>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
@@ -353,8 +351,6 @@ impl UTXOCohorts {
|
||||
D: IterableVec<DateIndex, Bitcoin> + Sync,
|
||||
HM: IterableVec<Height, Dollars> + Sync,
|
||||
DM: IterableVec<DateIndex, Dollars> + Sync,
|
||||
HR: IterableVec<Height, Dollars> + Sync,
|
||||
DR: IterableVec<DateIndex, Dollars> + Sync,
|
||||
{
|
||||
self.par_iter_mut().try_for_each(|v| {
|
||||
v.compute_rest_part2(
|
||||
@@ -365,8 +361,6 @@ impl UTXOCohorts {
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -49,7 +49,7 @@ pub fn compute_rest_part1(
|
||||
///
|
||||
/// Computes supply ratios, market cap ratios, etc. using total references.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2<S, D, HM, DM, HR, DR>(
|
||||
pub fn compute_rest_part2<S, D, HM, DM>(
|
||||
utxo_cohorts: &mut UTXOCohorts,
|
||||
address_cohorts: &mut AddressCohorts,
|
||||
indexes: &indexes::Vecs,
|
||||
@@ -59,8 +59,6 @@ pub fn compute_rest_part2<S, D, HM, DM, HR, DR>(
|
||||
dateindex_to_supply: &D,
|
||||
height_to_market_cap: Option<&HM>,
|
||||
dateindex_to_market_cap: Option<&DM>,
|
||||
height_to_realized_cap: Option<&HR>,
|
||||
dateindex_to_realized_cap: Option<&DR>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
@@ -68,8 +66,6 @@ where
|
||||
D: IterableVec<DateIndex, Bitcoin> + Sync,
|
||||
HM: IterableVec<Height, Dollars> + Sync,
|
||||
DM: IterableVec<DateIndex, Dollars> + Sync,
|
||||
HR: IterableVec<Height, Dollars> + Sync,
|
||||
DR: IterableVec<DateIndex, Dollars> + Sync,
|
||||
{
|
||||
info!("Computing rest part 2...");
|
||||
|
||||
@@ -81,8 +77,6 @@ where
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -94,8 +88,6 @@ where
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ impl CohortMetrics {
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
|
||||
let unrealized = compute_dollars
|
||||
.then(|| UnrealizedMetrics::forced_import(cfg))
|
||||
.transpose()?;
|
||||
|
||||
Ok(Self {
|
||||
filter: cfg.filter.clone(),
|
||||
supply: SupplyMetrics::forced_import(cfg)?,
|
||||
@@ -60,15 +64,14 @@ impl CohortMetrics {
|
||||
realized: compute_dollars
|
||||
.then(|| RealizedMetrics::forced_import(cfg))
|
||||
.transpose()?,
|
||||
unrealized: compute_dollars
|
||||
.then(|| UnrealizedMetrics::forced_import(cfg))
|
||||
.transpose()?,
|
||||
price_paid: compute_dollars
|
||||
.then(|| PricePaidMetrics::forced_import(cfg))
|
||||
.transpose()?,
|
||||
relative: compute_dollars
|
||||
.then(|| RelativeMetrics::forced_import(cfg))
|
||||
relative: unrealized
|
||||
.as_ref()
|
||||
.map(|u| RelativeMetrics::forced_import(cfg, u))
|
||||
.transpose()?,
|
||||
unrealized,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -261,7 +264,7 @@ impl CohortMetrics {
|
||||
.compute_rest_part1(indexes, price, starting_indexes, exit)?;
|
||||
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
realized.compute_rest_part1(indexes, price, starting_indexes, exit)?;
|
||||
realized.compute_rest_part1(indexes, starting_indexes, exit)?;
|
||||
}
|
||||
|
||||
if let Some(unrealized) = self.unrealized.as_mut() {
|
||||
@@ -286,21 +289,8 @@ impl CohortMetrics {
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply.compute_rest_part2(
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
height_to_supply,
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
if let Some(realized) = self.realized.as_mut() {
|
||||
realized.compute_rest_part2(
|
||||
indexes,
|
||||
@@ -321,11 +311,8 @@ impl CohortMetrics {
|
||||
dateindex_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
height_to_realized_cap,
|
||||
dateindex_to_realized_cap,
|
||||
&self.supply,
|
||||
self.unrealized.as_ref(),
|
||||
self.realized.as_ref(),
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, StoredF32, StoredF64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableVec, PcoVec};
|
||||
use vecdb::{
|
||||
AnyStoredVec, EagerVec, Exit, GenericStoredVec, Ident, ImportableVec, IterableCloneableVec,
|
||||
IterableVec, Negate, PcoVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{
|
||||
ComputedRatioVecsFromDateIndex, ComputedVecsFromDateIndex, ComputedVecsFromHeight, Source,
|
||||
VecBuilderOptions,
|
||||
ComputedRatioVecsFromDateIndex, ComputedVecsFromDateIndex, ComputedVecsFromHeight,
|
||||
LazyVecsFromHeight, Source, VecBuilderOptions,
|
||||
},
|
||||
indexes, price,
|
||||
stateful::states::RealizedState,
|
||||
@@ -33,7 +36,7 @@ pub struct RealizedMetrics {
|
||||
pub indexes_to_realized_profit: ComputedVecsFromHeight<Dollars>,
|
||||
pub height_to_realized_loss: EagerVec<PcoVec<Height, Dollars>>,
|
||||
pub indexes_to_realized_loss: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_neg_realized_loss: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_neg_realized_loss: LazyVecsFromHeight<Dollars>,
|
||||
pub indexes_to_net_realized_pnl: ComputedVecsFromHeight<Dollars>,
|
||||
pub indexes_to_realized_value: ComputedVecsFromHeight<Dollars>,
|
||||
|
||||
@@ -43,8 +46,7 @@ pub struct RealizedMetrics {
|
||||
pub indexes_to_net_realized_pnl_rel_to_realized_cap: ComputedVecsFromHeight<StoredF32>,
|
||||
|
||||
// === Total Realized PnL ===
|
||||
pub height_to_total_realized_pnl: EagerVec<PcoVec<Height, Dollars>>,
|
||||
pub indexes_to_total_realized_pnl: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub indexes_to_total_realized_pnl: LazyVecsFromHeight<Dollars>,
|
||||
pub dateindex_to_realized_profit_to_loss_ratio: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
|
||||
// === Value Created/Destroyed ===
|
||||
@@ -92,6 +94,47 @@ impl RealizedMetrics {
|
||||
let sum = VecBuilderOptions::default().add_sum();
|
||||
let sum_cum = VecBuilderOptions::default().add_sum().add_cumulative();
|
||||
|
||||
let height_to_realized_loss: EagerVec<PcoVec<Height, Dollars>> =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("realized_loss"), cfg.version + v0)?;
|
||||
|
||||
let indexes_to_realized_loss = ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_loss"),
|
||||
Source::None,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
sum_cum,
|
||||
)?;
|
||||
|
||||
let indexes_to_neg_realized_loss = LazyVecsFromHeight::from_computed::<Negate>(
|
||||
&cfg.name("neg_realized_loss"),
|
||||
cfg.version + v1,
|
||||
height_to_realized_loss.boxed_clone(),
|
||||
&indexes_to_realized_loss,
|
||||
);
|
||||
|
||||
// realized_value is the source for total_realized_pnl (they're identical)
|
||||
let indexes_to_realized_value = ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_value"),
|
||||
Source::Compute,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
sum,
|
||||
)?;
|
||||
|
||||
// total_realized_pnl is a lazy alias to realized_value
|
||||
let indexes_to_total_realized_pnl = LazyVecsFromHeight::from_computed::<Ident>(
|
||||
&cfg.name("total_realized_pnl"),
|
||||
cfg.version + v1,
|
||||
indexes_to_realized_value
|
||||
.height
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.boxed_clone(),
|
||||
&indexes_to_realized_value,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
// === Realized Cap ===
|
||||
height_to_realized_cap: EagerVec::forced_import(
|
||||
@@ -158,27 +201,9 @@ impl RealizedMetrics {
|
||||
cfg.indexes,
|
||||
sum_cum,
|
||||
)?,
|
||||
height_to_realized_loss: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_loss"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
indexes_to_realized_loss: ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_loss"),
|
||||
Source::None,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
sum_cum,
|
||||
)?,
|
||||
indexes_to_neg_realized_loss: ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_realized_loss"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
sum_cum,
|
||||
)?,
|
||||
height_to_realized_loss,
|
||||
indexes_to_realized_loss,
|
||||
indexes_to_neg_realized_loss,
|
||||
indexes_to_net_realized_pnl: ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_realized_pnl"),
|
||||
@@ -187,14 +212,7 @@ impl RealizedMetrics {
|
||||
cfg.indexes,
|
||||
sum_cum,
|
||||
)?,
|
||||
indexes_to_realized_value: ComputedVecsFromHeight::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_value"),
|
||||
Source::Compute,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
sum,
|
||||
)?,
|
||||
indexes_to_realized_value,
|
||||
|
||||
// === Realized vs Realized Cap Ratios ===
|
||||
indexes_to_realized_profit_rel_to_realized_cap: ComputedVecsFromHeight::forced_import(
|
||||
@@ -223,19 +241,7 @@ impl RealizedMetrics {
|
||||
)?,
|
||||
|
||||
// === Total Realized PnL ===
|
||||
height_to_total_realized_pnl: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("total_realized_pnl"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
indexes_to_total_realized_pnl: ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("total_realized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
sum,
|
||||
)?,
|
||||
indexes_to_total_realized_pnl,
|
||||
dateindex_to_realized_profit_to_loss_ratio: extended
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
@@ -555,7 +561,6 @@ impl RealizedMetrics {
|
||||
pub fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
_price: Option<&price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
@@ -580,18 +585,6 @@ impl RealizedMetrics {
|
||||
Some(&self.height_to_realized_loss),
|
||||
)?;
|
||||
|
||||
// neg_realized_loss = realized_loss * -1
|
||||
self.indexes_to_neg_realized_loss
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.height_to_realized_loss,
|
||||
|(i, v, ..)| (i, v * -1_i64),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// net_realized_pnl = profit - loss
|
||||
self.indexes_to_net_realized_pnl
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
@@ -605,6 +598,8 @@ impl RealizedMetrics {
|
||||
})?;
|
||||
|
||||
// realized_value = profit + loss
|
||||
// Note: total_realized_pnl is a lazy alias to realized_value since both
|
||||
// compute profit + loss with sum aggregation, making them identical.
|
||||
self.indexes_to_realized_value
|
||||
.compute_all(indexes, starting_indexes, exit, |vec| {
|
||||
vec.compute_add(
|
||||
@@ -616,14 +611,6 @@ impl RealizedMetrics {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// total_realized_pnl at height level = profit + loss
|
||||
self.height_to_total_realized_pnl.compute_add(
|
||||
starting_indexes.height,
|
||||
&self.height_to_realized_profit,
|
||||
&self.height_to_realized_loss,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.indexes_to_value_created.compute_rest(
|
||||
indexes,
|
||||
starting_indexes,
|
||||
@@ -705,18 +692,6 @@ impl RealizedMetrics {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// total_realized_pnl at dateindex level
|
||||
self.indexes_to_total_realized_pnl
|
||||
.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_add(
|
||||
starting_indexes.dateindex,
|
||||
self.indexes_to_realized_profit.dateindex.unwrap_sum(),
|
||||
self.indexes_to_realized_loss.dateindex.unwrap_sum(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// SOPR = value_created / value_destroyed
|
||||
self.dateindex_to_sopr.compute_divide(
|
||||
starting_indexes.dateindex,
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, StoredF32, StoredF64, Version};
|
||||
use vecdb::{EagerVec, Exit, ImportableVec, IterableVec, PcoVec};
|
||||
use vecdb::{
|
||||
EagerVec, Exit, ImportableVec, IterableCloneableVec, IterableVec, LazyVecFrom1, LazyVecFrom2,
|
||||
Negate, PcoVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{ComputedVecsFromDateIndex, ComputedVecsFromHeight, Source, VecBuilderOptions},
|
||||
grouped::{
|
||||
ComputedVecsFromDateIndex, ComputedVecsFromHeight, LazyVecsFrom2FromDateIndex,
|
||||
LazyVecsFromDateIndex, NegRatio32, Ratio32, Source, VecBuilderOptions,
|
||||
},
|
||||
indexes,
|
||||
};
|
||||
|
||||
use super::{ImportConfig, RealizedMetrics, SupplyMetrics};
|
||||
use super::{ImportConfig, SupplyMetrics, UnrealizedMetrics};
|
||||
|
||||
/// Relative metrics comparing cohort values to global values.
|
||||
#[derive(Clone, Traversable)]
|
||||
@@ -36,11 +42,12 @@ pub struct RelativeMetrics {
|
||||
// === Unrealized vs Market Cap ===
|
||||
pub height_to_unrealized_profit_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
pub height_to_unrealized_loss_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_market_cap:
|
||||
LazyVecFrom1<Height, StoredF32, Height, StoredF32>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_unrealized_loss_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_market_cap: LazyVecsFromDateIndex<StoredF32>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_market_cap: ComputedVecsFromDateIndex<StoredF32>,
|
||||
|
||||
// === Unrealized vs Own Market Cap (optional) ===
|
||||
@@ -49,7 +56,7 @@ pub struct RelativeMetrics {
|
||||
pub height_to_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom1<Height, StoredF32, Height, StoredF32>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_own_market_cap:
|
||||
@@ -57,32 +64,32 @@ pub struct RelativeMetrics {
|
||||
pub indexes_to_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
Option<LazyVecsFromDateIndex<StoredF32>>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL (optional) ===
|
||||
// === Unrealized vs Own Total Unrealized PnL (optional, lazy from unrealized sources) ===
|
||||
pub height_to_unrealized_profit_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
|
||||
Option<EagerVec<PcoVec<Height, StoredF32>>>,
|
||||
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
|
||||
pub indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
pub indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
|
||||
Option<ComputedVecsFromDateIndex<StoredF32>>,
|
||||
Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
|
||||
}
|
||||
|
||||
impl RelativeMetrics {
|
||||
/// Import relative metrics from database.
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
pub fn forced_import(cfg: &ImportConfig, unrealized: &UnrealizedMetrics) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let v1 = Version::ONE;
|
||||
let v2 = Version::new(2);
|
||||
@@ -90,6 +97,165 @@ impl RelativeMetrics {
|
||||
let compute_rel_to_all = cfg.compute_rel_to_all();
|
||||
let last = VecBuilderOptions::default().add_last();
|
||||
|
||||
// Create sources for lazy neg vecs
|
||||
let height_to_unrealized_loss_rel_to_market_cap: EagerVec<PcoVec<Height, StoredF32>> =
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
)?;
|
||||
|
||||
let height_to_neg_unrealized_loss_rel_to_market_cap = LazyVecFrom1::transformed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
height_to_unrealized_loss_rel_to_market_cap.boxed_clone(),
|
||||
);
|
||||
|
||||
let indexes_to_unrealized_loss_rel_to_market_cap =
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?;
|
||||
|
||||
let indexes_to_neg_unrealized_loss_rel_to_market_cap =
|
||||
LazyVecsFromDateIndex::from_computed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v1,
|
||||
indexes_to_unrealized_loss_rel_to_market_cap
|
||||
.dateindex
|
||||
.as_ref()
|
||||
.map(|v| v.boxed_clone()),
|
||||
&indexes_to_unrealized_loss_rel_to_market_cap,
|
||||
);
|
||||
|
||||
// Optional: own market cap vecs
|
||||
let height_to_unrealized_loss_rel_to_own_market_cap: Option<
|
||||
EagerVec<PcoVec<Height, StoredF32>>,
|
||||
> = (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let height_to_neg_unrealized_loss_rel_to_own_market_cap =
|
||||
height_to_unrealized_loss_rel_to_own_market_cap
|
||||
.as_ref()
|
||||
.map(|source| {
|
||||
LazyVecFrom1::transformed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
source.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_unrealized_loss_rel_to_own_market_cap: Option<
|
||||
ComputedVecsFromDateIndex<StoredF32>,
|
||||
> = (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v2,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let indexes_to_neg_unrealized_loss_rel_to_own_market_cap =
|
||||
indexes_to_unrealized_loss_rel_to_own_market_cap
|
||||
.as_ref()
|
||||
.map(|source| {
|
||||
LazyVecsFromDateIndex::from_computed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
source.dateindex.as_ref().map(|v| v.boxed_clone()),
|
||||
source,
|
||||
)
|
||||
});
|
||||
|
||||
// Optional: own total unrealized pnl vecs (lazy from unrealized sources)
|
||||
let height_to_unrealized_profit_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_profit.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let height_to_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<NegRatio32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
unrealized.height_to_unrealized_loss.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecFrom2::transformed::<Ratio32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
unrealized.height_to_net_unrealized_pnl.boxed_clone(),
|
||||
unrealized.height_to_total_unrealized_pnl.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_profit,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<NegRatio32>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_unrealized_loss,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
let indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl = extended.then(|| {
|
||||
LazyVecsFrom2FromDateIndex::from_computed::<Ratio32>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.indexes_to_net_unrealized_pnl,
|
||||
&unrealized.indexes_to_total_unrealized_pnl,
|
||||
)
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
// === Supply Relative to Circulating Supply ===
|
||||
indexes_to_supply_rel_to_circulating_supply: compute_rel_to_all
|
||||
@@ -184,16 +350,8 @@ impl RelativeMetrics {
|
||||
&cfg.name("unrealized_profit_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
height_to_unrealized_loss_rel_to_market_cap: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
height_to_neg_unrealized_loss_rel_to_market_cap: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
height_to_unrealized_loss_rel_to_market_cap,
|
||||
height_to_neg_unrealized_loss_rel_to_market_cap,
|
||||
height_to_net_unrealized_pnl_rel_to_market_cap: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_unrealized_pnl_rel_to_market_cap"),
|
||||
@@ -208,23 +366,8 @@ impl RelativeMetrics {
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?,
|
||||
indexes_to_unrealized_loss_rel_to_market_cap: ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?,
|
||||
indexes_to_neg_unrealized_loss_rel_to_market_cap:
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?,
|
||||
indexes_to_unrealized_loss_rel_to_market_cap,
|
||||
indexes_to_neg_unrealized_loss_rel_to_market_cap,
|
||||
indexes_to_net_unrealized_pnl_rel_to_market_cap:
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
@@ -245,24 +388,8 @@ impl RelativeMetrics {
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_neg_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_unrealized_loss_rel_to_own_market_cap,
|
||||
height_to_neg_unrealized_loss_rel_to_own_market_cap,
|
||||
height_to_net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
@@ -284,30 +411,8 @@ impl RelativeMetrics {
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v2,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
Source::Compute,
|
||||
cfg.version + v2,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_unrealized_loss_rel_to_own_market_cap,
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_market_cap,
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
@@ -322,90 +427,14 @@ impl RelativeMetrics {
|
||||
.transpose()?,
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL (optional) ===
|
||||
height_to_unrealized_profit_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_unrealized_loss_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl: extended
|
||||
.then(|| {
|
||||
ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
height_to_unrealized_profit_rel_to_own_total_unrealized_pnl,
|
||||
height_to_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl,
|
||||
indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -414,7 +443,7 @@ impl RelativeMetrics {
|
||||
/// This computes percentage ratios comparing cohort metrics to global metrics:
|
||||
/// - Supply relative to circulating supply
|
||||
/// - Supply in profit/loss relative to own supply and circulating supply
|
||||
/// - Unrealized profit/loss relative to market cap, own market cap, total unrealized
|
||||
/// - Unrealized profit/loss relative to market cap, total unrealized
|
||||
///
|
||||
/// See `stateful/common/compute.rs` lines 800-1200 for the full original implementation.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -426,11 +455,8 @@ impl RelativeMetrics {
|
||||
dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
_height_to_realized_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
_dateindex_to_realized_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
supply: &SupplyMetrics,
|
||||
unrealized: Option<&super::UnrealizedMetrics>,
|
||||
_realized: Option<&RealizedMetrics>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// === Supply Relative to Circulating Supply ===
|
||||
@@ -546,13 +572,6 @@ impl RelativeMetrics {
|
||||
height_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_neg_unrealized_loss_rel_to_market_cap
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_neg_unrealized_loss,
|
||||
height_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_percentage(
|
||||
starting_indexes.height,
|
||||
@@ -587,37 +606,20 @@ impl RelativeMetrics {
|
||||
})?;
|
||||
}
|
||||
|
||||
// indexes_to_neg_unrealized_loss_rel_to_market_cap
|
||||
if let Some(dateindex_to_mc) = dateindex_to_market_cap
|
||||
&& let Some(unrealized) = unrealized
|
||||
&& let Some(dateindex_vec) = unrealized.indexes_to_net_unrealized_pnl.dateindex.as_ref()
|
||||
{
|
||||
if let Some(dateindex_vec) =
|
||||
unrealized.indexes_to_neg_unrealized_loss.dateindex.as_ref()
|
||||
{
|
||||
self.indexes_to_neg_unrealized_loss_rel_to_market_cap
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_vec,
|
||||
dateindex_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
if let Some(dateindex_vec) = unrealized.indexes_to_net_unrealized_pnl.dateindex.as_ref()
|
||||
{
|
||||
self.indexes_to_net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_vec,
|
||||
dateindex_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
self.indexes_to_net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
dateindex_vec,
|
||||
dateindex_to_mc,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply (indexes) ===
|
||||
@@ -690,18 +692,6 @@ impl RelativeMetrics {
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_neg_unrealized_loss_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
&& let Some(supply_dollars) = supply.height_to_supply_value.dollars.as_ref()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_neg_unrealized_loss,
|
||||
supply_dollars,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_net_unrealized_pnl_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
@@ -754,27 +744,6 @@ impl RelativeMetrics {
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.indexes_to_neg_unrealized_loss_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
&& let Some(supply_dollars_dateindex) = supply
|
||||
.indexes_to_supply
|
||||
.dollars
|
||||
.as_ref()
|
||||
.and_then(|d| d.dateindex.as_ref())
|
||||
&& let Some(neg_loss_dateindex) =
|
||||
unrealized.indexes_to_neg_unrealized_loss.dateindex.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
neg_loss_dateindex,
|
||||
supply_dollars_dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.indexes_to_net_unrealized_pnl_rel_to_own_market_cap
|
||||
.as_mut()
|
||||
@@ -796,132 +765,6 @@ impl RelativeMetrics {
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL ===
|
||||
if let Some(v) = self
|
||||
.height_to_unrealized_profit_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_unrealized_profit,
|
||||
&unrealized.height_to_total_unrealized_pnl,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_unrealized_loss_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_unrealized_loss,
|
||||
&unrealized.height_to_total_unrealized_pnl,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_neg_unrealized_loss,
|
||||
&unrealized.height_to_total_unrealized_pnl,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.height_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
{
|
||||
v.compute_percentage(
|
||||
starting_indexes.height,
|
||||
&unrealized.height_to_net_unrealized_pnl,
|
||||
&unrealized.height_to_total_unrealized_pnl,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// indexes versions for own total unrealized pnl
|
||||
if let Some(v) = self
|
||||
.indexes_to_unrealized_profit_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
&& let Some(total_pnl_dateindex) = unrealized
|
||||
.indexes_to_total_unrealized_pnl
|
||||
.dateindex
|
||||
.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
&unrealized.dateindex_to_unrealized_profit,
|
||||
total_pnl_dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
if let Some(v) = self
|
||||
.indexes_to_unrealized_loss_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
&& let Some(total_pnl_dateindex) = unrealized
|
||||
.indexes_to_total_unrealized_pnl
|
||||
.dateindex
|
||||
.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
&unrealized.dateindex_to_unrealized_loss,
|
||||
total_pnl_dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(v) = self
|
||||
.indexes_to_neg_unrealized_loss_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
&& let Some(total_pnl_dateindex) = unrealized
|
||||
.indexes_to_total_unrealized_pnl
|
||||
.dateindex
|
||||
.as_ref()
|
||||
&& let Some(neg_loss_dateindex) =
|
||||
unrealized.indexes_to_neg_unrealized_loss.dateindex.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
neg_loss_dateindex,
|
||||
total_pnl_dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
if let Some(v) = self
|
||||
.indexes_to_net_unrealized_pnl_rel_to_own_total_unrealized_pnl
|
||||
.as_mut()
|
||||
&& let Some(total_pnl_dateindex) = unrealized
|
||||
.indexes_to_total_unrealized_pnl
|
||||
.dateindex
|
||||
.as_ref()
|
||||
&& let Some(net_pnl_dateindex) =
|
||||
unrealized.indexes_to_net_unrealized_pnl.dateindex.as_ref()
|
||||
{
|
||||
v.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_percentage(
|
||||
starting_indexes.dateindex,
|
||||
net_pnl_dateindex,
|
||||
total_pnl_dateindex,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, StoredU64, Version};
|
||||
use brk_types::{Height, Sats, StoredU64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableVec, PcoVec,
|
||||
TypedVecIterator,
|
||||
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableCloneableVec,
|
||||
PcoVec, TypedVecIterator,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -52,20 +52,20 @@ impl SupplyMetrics {
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
let last = VecBuilderOptions::default().add_last();
|
||||
|
||||
Ok(Self {
|
||||
height_to_supply: EagerVec::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply"),
|
||||
cfg.version + v0,
|
||||
)?,
|
||||
let height_to_supply: EagerVec<PcoVec<Height, Sats>> =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("supply"), cfg.version + v0)?;
|
||||
|
||||
height_to_supply_value: ComputedHeightValueVecs::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply"),
|
||||
Source::None,
|
||||
cfg.version + v0,
|
||||
compute_dollars,
|
||||
)?,
|
||||
let height_to_supply_value = ComputedHeightValueVecs::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply"),
|
||||
Source::Vec(height_to_supply.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
compute_dollars,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
height_to_supply,
|
||||
height_to_supply_value,
|
||||
|
||||
indexes_to_supply: ComputedValueVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
@@ -183,12 +183,8 @@ impl SupplyMetrics {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height_to_supply_value.compute_rest(
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.height_to_supply),
|
||||
)?;
|
||||
self.height_to_supply_value
|
||||
.compute_rest(price, starting_indexes, exit)?;
|
||||
|
||||
self.indexes_to_supply
|
||||
.compute_all(price, starting_indexes, exit, |v| {
|
||||
@@ -242,29 +238,4 @@ impl SupplyMetrics {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Second phase of computed metrics (ratios, relative values).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn compute_rest_part2(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
_starting_indexes: &Indexes,
|
||||
height_to_supply: &impl IterableVec<Height, Bitcoin>,
|
||||
_dateindex_to_supply: &impl IterableVec<DateIndex, Bitcoin>,
|
||||
height_to_market_cap: Option<&impl IterableVec<Height, Dollars>>,
|
||||
dateindex_to_market_cap: Option<&impl IterableVec<DateIndex, Dollars>>,
|
||||
_exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let _ = (
|
||||
indexes,
|
||||
price,
|
||||
height_to_supply,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
);
|
||||
|
||||
// Supply relative metrics computed here if needed
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,404 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DateIndex, Dollars, Height, Sats, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyStoredVec, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableCloneableVec,
|
||||
LazyVecFrom1, LazyVecFrom2, Negate, PcoVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Indexes,
|
||||
grouped::{
|
||||
ComputedHeightValueVecs, ComputedValueVecsFromDateIndex, ComputedVecsFromDateIndex,
|
||||
DollarsMinus, DollarsPlus, LazyVecsFromDateIndex, Source, VecBuilderOptions,
|
||||
},
|
||||
stateful::states::UnrealizedState,
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
|
||||
/// Unrealized profit/loss metrics.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct UnrealizedMetrics {
|
||||
// === Supply in Profit/Loss ===
|
||||
pub height_to_supply_in_profit: EagerVec<PcoVec<Height, Sats>>,
|
||||
pub indexes_to_supply_in_profit: ComputedValueVecsFromDateIndex,
|
||||
pub height_to_supply_in_loss: EagerVec<PcoVec<Height, Sats>>,
|
||||
pub indexes_to_supply_in_loss: ComputedValueVecsFromDateIndex,
|
||||
pub dateindex_to_supply_in_profit: EagerVec<PcoVec<DateIndex, Sats>>,
|
||||
pub dateindex_to_supply_in_loss: EagerVec<PcoVec<DateIndex, Sats>>,
|
||||
pub height_to_supply_in_profit_value: ComputedHeightValueVecs,
|
||||
pub height_to_supply_in_loss_value: ComputedHeightValueVecs,
|
||||
|
||||
// === Unrealized Profit/Loss ===
|
||||
pub height_to_unrealized_profit: EagerVec<PcoVec<Height, Dollars>>,
|
||||
pub indexes_to_unrealized_profit: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub height_to_unrealized_loss: EagerVec<PcoVec<Height, Dollars>>,
|
||||
pub indexes_to_unrealized_loss: ComputedVecsFromDateIndex<Dollars>,
|
||||
pub dateindex_to_unrealized_profit: EagerVec<PcoVec<DateIndex, Dollars>>,
|
||||
pub dateindex_to_unrealized_loss: EagerVec<PcoVec<DateIndex, Dollars>>,
|
||||
|
||||
// === Negated and Net ===
|
||||
pub height_to_neg_unrealized_loss: LazyVecFrom1<Height, Dollars, Height, Dollars>,
|
||||
pub indexes_to_neg_unrealized_loss: LazyVecsFromDateIndex<Dollars>,
|
||||
|
||||
// net = profit - loss (height is lazy, indexes computed)
|
||||
pub height_to_net_unrealized_pnl:
|
||||
LazyVecFrom2<Height, Dollars, Height, Dollars, Height, Dollars>,
|
||||
pub indexes_to_net_unrealized_pnl: ComputedVecsFromDateIndex<Dollars>,
|
||||
|
||||
// total = profit + loss (height is lazy, indexes computed)
|
||||
pub height_to_total_unrealized_pnl:
|
||||
LazyVecFrom2<Height, Dollars, Height, Dollars, Height, Dollars>,
|
||||
pub indexes_to_total_unrealized_pnl: ComputedVecsFromDateIndex<Dollars>,
|
||||
}
|
||||
|
||||
impl UnrealizedMetrics {
|
||||
/// Import unrealized metrics from database.
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
let last = VecBuilderOptions::default().add_last();
|
||||
|
||||
let dateindex_to_supply_in_profit =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("supply_in_profit"), cfg.version + v0)?;
|
||||
let dateindex_to_supply_in_loss =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("supply_in_loss"), cfg.version + v0)?;
|
||||
let dateindex_to_unrealized_profit =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("unrealized_profit"), cfg.version + v0)?;
|
||||
let dateindex_to_unrealized_loss =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("unrealized_loss"), cfg.version + v0)?;
|
||||
let height_to_unrealized_loss: EagerVec<PcoVec<Height, Dollars>> =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("unrealized_loss"), cfg.version + v0)?;
|
||||
let height_to_neg_unrealized_loss = LazyVecFrom1::transformed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version + v0,
|
||||
height_to_unrealized_loss.boxed_clone(),
|
||||
);
|
||||
|
||||
let indexes_to_unrealized_loss = ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss"),
|
||||
Source::Vec(dateindex_to_unrealized_loss.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?;
|
||||
|
||||
let indexes_to_neg_unrealized_loss = LazyVecsFromDateIndex::from_computed::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version + v0,
|
||||
Some(dateindex_to_unrealized_loss.boxed_clone()),
|
||||
&indexes_to_unrealized_loss,
|
||||
);
|
||||
|
||||
// Extract profit sources for lazy net/total vecs
|
||||
let height_to_unrealized_profit: EagerVec<PcoVec<Height, Dollars>> =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("unrealized_profit"), cfg.version + v0)?;
|
||||
let indexes_to_unrealized_profit = ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_profit"),
|
||||
Source::Vec(dateindex_to_unrealized_profit.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?;
|
||||
|
||||
// Create lazy height vecs from profit/loss sources
|
||||
let height_to_net_unrealized_pnl = LazyVecFrom2::transformed::<DollarsMinus>(
|
||||
&cfg.name("net_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
height_to_unrealized_profit.boxed_clone(),
|
||||
height_to_unrealized_loss.boxed_clone(),
|
||||
);
|
||||
let height_to_total_unrealized_pnl = LazyVecFrom2::transformed::<DollarsPlus>(
|
||||
&cfg.name("total_unrealized_pnl"),
|
||||
cfg.version + v0,
|
||||
height_to_unrealized_profit.boxed_clone(),
|
||||
height_to_unrealized_loss.boxed_clone(),
|
||||
);
|
||||
|
||||
// indexes_to_net/total remain computed (needed by relative.rs)
|
||||
let indexes_to_net_unrealized_pnl = ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_unrealized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?;
|
||||
let indexes_to_total_unrealized_pnl = ComputedVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("total_unrealized_pnl"),
|
||||
Source::Compute,
|
||||
cfg.version + v0,
|
||||
cfg.indexes,
|
||||
last,
|
||||
)?;
|
||||
|
||||
let height_to_supply_in_profit: EagerVec<PcoVec<Height, Sats>> =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("supply_in_profit"), cfg.version + v0)?;
|
||||
let height_to_supply_in_loss: EagerVec<PcoVec<Height, Sats>> =
|
||||
EagerVec::forced_import(cfg.db, &cfg.name("supply_in_loss"), cfg.version + v0)?;
|
||||
|
||||
let height_to_supply_in_profit_value = ComputedHeightValueVecs::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_profit"),
|
||||
Source::Vec(height_to_supply_in_profit.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
compute_dollars,
|
||||
)?;
|
||||
let height_to_supply_in_loss_value = ComputedHeightValueVecs::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_loss"),
|
||||
Source::Vec(height_to_supply_in_loss.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
compute_dollars,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
// === Supply in Profit/Loss ===
|
||||
height_to_supply_in_profit,
|
||||
indexes_to_supply_in_profit: ComputedValueVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_profit"),
|
||||
Source::Vec(dateindex_to_supply_in_profit.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
last,
|
||||
compute_dollars,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
height_to_supply_in_loss,
|
||||
indexes_to_supply_in_loss: ComputedValueVecsFromDateIndex::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_loss"),
|
||||
Source::Vec(dateindex_to_supply_in_loss.boxed_clone()),
|
||||
cfg.version + v0,
|
||||
last,
|
||||
compute_dollars,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
dateindex_to_supply_in_profit,
|
||||
dateindex_to_supply_in_loss,
|
||||
height_to_supply_in_profit_value,
|
||||
height_to_supply_in_loss_value,
|
||||
|
||||
// === Unrealized Profit/Loss ===
|
||||
height_to_unrealized_profit,
|
||||
indexes_to_unrealized_profit,
|
||||
height_to_unrealized_loss,
|
||||
indexes_to_unrealized_loss,
|
||||
dateindex_to_unrealized_profit,
|
||||
dateindex_to_unrealized_loss,
|
||||
|
||||
height_to_neg_unrealized_loss,
|
||||
indexes_to_neg_unrealized_loss,
|
||||
height_to_net_unrealized_pnl,
|
||||
indexes_to_net_unrealized_pnl,
|
||||
height_to_total_unrealized_pnl,
|
||||
indexes_to_total_unrealized_pnl,
|
||||
})
|
||||
}
|
||||
|
||||
/// Push unrealized state values to height-indexed vectors.
|
||||
pub fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
dateindex: Option<DateIndex>,
|
||||
height_state: &UnrealizedState,
|
||||
date_state: Option<&UnrealizedState>,
|
||||
) -> Result<()> {
|
||||
self.height_to_supply_in_profit
|
||||
.truncate_push(height, height_state.supply_in_profit)?;
|
||||
self.height_to_supply_in_loss
|
||||
.truncate_push(height, height_state.supply_in_loss)?;
|
||||
self.height_to_unrealized_profit
|
||||
.truncate_push(height, height_state.unrealized_profit)?;
|
||||
self.height_to_unrealized_loss
|
||||
.truncate_push(height, height_state.unrealized_loss)?;
|
||||
|
||||
if let (Some(dateindex), Some(date_state)) = (dateindex, date_state) {
|
||||
self.dateindex_to_supply_in_profit
|
||||
.truncate_push(dateindex, date_state.supply_in_profit)?;
|
||||
self.dateindex_to_supply_in_loss
|
||||
.truncate_push(dateindex, date_state.supply_in_loss)?;
|
||||
self.dateindex_to_unrealized_profit
|
||||
.truncate_push(dateindex, date_state.unrealized_profit)?;
|
||||
self.dateindex_to_unrealized_loss
|
||||
.truncate_push(dateindex, date_state.unrealized_loss)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write height-indexed vectors to disk.
|
||||
pub fn write(&mut self) -> Result<()> {
|
||||
self.height_to_supply_in_profit.write()?;
|
||||
self.height_to_supply_in_loss.write()?;
|
||||
self.height_to_unrealized_profit.write()?;
|
||||
self.height_to_unrealized_loss.write()?;
|
||||
self.dateindex_to_supply_in_profit.write()?;
|
||||
self.dateindex_to_supply_in_loss.write()?;
|
||||
self.dateindex_to_unrealized_profit.write()?;
|
||||
self.dateindex_to_unrealized_loss.write()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.height_to_supply_in_profit as &mut dyn AnyStoredVec,
|
||||
&mut self.height_to_supply_in_loss as &mut dyn AnyStoredVec,
|
||||
&mut self.height_to_unrealized_profit as &mut dyn AnyStoredVec,
|
||||
&mut self.height_to_unrealized_loss as &mut dyn AnyStoredVec,
|
||||
&mut self.dateindex_to_supply_in_profit as &mut dyn AnyStoredVec,
|
||||
&mut self.dateindex_to_supply_in_loss as &mut dyn AnyStoredVec,
|
||||
&mut self.dateindex_to_unrealized_profit as &mut dyn AnyStoredVec,
|
||||
&mut self.dateindex_to_unrealized_loss as &mut dyn AnyStoredVec,
|
||||
]
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
/// Compute aggregate values from separate cohorts.
|
||||
pub fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height_to_supply_in_profit.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_supply_in_profit)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_supply_in_loss.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_supply_in_loss)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_unrealized_profit.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_unrealized_profit)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.height_to_unrealized_loss.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.height_to_unrealized_loss)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.dateindex_to_supply_in_profit.compute_sum_of_others(
|
||||
starting_indexes.dateindex,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.dateindex_to_supply_in_profit)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.dateindex_to_supply_in_loss.compute_sum_of_others(
|
||||
starting_indexes.dateindex,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.dateindex_to_supply_in_loss)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.dateindex_to_unrealized_profit.compute_sum_of_others(
|
||||
starting_indexes.dateindex,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.dateindex_to_unrealized_profit)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.dateindex_to_unrealized_loss.compute_sum_of_others(
|
||||
starting_indexes.dateindex,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.dateindex_to_unrealized_loss)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// First phase of computed metrics.
|
||||
pub fn compute_rest_part1(
|
||||
&mut self,
|
||||
price: Option<&crate::price::Vecs>,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Compute supply value from sats
|
||||
self.height_to_supply_in_profit_value
|
||||
.compute_rest(price, starting_indexes, exit)?;
|
||||
self.height_to_supply_in_loss_value
|
||||
.compute_rest(price, starting_indexes, exit)?;
|
||||
|
||||
// Compute indexes from dateindex sources
|
||||
self.indexes_to_supply_in_profit.compute_rest(
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.dateindex_to_supply_in_profit),
|
||||
)?;
|
||||
|
||||
self.indexes_to_supply_in_loss.compute_rest(
|
||||
price,
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.dateindex_to_supply_in_loss),
|
||||
)?;
|
||||
|
||||
self.indexes_to_unrealized_profit.compute_rest(
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.dateindex_to_unrealized_profit),
|
||||
)?;
|
||||
|
||||
self.indexes_to_unrealized_loss.compute_rest(
|
||||
starting_indexes,
|
||||
exit,
|
||||
Some(&self.dateindex_to_unrealized_loss),
|
||||
)?;
|
||||
|
||||
// height_to_net/total are lazy, but indexes still need compute
|
||||
// total_unrealized_pnl = profit + loss
|
||||
self.indexes_to_total_unrealized_pnl
|
||||
.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_add(
|
||||
starting_indexes.dateindex,
|
||||
&self.dateindex_to_unrealized_profit,
|
||||
&self.dateindex_to_unrealized_loss,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// net_unrealized_pnl = profit - loss
|
||||
self.indexes_to_net_unrealized_pnl
|
||||
.compute_all(starting_indexes, exit, |vec| {
|
||||
vec.compute_subtract(
|
||||
starting_indexes.dateindex,
|
||||
&self.dateindex_to_unrealized_profit,
|
||||
&self.dateindex_to_unrealized_loss,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -462,27 +462,9 @@ impl Vecs {
|
||||
.as_ref()
|
||||
.map(|v| v.dateindex.u().clone());
|
||||
|
||||
let height_to_realized_cap = self
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.realized
|
||||
.as_ref()
|
||||
.map(|r| r.height_to_realized_cap.clone());
|
||||
|
||||
let dateindex_to_realized_cap = self
|
||||
.utxo_cohorts
|
||||
.all
|
||||
.metrics
|
||||
.realized
|
||||
.as_ref()
|
||||
.map(|r| r.indexes_to_realized_cap.dateindex.unwrap_last().clone());
|
||||
|
||||
let dateindex_to_supply_ref = dateindex_to_supply.u();
|
||||
let height_to_market_cap_ref = height_to_market_cap.as_ref();
|
||||
let dateindex_to_market_cap_ref = dateindex_to_market_cap.as_ref();
|
||||
let height_to_realized_cap_ref = height_to_realized_cap.as_ref();
|
||||
let dateindex_to_realized_cap_ref = dateindex_to_realized_cap.as_ref();
|
||||
|
||||
aggregates::compute_rest_part2(
|
||||
&mut self.utxo_cohorts,
|
||||
@@ -494,8 +476,6 @@ impl Vecs {
|
||||
dateindex_to_supply_ref,
|
||||
height_to_market_cap_ref,
|
||||
dateindex_to_market_cap_ref,
|
||||
height_to_realized_cap_ref,
|
||||
dateindex_to_realized_cap_ref,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user