computer: snapshot

This commit is contained in:
nym21
2026-02-27 11:17:06 +01:00
parent c75421f46e
commit e7a5ab9450
45 changed files with 559 additions and 611 deletions
+49
View File
@@ -0,0 +1,49 @@
use brk_traversable::Traversable;
use brk_types::{Cents, Dollars, Height, OHLCCents, OHLCDollars, OHLCSats, Sats};
use vecdb::{LazyVecFrom1, PcoVec, Rw, StorageMode};
use crate::internal::{ComputedHeightDerivedLast, EagerIndexes, LazyEagerIndexes};
use super::ohlcs::{LazyOhlcVecs, OhlcVecs};
// ── SplitByUnit ─────────────────────────────────────────────────────
#[derive(Traversable)]
pub struct SplitByUnit<M: StorageMode = Rw> {
pub open: SplitIndexesByUnit<M>,
pub high: SplitIndexesByUnit<M>,
pub low: SplitIndexesByUnit<M>,
pub close: SplitCloseByUnit,
}
#[derive(Traversable)]
pub struct SplitIndexesByUnit<M: StorageMode = Rw> {
pub cents: EagerIndexes<Cents, M>,
pub usd: LazyEagerIndexes<Dollars, Cents>,
pub sats: LazyEagerIndexes<Sats, Cents>,
}
#[derive(Clone, Traversable)]
pub struct SplitCloseByUnit {
pub cents: ComputedHeightDerivedLast<Cents>,
pub usd: ComputedHeightDerivedLast<Dollars>,
pub sats: ComputedHeightDerivedLast<Sats>,
}
// ── OhlcByUnit ──────────────────────────────────────────────────────
#[derive(Traversable)]
pub struct OhlcByUnit<M: StorageMode = Rw> {
pub cents: OhlcVecs<OHLCCents, M>,
pub usd: LazyOhlcVecs<OHLCDollars, OHLCCents>,
pub sats: LazyOhlcVecs<OHLCSats, OHLCCents>,
}
// ── PriceByUnit ─────────────────────────────────────────────────────
#[derive(Traversable)]
pub struct PriceByUnit<M: StorageMode = Rw> {
pub cents: M::Stored<PcoVec<Height, Cents>>,
pub usd: LazyVecFrom1<Height, Dollars, Height, Cents>,
pub sats: LazyVecFrom1<Height, Sats, Height, Cents>,
}
@@ -1,207 +0,0 @@
use std::ops::Range;
use brk_error::Result;
use brk_indexer::Indexer;
use brk_oracle::{Config, NUM_BINS, Oracle, START_HEIGHT, bin_to_cents, cents_to_bin};
use brk_types::{Cents, OutputType, Sats, TxIndex, TxOutIndex};
use tracing::info;
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, StorageMode, VecIndex, WritableVec};
use super::Vecs;
use crate::{ComputeIndexes, indexes};
impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
indexes: &indexes::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.compute_prices(indexer, starting_indexes, exit)?;
self.split
.open
.compute_first(starting_indexes, &self.price, indexes, exit)?;
self.split
.high
.compute_max(starting_indexes, &self.price, indexes, exit)?;
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(())
}
fn compute_prices(
&mut self,
indexer: &Indexer,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let source_version =
indexer.vecs.outputs.value.version() + indexer.vecs.outputs.outputtype.version();
self.price
.validate_computed_version_or_reset(source_version)?;
let total_heights = indexer.vecs.blocks.timestamp.len();
if total_heights <= START_HEIGHT {
return Ok(());
}
// Reorg: truncate to starting_indexes
let truncate_to = self.price.len().min(starting_indexes.height.to_usize());
self.price.truncate_if_needed_at(truncate_to)?;
if self.price.len() < START_HEIGHT {
for line in brk_oracle::PRICES.lines().skip(self.price.len()) {
if self.price.len() >= START_HEIGHT {
break;
}
let dollars: f64 = line.parse().unwrap_or(0.0);
let cents = (dollars * 100.0).round() as u64;
self.price.push(Cents::new(cents));
}
}
if self.price.len() >= total_heights {
return Ok(());
}
let config = Config::default();
let committed = self.price.len();
let prev_cents = self.price.collect_one_at(committed - 1).unwrap();
let seed_bin = cents_to_bin(prev_cents.inner() as f64);
let warmup = config.window_size.min(committed - START_HEIGHT);
let mut oracle = Oracle::from_checkpoint(seed_bin, config, |o| {
Self::feed_blocks(o, indexer, (committed - warmup)..committed);
});
let num_new = total_heights - committed;
info!(
"Computing oracle prices: {} to {} ({warmup} warmup)",
committed, total_heights
);
let ref_bins = Self::feed_blocks(&mut oracle, indexer, committed..total_heights);
for (i, ref_bin) in ref_bins.into_iter().enumerate() {
self.price.push(Cents::new(bin_to_cents(ref_bin)));
let progress = ((i + 1) * 100 / num_new) as u8;
if i > 0 && progress > ((i * 100 / num_new) as u8) {
info!("Oracle price computation: {}%", progress);
}
}
{
let _lock = exit.lock();
self.price.write()?;
}
info!("Oracle prices complete: {} committed", self.price.len());
Ok(())
}
/// Feed a range of blocks from the indexer into an Oracle (skipping coinbase),
/// returning per-block ref_bin values.
fn feed_blocks<M: StorageMode>(
oracle: &mut Oracle,
indexer: &Indexer<M>,
range: Range<usize>,
) -> Vec<f64> {
let total_txs = indexer.vecs.transactions.height.len();
let total_outputs = indexer.vecs.outputs.value.len();
// Pre-collect height-indexed data for the range (plus one extra for next-block lookups)
let collect_end = (range.end + 1).min(indexer.vecs.transactions.first_txindex.len());
let first_txindexes: Vec<TxIndex> = indexer
.vecs
.transactions
.first_txindex
.collect_range_at(range.start, collect_end);
let out_firsts: Vec<TxOutIndex> = indexer
.vecs
.outputs
.first_txoutindex
.collect_range_at(range.start, collect_end);
let mut ref_bins = Vec::with_capacity(range.len());
// Cursor avoids per-block PcoVec page decompression for
// the tx-indexed first_txoutindex lookup. The accessed
// txindex values (first_txindex + 1) are strictly increasing
// 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
.get(idx + 1)
.copied()
.unwrap_or(TxIndex::from(total_txs));
let out_start = if first_txindex.to_usize() + 1 < next_first_txindex.to_usize() {
let target = first_txindex.to_usize() + 1;
txout_cursor.advance(target - txout_cursor.position());
txout_cursor.next().unwrap().to_usize()
} else {
out_firsts
.get(idx + 1)
.copied()
.unwrap_or(TxOutIndex::from(total_outputs))
.to_usize()
};
let out_end = out_firsts
.get(idx + 1)
.copied()
.unwrap_or(TxOutIndex::from(total_outputs))
.to_usize();
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() {
if let Some(bin) = oracle.output_to_bin(values[i], output_types[i]) {
hist[bin] += 1;
}
}
ref_bins.push(oracle.process_histogram(&hist));
}
ref_bins
}
}
impl<M: StorageMode> Vecs<M> {
/// Returns an Oracle seeded from the last committed price, with the last
/// window_size blocks already processed. Ready for additional blocks (e.g. mempool).
pub fn live_oracle<IM: StorageMode>(&self, indexer: &Indexer<IM>) -> Result<Oracle> {
let config = Config::default();
let height = indexer.vecs.blocks.timestamp.len();
let last_cents = self.price.collect_one_at(self.price.len() - 1).unwrap();
let seed_bin = cents_to_bin(last_cents.inner() as f64);
let window_size = config.window_size;
let oracle = Oracle::from_checkpoint(seed_bin, config, |o| {
Vecs::feed_blocks(o, indexer, height.saturating_sub(window_size)..height);
});
Ok(oracle)
}
}
@@ -1,42 +0,0 @@
use brk_error::Result;
use brk_types::Version;
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(
db: &Database,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let version = parent_version + Version::new(11);
let price = PcoVec::forced_import(db, "price_cents", 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_close_cents",
price.read_only_boxed_clone(),
version,
indexes,
);
let split = SplitOhlc {
open,
high,
low,
close,
};
let ohlc = OhlcVecs::forced_import(db, "price_ohlc_cents", version)?;
Ok(Self { split, ohlc, price })
}
}
@@ -1,5 +0,0 @@
mod compute;
mod import;
mod vecs;
pub use vecs::Vecs;
@@ -1,19 +0,0 @@
use brk_traversable::Traversable;
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>>,
}
+194 -3
View File
@@ -1,6 +1,11 @@
use std::ops::Range;
use brk_error::Result;
use brk_indexer::Indexer;
use vecdb::Exit;
use brk_oracle::{Config, NUM_BINS, Oracle, START_HEIGHT, bin_to_cents, cents_to_bin};
use brk_types::{Cents, OutputType, Sats, TxIndex, TxOutIndex};
use tracing::info;
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, StorageMode, VecIndex, WritableVec};
use super::Vecs;
use crate::{ComputeIndexes, indexes};
@@ -13,11 +18,197 @@ impl Vecs {
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
self.cents
.compute(indexer, indexes, starting_indexes, exit)?;
self.compute_prices(indexer, starting_indexes, exit)?;
self.split
.open
.cents
.compute_first(starting_indexes, &self.price.cents, indexes, exit)?;
self.split
.high
.cents
.compute_max(starting_indexes, &self.price.cents, indexes, exit)?;
self.split
.low
.cents
.compute_min(starting_indexes, &self.price.cents, indexes, exit)?;
self.ohlc.cents.compute_from_split(
starting_indexes,
&self.split.open.cents,
&self.split.high.cents,
&self.split.low.cents,
&self.split.close.cents,
indexes,
exit,
)?;
let _lock = exit.lock();
self.db().compact()?;
Ok(())
}
fn compute_prices(
&mut self,
indexer: &Indexer,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let source_version =
indexer.vecs.outputs.value.version() + indexer.vecs.outputs.outputtype.version();
self.price
.cents
.validate_computed_version_or_reset(source_version)?;
let total_heights = indexer.vecs.blocks.timestamp.len();
if total_heights <= START_HEIGHT {
return Ok(());
}
// Reorg: truncate to starting_indexes
let truncate_to = self.price.cents.len().min(starting_indexes.height.to_usize());
self.price.cents.truncate_if_needed_at(truncate_to)?;
if self.price.cents.len() < START_HEIGHT {
for line in brk_oracle::PRICES.lines().skip(self.price.cents.len()) {
if self.price.cents.len() >= START_HEIGHT {
break;
}
let dollars: f64 = line.parse().unwrap_or(0.0);
let cents = (dollars * 100.0).round() as u64;
self.price.cents.push(Cents::new(cents));
}
}
if self.price.cents.len() >= total_heights {
return Ok(());
}
let config = Config::default();
let committed = self.price.cents.len();
let prev_cents = self.price.cents.collect_one_at(committed - 1).unwrap();
let seed_bin = cents_to_bin(prev_cents.inner() as f64);
let warmup = config.window_size.min(committed - START_HEIGHT);
let mut oracle = Oracle::from_checkpoint(seed_bin, config, |o| {
Self::feed_blocks(o, indexer, (committed - warmup)..committed);
});
let num_new = total_heights - committed;
info!(
"Computing oracle prices: {} to {} ({warmup} warmup)",
committed, total_heights
);
let ref_bins = Self::feed_blocks(&mut oracle, indexer, committed..total_heights);
for (i, ref_bin) in ref_bins.into_iter().enumerate() {
self.price.cents.push(Cents::new(bin_to_cents(ref_bin)));
let progress = ((i + 1) * 100 / num_new) as u8;
if i > 0 && progress > ((i * 100 / num_new) as u8) {
info!("Oracle price computation: {}%", progress);
}
}
{
let _lock = exit.lock();
self.price.cents.write()?;
}
info!("Oracle prices complete: {} committed", self.price.cents.len());
Ok(())
}
/// Feed a range of blocks from the indexer into an Oracle (skipping coinbase),
/// returning per-block ref_bin values.
fn feed_blocks<M: StorageMode>(
oracle: &mut Oracle,
indexer: &Indexer<M>,
range: Range<usize>,
) -> Vec<f64> {
let total_txs = indexer.vecs.transactions.height.len();
let total_outputs = indexer.vecs.outputs.value.len();
// Pre-collect height-indexed data for the range (plus one extra for next-block lookups)
let collect_end = (range.end + 1).min(indexer.vecs.transactions.first_txindex.len());
let first_txindexes: Vec<TxIndex> = indexer
.vecs
.transactions
.first_txindex
.collect_range_at(range.start, collect_end);
let out_firsts: Vec<TxOutIndex> = indexer
.vecs
.outputs
.first_txoutindex
.collect_range_at(range.start, collect_end);
let mut ref_bins = Vec::with_capacity(range.len());
// Cursor avoids per-block PcoVec page decompression for
// the tx-indexed first_txoutindex lookup. The accessed
// txindex values (first_txindex + 1) are strictly increasing
// 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
.get(idx + 1)
.copied()
.unwrap_or(TxIndex::from(total_txs));
let out_start = if first_txindex.to_usize() + 1 < next_first_txindex.to_usize() {
let target = first_txindex.to_usize() + 1;
txout_cursor.advance(target - txout_cursor.position());
txout_cursor.next().unwrap().to_usize()
} else {
out_firsts
.get(idx + 1)
.copied()
.unwrap_or(TxOutIndex::from(total_outputs))
.to_usize()
};
let out_end = out_firsts
.get(idx + 1)
.copied()
.unwrap_or(TxOutIndex::from(total_outputs))
.to_usize();
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() {
if let Some(bin) = oracle.output_to_bin(values[i], output_types[i]) {
hist[bin] += 1;
}
}
ref_bins.push(oracle.process_histogram(&hist));
}
ref_bins
}
}
impl<M: StorageMode> Vecs<M> {
/// Returns an Oracle seeded from the last committed price, with the last
/// window_size blocks already processed. Ready for additional blocks (e.g. mempool).
pub fn live_oracle<IM: StorageMode>(&self, indexer: &Indexer<IM>) -> Result<Oracle> {
let config = Config::default();
let height = indexer.vecs.blocks.timestamp.len();
let last_cents = self.price.cents.collect_one_at(self.price.cents.len() - 1).unwrap();
let seed_bin = cents_to_bin(last_cents.inner() as f64);
let window_size = config.window_size;
let oracle = Oracle::from_checkpoint(seed_bin, config, |o| {
Vecs::feed_blocks(o, indexer, height.saturating_sub(window_size)..height);
});
Ok(oracle)
}
}
+154 -20
View File
@@ -1,22 +1,28 @@
pub(crate) mod by_unit;
mod compute;
pub(crate) mod ohlcs;
pub(crate) mod split;
pub mod cents;
pub mod sats;
pub mod usd;
pub use cents::Vecs as CentsVecs;
pub use sats::Vecs as SatsVecs;
pub use usd::Vecs as UsdVecs;
use std::path::Path;
use brk_traversable::Traversable;
use brk_types::Version;
use vecdb::{Database, Rw, StorageMode, PAGE_SIZE};
use vecdb::{
Database, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec, Rw, StorageMode,
PAGE_SIZE,
};
use crate::indexes;
use crate::{
indexes,
internal::{
CentsUnsignedToDollars, CentsUnsignedToSats, ComputedHeightDerivedLast, EagerIndexes,
LazyEagerIndexes, OhlcCentsToDollars, OhlcCentsToSats,
},
};
use by_unit::{
OhlcByUnit, PriceByUnit, SplitByUnit, SplitCloseByUnit, SplitIndexesByUnit,
};
use ohlcs::{LazyOhlcVecs, OhlcVecs};
pub const DB_NAME: &str = "prices";
@@ -25,9 +31,9 @@ pub struct Vecs<M: StorageMode = Rw> {
#[traversable(skip)]
pub(crate) db: Database,
pub cents: CentsVecs<M>,
pub usd: UsdVecs,
pub sats: SatsVecs,
pub split: SplitByUnit<M>,
pub ohlc: OhlcByUnit<M>,
pub price: PriceByUnit<M>,
}
impl Vecs {
@@ -56,15 +62,143 @@ impl Vecs {
version: Version,
indexes: &indexes::Vecs,
) -> brk_error::Result<Self> {
let cents = CentsVecs::forced_import(db, version, indexes)?;
let usd = UsdVecs::forced_import(version, indexes, &cents);
let sats = SatsVecs::forced_import(version, indexes, &cents);
let version = version + Version::new(11);
// ── Cents (eager, stored) ───────────────────────────────────
let price_cents = PcoVec::forced_import(db, "price_cents", version)?;
let open_cents = EagerIndexes::forced_import(db, "price_open_cents", version)?;
let high_cents = EagerIndexes::forced_import(db, "price_high_cents", version)?;
let low_cents = EagerIndexes::forced_import(db, "price_low_cents", version)?;
let close_cents = ComputedHeightDerivedLast::forced_import(
"price_close_cents",
price_cents.read_only_boxed_clone(),
version,
indexes,
);
let ohlc_cents = OhlcVecs::forced_import(db, "price_ohlc_cents", version)?;
// ── USD (lazy from cents) ───────────────────────────────────
let price_usd = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
"price",
version,
price_cents.read_only_boxed_clone(),
);
let open_usd = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
"price_open",
version,
&open_cents,
);
let high_usd = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
"price_high",
version,
&high_cents,
);
let low_usd = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
"price_low",
version,
&low_cents,
);
let close_usd = ComputedHeightDerivedLast::forced_import(
"price_close",
price_usd.read_only_boxed_clone(),
version,
indexes,
);
let ohlc_usd = LazyOhlcVecs::from_eager_ohlc_indexes::<OhlcCentsToDollars>(
"price_ohlc",
version,
&ohlc_cents,
);
// ── Sats (lazy from cents, high↔low swapped) ───────────────
let price_sats = LazyVecFrom1::transformed::<CentsUnsignedToSats>(
"price_sats",
version,
price_cents.read_only_boxed_clone(),
);
let open_sats = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
"price_open_sats",
version,
&open_cents,
);
// Sats are inversely related to cents (sats = 10B/cents), so high↔low are swapped
let high_sats = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
"price_high_sats",
version,
&low_cents,
);
let low_sats = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
"price_low_sats",
version,
&high_cents,
);
let close_sats = ComputedHeightDerivedLast::forced_import(
"price_close_sats",
price_sats.read_only_boxed_clone(),
version,
indexes,
);
// OhlcCentsToSats handles the high↔low swap internally
let ohlc_sats = LazyOhlcVecs::from_eager_ohlc_indexes::<OhlcCentsToSats>(
"price_ohlc_sats",
version,
&ohlc_cents,
);
// ── Assemble pivoted structure ──────────────────────────────
let split = SplitByUnit {
open: SplitIndexesByUnit {
cents: open_cents,
usd: open_usd,
sats: open_sats,
},
high: SplitIndexesByUnit {
cents: high_cents,
usd: high_usd,
sats: high_sats,
},
low: SplitIndexesByUnit {
cents: low_cents,
usd: low_usd,
sats: low_sats,
},
close: SplitCloseByUnit {
cents: close_cents,
usd: close_usd,
sats: close_sats,
},
};
let ohlc = OhlcByUnit {
cents: ohlc_cents,
usd: ohlc_usd,
sats: ohlc_sats,
};
let price = PriceByUnit {
cents: price_cents,
usd: price_usd,
sats: price_sats,
};
Ok(Self {
db: db.clone(),
cents,
usd,
sats,
split,
ohlc,
price,
})
}
@@ -1,64 +0,0 @@
use brk_types::Version;
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, OhlcCentsToSats},
};
impl Vecs {
pub(crate) fn forced_import(
version: Version,
indexes: &indexes::Vecs,
cents: &cents::Vecs,
) -> Self {
let price = LazyVecFrom1::transformed::<CentsUnsignedToSats>(
"price_sats",
version,
cents.price.read_only_boxed_clone(),
);
// Sats are inversely related to cents (sats = 10B/cents), so high↔low are swapped
let open = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
"price_open_sats",
version,
&cents.split.open,
);
let high = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
"price_high_sats",
version,
&cents.split.low,
);
let low = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToSats>(
"price_low_sats",
version,
&cents.split.high,
);
let close = ComputedHeightDerivedLast::forced_import(
"price_close_sats",
price.read_only_boxed_clone(),
version,
indexes,
);
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,
&cents.ohlc,
);
Self { split, ohlc, price }
}
}
@@ -1,4 +0,0 @@
mod import;
mod vecs;
pub use vecs::Vecs;
@@ -1,19 +0,0 @@
use brk_traversable::Traversable;
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>,
}
-9
View File
@@ -1,9 +0,0 @@
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,
}
@@ -1,65 +0,0 @@
use brk_types::Version;
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, OhlcCentsToDollars,
},
};
impl Vecs {
pub(crate) fn forced_import(
version: Version,
indexes: &indexes::Vecs,
cents: &cents::Vecs,
) -> Self {
let price = LazyVecFrom1::transformed::<CentsUnsignedToDollars>(
"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_open",
version,
&cents.split.open,
);
let high = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
"price_high",
version,
&cents.split.high,
);
let low = LazyEagerIndexes::from_eager_indexes::<CentsUnsignedToDollars>(
"price_low",
version,
&cents.split.low,
);
let close = ComputedHeightDerivedLast::forced_import(
"price_close",
price.read_only_boxed_clone(),
version,
indexes,
);
let split = SplitOhlc {
open,
high,
low,
close,
};
let ohlc = LazyOhlcVecs::from_eager_ohlc_indexes::<OhlcCentsToDollars>(
"price_ohlc",
version,
&cents.ohlc,
);
Self { split, ohlc, price }
}
}
@@ -1,4 +0,0 @@
mod import;
mod vecs;
pub use vecs::Vecs;
@@ -1,19 +0,0 @@
use brk_traversable::Traversable;
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>,
}