mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-18 06:28:11 -07:00
computer: snapshot
This commit is contained in:
@@ -19,12 +19,24 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.compute_prices(indexer, starting_indexes, exit)?;
|
||||
self.open
|
||||
self.split
|
||||
.open
|
||||
.compute_first(starting_indexes, &self.price, indexes, exit)?;
|
||||
self.high
|
||||
self.split
|
||||
.high
|
||||
.compute_max(starting_indexes, &self.price, indexes, exit)?;
|
||||
self.low
|
||||
self.split
|
||||
.low
|
||||
.compute_min(starting_indexes, &self.price, indexes, exit)?;
|
||||
self.ohlc.compute_from_split(
|
||||
starting_indexes,
|
||||
&self.split.open,
|
||||
&self.split.high,
|
||||
&self.split.low,
|
||||
&self.split.close,
|
||||
indexes,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -132,6 +144,10 @@ impl Vecs {
|
||||
// across blocks, so the cursor only advances forward.
|
||||
let mut txout_cursor = indexer.vecs.transactions.first_txoutindex.cursor();
|
||||
|
||||
// Reusable buffers — avoid per-block allocation
|
||||
let mut values: Vec<Sats> = Vec::new();
|
||||
let mut output_types: Vec<OutputType> = Vec::new();
|
||||
|
||||
for (idx, _h) in range.enumerate() {
|
||||
let first_txindex = first_txindexes[idx];
|
||||
let next_first_txindex = first_txindexes
|
||||
@@ -156,16 +172,8 @@ impl Vecs {
|
||||
.unwrap_or(TxOutIndex::from(total_outputs))
|
||||
.to_usize();
|
||||
|
||||
let values: Vec<Sats> = indexer
|
||||
.vecs
|
||||
.outputs
|
||||
.value
|
||||
.collect_range_at(out_start, out_end);
|
||||
let output_types: Vec<OutputType> = indexer
|
||||
.vecs
|
||||
.outputs
|
||||
.outputtype
|
||||
.collect_range_at(out_start, out_end);
|
||||
indexer.vecs.outputs.value.collect_range_into_at(out_start, out_end, &mut values);
|
||||
indexer.vecs.outputs.outputtype.collect_range_into_at(out_start, out_end, &mut output_types);
|
||||
|
||||
let mut hist = [0u32; NUM_BINS];
|
||||
for i in 0..values.len() {
|
||||
|
||||
@@ -5,6 +5,7 @@ use vecdb::{Database, ImportableVec, PcoVec, ReadableCloneableVec};
|
||||
use super::Vecs;
|
||||
use crate::indexes;
|
||||
use crate::internal::{ComputedHeightDerivedLast, EagerIndexes};
|
||||
use crate::prices::{ohlcs::OhlcVecs, split::SplitOhlc};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -16,23 +17,26 @@ impl Vecs {
|
||||
|
||||
let price = PcoVec::forced_import(db, "price_cents", version)?;
|
||||
|
||||
let open = EagerIndexes::forced_import(db, "price_cents_open", version)?;
|
||||
let high = EagerIndexes::forced_import(db, "price_cents_high", version)?;
|
||||
let low = EagerIndexes::forced_import(db, "price_cents_low", version)?;
|
||||
let open = EagerIndexes::forced_import(db, "price_open_cents", version)?;
|
||||
let high = EagerIndexes::forced_import(db, "price_high_cents", version)?;
|
||||
let low = EagerIndexes::forced_import(db, "price_low_cents", version)?;
|
||||
|
||||
let close = ComputedHeightDerivedLast::forced_import(
|
||||
"price_cents_close",
|
||||
"price_close_cents",
|
||||
price.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
price,
|
||||
let split = SplitOhlc {
|
||||
open,
|
||||
high,
|
||||
low,
|
||||
close,
|
||||
})
|
||||
};
|
||||
|
||||
let ohlc = OhlcVecs::forced_import(db, "price_ohlc_cents", version)?;
|
||||
|
||||
Ok(Self { split, ohlc, price })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height};
|
||||
use brk_types::{Cents, Height, OHLCCents};
|
||||
use vecdb::{PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedLast, EagerIndexes};
|
||||
use crate::prices::{ohlcs::OhlcVecs, split::SplitOhlc};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub split: SplitOhlc<
|
||||
EagerIndexes<Cents, M>,
|
||||
EagerIndexes<Cents, M>,
|
||||
EagerIndexes<Cents, M>,
|
||||
ComputedHeightDerivedLast<Cents>,
|
||||
>,
|
||||
pub ohlc: OhlcVecs<OHLCCents, M>,
|
||||
pub price: M::Stored<PcoVec<Height, Cents>>,
|
||||
pub open: EagerIndexes<Cents, M>,
|
||||
pub high: EagerIndexes<Cents, M>,
|
||||
pub low: EagerIndexes<Cents, M>,
|
||||
pub close: ComputedHeightDerivedLast<Cents>,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
mod compute;
|
||||
pub(crate) mod ohlcs;
|
||||
pub(crate) mod split;
|
||||
|
||||
pub mod cents;
|
||||
pub mod sats;
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Cents, Close, Day1, Day3, DifficultyEpoch, HalvingEpoch, High, Hour1, Hour4, Hour12, Low,
|
||||
Minute1, Minute5, Minute10, Minute30, Month1, Month3, Month6, OHLCCents, Open, Version, Week1,
|
||||
Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use vecdb::{
|
||||
BytesVec, BytesVecValue, Database, EagerVec, Exit, Formattable, ImportableVec, LazyVecFrom1,
|
||||
ReadableCloneableVec, ReadableVec, Rw, StorageMode, UnaryTransform,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes, indexes_from,
|
||||
internal::{ComputedHeightDerivedLast, EagerIndexes, Indexes},
|
||||
};
|
||||
|
||||
// ── EagerOhlcIndexes ─────────────────────────────────────────────────
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct OhlcVecs<T, M: StorageMode = Rw>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute5, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Minute30, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Hour1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Hour4, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Hour12, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Day1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Day3, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Week1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Month1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Month3, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Month6, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Year1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<Year10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<HalvingEpoch, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<BytesVec<DifficultyEpoch, T>>>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
T: BytesVecValue + Formattable + Serialize + JsonSchema;
|
||||
|
||||
const EAGER_VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> OhlcVecs<T>
|
||||
where
|
||||
T: BytesVecValue + Formattable + Serialize + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let v = version + EAGER_VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
ImportableVec::forced_import(db, &format!("{name}_{}", stringify!($idx)), v)?
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self(indexes_from!(period)))
|
||||
}
|
||||
}
|
||||
|
||||
impl OhlcVecs<OHLCCents> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute_from_split(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
open: &EagerIndexes<Cents>,
|
||||
high: &EagerIndexes<Cents>,
|
||||
low: &EagerIndexes<Cents>,
|
||||
close: &ComputedHeightDerivedLast<Cents>,
|
||||
indexes: &indexes::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
macro_rules! period {
|
||||
($field:ident) => {
|
||||
self.0.$field.compute_transform(
|
||||
starting_indexes.$field,
|
||||
&indexes.$field.first_height,
|
||||
|(idx, _first_h, _)| {
|
||||
let o = open.$field.collect_one(idx).unwrap_or_default();
|
||||
let h = high.$field.collect_one(idx).unwrap_or_default();
|
||||
let l = low.$field.collect_one(idx).unwrap_or_default();
|
||||
let c = close.$field.collect_one(idx).flatten().unwrap_or_default();
|
||||
(
|
||||
idx,
|
||||
OHLCCents {
|
||||
open: Open::new(o),
|
||||
high: High::new(h),
|
||||
low: Low::new(l),
|
||||
close: Close::new(c),
|
||||
},
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($field:ident) => {
|
||||
self.0.$field.compute_transform(
|
||||
starting_indexes.$field,
|
||||
&indexes.$field.first_height,
|
||||
|(idx, _first_h, _)| {
|
||||
let o = open.$field.collect_one(idx).unwrap_or_default();
|
||||
let h = high.$field.collect_one(idx).unwrap_or_default();
|
||||
let l = low.$field.collect_one(idx).unwrap_or_default();
|
||||
let c = close.$field.collect_one(idx).unwrap_or_default();
|
||||
(
|
||||
idx,
|
||||
OHLCCents {
|
||||
open: Open::new(o),
|
||||
high: High::new(h),
|
||||
low: Low::new(l),
|
||||
close: Close::new(c),
|
||||
},
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
};
|
||||
}
|
||||
|
||||
period!(minute1);
|
||||
period!(minute5);
|
||||
period!(minute10);
|
||||
period!(minute30);
|
||||
period!(hour1);
|
||||
period!(hour4);
|
||||
period!(hour12);
|
||||
period!(day1);
|
||||
period!(day3);
|
||||
period!(week1);
|
||||
period!(month1);
|
||||
period!(month3);
|
||||
period!(month6);
|
||||
period!(year1);
|
||||
period!(year10);
|
||||
epoch!(halvingepoch);
|
||||
epoch!(difficultyepoch);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ── LazyOhlcIndexes ──────────────────────────────────────────────────
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyOhlcVecs<T, S>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyVecFrom1<Minute1, T, Minute1, S>,
|
||||
LazyVecFrom1<Minute5, T, Minute5, S>,
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
LazyVecFrom1<Hour4, T, Hour4, S>,
|
||||
LazyVecFrom1<Hour12, T, Hour12, S>,
|
||||
LazyVecFrom1<Day1, T, Day1, S>,
|
||||
LazyVecFrom1<Day3, T, Day3, S>,
|
||||
LazyVecFrom1<Week1, T, Week1, S>,
|
||||
LazyVecFrom1<Month1, T, Month1, S>,
|
||||
LazyVecFrom1<Month3, T, Month3, S>,
|
||||
LazyVecFrom1<Month6, T, Month6, S>,
|
||||
LazyVecFrom1<Year1, T, Year1, S>,
|
||||
LazyVecFrom1<Year10, T, Year10, S>,
|
||||
LazyVecFrom1<HalvingEpoch, T, HalvingEpoch, S>,
|
||||
LazyVecFrom1<DifficultyEpoch, T, DifficultyEpoch, S>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
T: BytesVecValue + Formattable + Serialize + JsonSchema,
|
||||
S: BytesVecValue;
|
||||
|
||||
impl<T, S> LazyOhlcVecs<T, S>
|
||||
where
|
||||
T: BytesVecValue + Formattable + Serialize + JsonSchema,
|
||||
S: BytesVecValue + Formattable + Serialize + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_eager_ohlc_indexes<Transform: UnaryTransform<S, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &OhlcVecs<S>,
|
||||
) -> Self {
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyVecFrom1::transformed::<Transform>(
|
||||
&format!("{name}_{}", stringify!($idx)),
|
||||
version,
|
||||
source.$idx.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@ use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
use super::super::cents;
|
||||
use super::Vecs;
|
||||
use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CentsUnsignedToSats, ComputedHeightDerivedLast, LazyEagerIndexes},
|
||||
internal::{CentsUnsignedToSats, ComputedHeightDerivedLast, LazyEagerIndexes, OhlcCentsToSats},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
@@ -21,26 +22,43 @@ impl Vecs {
|
||||
);
|
||||
|
||||
// Sats are inversely related to cents (sats = 10B/cents), so high↔low are swapped
|
||||
let open =
|
||||
LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>("price_sats_open", version, ¢s.open);
|
||||
let high =
|
||||
LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>("price_sats_high", version, ¢s.low);
|
||||
let low =
|
||||
LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>("price_sats_low", version, ¢s.high);
|
||||
let open = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
|
||||
"price_open_sats",
|
||||
version,
|
||||
¢s.split.open,
|
||||
);
|
||||
let high = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
|
||||
"price_high_sats",
|
||||
version,
|
||||
¢s.split.low,
|
||||
);
|
||||
let low = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
|
||||
"price_low_sats",
|
||||
version,
|
||||
¢s.split.high,
|
||||
);
|
||||
|
||||
let close = ComputedHeightDerivedLast::forced_import(
|
||||
"price_sats_close",
|
||||
"price_close_sats",
|
||||
price.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Self {
|
||||
price,
|
||||
let split = SplitOhlc {
|
||||
open,
|
||||
high,
|
||||
low,
|
||||
close,
|
||||
}
|
||||
};
|
||||
|
||||
// OhlcCentsToSats handles the high↔low swap internally
|
||||
let ohlc = LazyOhlcVecs::from_eager_ohlc_indexes::<OhlcCentsToSats>(
|
||||
"price_ohlc_sats",
|
||||
version,
|
||||
¢s.ohlc,
|
||||
);
|
||||
|
||||
Self { split, ohlc, price }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Sats};
|
||||
use brk_types::{Cents, Height, OHLCCents, OHLCSats, Sats};
|
||||
use vecdb::LazyVecFrom1;
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedLast, LazyEagerIndexes};
|
||||
use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub split: SplitOhlc<
|
||||
LazyEagerIndexes<Sats, Cents>,
|
||||
LazyEagerIndexes<Sats, Cents>,
|
||||
LazyEagerIndexes<Sats, Cents>,
|
||||
ComputedHeightDerivedLast<Sats>,
|
||||
>,
|
||||
pub ohlc: LazyOhlcVecs<OHLCSats, OHLCCents>,
|
||||
pub price: LazyVecFrom1<Height, Sats, Height, Cents>,
|
||||
pub open: LazyEagerIndexes<Sats, Cents>,
|
||||
pub high: LazyEagerIndexes<Sats, Cents>,
|
||||
pub low: LazyEagerIndexes<Sats, Cents>,
|
||||
pub close: ComputedHeightDerivedLast<Sats>,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct SplitOhlc<O, H, L, C> {
|
||||
pub open: O,
|
||||
pub high: H,
|
||||
pub low: L,
|
||||
pub close: C,
|
||||
}
|
||||
@@ -3,9 +3,12 @@ use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
use super::super::cents;
|
||||
use super::Vecs;
|
||||
use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CentsUnsignedToDollars, ComputedHeightDerivedLast, LazyEagerIndexes},
|
||||
internal::{
|
||||
CentsUnsignedToDollars, ComputedHeightDerivedLast, LazyEagerIndexes, OhlcCentsToDollars,
|
||||
},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
@@ -15,32 +18,48 @@ impl Vecs {
|
||||
cents: ¢s::Vecs,
|
||||
) -> Self {
|
||||
let price = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
|
||||
"price_usd",
|
||||
"price",
|
||||
version,
|
||||
cents.price.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
// Dollars are monotonically increasing from cents, so open→open, high→high, low→low
|
||||
let open =
|
||||
LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>("price_usd_open", version, ¢s.open);
|
||||
let high =
|
||||
LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>("price_usd_high", version, ¢s.high);
|
||||
let low =
|
||||
LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>("price_usd_low", version, ¢s.low);
|
||||
let open = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
|
||||
"price_open",
|
||||
version,
|
||||
¢s.split.open,
|
||||
);
|
||||
let high = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
|
||||
"price_high",
|
||||
version,
|
||||
¢s.split.high,
|
||||
);
|
||||
let low = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
|
||||
"price_low",
|
||||
version,
|
||||
¢s.split.low,
|
||||
);
|
||||
|
||||
let close = ComputedHeightDerivedLast::forced_import(
|
||||
"price_usd_close",
|
||||
"price_close",
|
||||
price.read_only_boxed_clone(),
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Self {
|
||||
price,
|
||||
let split = SplitOhlc {
|
||||
open,
|
||||
high,
|
||||
low,
|
||||
close,
|
||||
}
|
||||
};
|
||||
|
||||
let ohlc = LazyOhlcVecs::from_eager_ohlc_indexes::<OhlcCentsToDollars>(
|
||||
"price_ohlc",
|
||||
version,
|
||||
¢s.ohlc,
|
||||
);
|
||||
|
||||
Self { split, ohlc, price }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Dollars, Height};
|
||||
use brk_types::{Cents, Dollars, Height, OHLCCents, OHLCDollars};
|
||||
use vecdb::LazyVecFrom1;
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedLast, LazyEagerIndexes};
|
||||
use crate::prices::{ohlcs::LazyOhlcVecs, split::SplitOhlc};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub split: SplitOhlc<
|
||||
LazyEagerIndexes<Dollars, Cents>,
|
||||
LazyEagerIndexes<Dollars, Cents>,
|
||||
LazyEagerIndexes<Dollars, Cents>,
|
||||
ComputedHeightDerivedLast<Dollars>,
|
||||
>,
|
||||
pub ohlc: LazyOhlcVecs<OHLCDollars, OHLCCents>,
|
||||
pub price: LazyVecFrom1<Height, Dollars, Height, Cents>,
|
||||
pub open: LazyEagerIndexes<Dollars, Cents>,
|
||||
pub high: LazyEagerIndexes<Dollars, Cents>,
|
||||
pub low: LazyEagerIndexes<Dollars, Cents>,
|
||||
pub close: ComputedHeightDerivedLast<Dollars>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user