heatmaps: part 12

This commit is contained in:
nym21
2026-06-01 10:30:44 +02:00
parent 087a3b6fd6
commit a94d31dfdf
10 changed files with 284 additions and 39 deletions
+19 -5
View File
@@ -184,6 +184,23 @@ impl Vecs {
range: Range<usize>,
cap: Option<&Lengths>,
) -> Vec<f64> {
let mut ref_bins = Vec::with_capacity(range.len());
Self::feed_blocks_with(oracle, indexer, range, cap, |_, _, ref_bin| {
ref_bins.push(ref_bin);
});
ref_bins
}
/// Feed a range of blocks into an Oracle and call `on_block` after each
/// processed block. This lets callers observe derived state such as EMA
/// without duplicating the histogram extraction path.
pub fn feed_blocks_with<IM: StorageMode>(
oracle: &mut Oracle,
indexer: &Indexer<IM>,
range: Range<usize>,
cap: Option<&Lengths>,
mut on_block: impl FnMut(usize, &Oracle, f64),
) {
let (total_txs, total_outputs, height_len) = match cap {
Some(c) => (
c.tx_index.to_usize(),
@@ -211,8 +228,6 @@ impl Vecs {
.first_txout_index
.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_txout_index lookup. Accessed tx_index values
// are strictly increasing across blocks, so it only advances forward.
@@ -273,9 +288,8 @@ impl Vecs {
});
}
ref_bins.push(oracle.process_histogram(&hist));
let ref_bin = oracle.process_histogram(&hist);
on_block(range.start + idx, oracle, ref_bin);
}
ref_bins
}
}
+70 -9
View File
@@ -4,7 +4,7 @@ use brk_computer::prices::Vecs as PricesVecs;
use brk_error::{Error, Result};
use brk_indexer::Lengths;
use brk_oracle::{
Config, HistogramEma, HistogramEmaCompact, HistogramRaw, Oracle,
Config, HistogramEma, HistogramEmaCompact, HistogramRaw, Oracle, START_HEIGHT_FAST,
cents_to_bin, sats_to_bin,
};
use brk_types::{Day1, Dollars, Sats, TxOutIndex};
@@ -37,16 +37,36 @@ impl Query {
pub fn confirmed_histogram_ema_day(&self, day: Day1) -> Result<HistogramEmaCompact> {
let safe = self.safe_lengths();
let range = self.day_block_range(day, &safe)?;
let count = range.len() as f64;
Ok(self.average_histogram_ema_range(range, &safe)?.to_compact())
}
fn average_histogram_ema_range(
&self,
range: Range<usize>,
safe: &Lengths,
) -> Result<HistogramEma> {
let count = range.len();
let mut acc = HistogramEma::zeros();
for height in range {
let oracle = self.ema_oracle_at(height, &safe)?;
acc.iter_mut()
.zip(oracle.ema().iter())
.for_each(|(a, &e)| *a += e);
for segment in ema_config_segments(range) {
let mut oracle = self.ema_oracle_at(segment.start, safe)?;
add_ema(&mut acc, oracle.ema());
let feed_start = segment.start + 1;
if feed_start < segment.end {
PricesVecs::feed_blocks_with(
&mut oracle,
self.indexer(),
feed_start..segment.end,
Some(safe),
|_, oracle, _| add_ema(&mut acc, oracle.ema()),
);
}
}
let count = count as f64;
acc.iter_mut().for_each(|a| *a /= count);
Ok(acc.to_compact())
Ok(acc)
}
/// Unfiltered per-bin output counts at the live tip: every forming-block
@@ -137,7 +157,7 @@ impl Query {
let config = Config::for_height(end.saturating_sub(1));
let start = end.saturating_sub(config.window_size);
Oracle::from_checkpoint(seed_bin, config, |o| {
PricesVecs::feed_blocks(o, self.indexer(), start..end, Some(safe));
PricesVecs::feed_blocks_with(o, self.indexer(), start..end, Some(safe), |_, _, _| {});
})
}
@@ -236,3 +256,44 @@ impl Query {
hist
}
}
fn add_ema(acc: &mut HistogramEma, ema: &HistogramEma) {
acc.iter_mut()
.zip(ema.iter())
.for_each(|(a, &e)| *a += e);
}
fn ema_config_segments(range: Range<usize>) -> impl Iterator<Item = Range<usize>> {
let split = START_HEIGHT_FAST.clamp(range.start, range.end);
[range.start..split, split..range.end]
.into_iter()
.filter(|range| !range.is_empty())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ema_config_segments_split_at_fast_start() {
let segments: Vec<_> =
ema_config_segments((START_HEIGHT_FAST - 2)..(START_HEIGHT_FAST + 2)).collect();
assert_eq!(
segments,
vec![
(START_HEIGHT_FAST - 2)..START_HEIGHT_FAST,
START_HEIGHT_FAST..(START_HEIGHT_FAST + 2),
]
);
}
#[test]
fn ema_config_segments_omits_empty_sides() {
let slow: Vec<_> = ema_config_segments((START_HEIGHT_FAST - 2)..START_HEIGHT_FAST).collect();
assert_eq!(slow, vec![(START_HEIGHT_FAST - 2)..START_HEIGHT_FAST]);
let fast: Vec<_> =
ema_config_segments(START_HEIGHT_FAST..(START_HEIGHT_FAST + 2)).collect();
assert_eq!(fast, vec![START_HEIGHT_FAST..(START_HEIGHT_FAST + 2)]);
}
}