mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -8,7 +8,7 @@ use std::{
|
||||
use std::fs;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use libproc::pid_rusage::{pidrusage, RUsageInfoV2};
|
||||
use libproc::pid_rusage::{RUsageInfoV2, pidrusage};
|
||||
|
||||
pub struct IoMonitor {
|
||||
pid: u32,
|
||||
|
||||
@@ -113,7 +113,9 @@ impl Bencher {
|
||||
self.0.stop_flag.store(true, Ordering::Relaxed);
|
||||
|
||||
if let Some(handle) = self.0.monitor_thread.lock().take() {
|
||||
handle.join().map_err(|_| Error::Internal("Monitor thread panicked"))??;
|
||||
handle
|
||||
.join()
|
||||
.map_err(|_| Error::Internal("Monitor thread panicked"))??;
|
||||
}
|
||||
|
||||
self.0.progression.flush()?;
|
||||
|
||||
@@ -43,14 +43,18 @@ pub fn generate(config: ChartConfig, runs: &[Run]) -> Result<()> {
|
||||
let max_value = runs.iter().map(|r| r.max_value()).fold(0.0, f64::max);
|
||||
|
||||
let (time_scaled, time_divisor, time_label) = format::time(max_time_s);
|
||||
let (value_scaled, scale_factor, y_label) = scale_y_axis(max_value, &config.y_label, &config.y_format);
|
||||
let (value_scaled, scale_factor, y_label) =
|
||||
scale_y_axis(max_value, &config.y_label, &config.y_format);
|
||||
let x_labels = label_count(time_scaled);
|
||||
|
||||
let root = SVGBackend::new(config.output_path, SIZE).into_drawing_area();
|
||||
root.fill(&BG_COLOR)?;
|
||||
|
||||
let mut chart = ChartBuilder::on(&root)
|
||||
.caption(&config.title, (FONT, FONT_SIZE_BIG).into_font().color(&TEXT_COLOR))
|
||||
.caption(
|
||||
&config.title,
|
||||
(FONT, FONT_SIZE_BIG).into_font().color(&TEXT_COLOR),
|
||||
)
|
||||
.margin(20)
|
||||
.margin_right(40)
|
||||
.x_label_area_size(50)
|
||||
@@ -62,7 +66,14 @@ pub fn generate(config: ChartConfig, runs: &[Run]) -> Result<()> {
|
||||
|
||||
for (idx, run) in runs.iter().enumerate() {
|
||||
let color = COLORS[idx % COLORS.len()];
|
||||
draw_series(&mut chart, &run.data, &run.id, color, time_divisor, scale_factor)?;
|
||||
draw_series(
|
||||
&mut chart,
|
||||
&run.data,
|
||||
&run.id,
|
||||
color,
|
||||
time_divisor,
|
||||
scale_factor,
|
||||
)?;
|
||||
}
|
||||
|
||||
configure_legend(&mut chart)?;
|
||||
@@ -93,14 +104,18 @@ pub fn generate_dual(
|
||||
let max_value = runs.iter().map(|r| r.max_value()).fold(0.0, f64::max);
|
||||
|
||||
let (time_scaled, time_divisor, time_label) = format::time(max_time_s);
|
||||
let (value_scaled, scale_factor, y_label) = scale_y_axis(max_value, &config.y_label, &config.y_format);
|
||||
let (value_scaled, scale_factor, y_label) =
|
||||
scale_y_axis(max_value, &config.y_label, &config.y_format);
|
||||
let x_labels = label_count(time_scaled);
|
||||
|
||||
let root = SVGBackend::new(config.output_path, SIZE).into_drawing_area();
|
||||
root.fill(&BG_COLOR)?;
|
||||
|
||||
let mut chart = ChartBuilder::on(&root)
|
||||
.caption(&config.title, (FONT, FONT_SIZE_BIG).into_font().color(&TEXT_COLOR))
|
||||
.caption(
|
||||
&config.title,
|
||||
(FONT, FONT_SIZE_BIG).into_font().color(&TEXT_COLOR),
|
||||
)
|
||||
.margin(20)
|
||||
.margin_right(40)
|
||||
.x_label_area_size(50)
|
||||
@@ -164,7 +179,13 @@ type Chart<'a, 'b> = ChartContext<
|
||||
Cartesian2d<plotters::coord::types::RangedCoordf64, plotters::coord::types::RangedCoordf64>,
|
||||
>;
|
||||
|
||||
fn configure_mesh(chart: &mut Chart, x_label: &str, y_label: &str, y_format: &YAxisFormat, x_labels: usize) -> Result<()> {
|
||||
fn configure_mesh(
|
||||
chart: &mut Chart,
|
||||
x_label: &str,
|
||||
y_label: &str,
|
||||
y_format: &YAxisFormat,
|
||||
x_labels: usize,
|
||||
) -> Result<()> {
|
||||
let y_formatter: Box<dyn Fn(&f64) -> String> = match y_format {
|
||||
YAxisFormat::Bytes => Box::new(|y: &f64| {
|
||||
if y.fract() == 0.0 {
|
||||
@@ -200,9 +221,12 @@ fn draw_series(
|
||||
time_divisor: f64,
|
||||
scale_factor: f64,
|
||||
) -> Result<()> {
|
||||
let points = data
|
||||
.iter()
|
||||
.map(|d| (d.timestamp_ms as f64 / 1000.0 / time_divisor, d.value / scale_factor));
|
||||
let points = data.iter().map(|d| {
|
||||
(
|
||||
d.timestamp_ms as f64 / 1000.0 / time_divisor,
|
||||
d.value / scale_factor,
|
||||
)
|
||||
});
|
||||
|
||||
chart
|
||||
.draw_series(LineSeries::new(points, color.stroke_width(1)))?
|
||||
@@ -221,7 +245,12 @@ fn draw_dashed_series(
|
||||
) -> Result<()> {
|
||||
let points: Vec<_> = data
|
||||
.iter()
|
||||
.map(|d| (d.timestamp_ms as f64 / 1000.0 / time_divisor, d.value / scale_factor))
|
||||
.map(|d| {
|
||||
(
|
||||
d.timestamp_ms as f64 / 1000.0 / time_divisor,
|
||||
d.value / scale_factor,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Draw dashed line by skipping every other segment
|
||||
@@ -234,7 +263,12 @@ fn draw_dashed_series(
|
||||
.map(|(_, w)| PathElement::new(vec![w[0], w[1]], color.stroke_width(2))),
|
||||
)?
|
||||
.label(label)
|
||||
.legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 10, y), (x + 20, y)], color.stroke_width(2)));
|
||||
.legend(move |(x, y)| {
|
||||
PathElement::new(
|
||||
vec![(x, y), (x + 10, y), (x + 20, y)],
|
||||
color.stroke_width(2),
|
||||
)
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ mod chart;
|
||||
mod data;
|
||||
mod format;
|
||||
|
||||
use data::{read_dual_runs, read_runs, Cutoffs, DualRun, Result, Run};
|
||||
use data::{Cutoffs, DualRun, Result, Run, read_dual_runs, read_runs};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
@@ -57,10 +57,24 @@ impl Visualizer {
|
||||
let io_runs = read_dual_runs(crate_path, "io.csv")?;
|
||||
|
||||
// Combined charts (all runs)
|
||||
self.generate_combined_charts(crate_path, crate_name, &disk_runs, &memory_runs, &progress_runs, &io_runs)?;
|
||||
self.generate_combined_charts(
|
||||
crate_path,
|
||||
crate_name,
|
||||
&disk_runs,
|
||||
&memory_runs,
|
||||
&progress_runs,
|
||||
&io_runs,
|
||||
)?;
|
||||
|
||||
// Individual charts (one per run)
|
||||
self.generate_individual_charts(crate_path, crate_name, &disk_runs, &memory_runs, &progress_runs, &io_runs)?;
|
||||
self.generate_individual_charts(
|
||||
crate_path,
|
||||
crate_name,
|
||||
&disk_runs,
|
||||
&memory_runs,
|
||||
&progress_runs,
|
||||
&io_runs,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -246,4 +260,3 @@ impl Visualizer {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_common_prefix_lth() {
|
||||
let names = vec!["lth_cost_basis_max", "lth_cost_basis_min", "lth_cost_basis"];
|
||||
assert_eq!(find_common_prefix(&names), Some("lth_cost_basis_".to_string()));
|
||||
assert_eq!(
|
||||
find_common_prefix(&names),
|
||||
Some("lth_cost_basis_".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,11 +9,11 @@ use brk_cohort::{
|
||||
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, EPOCH_NAMES, GE_AMOUNT_NAMES, LT_AMOUNT_NAMES,
|
||||
MAX_AGE_NAMES, MIN_AGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES, YEAR_NAMES,
|
||||
};
|
||||
use brk_types::{pools, Index, PoolSlug};
|
||||
use brk_types::{Index, PoolSlug, pools};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{to_camel_case, VERSION};
|
||||
use crate::{VERSION, to_camel_case};
|
||||
|
||||
/// Collected constant data for client generation.
|
||||
pub struct ClientConstants {
|
||||
|
||||
@@ -73,11 +73,17 @@ pub fn generate_parameterized_field<S: LanguageSyntax>(
|
||||
indent: &str,
|
||||
) {
|
||||
let field_name = syntax.field_name(&field.name);
|
||||
let type_ann = metadata.field_type_annotation(field, pattern.is_generic, None, syntax.generic_syntax());
|
||||
let type_ann =
|
||||
metadata.field_type_annotation(field, pattern.is_generic, None, syntax.generic_syntax());
|
||||
let path_expr = compute_path_expr(syntax, pattern, field, "acc");
|
||||
let value = compute_field_value(syntax, field, metadata, &path_expr);
|
||||
|
||||
writeln!(output, "{}", syntax.field_init(indent, &field_name, &type_ann, &value)).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
"{}",
|
||||
syntax.field_init(indent, &field_name, &type_ann, &value)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Generate a tree node field with a specific child node for pattern instance base detection.
|
||||
@@ -127,7 +133,12 @@ pub fn generate_tree_node_field<S: LanguageSyntax>(
|
||||
)
|
||||
};
|
||||
|
||||
writeln!(output, "{}", syntax.field_init(indent, &field_name, &type_ann, &value)).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
"{}",
|
||||
syntax.field_init(indent, &field_name, &type_ann, &value)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Generate a leaf field using the actual metric name from the TreeNode::Leaf.
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::{Endpoint, Parameter, generators::{normalize_return_type, write_description}, to_camel_case};
|
||||
use crate::{
|
||||
Endpoint, Parameter,
|
||||
generators::{normalize_return_type, write_description},
|
||||
to_camel_case,
|
||||
};
|
||||
|
||||
/// Generate API methods for the BrkClient class.
|
||||
pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
@@ -33,7 +37,13 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
|
||||
// Add endpoint path
|
||||
writeln!(output, " *").unwrap();
|
||||
writeln!(output, " * Endpoint: `{} {}`", endpoint.method.to_uppercase(), endpoint.path).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" * Endpoint: `{} {}`",
|
||||
endpoint.method.to_uppercase(),
|
||||
endpoint.path
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if !endpoint.path_params.is_empty() || !endpoint.query_params.is_empty() {
|
||||
writeln!(output, " *").unwrap();
|
||||
@@ -89,7 +99,12 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
}
|
||||
}
|
||||
writeln!(output, " const query = params.toString();").unwrap();
|
||||
writeln!(output, " const path = `{}${{query ? '?' + query : ''}}`;", path).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" const path = `{}${{query ? '?' + query : ''}}`;",
|
||||
path
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if endpoint.supports_csv {
|
||||
writeln!(output, " if (format === 'csv') {{").unwrap();
|
||||
|
||||
@@ -506,7 +506,11 @@ pub fn generate_static_constants(output: &mut String) {
|
||||
// VERSION, INDEXES, POOL_ID_TO_POOL_NAME
|
||||
writeln!(output, " VERSION = \"{}\";\n", constants.version).unwrap();
|
||||
write_static_const(output, "INDEXES", &format_json(&constants.indexes));
|
||||
write_static_const(output, "POOL_ID_TO_POOL_NAME", &format_json(&constants.pool_map));
|
||||
write_static_const(
|
||||
output,
|
||||
"POOL_ID_TO_POOL_NAME",
|
||||
&format_json(&constants.pool_map),
|
||||
);
|
||||
|
||||
// Cohort constants with camelCase keys
|
||||
for (name, value) in CohortConstants::all() {
|
||||
|
||||
@@ -4,7 +4,11 @@ use std::fmt::Write;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{TypeSchemas, generators::{MANUAL_GENERIC_TYPES, write_description}, get_union_variants, ref_to_type_name, to_camel_case};
|
||||
use crate::{
|
||||
TypeSchemas,
|
||||
generators::{MANUAL_GENERIC_TYPES, write_description},
|
||||
get_union_variants, ref_to_type_name, to_camel_case,
|
||||
};
|
||||
|
||||
/// Generate JSDoc type definitions from OpenAPI schemas.
|
||||
pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) {
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::{Endpoint, Parameter, escape_python_keyword, generators::{normalize_return_type, write_description}, to_snake_case};
|
||||
use crate::{
|
||||
Endpoint, Parameter, escape_python_keyword,
|
||||
generators::{normalize_return_type, write_description},
|
||||
to_snake_case,
|
||||
};
|
||||
|
||||
use super::client::generate_class_constants;
|
||||
use super::types::js_type_to_python;
|
||||
@@ -30,22 +34,58 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) {
|
||||
writeln!(output).unwrap();
|
||||
|
||||
// Generate metric() method for dynamic metric access
|
||||
writeln!(output, " def metric(self, metric: str, index: Index) -> MetricEndpointBuilder[Any]:").unwrap();
|
||||
writeln!(output, " \"\"\"Create a dynamic metric endpoint builder for any metric/index combination.").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" def metric(self, metric: str, index: Index) -> MetricEndpointBuilder[Any]:"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" \"\"\"Create a dynamic metric endpoint builder for any metric/index combination."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output).unwrap();
|
||||
writeln!(output, " Use this for programmatic access when the metric name is determined at runtime.").unwrap();
|
||||
writeln!(output, " For type-safe access, use the `metrics` tree instead.").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" Use this for programmatic access when the metric name is determined at runtime."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" For type-safe access, use the `metrics` tree instead."
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, " \"\"\"").unwrap();
|
||||
writeln!(output, " return MetricEndpointBuilder(self, metric, index)").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" return MetricEndpointBuilder(self, metric, index)"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output).unwrap();
|
||||
|
||||
// Generate helper methods
|
||||
writeln!(output, " def index_to_date(self, index: Index, i: int) -> Union[date, datetime]:").unwrap();
|
||||
writeln!(output, " \"\"\"Convert an index value to a date/datetime for date-based indexes.\"\"\"").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" def index_to_date(self, index: Index, i: int) -> Union[date, datetime]:"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" \"\"\"Convert an index value to a date/datetime for date-based indexes.\"\"\""
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, " return _index_to_date(index, i)").unwrap();
|
||||
writeln!(output).unwrap();
|
||||
writeln!(output, " def date_to_index(self, index: Index, d: Union[date, datetime]) -> int:").unwrap();
|
||||
writeln!(output, " \"\"\"Convert a date/datetime to an index value for date-based indexes.\"\"\"").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" def date_to_index(self, index: Index, d: Union[date, datetime]) -> int:"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" \"\"\"Convert a date/datetime to an index value for date-based indexes.\"\"\""
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, " return _date_to_index(index, d)").unwrap();
|
||||
writeln!(output).unwrap();
|
||||
// Generate API methods
|
||||
@@ -112,7 +152,13 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
}
|
||||
}
|
||||
writeln!(output).unwrap();
|
||||
writeln!(output, " Endpoint: `{} {}`\"\"\"", endpoint.method.to_uppercase(), endpoint.path).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" Endpoint: `{} {}`\"\"\"",
|
||||
endpoint.method.to_uppercase(),
|
||||
endpoint.path
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Build path
|
||||
let path = build_path_template(&endpoint.path, &endpoint.path_params);
|
||||
|
||||
@@ -654,7 +654,12 @@ def _dep(c: BrkClientBase, n: str, i: Index) -> DateMetricEndpointBuilder[Any]:
|
||||
.unwrap();
|
||||
writeln!(output, " @property").unwrap();
|
||||
writeln!(output, " def name(self) -> str: return self._n").unwrap();
|
||||
writeln!(output, " def indexes(self) -> List[str]: return list({})", idx_var).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" def indexes(self) -> List[str]: return list({})",
|
||||
idx_var
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" def get(self, index: Index) -> Optional[MetricEndpointBuilder[T]]: return _ep(self.by._c, self._n, index) if index in {} else None",
|
||||
|
||||
@@ -38,11 +38,23 @@ pub fn generate_python_client(
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, "from urllib.parse import urlparse").unwrap();
|
||||
writeln!(output, "from datetime import date, datetime, timedelta, timezone").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
"from datetime import date, datetime, timedelta, timezone"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, "import json\n").unwrap();
|
||||
writeln!(output, "if TYPE_CHECKING:").unwrap();
|
||||
writeln!(output, " import pandas as pd # type: ignore[import-not-found]").unwrap();
|
||||
writeln!(output, " import polars as pl # type: ignore[import-not-found]\n").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" import pandas as pd # type: ignore[import-not-found]"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" import polars as pl # type: ignore[import-not-found]\n"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, "T = TypeVar('T')\n").unwrap();
|
||||
|
||||
types::generate_type_definitions(&mut output, schemas);
|
||||
|
||||
@@ -115,7 +115,13 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
}
|
||||
// Add endpoint path
|
||||
writeln!(output, " ///").unwrap();
|
||||
writeln!(output, " /// Endpoint: `{} {}`", endpoint.method.to_uppercase(), endpoint.path).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" /// Endpoint: `{} {}`",
|
||||
endpoint.method.to_uppercase(),
|
||||
endpoint.path
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let params = build_method_params(endpoint);
|
||||
writeln!(
|
||||
@@ -128,7 +134,12 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
let (path, index_arg) = build_path_template(endpoint);
|
||||
|
||||
if endpoint.query_params.is_empty() {
|
||||
writeln!(output, " self.base.get_json(&format!(\"{}\"{}))", path, index_arg).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" self.base.get_json(&format!(\"{}\"{}))",
|
||||
path, index_arg
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
writeln!(output, " let mut query = Vec::new();").unwrap();
|
||||
for param in &endpoint.query_params {
|
||||
@@ -149,13 +160,26 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) {
|
||||
}
|
||||
}
|
||||
writeln!(output, " let query_str = if query.is_empty() {{ String::new() }} else {{ format!(\"?{{}}\", query.join(\"&\")) }};").unwrap();
|
||||
writeln!(output, " let path = format!(\"{}{{}}\"{}, query_str);", path, index_arg).unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" let path = format!(\"{}{{}}\"{}, query_str);",
|
||||
path, index_arg
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if endpoint.supports_csv {
|
||||
writeln!(output, " if format == Some(Format::CSV) {{").unwrap();
|
||||
writeln!(output, " self.base.get_text(&path).map(FormatResponse::Csv)").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" self.base.get_text(&path).map(FormatResponse::Csv)"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, " }} else {{").unwrap();
|
||||
writeln!(output, " self.base.get_json(&path).map(FormatResponse::Json)").unwrap();
|
||||
writeln!(
|
||||
output,
|
||||
" self.base.get_json(&path).map(FormatResponse::Json)"
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(output, " }}").unwrap();
|
||||
} else {
|
||||
writeln!(output, " self.base.get_json(&path)").unwrap();
|
||||
@@ -199,7 +223,10 @@ fn param_type_to_rust(param_type: &str) -> String {
|
||||
|
||||
/// Build path template and extra format args for Index params.
|
||||
fn build_path_template(endpoint: &Endpoint) -> (String, &'static str) {
|
||||
let has_index_param = endpoint.path_params.iter().any(|p| p.name == "index" && p.param_type == "Index");
|
||||
let has_index_param = endpoint
|
||||
.path_params
|
||||
.iter()
|
||||
.any(|p| p.name == "index" && p.param_type == "Index");
|
||||
if has_index_param {
|
||||
(endpoint.path.replace("{index}", "{}"), ", index.name()")
|
||||
} else {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use brk_types::Age;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Age;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -32,9 +32,9 @@ pub const HOURS_15Y: usize = 24 * 15 * 365;
|
||||
/// Age boundaries in hours. Defines the cohort ranges:
|
||||
/// [0, 1h), [1h, 1d), [1d, 1w), [1w, 1m), ..., [15y, ∞)
|
||||
pub const AGE_BOUNDARIES: [usize; 20] = [
|
||||
HOURS_1H, HOURS_1D, HOURS_1W, HOURS_1M, HOURS_2M, HOURS_3M, HOURS_4M,
|
||||
HOURS_5M, HOURS_6M, HOURS_1Y, HOURS_2Y, HOURS_3Y, HOURS_4Y, HOURS_5Y,
|
||||
HOURS_6Y, HOURS_7Y, HOURS_8Y, HOURS_10Y, HOURS_12Y, HOURS_15Y,
|
||||
HOURS_1H, HOURS_1D, HOURS_1W, HOURS_1M, HOURS_2M, HOURS_3M, HOURS_4M, HOURS_5M, HOURS_6M,
|
||||
HOURS_1Y, HOURS_2Y, HOURS_3Y, HOURS_4Y, HOURS_5Y, HOURS_6Y, HOURS_7Y, HOURS_8Y, HOURS_10Y,
|
||||
HOURS_12Y, HOURS_15Y,
|
||||
];
|
||||
|
||||
/// Age range bounds (end = usize::MAX means unbounded)
|
||||
|
||||
@@ -81,11 +81,7 @@ pub const AMOUNT_RANGE_BOUNDS: ByAmountRange<Range<Sats>> = ByAmountRange {
|
||||
pub const AMOUNT_RANGE_NAMES: ByAmountRange<CohortName> = ByAmountRange {
|
||||
_0sats: CohortName::new("with_0sats", "0 sats", "0 Sats"),
|
||||
_1sat_to_10sats: CohortName::new("above_1sat_under_10sats", "1-10 sats", "1-10 Sats"),
|
||||
_10sats_to_100sats: CohortName::new(
|
||||
"above_10sats_under_100sats",
|
||||
"10-100 sats",
|
||||
"10-100 Sats",
|
||||
),
|
||||
_10sats_to_100sats: CohortName::new("above_10sats_under_100sats", "10-100 sats", "10-100 Sats"),
|
||||
_100sats_to_1k_sats: CohortName::new(
|
||||
"above_100sats_under_1k_sats",
|
||||
"100-1k sats",
|
||||
@@ -115,11 +111,7 @@ pub const AMOUNT_RANGE_NAMES: ByAmountRange<CohortName> = ByAmountRange {
|
||||
_1btc_to_10btc: CohortName::new("above_1btc_under_10btc", "1-10 BTC", "1-10 BTC"),
|
||||
_10btc_to_100btc: CohortName::new("above_10btc_under_100btc", "10-100 BTC", "10-100 BTC"),
|
||||
_100btc_to_1k_btc: CohortName::new("above_100btc_under_1k_btc", "100-1k BTC", "100-1K BTC"),
|
||||
_1k_btc_to_10k_btc: CohortName::new(
|
||||
"above_1k_btc_under_10k_btc",
|
||||
"1k-10k BTC",
|
||||
"1K-10K BTC",
|
||||
),
|
||||
_1k_btc_to_10k_btc: CohortName::new("above_1k_btc_under_10k_btc", "1k-10k BTC", "1K-10K BTC"),
|
||||
_10k_btc_to_100k_btc: CohortName::new(
|
||||
"above_10k_btc_under_100k_btc",
|
||||
"10k-100k BTC",
|
||||
|
||||
@@ -3,9 +3,9 @@ use rayon::prelude::*;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{
|
||||
CohortName, Filter, TimeFilter, HOURS_10Y, HOURS_12Y, HOURS_15Y, HOURS_1M, HOURS_1W, HOURS_1Y,
|
||||
HOURS_2M, HOURS_2Y, HOURS_3M, HOURS_3Y, HOURS_4M, HOURS_4Y, HOURS_5M, HOURS_5Y, HOURS_6M,
|
||||
HOURS_6Y, HOURS_7Y, HOURS_8Y,
|
||||
CohortName, Filter, HOURS_1M, HOURS_1W, HOURS_1Y, HOURS_2M, HOURS_2Y, HOURS_3M, HOURS_3Y,
|
||||
HOURS_4M, HOURS_4Y, HOURS_5M, HOURS_5Y, HOURS_6M, HOURS_6Y, HOURS_7Y, HOURS_8Y, HOURS_10Y,
|
||||
HOURS_12Y, HOURS_15Y, TimeFilter,
|
||||
};
|
||||
|
||||
/// Max age thresholds in hours
|
||||
|
||||
@@ -3,9 +3,9 @@ use rayon::prelude::*;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{
|
||||
CohortName, Filter, TimeFilter, HOURS_10Y, HOURS_12Y, HOURS_1D, HOURS_1M, HOURS_1W, HOURS_1Y,
|
||||
HOURS_2M, HOURS_2Y, HOURS_3M, HOURS_3Y, HOURS_4M, HOURS_4Y, HOURS_5M, HOURS_5Y, HOURS_6M,
|
||||
HOURS_6Y, HOURS_7Y, HOURS_8Y,
|
||||
CohortName, Filter, HOURS_1D, HOURS_1M, HOURS_1W, HOURS_1Y, HOURS_2M, HOURS_2Y, HOURS_3M,
|
||||
HOURS_3Y, HOURS_4M, HOURS_4Y, HOURS_5M, HOURS_5Y, HOURS_6M, HOURS_6Y, HOURS_7Y, HOURS_8Y,
|
||||
HOURS_10Y, HOURS_12Y, TimeFilter,
|
||||
};
|
||||
|
||||
/// Min age thresholds in hours
|
||||
|
||||
@@ -28,9 +28,11 @@ impl CohortContext {
|
||||
/// - Context prefix: `Time`, `Amount`
|
||||
pub fn full_name(&self, filter: &Filter, name: &str) -> String {
|
||||
match filter {
|
||||
Filter::All | Filter::Term(_) | Filter::Epoch(_) | Filter::Year(_) | Filter::Type(_) => {
|
||||
name.to_string()
|
||||
}
|
||||
Filter::All
|
||||
| Filter::Term(_)
|
||||
| Filter::Epoch(_)
|
||||
| Filter::Year(_)
|
||||
| Filter::Type(_) => name.to_string(),
|
||||
Filter::Time(_) | Filter::Amount(_) => self.prefixed(name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ mod by_amount_range;
|
||||
mod by_any_address;
|
||||
mod by_epoch;
|
||||
mod by_ge_amount;
|
||||
mod by_year;
|
||||
mod by_lt_amount;
|
||||
mod by_max_age;
|
||||
mod by_min_age;
|
||||
@@ -16,6 +15,7 @@ mod by_spendable_type;
|
||||
mod by_term;
|
||||
mod by_type;
|
||||
mod by_unspendable_type;
|
||||
mod by_year;
|
||||
mod cohort_context;
|
||||
mod cohort_name;
|
||||
mod filter;
|
||||
|
||||
@@ -22,7 +22,8 @@ impl Vecs {
|
||||
.compute(indexer, &self.time, starting_indexes, exit)?;
|
||||
self.interval
|
||||
.compute(indexer, &self.count, starting_indexes, exit)?;
|
||||
self.size.compute(indexer, &self.count, starting_indexes, exit)?;
|
||||
self.size
|
||||
.compute(indexer, &self.count, starting_indexes, exit)?;
|
||||
self.weight
|
||||
.compute(indexer, &self.count, starting_indexes, exit)?;
|
||||
self.difficulty
|
||||
|
||||
@@ -32,120 +32,54 @@ impl Vecs {
|
||||
self.compute_rolling_start_hours(time, starting_indexes, exit, 1, |s| {
|
||||
&mut s.height_1h_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1, |s| {
|
||||
&mut s.height_24h_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 3, |s| {
|
||||
&mut s.height_3d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 7, |s| {
|
||||
&mut s.height_1w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 8, |s| {
|
||||
&mut s.height_8d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 9, |s| {
|
||||
&mut s.height_9d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 12, |s| {
|
||||
&mut s.height_12d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 13, |s| {
|
||||
&mut s.height_13d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 14, |s| {
|
||||
&mut s.height_2w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 21, |s| {
|
||||
&mut s.height_21d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 26, |s| {
|
||||
&mut s.height_26d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 30, |s| {
|
||||
&mut s.height_1m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 34, |s| {
|
||||
&mut s.height_34d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 55, |s| {
|
||||
&mut s.height_55d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 60, |s| {
|
||||
&mut s.height_2m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 63, |s| {
|
||||
&mut s.height_9w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 84, |s| {
|
||||
&mut s.height_12w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 89, |s| {
|
||||
&mut s.height_89d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 90, |s| {
|
||||
&mut s.height_3m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 98, |s| {
|
||||
&mut s.height_14w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1, |s| &mut s.height_24h_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 3, |s| &mut s.height_3d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 7, |s| &mut s.height_1w_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 8, |s| &mut s.height_8d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 9, |s| &mut s.height_9d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 12, |s| &mut s.height_12d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 13, |s| &mut s.height_13d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 14, |s| &mut s.height_2w_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 21, |s| &mut s.height_21d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 26, |s| &mut s.height_26d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 30, |s| &mut s.height_1m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 34, |s| &mut s.height_34d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 55, |s| &mut s.height_55d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 60, |s| &mut s.height_2m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 63, |s| &mut s.height_9w_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 84, |s| &mut s.height_12w_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 89, |s| &mut s.height_89d_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 90, |s| &mut s.height_3m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 98, |s| &mut s.height_14w_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 111, |s| {
|
||||
&mut s.height_111d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 144, |s| {
|
||||
&mut s.height_144d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 180, |s| {
|
||||
&mut s.height_6m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 182, |s| {
|
||||
&mut s.height_26w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 180, |s| &mut s.height_6m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 182, |s| &mut s.height_26w_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 200, |s| {
|
||||
&mut s.height_200d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 270, |s| {
|
||||
&mut s.height_9m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 270, |s| &mut s.height_9m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 350, |s| {
|
||||
&mut s.height_350d_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 360, |s| {
|
||||
&mut s.height_12m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 365, |s| {
|
||||
&mut s.height_1y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 420, |s| {
|
||||
&mut s.height_14m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 730, |s| {
|
||||
&mut s.height_2y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 780, |s| {
|
||||
&mut s.height_26m_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1095, |s| {
|
||||
&mut s.height_3y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 360, |s| &mut s.height_12m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 365, |s| &mut s.height_1y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 420, |s| &mut s.height_14m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 730, |s| &mut s.height_2y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 780, |s| &mut s.height_26m_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1095, |s| &mut s.height_3y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1400, |s| {
|
||||
&mut s.height_200w_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1460, |s| {
|
||||
&mut s.height_4y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1825, |s| {
|
||||
&mut s.height_5y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 2190, |s| {
|
||||
&mut s.height_6y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 2920, |s| {
|
||||
&mut s.height_8y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 3285, |s| {
|
||||
&mut s.height_9y_ago
|
||||
})?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1460, |s| &mut s.height_4y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 1825, |s| &mut s.height_5y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 2190, |s| &mut s.height_6y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 2920, |s| &mut s.height_8y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 3285, |s| &mut s.height_9y_ago)?;
|
||||
self.compute_rolling_start(time, starting_indexes, exit, 3650, |s| {
|
||||
&mut s.height_10y_ago
|
||||
})?;
|
||||
@@ -193,13 +127,9 @@ impl Vecs {
|
||||
where
|
||||
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
||||
{
|
||||
self.compute_rolling_start_inner(
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
get_field,
|
||||
|t, prev_ts| t.difference_in_days_between(prev_ts) >= days,
|
||||
)
|
||||
self.compute_rolling_start_inner(time, starting_indexes, exit, get_field, |t, prev_ts| {
|
||||
t.difference_in_days_between(prev_ts) >= days
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_rolling_start_hours<F>(
|
||||
@@ -213,13 +143,9 @@ impl Vecs {
|
||||
where
|
||||
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
||||
{
|
||||
self.compute_rolling_start_inner(
|
||||
time,
|
||||
starting_indexes,
|
||||
exit,
|
||||
get_field,
|
||||
|t, prev_ts| t.difference_in_hours_between(prev_ts) >= hours,
|
||||
)
|
||||
self.compute_rolling_start_inner(time, starting_indexes, exit, get_field, |t, prev_ts| {
|
||||
t.difference_in_hours_between(prev_ts) >= hours
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_rolling_start_inner<F, D>(
|
||||
|
||||
@@ -9,7 +9,11 @@ use crate::{
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
block_count_target: ConstantVecs::new::<BlockCountTarget>(
|
||||
"block_count_target",
|
||||
|
||||
@@ -12,54 +12,52 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub block_count: ComputedFromHeightCumulativeSum<StoredU32, M>,
|
||||
pub block_count_sum: RollingWindows<StoredU32, M>,
|
||||
|
||||
// Window starts sorted by duration
|
||||
pub height_1h_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_24h_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1d
|
||||
pub height_24h_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1d
|
||||
pub height_3d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_1w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 7d
|
||||
pub height_1w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 7d
|
||||
pub height_8d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_9d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_12d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_13d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_2w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 14d
|
||||
pub height_2w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 14d
|
||||
pub height_21d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_26d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_1m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 30d
|
||||
pub height_1m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 30d
|
||||
pub height_34d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_55d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_2m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 60d
|
||||
pub height_9w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 63d
|
||||
pub height_12w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 84d
|
||||
pub height_2m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 60d
|
||||
pub height_9w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 63d
|
||||
pub height_12w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 84d
|
||||
pub height_89d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_3m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 90d
|
||||
pub height_14w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 98d
|
||||
pub height_3m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 90d
|
||||
pub height_14w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 98d
|
||||
pub height_111d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_144d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_6m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 180d
|
||||
pub height_26w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 182d
|
||||
pub height_6m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 180d
|
||||
pub height_26w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 182d
|
||||
pub height_200d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_9m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 270d
|
||||
pub height_9m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 270d
|
||||
pub height_350d_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>,
|
||||
pub height_12m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 360d
|
||||
pub height_1y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 365d
|
||||
pub height_14m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 420d
|
||||
pub height_2y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 730d
|
||||
pub height_26m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 780d
|
||||
pub height_3y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1095d
|
||||
pub height_200w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1400d
|
||||
pub height_4y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1460d
|
||||
pub height_5y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1825d
|
||||
pub height_6y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 2190d
|
||||
pub height_8y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 2920d
|
||||
pub height_9y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 3285d
|
||||
pub height_10y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 3650d
|
||||
pub height_12y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 4380d
|
||||
pub height_14y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 5110d
|
||||
pub height_26y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 9490d
|
||||
pub height_12m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 360d
|
||||
pub height_1y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 365d
|
||||
pub height_14m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 420d
|
||||
pub height_2y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 730d
|
||||
pub height_26m_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 780d
|
||||
pub height_3y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1095d
|
||||
pub height_200w_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1400d
|
||||
pub height_4y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1460d
|
||||
pub height_5y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 1825d
|
||||
pub height_6y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 2190d
|
||||
pub height_8y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 2920d
|
||||
pub height_9y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 3285d
|
||||
pub height_10y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 3650d
|
||||
pub height_12y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 4380d
|
||||
pub height_14y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 5110d
|
||||
pub height_26y_ago: M::Stored<EagerVec<PcoVec<Height, Height>>>, // 9490d
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
/// Get the standard 4 rolling window start heights (24h, 1w, 1m, 1y).
|
||||
pub fn window_starts(&self) -> WindowStarts<'_> {
|
||||
WindowStarts {
|
||||
_24h: &self.height_24h_ago,
|
||||
|
||||
@@ -43,12 +43,14 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
// Compute blocks before next adjustment
|
||||
self.blocks_before_next_adjustment.height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.identity,
|
||||
|(h, ..)| (h, StoredU32::from(h.left_before_next_diff_adj())),
|
||||
exit,
|
||||
)?;
|
||||
self.blocks_before_next_adjustment
|
||||
.height
|
||||
.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexes.height.identity,
|
||||
|(h, ..)| (h, StoredU32::from(h.left_before_next_diff_adj())),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Compute days before next adjustment
|
||||
self.days_before_next_adjustment.height.compute_transform(
|
||||
|
||||
@@ -26,7 +26,12 @@ impl Vecs {
|
||||
indexes,
|
||||
),
|
||||
as_hash: ComputedFromHeight::forced_import(db, "difficulty_as_hash", version, indexes)?,
|
||||
adjustment: PercentFromHeight::forced_import(db, "difficulty_adjustment", version, indexes)?,
|
||||
adjustment: PercentFromHeight::forced_import(
|
||||
db,
|
||||
"difficulty_adjustment",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
epoch: ComputedFromHeight::forced_import(db, "difficulty_epoch", version, indexes)?,
|
||||
blocks_before_next_adjustment: ComputedFromHeight::forced_import(
|
||||
db,
|
||||
|
||||
@@ -3,8 +3,6 @@ use brk_types::{BasisPointsSigned32, DifficultyEpoch, StoredF32, StoredF64, Stor
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedFromHeight, ComputedHeightDerived, PercentFromHeight};
|
||||
|
||||
/// Difficulty metrics: raw difficulty, derived stats, adjustment, and countdown
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub raw: ComputedHeightDerived<StoredF64>,
|
||||
|
||||
@@ -3,8 +3,6 @@ use brk_types::{HalvingEpoch, StoredF32, StoredU32};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::ComputedFromHeight;
|
||||
|
||||
/// Halving epoch metrics and countdown
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub epoch: ComputedFromHeight<HalvingEpoch, M>,
|
||||
|
||||
@@ -2,15 +2,15 @@ use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, PAGE_SIZE};
|
||||
|
||||
use crate::indexes;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{finalize_db, open_db},
|
||||
};
|
||||
|
||||
use super::{
|
||||
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, SizeVecs,
|
||||
TimeVecs, Vecs, WeightVecs,
|
||||
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, SizeVecs, TimeVecs, Vecs, WeightVecs,
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
@@ -20,9 +20,7 @@ impl Vecs {
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent_path.join(super::DB_NAME))?;
|
||||
db.set_min_len(PAGE_SIZE * 50_000_000)?;
|
||||
|
||||
let db = open_db(parent_path, super::DB_NAME, 50_000_000)?;
|
||||
let version = parent_version;
|
||||
|
||||
let count = CountVecs::forced_import(&db, version, indexes)?;
|
||||
@@ -43,14 +41,7 @@ impl Vecs {
|
||||
difficulty,
|
||||
halving,
|
||||
};
|
||||
|
||||
this.db.retain_regions(
|
||||
this.iter_any_exportable()
|
||||
.flat_map(|v| v.region_names())
|
||||
.collect(),
|
||||
)?;
|
||||
this.db.compact()?;
|
||||
|
||||
finalize_db(&this.db, &this)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,8 @@ impl Vecs {
|
||||
) -> Result<()> {
|
||||
let mut prev_timestamp = None;
|
||||
let window_starts = count_vecs.window_starts();
|
||||
self.0.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
exit,
|
||||
|vec| {
|
||||
self.0
|
||||
.compute(starting_indexes.height, &window_starts, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.timestamp,
|
||||
@@ -39,8 +36,7 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@ use crate::internal::ComputedFromHeightDistribution;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw>(
|
||||
#[traversable(flatten)]
|
||||
pub ComputedFromHeightDistribution<Timestamp, M>,
|
||||
#[traversable(flatten)] pub ComputedFromHeightDistribution<Timestamp, M>,
|
||||
);
|
||||
|
||||
@@ -17,19 +17,15 @@ impl Vecs {
|
||||
let window_starts = count_vecs.window_starts();
|
||||
|
||||
// vbytes = floor(weight / 4), stored at height level
|
||||
self.vbytes.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
exit,
|
||||
|height| {
|
||||
self.vbytes
|
||||
.compute(starting_indexes.height, &window_starts, exit, |height| {
|
||||
Ok(height.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
|(h, weight, ..)| (h, StoredU64::from(weight.to_vbytes_floor())),
|
||||
exit,
|
||||
)?)
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
|
||||
// size from indexer total_size
|
||||
self.size.compute(
|
||||
|
||||
@@ -15,18 +15,8 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
vbytes: ComputedFromHeightFull::forced_import(
|
||||
db,
|
||||
"block_vbytes",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
size: ComputedHeightDerivedFull::forced_import(
|
||||
db,
|
||||
"block_size",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
vbytes: ComputedFromHeightFull::forced_import(db, "block_vbytes", version, indexes)?,
|
||||
size: ComputedHeightDerivedFull::forced_import(db, "block_size", version, indexes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ impl Vecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let timestamp_monotonic =
|
||||
EagerVec::forced_import(db, "timestamp_monotonic", version)?;
|
||||
let timestamp_monotonic = EagerVec::forced_import(db, "timestamp_monotonic", version)?;
|
||||
|
||||
Ok(Self {
|
||||
date: LazyVecFrom1::init(
|
||||
@@ -28,11 +27,7 @@ impl Vecs {
|
||||
}
|
||||
|
||||
impl TimestampIndexes {
|
||||
fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
macro_rules! period {
|
||||
($field:ident) => {
|
||||
LazyVecFrom1::init(
|
||||
@@ -50,6 +45,22 @@ impl TimestampIndexes {
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self(crate::indexes_from!(period, epoch)))
|
||||
Ok(Self(crate::internal::PerPeriod {
|
||||
minute10: period!(minute10),
|
||||
minute30: period!(minute30),
|
||||
hour1: period!(hour1),
|
||||
hour4: period!(hour4),
|
||||
hour12: period!(hour12),
|
||||
day1: period!(day1),
|
||||
day3: period!(day3),
|
||||
week1: period!(week1),
|
||||
month1: period!(month1),
|
||||
month3: period!(month3),
|
||||
month6: period!(month6),
|
||||
year1: period!(year1),
|
||||
year10: period!(year10),
|
||||
halvingepoch: epoch!(halvingepoch),
|
||||
difficultyepoch: epoch!(difficultyepoch),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Date, Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4,
|
||||
Indexes, Minute10, Minute30, Month1, Month3, Month6, Timestamp, Week1, Year1, Year10,
|
||||
Date, Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Indexes,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Timestamp, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{EagerVec, Exit, LazyVecFrom1, PcoVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{indexes, internal::PerPeriod};
|
||||
|
||||
/// Timestamp and date metrics for blocks
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub date: LazyVecFrom1<Height, Date, Height, Timestamp>,
|
||||
@@ -58,13 +56,21 @@ impl TimestampIndexes {
|
||||
) -> Result<()> {
|
||||
let prev_height = starting_indexes.height.decremented().unwrap_or_default();
|
||||
self.halvingepoch.compute_indirect_sequential(
|
||||
indexes.height.halvingepoch.collect_one(prev_height).unwrap_or_default(),
|
||||
indexes
|
||||
.height
|
||||
.halvingepoch
|
||||
.collect_one(prev_height)
|
||||
.unwrap_or_default(),
|
||||
&indexes.halvingepoch.first_height,
|
||||
&indexer.vecs.blocks.timestamp,
|
||||
exit,
|
||||
)?;
|
||||
self.difficultyepoch.compute_indirect_sequential(
|
||||
indexes.height.difficultyepoch.collect_one(prev_height).unwrap_or_default(),
|
||||
indexes
|
||||
.height
|
||||
.difficultyepoch
|
||||
.collect_one(prev_height)
|
||||
.unwrap_or_default(),
|
||||
&indexes.difficultyepoch.first_height,
|
||||
&indexer.vecs.blocks.timestamp,
|
||||
exit,
|
||||
|
||||
@@ -23,11 +23,8 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.fullness.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
exit,
|
||||
|vec| {
|
||||
self.fullness
|
||||
.compute(starting_indexes.height, &window_starts, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
@@ -35,8 +32,7 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -14,12 +14,8 @@ impl Vecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let weight = ComputedHeightDerivedFull::forced_import(
|
||||
db,
|
||||
"block_weight",
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
let weight =
|
||||
ComputedHeightDerivedFull::forced_import(db, "block_weight", version, indexes)?;
|
||||
|
||||
let fullness =
|
||||
PercentFromHeightDistribution::forced_import(db, "block_fullness", version, indexes)?;
|
||||
|
||||
@@ -5,11 +5,15 @@ use vecdb::Database;
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightCumulativeSum, ComputedFromHeight},
|
||||
internal::{ComputedFromHeight, ComputedFromHeightCumulativeSum},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
coinblocks_created: ComputedFromHeightCumulativeSum::forced_import(
|
||||
db,
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
|
||||
use brk_types::StoredF64;
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{ComputedFromHeightCumulativeSum, ComputedFromHeight};
|
||||
use crate::internal::{ComputedFromHeight, ComputedFromHeightCumulativeSum};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
|
||||
@@ -14,31 +14,35 @@ impl Vecs {
|
||||
activity: &activity::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.cointime_adj_inflation_rate.bps.height.compute_transform2(
|
||||
self.cointime_adj_inflation_rate
|
||||
.bps
|
||||
.height
|
||||
.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&activity.liveliness.height,
|
||||
&supply.inflation_rate.bps.height,
|
||||
|(h, liveliness, inflation, ..)| {
|
||||
(
|
||||
h,
|
||||
BasisPointsSigned32::from(f64::from(liveliness) * f64::from(inflation)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cointime_adj_tx_velocity_btc.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&activity.liveliness.height,
|
||||
&supply.inflation_rate.bps.height,
|
||||
|(h, liveliness, inflation, ..)| (h, BasisPointsSigned32::from(f64::from(liveliness) * f64::from(inflation))),
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.velocity.btc.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cointime_adj_tx_velocity_btc
|
||||
.height
|
||||
.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.velocity.btc.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.cointime_adj_tx_velocity_usd
|
||||
.height
|
||||
.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.velocity.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
self.cointime_adj_tx_velocity_usd.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&activity.activity_to_vaultedness_ratio.height,
|
||||
&supply.velocity.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -9,7 +9,11 @@ use crate::{
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
cointime_adj_inflation_rate: PercentFromHeight::forced_import(
|
||||
db,
|
||||
|
||||
@@ -6,7 +6,11 @@ use super::Vecs;
|
||||
use crate::{indexes, internal::FiatFromHeight};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
thermo_cap: FiatFromHeight::forced_import(db, "thermo_cap", version, indexes)?,
|
||||
investor_cap: FiatFromHeight::forced_import(db, "investor_cap", version, indexes)?,
|
||||
|
||||
@@ -22,12 +22,8 @@ impl Vecs {
|
||||
.compute(starting_indexes, blocks, distribution, exit)?;
|
||||
|
||||
// Supply computes next (depends on activity)
|
||||
self.supply.compute(
|
||||
starting_indexes,
|
||||
distribution,
|
||||
&self.activity,
|
||||
exit,
|
||||
)?;
|
||||
self.supply
|
||||
.compute(starting_indexes, distribution, &self.activity, exit)?;
|
||||
|
||||
// Adjusted velocity metrics (BTC) - can compute without price
|
||||
self.adjusted
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use vecdb::{Database, PAGE_SIZE};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{finalize_db, open_db},
|
||||
};
|
||||
|
||||
use super::{
|
||||
ActivityVecs, AdjustedVecs, CapVecs, DB_NAME, PricingVecs, ReserveRiskVecs, SupplyVecs,
|
||||
ValueVecs, Vecs,
|
||||
};
|
||||
use crate::indexes;
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -17,9 +19,7 @@ impl Vecs {
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let db = Database::open(&parent_path.join(DB_NAME))?;
|
||||
db.set_min_len(PAGE_SIZE * 1_000_000)?;
|
||||
|
||||
let db = open_db(parent_path, DB_NAME, 1_000_000)?;
|
||||
let version = parent_version;
|
||||
let v1 = version + Version::ONE;
|
||||
let activity = ActivityVecs::forced_import(&db, version, indexes)?;
|
||||
@@ -40,14 +40,7 @@ impl Vecs {
|
||||
adjusted,
|
||||
reserve_risk,
|
||||
};
|
||||
|
||||
this.db.retain_regions(
|
||||
this.iter_any_exportable()
|
||||
.flat_map(|v| v.region_names())
|
||||
.collect(),
|
||||
)?;
|
||||
this.db.compact()?;
|
||||
|
||||
finalize_db(&this.db, &this)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use brk_types::{Indexes, StoredF64};
|
||||
use vecdb::Exit;
|
||||
|
||||
use super::{super::value, Vecs};
|
||||
use crate::{blocks, prices, traits::ComputeRollingMedianFromStarts};
|
||||
use crate::{blocks, internal::ComputeRollingMedianFromStarts, prices};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn compute(
|
||||
|
||||
@@ -12,18 +12,8 @@ impl Vecs {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
vaulted_supply: ValueFromHeight::forced_import(
|
||||
db,
|
||||
"vaulted_supply",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
active_supply: ValueFromHeight::forced_import(
|
||||
db,
|
||||
"active_supply",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
vaulted_supply: ValueFromHeight::forced_import(db, "vaulted_supply", version, indexes)?,
|
||||
active_supply: ValueFromHeight::forced_import(db, "active_supply", version, indexes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,11 @@ impl Vecs {
|
||||
.btc
|
||||
.height;
|
||||
|
||||
self.cointime_value_destroyed
|
||||
.compute(starting_indexes.height, &window_starts, exit, |vec| {
|
||||
self.cointime_value_destroyed.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
exit,
|
||||
|vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
@@ -50,10 +53,14 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
|
||||
self.cointime_value_created
|
||||
.compute(starting_indexes.height, &window_starts, exit, |vec| {
|
||||
self.cointime_value_created.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
exit,
|
||||
|vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
@@ -61,10 +68,14 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
|
||||
self.cointime_value_stored
|
||||
.compute(starting_indexes.height, &window_starts, exit, |vec| {
|
||||
self.cointime_value_stored.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
exit,
|
||||
|vec| {
|
||||
vec.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&prices.price.usd.height,
|
||||
@@ -72,7 +83,8 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
|
||||
// VOCDD: Value of Coin Days Destroyed = price × (CDD / circulating_supply)
|
||||
// Supply-adjusted to account for growing supply over time
|
||||
|
||||
@@ -6,7 +6,11 @@ use super::Vecs;
|
||||
use crate::{indexes, internal::ComputedFromHeightCumulativeSum};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
cointime_value_destroyed: ComputedFromHeightCumulativeSum::forced_import(
|
||||
db,
|
||||
|
||||
@@ -22,7 +22,10 @@ use derive_more::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{indexes, internal::{ComputedFromHeightDistribution, WindowStarts}};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightDistribution, WindowStarts},
|
||||
};
|
||||
|
||||
/// Per-block activity counts - reset each block.
|
||||
///
|
||||
@@ -137,7 +140,9 @@ impl ActivityCountVecs {
|
||||
.min(self.both.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
[
|
||||
&mut self.reactivated.height as &mut dyn AnyStoredVec,
|
||||
&mut self.sending.height as &mut dyn AnyStoredVec,
|
||||
@@ -181,9 +186,7 @@ impl ActivityCountVecs {
|
||||
self.balance_decreased
|
||||
.height
|
||||
.truncate_push(height, (counts.sending - counts.both).into())?;
|
||||
self.both
|
||||
.height
|
||||
.truncate_push(height, counts.both.into())?;
|
||||
self.both.height.truncate_push(height, counts.both.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -196,8 +199,10 @@ impl ActivityCountVecs {
|
||||
self.reactivated.compute_rest(max_from, windows, exit)?;
|
||||
self.sending.compute_rest(max_from, windows, exit)?;
|
||||
self.receiving.compute_rest(max_from, windows, exit)?;
|
||||
self.balance_increased.compute_rest(max_from, windows, exit)?;
|
||||
self.balance_decreased.compute_rest(max_from, windows, exit)?;
|
||||
self.balance_increased
|
||||
.compute_rest(max_from, windows, exit)?;
|
||||
self.balance_decreased
|
||||
.compute_rest(max_from, windows, exit)?;
|
||||
self.both.compute_rest(max_from, windows, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -223,16 +228,27 @@ impl AddressTypeToActivityCountVecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self::from(
|
||||
ByAddressType::<ActivityCountVecs>::new_with_name(|type_name| {
|
||||
ActivityCountVecs::forced_import(db, &format!("{type_name}_{name}"), version, indexes)
|
||||
ActivityCountVecs::forced_import(
|
||||
db,
|
||||
&format!("{type_name}_{name}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.0.values().map(|v| v.min_stateful_height()).min().unwrap_or(0)
|
||||
self.0
|
||||
.values()
|
||||
.map(|v| v.min_stateful_height())
|
||||
.min()
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
let mut vecs: Vec<&mut dyn AnyStoredVec> = Vec::new();
|
||||
for type_vecs in self.0.values_mut() {
|
||||
vecs.push(&mut type_vecs.reactivated.height);
|
||||
@@ -274,7 +290,6 @@ impl AddressTypeToActivityCountVecs {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Storage for activity metrics (global + per type).
|
||||
@@ -301,10 +316,14 @@ impl AddressActivityVecs {
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.all.min_stateful_height().min(self.by_addresstype.min_stateful_height())
|
||||
self.all
|
||||
.min_stateful_height()
|
||||
.min(self.by_addresstype.min_stateful_height())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.all
|
||||
.par_iter_height_mut()
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
@@ -337,5 +356,4 @@ impl AddressActivityVecs {
|
||||
self.by_addresstype.truncate_push_height(height, counts)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -70,14 +70,62 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
fn from((groups, starting_height): (&AddressTypeToAddrCountVecs, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups.p2pk65.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2pk33: groups.p2pk33.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2pkh: groups.p2pkh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2sh: groups.p2sh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2wpkh: groups.p2wpkh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2wsh: groups.p2wsh.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2tr: groups.p2tr.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2a: groups.p2a.count.height.collect_one(prev_height).unwrap().into(),
|
||||
p2pk65: groups
|
||||
.p2pk65
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2pk33: groups
|
||||
.p2pk33
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2pkh: groups
|
||||
.p2pkh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2sh: groups
|
||||
.p2sh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2wpkh: groups
|
||||
.p2wpkh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2wsh: groups
|
||||
.p2wsh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2tr: groups
|
||||
.p2tr
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2a: groups
|
||||
.p2a
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
})
|
||||
} else {
|
||||
Default::default()
|
||||
@@ -103,24 +151,23 @@ impl AddressTypeToAddrCountVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self::from(
|
||||
ByAddressType::<AddrCountVecs>::new_with_name(|type_name| {
|
||||
AddrCountVecs::forced_import(
|
||||
db,
|
||||
&format!("{type_name}_{name}"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?,
|
||||
))
|
||||
Ok(Self::from(ByAddressType::<AddrCountVecs>::new_with_name(
|
||||
|type_name| {
|
||||
AddrCountVecs::forced_import(db, &format!("{type_name}_{name}"), version, indexes)
|
||||
},
|
||||
)?))
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.0.values().map(|v| v.count.height.len()).min().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.0.par_values_mut().map(|v| &mut v.count.height as &mut dyn AnyStoredVec)
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.0
|
||||
.par_values_mut()
|
||||
.map(|v| &mut v.count.height as &mut dyn AnyStoredVec)
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push_height(
|
||||
@@ -180,10 +227,16 @@ impl AddrCountsVecs {
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.all.count.height.len().min(self.by_addresstype.min_stateful_height())
|
||||
self.all
|
||||
.count
|
||||
.height
|
||||
.len()
|
||||
.min(self.by_addresstype.min_stateful_height())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.all.count.height as &mut dyn AnyStoredVec)
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ use brk_types::{
|
||||
EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, Height,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec,
|
||||
};
|
||||
use vecdb::{AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec};
|
||||
|
||||
/// Storage for both funded and empty address data.
|
||||
#[derive(Traversable)]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! Growth rate: new_addr_count / addr_count (global + per-type)
|
||||
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
@@ -27,12 +25,8 @@ impl GrowthRateVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = PercentFromHeightDistribution::forced_import(
|
||||
db,
|
||||
"growth_rate",
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
let all =
|
||||
PercentFromHeightDistribution::forced_import(db, "growth_rate", version, indexes)?;
|
||||
|
||||
let by_addresstype = ByAddressType::new_with_name(|name| {
|
||||
PercentFromHeightDistribution::forced_import(
|
||||
@@ -43,7 +37,10 @@ impl GrowthRateVecs {
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Self { all, by_addresstype })
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
@@ -64,24 +61,14 @@ impl GrowthRateVecs {
|
||||
)
|
||||
})?;
|
||||
|
||||
for ((_, growth), ((_, new), (_, addr))) in self
|
||||
.by_addresstype
|
||||
.iter_mut()
|
||||
.zip(
|
||||
new_addr_count
|
||||
.by_addresstype
|
||||
.iter()
|
||||
.zip(addr_count.by_addresstype.iter()),
|
||||
)
|
||||
{
|
||||
for ((_, growth), ((_, new), (_, addr))) in self.by_addresstype.iter_mut().zip(
|
||||
new_addr_count
|
||||
.by_addresstype
|
||||
.iter()
|
||||
.zip(addr_count.by_addresstype.iter()),
|
||||
) {
|
||||
growth.compute(max_from, windows, exit, |target| {
|
||||
compute_ratio(
|
||||
target,
|
||||
max_from,
|
||||
&new.height,
|
||||
&addr.count.height,
|
||||
exit,
|
||||
)
|
||||
compute_ratio(target, max_from, &new.height, &addr.count.height, exit)
|
||||
})?;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
//! New address count: per-block delta of total_addr_count (global + per-type)
|
||||
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU64, Version};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{indexes, internal::{ComputedFromHeightFull, WindowStarts}};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightFull, WindowStarts},
|
||||
};
|
||||
|
||||
use super::TotalAddrCountVecs;
|
||||
|
||||
@@ -24,12 +25,7 @@ impl NewAddrCountVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = ComputedFromHeightFull::forced_import(
|
||||
db,
|
||||
"new_addr_count",
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
let all = ComputedFromHeightFull::forced_import(db, "new_addr_count", version, indexes)?;
|
||||
|
||||
let by_addresstype: ByAddressType<ComputedFromHeightFull<StoredU64>> =
|
||||
ByAddressType::new_with_name(|name| {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
//! Total address count: addr_count + empty_addr_count (global + per-type)
|
||||
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
@@ -24,25 +22,22 @@ impl TotalAddrCountVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = ComputedFromHeight::forced_import(
|
||||
db,
|
||||
"total_addr_count",
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
let all = ComputedFromHeight::forced_import(db, "total_addr_count", version, indexes)?;
|
||||
|
||||
let by_addresstype: ByAddressType<ComputedFromHeight<StoredU64>> = ByAddressType::new_with_name(
|
||||
|name| {
|
||||
let by_addresstype: ByAddressType<ComputedFromHeight<StoredU64>> =
|
||||
ByAddressType::new_with_name(|name| {
|
||||
ComputedFromHeight::forced_import(
|
||||
db,
|
||||
&format!("{name}_total_addr_count"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
|
||||
Ok(Self { all, by_addresstype })
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly compute total = addr_count + empty_addr_count.
|
||||
@@ -60,22 +55,15 @@ impl TotalAddrCountVecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
for ((_, total), ((_, addr), (_, empty))) in self
|
||||
.by_addresstype
|
||||
.iter_mut()
|
||||
.zip(
|
||||
addr_count
|
||||
.by_addresstype
|
||||
.iter()
|
||||
.zip(empty_addr_count.by_addresstype.iter()),
|
||||
)
|
||||
{
|
||||
total.height.compute_add(
|
||||
max_from,
|
||||
&addr.count.height,
|
||||
&empty.count.height,
|
||||
exit,
|
||||
)?;
|
||||
for ((_, total), ((_, addr), (_, empty))) in self.by_addresstype.iter_mut().zip(
|
||||
addr_count
|
||||
.by_addresstype
|
||||
.iter()
|
||||
.zip(empty_addr_count.by_addresstype.iter()),
|
||||
) {
|
||||
total
|
||||
.height
|
||||
.compute_add(max_from, &addr.count.height, &empty.count.height, exit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -60,7 +60,12 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
}
|
||||
|
||||
/// Insert a value for a specific address type and typeindex.
|
||||
pub(crate) fn insert_for_type(&mut self, address_type: OutputType, typeindex: TypeIndex, value: T) {
|
||||
pub(crate) fn insert_for_type(
|
||||
&mut self,
|
||||
address_type: OutputType,
|
||||
typeindex: TypeIndex,
|
||||
value: T,
|
||||
) {
|
||||
self.get_mut(address_type).unwrap().insert(typeindex, value);
|
||||
}
|
||||
|
||||
@@ -76,7 +81,9 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
}
|
||||
|
||||
/// Iterate mutably over entries by address type.
|
||||
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut FxHashMap<TypeIndex, T>)> {
|
||||
pub(crate) fn iter_mut(
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = (OutputType, &mut FxHashMap<TypeIndex, T>)> {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,10 @@ impl AddressCache {
|
||||
|
||||
/// Merge address data into funded cache.
|
||||
#[inline]
|
||||
pub(crate) fn merge_funded(&mut self, data: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>) {
|
||||
pub(crate) fn merge_funded(
|
||||
&mut self,
|
||||
data: AddressTypeToTypeIndexMap<WithAddressDataSource<FundedAddressData>>,
|
||||
) {
|
||||
self.funded.merge_mut(data);
|
||||
}
|
||||
|
||||
@@ -63,7 +66,10 @@ impl AddressCache {
|
||||
}
|
||||
|
||||
/// Update transaction counts for addresses.
|
||||
pub(crate) fn update_tx_counts(&mut self, txindex_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>) {
|
||||
pub(crate) fn update_tx_counts(
|
||||
&mut self,
|
||||
txindex_vecs: AddressTypeToTypeIndexMap<SmallVec<[TxIndex; 4]>>,
|
||||
) {
|
||||
update_tx_counts(&mut self.funded, &mut self.empty, txindex_vecs);
|
||||
}
|
||||
|
||||
@@ -97,7 +103,9 @@ pub(crate) fn load_uncached_address_data(
|
||||
// Check if this is a new address (typeindex >= first for this height)
|
||||
let first = *first_addressindexes.get(address_type).unwrap();
|
||||
if first <= typeindex {
|
||||
return Ok(Some(WithAddressDataSource::New(FundedAddressData::default())));
|
||||
return Ok(Some(WithAddressDataSource::New(
|
||||
FundedAddressData::default(),
|
||||
)));
|
||||
}
|
||||
|
||||
// Skip if already in cache
|
||||
|
||||
@@ -26,7 +26,10 @@ impl<'a> AddressLookup<'a> {
|
||||
&mut self,
|
||||
output_type: OutputType,
|
||||
type_index: TypeIndex,
|
||||
) -> (&mut WithAddressDataSource<FundedAddressData>, TrackingStatus) {
|
||||
) -> (
|
||||
&mut WithAddressDataSource<FundedAddressData>,
|
||||
TrackingStatus,
|
||||
) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let map = self.funded.get_mut(output_type).unwrap();
|
||||
|
||||
@@ -150,14 +150,7 @@ pub(crate) fn process_sent(
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send(
|
||||
addr_data,
|
||||
value,
|
||||
current_price,
|
||||
prev_price,
|
||||
peak_price,
|
||||
age,
|
||||
)?;
|
||||
.send(addr_data, value, current_price, prev_price, peak_price, age)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,9 @@ pub(crate) fn process_inputs(
|
||||
);
|
||||
let mut sent_data = HeightToAddressTypeToVec::with_capacity(estimated_unique_heights);
|
||||
let mut address_data =
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(estimated_per_type);
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(
|
||||
estimated_per_type,
|
||||
);
|
||||
let mut txindex_vecs =
|
||||
AddressTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ pub(crate) fn process_outputs(
|
||||
let mut transacted = Transacted::default();
|
||||
let mut received_data = AddressTypeToVec::with_capacity(estimated_per_type);
|
||||
let mut address_data =
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(estimated_per_type);
|
||||
AddressTypeToTypeIndexMap::<WithAddressDataSource<FundedAddressData>>::with_capacity(
|
||||
estimated_per_type,
|
||||
);
|
||||
let mut txindex_vecs =
|
||||
AddressTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||
|
||||
|
||||
@@ -33,13 +33,11 @@ impl AddressCohorts {
|
||||
let v = version + VERSION;
|
||||
|
||||
// Helper to create a cohort - only amount_range cohorts have state
|
||||
let create = |filter: Filter,
|
||||
name: &'static str,
|
||||
has_state: bool|
|
||||
-> Result<AddressCohortVecs> {
|
||||
let sp = if has_state { Some(states_path) } else { None };
|
||||
AddressCohortVecs::forced_import(db, filter, name, v, indexes, sp)
|
||||
};
|
||||
let create =
|
||||
|filter: Filter, name: &'static str, has_state: bool| -> Result<AddressCohortVecs> {
|
||||
let sp = if has_state { Some(states_path) } else { None };
|
||||
AddressCohortVecs::forced_import(db, filter, name, v, indexes, sp)
|
||||
};
|
||||
|
||||
let full = |f: Filter, name: &'static str| create(f, name, true);
|
||||
let none = |f: Filter, name: &'static str| create(f, name, false);
|
||||
@@ -156,7 +154,9 @@ impl AddressCohorts {
|
||||
}
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub(crate) fn par_iter_vecs_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_vecs_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
// Collect all vecs from all cohorts
|
||||
self.0
|
||||
.iter_mut()
|
||||
|
||||
@@ -5,21 +5,15 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Dollars, Height, Indexes, Sats, StoredF64, StoredU64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, WritableVec, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::state::AddressCohortState,
|
||||
indexes,
|
||||
internal::ComputedFromHeight,
|
||||
prices,
|
||||
blocks, distribution::state::AddressCohortState, indexes, internal::ComputedFromHeight, prices,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::{BasicCohortMetrics, CohortMetricsBase, ImportConfig};
|
||||
|
||||
use super::super::traits::{CohortVecs, DynCohortVecs};
|
||||
|
||||
/// Address cohort with metrics and optional runtime state.
|
||||
#[derive(Traversable)]
|
||||
pub struct AddressCohortVecs<M: StorageMode = Rw> {
|
||||
/// Starting height when state was imported
|
||||
@@ -60,8 +54,7 @@ impl AddressCohortVecs {
|
||||
Ok(Self {
|
||||
starting_height: None,
|
||||
|
||||
state: states_path
|
||||
.map(|path| Box::new(AddressCohortState::new(path, &full_name))),
|
||||
state: states_path.map(|path| Box::new(AddressCohortState::new(path, &full_name))),
|
||||
|
||||
metrics: BasicCohortMetrics::forced_import(&cfg)?,
|
||||
|
||||
@@ -86,7 +79,9 @@ impl AddressCohortVecs {
|
||||
}
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub(crate) fn par_iter_vecs_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
pub(crate) fn par_iter_vecs_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.addr_count.height as &mut dyn AnyStoredVec)
|
||||
.chain(self.metrics.par_iter_mut())
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::{cmp::Reverse, collections::BinaryHeap, fs, path::Path};
|
||||
|
||||
use brk_cohort::{
|
||||
ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount,
|
||||
ByMaxAge, ByMinAge, BySpendableType, ByYear, CohortContext, Filter, Filtered, TERM_NAMES, Term,
|
||||
ByAgeRange, ByAmountRange, ByEpoch, ByGreatEqualAmount, ByLowerThanAmount, ByMaxAge, ByMinAge,
|
||||
BySpendableType, ByYear, CohortContext, Filter, Filtered, TERM_NAMES, Term,
|
||||
};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
@@ -11,7 +11,9 @@ use brk_types::{
|
||||
Sats, Version,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, Database, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{
|
||||
AnyStoredVec, Database, Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
@@ -23,8 +25,7 @@ use crate::{
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
AdjustedCohortMetrics, AllCohortMetrics, BasicCohortMetrics, CohortMetricsBase,
|
||||
ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig,
|
||||
SupplyMetrics,
|
||||
ExtendedAdjustedCohortMetrics, ExtendedCohortMetrics, ImportConfig, SupplyMetrics,
|
||||
};
|
||||
|
||||
use super::vecs::UTXOCohortVecs;
|
||||
@@ -146,12 +147,7 @@ impl UTXOCohorts<Rw> {
|
||||
version: v,
|
||||
indexes,
|
||||
};
|
||||
UTXOCohortVecs::new(
|
||||
None,
|
||||
ExtendedAdjustedCohortMetrics::forced_import(
|
||||
&cfg,
|
||||
)?,
|
||||
)
|
||||
UTXOCohortVecs::new(None, ExtendedAdjustedCohortMetrics::forced_import(&cfg)?)
|
||||
};
|
||||
|
||||
// lth: ExtendedCohortMetrics
|
||||
@@ -165,10 +161,7 @@ impl UTXOCohorts<Rw> {
|
||||
version: v,
|
||||
indexes,
|
||||
};
|
||||
UTXOCohortVecs::new(
|
||||
None,
|
||||
ExtendedCohortMetrics::forced_import(&cfg)?,
|
||||
)
|
||||
UTXOCohortVecs::new(None, ExtendedCohortMetrics::forced_import(&cfg)?)
|
||||
};
|
||||
|
||||
// max_age: AdjustedCohortMetrics (adjusted + peak_regret)
|
||||
@@ -243,9 +236,6 @@ impl UTXOCohorts<Rw> {
|
||||
})
|
||||
}
|
||||
|
||||
// === Iteration helpers ===
|
||||
|
||||
/// Parallel iterator over all separate (stateful) cohorts.
|
||||
pub(crate) fn par_iter_separate_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn DynCohortVecs> {
|
||||
@@ -296,9 +286,6 @@ impl UTXOCohorts<Rw> {
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
// === Computation methods ===
|
||||
|
||||
/// Compute overlapping cohorts from component age/amount range cohorts.
|
||||
pub(crate) fn compute_overlapping_vecs(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
@@ -573,8 +560,22 @@ impl UTXOCohorts<Rw> {
|
||||
HM: ReadableVec<Height, Dollars> + Sync,
|
||||
{
|
||||
// Get up_to_1h value sources for adjusted computation (cloned to avoid borrow conflicts).
|
||||
let up_to_1h_value_created = self.age_range.up_to_1h.metrics.realized.value_created.height.read_only_clone();
|
||||
let up_to_1h_value_destroyed = self.age_range.up_to_1h.metrics.realized.value_destroyed.height.read_only_clone();
|
||||
let up_to_1h_value_created = self
|
||||
.age_range
|
||||
.up_to_1h
|
||||
.metrics
|
||||
.realized
|
||||
.value_created
|
||||
.height
|
||||
.read_only_clone();
|
||||
let up_to_1h_value_destroyed = self
|
||||
.age_range
|
||||
.up_to_1h
|
||||
.metrics
|
||||
.realized
|
||||
.value_destroyed
|
||||
.height
|
||||
.read_only_clone();
|
||||
|
||||
// "all" cohort computed first (no all_supply_sats needed).
|
||||
self.all.metrics.compute_rest_part2(
|
||||
@@ -1024,5 +1025,4 @@ impl UTXOCohorts<Rw> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,19 +24,32 @@ impl UTXOCohorts<Rw> {
|
||||
let supply_state = received.spendable_supply;
|
||||
|
||||
// New UTXOs go into up_to_1h, current epoch, and current year
|
||||
self.age_range.up_to_1h.state.as_mut().unwrap().receive_utxo(&supply_state, price);
|
||||
self.epoch.mut_vec_from_height(height).state.as_mut().unwrap().receive_utxo(&supply_state, price);
|
||||
self.year.mut_vec_from_timestamp(timestamp).state.as_mut().unwrap().receive_utxo(&supply_state, price);
|
||||
self.age_range
|
||||
.up_to_1h
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo(&supply_state, price);
|
||||
self.epoch
|
||||
.mut_vec_from_height(height)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo(&supply_state, price);
|
||||
self.year
|
||||
.mut_vec_from_timestamp(timestamp)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo(&supply_state, price);
|
||||
|
||||
// Update output type cohorts
|
||||
self.type_
|
||||
.iter_typed_mut()
|
||||
.for_each(|(output_type, vecs)| {
|
||||
vecs.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo(received.by_type.get(output_type), price)
|
||||
});
|
||||
self.type_.iter_typed_mut().for_each(|(output_type, vecs)| {
|
||||
vecs.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo(received.by_type.get(output_type), price)
|
||||
});
|
||||
|
||||
// Update amount range cohorts
|
||||
received
|
||||
|
||||
@@ -36,9 +36,8 @@ impl UTXOCohorts<Rw> {
|
||||
let mut min_receive_height: Option<Height> = None;
|
||||
|
||||
for (receive_height, sent) in height_to_sent {
|
||||
min_receive_height = Some(
|
||||
min_receive_height.map_or(receive_height, |cur| cur.min(receive_height)),
|
||||
);
|
||||
min_receive_height =
|
||||
Some(min_receive_height.map_or(receive_height, |cur| cur.min(receive_height)));
|
||||
// Update chain_state to reflect spent supply
|
||||
chain_state[receive_height.to_usize()].supply -= &sent.spendable_supply;
|
||||
|
||||
@@ -52,19 +51,25 @@ impl UTXOCohorts<Rw> {
|
||||
let peak_price = price_range_max.max_between(receive_height, send_height);
|
||||
|
||||
// Update age range cohort (direct index lookup)
|
||||
self.age_range.get_mut(age).state.as_mut().unwrap().send_utxo(
|
||||
&sent.spendable_supply,
|
||||
current_price,
|
||||
prev_price,
|
||||
peak_price,
|
||||
age,
|
||||
);
|
||||
self.age_range
|
||||
.get_mut(age)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo(
|
||||
&sent.spendable_supply,
|
||||
current_price,
|
||||
prev_price,
|
||||
peak_price,
|
||||
age,
|
||||
);
|
||||
|
||||
// Update epoch cohort (direct lookup by height)
|
||||
self.epoch
|
||||
.mut_vec_from_height(receive_height)
|
||||
.state
|
||||
.as_mut().unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo(
|
||||
&sent.spendable_supply,
|
||||
current_price,
|
||||
@@ -77,7 +82,8 @@ impl UTXOCohorts<Rw> {
|
||||
self.year
|
||||
.mut_vec_from_timestamp(block_state.timestamp)
|
||||
.state
|
||||
.as_mut().unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo(
|
||||
&sent.spendable_supply,
|
||||
current_price,
|
||||
@@ -91,26 +97,24 @@ impl UTXOCohorts<Rw> {
|
||||
.spendable
|
||||
.iter_typed()
|
||||
.for_each(|(output_type, supply_state)| {
|
||||
self.type_.get_mut(output_type).state.as_mut().unwrap().send_utxo(
|
||||
supply_state,
|
||||
current_price,
|
||||
prev_price,
|
||||
peak_price,
|
||||
age,
|
||||
)
|
||||
self.type_
|
||||
.get_mut(output_type)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo(supply_state, current_price, prev_price, peak_price, age)
|
||||
});
|
||||
|
||||
// Update amount range cohorts
|
||||
sent.by_size_group
|
||||
.iter_typed()
|
||||
.for_each(|(group, supply_state)| {
|
||||
self.amount_range.get_mut(group).state.as_mut().unwrap().send_utxo(
|
||||
supply_state,
|
||||
current_price,
|
||||
prev_price,
|
||||
peak_price,
|
||||
age,
|
||||
);
|
||||
self.amount_range
|
||||
.get_mut(group)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo(supply_state, current_price, prev_price, peak_price, age);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ impl UTXOCohorts<Rw> {
|
||||
/// - k = 20 boundaries to check
|
||||
/// - n = total blocks in chain_state
|
||||
/// - Linear scan for end_idx is faster than binary search since typically 0-2 blocks cross each boundary
|
||||
pub(crate) fn tick_tock_next_block(&mut self, chain_state: &[BlockState], timestamp: Timestamp) {
|
||||
pub(crate) fn tick_tock_next_block(
|
||||
&mut self,
|
||||
chain_state: &[BlockState],
|
||||
timestamp: Timestamp,
|
||||
) {
|
||||
if chain_state.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ use crate::distribution::metrics::CohortMetricsBase;
|
||||
|
||||
use super::super::traits::DynCohortVecs;
|
||||
|
||||
/// UTXO cohort with metrics and optional runtime state.
|
||||
///
|
||||
/// Generic over the metrics type to support different cohort configurations
|
||||
/// (e.g. AllCohortMetrics, ExtendedCohortMetrics, BasicCohortMetrics, etc.)
|
||||
#[derive(Traversable)]
|
||||
pub struct UTXOCohortVecs<Metrics> {
|
||||
/// Starting height when state was imported
|
||||
@@ -38,7 +34,6 @@ impl<Metrics> UTXOCohortVecs<Metrics> {
|
||||
metrics,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<Metrics: CohortMetricsBase + Traversable> Filtered for UTXOCohortVecs<Metrics> {
|
||||
@@ -117,8 +112,11 @@ impl<Metrics: CohortMetricsBase + Traversable> DynCohortVecs for UTXOCohortVecs<
|
||||
height_price: Cents,
|
||||
) -> Result<()> {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
self.metrics
|
||||
.compute_then_truncate_push_unrealized_states(height, height_price, state)?;
|
||||
self.metrics.compute_then_truncate_push_unrealized_states(
|
||||
height,
|
||||
height_price,
|
||||
state,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ use std::thread;
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{Cents, Date, Height, OutputType, Sats, Timestamp, TxIndex, TypeIndex, ONE_DAY_IN_SEC};
|
||||
use brk_types::{
|
||||
Cents, Date, Height, ONE_DAY_IN_SEC, OutputType, Sats, Timestamp, TxIndex, TypeIndex,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashSet;
|
||||
use tracing::{debug, info};
|
||||
@@ -401,12 +403,11 @@ pub(crate) fn process_blocks(
|
||||
// Main thread: Update UTXO cohorts
|
||||
vecs.utxo_cohorts
|
||||
.receive(transacted, height, timestamp, block_price);
|
||||
if let Some(min_h) = vecs.utxo_cohorts
|
||||
.send(height_to_sent, chain_state, ctx.price_range_max)
|
||||
if let Some(min_h) =
|
||||
vecs.utxo_cohorts
|
||||
.send(height_to_sent, chain_state, ctx.price_range_max)
|
||||
{
|
||||
min_supply_modified = Some(
|
||||
min_supply_modified.map_or(min_h, |cur| cur.min(min_h)),
|
||||
);
|
||||
min_supply_modified = Some(min_supply_modified.map_or(min_h, |cur| cur.min(min_h)));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -423,8 +424,7 @@ pub(crate) fn process_blocks(
|
||||
|
||||
let h = height.to_usize();
|
||||
let is_last_of_day = height == last_height
|
||||
|| *cached_timestamps[h] / ONE_DAY_IN_SEC
|
||||
!= *cached_timestamps[h + 1] / ONE_DAY_IN_SEC;
|
||||
|| *cached_timestamps[h] / ONE_DAY_IN_SEC != *cached_timestamps[h + 1] / ONE_DAY_IN_SEC;
|
||||
let date_opt = is_last_of_day.then(|| Date::from(timestamp));
|
||||
|
||||
push_cohort_states(
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use brk_cohort::{ByAddressType, ByAnyAddress};
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{
|
||||
Height, OutPoint, OutputType, Sats, StoredU64, TxIndex, TypeIndex,
|
||||
};
|
||||
use vecdb::{Reader, ReadableVec, VecIndex};
|
||||
use brk_types::{Height, OutPoint, OutputType, Sats, StoredU64, TxIndex, TypeIndex};
|
||||
use vecdb::{ReadableVec, Reader, VecIndex};
|
||||
|
||||
use crate::{
|
||||
distribution::{
|
||||
@@ -46,9 +44,21 @@ impl<'a> TxOutReaders<'a> {
|
||||
output_count: usize,
|
||||
) -> Vec<TxOutData> {
|
||||
let end = first_txoutindex + output_count;
|
||||
self.indexer.vecs.outputs.value.collect_range_into_at(first_txoutindex, end, &mut self.values_buf);
|
||||
self.indexer.vecs.outputs.outputtype.collect_range_into_at(first_txoutindex, end, &mut self.outputtypes_buf);
|
||||
self.indexer.vecs.outputs.typeindex.collect_range_into_at(first_txoutindex, end, &mut self.typeindexes_buf);
|
||||
self.indexer.vecs.outputs.value.collect_range_into_at(
|
||||
first_txoutindex,
|
||||
end,
|
||||
&mut self.values_buf,
|
||||
);
|
||||
self.indexer.vecs.outputs.outputtype.collect_range_into_at(
|
||||
first_txoutindex,
|
||||
end,
|
||||
&mut self.outputtypes_buf,
|
||||
);
|
||||
self.indexer.vecs.outputs.typeindex.collect_range_into_at(
|
||||
first_txoutindex,
|
||||
end,
|
||||
&mut self.typeindexes_buf,
|
||||
);
|
||||
|
||||
self.values_buf
|
||||
.iter()
|
||||
@@ -94,12 +104,31 @@ impl<'a> TxInReaders<'a> {
|
||||
current_height: Height,
|
||||
) -> (Vec<Sats>, Vec<Height>, Vec<OutputType>, Vec<TypeIndex>) {
|
||||
let end = first_txinindex + input_count;
|
||||
let values: Vec<Sats> = self.txins.spent.value.collect_range_at(first_txinindex, end);
|
||||
self.indexer.vecs.inputs.outpoint.collect_range_into_at(first_txinindex, end, &mut self.outpoints_buf);
|
||||
let outputtypes: Vec<OutputType> = self.indexer.vecs.inputs.outputtype.collect_range_at(first_txinindex, end);
|
||||
let typeindexes: Vec<TypeIndex> = self.indexer.vecs.inputs.typeindex.collect_range_at(first_txinindex, end);
|
||||
let values: Vec<Sats> = self
|
||||
.txins
|
||||
.spent
|
||||
.value
|
||||
.collect_range_at(first_txinindex, end);
|
||||
self.indexer.vecs.inputs.outpoint.collect_range_into_at(
|
||||
first_txinindex,
|
||||
end,
|
||||
&mut self.outpoints_buf,
|
||||
);
|
||||
let outputtypes: Vec<OutputType> = self
|
||||
.indexer
|
||||
.vecs
|
||||
.inputs
|
||||
.outputtype
|
||||
.collect_range_at(first_txinindex, end);
|
||||
let typeindexes: Vec<TypeIndex> = self
|
||||
.indexer
|
||||
.vecs
|
||||
.inputs
|
||||
.typeindex
|
||||
.collect_range_at(first_txinindex, end);
|
||||
|
||||
let prev_heights: Vec<Height> = self.outpoints_buf
|
||||
let prev_heights: Vec<Height> = self
|
||||
.outpoints_buf
|
||||
.iter()
|
||||
.map(|outpoint| {
|
||||
if outpoint.is_coinbase() {
|
||||
@@ -175,7 +204,11 @@ impl IndexToTxIndexBuf {
|
||||
txindex_to_count: &impl ReadableVec<TxIndex, StoredU64>,
|
||||
) -> &[TxIndex] {
|
||||
let first = block_first_txindex.to_usize();
|
||||
txindex_to_count.collect_range_into_at(first, first + block_tx_count as usize, &mut self.counts);
|
||||
txindex_to_count.collect_range_into_at(
|
||||
first,
|
||||
first + block_tx_count as usize,
|
||||
&mut self.counts,
|
||||
);
|
||||
|
||||
let total: u64 = self.counts.iter().map(|c| u64::from(*c)).sum();
|
||||
self.result.clear();
|
||||
@@ -183,7 +216,8 @@ impl IndexToTxIndexBuf {
|
||||
|
||||
for (offset, count) in self.counts.iter().enumerate() {
|
||||
let txindex = TxIndex::from(first + offset);
|
||||
self.result.extend(std::iter::repeat_n(txindex, u64::from(*count) as usize));
|
||||
self.result
|
||||
.extend(std::iter::repeat_n(txindex, u64::from(*count) as usize));
|
||||
}
|
||||
|
||||
&self.result
|
||||
|
||||
@@ -71,9 +71,15 @@ pub(crate) fn recover_state(
|
||||
}
|
||||
|
||||
// Import UTXO cohort states - all must succeed
|
||||
debug!("importing UTXO cohort states at height {}", consistent_height);
|
||||
debug!(
|
||||
"importing UTXO cohort states at height {}",
|
||||
consistent_height
|
||||
);
|
||||
if !utxo_cohorts.import_separate_states(consistent_height) {
|
||||
warn!("UTXO cohort state import failed at height {}", consistent_height);
|
||||
warn!(
|
||||
"UTXO cohort state import failed at height {}",
|
||||
consistent_height
|
||||
);
|
||||
return Ok(RecoveredState {
|
||||
starting_height: Height::ZERO,
|
||||
});
|
||||
@@ -81,9 +87,15 @@ pub(crate) fn recover_state(
|
||||
debug!("UTXO cohort states imported");
|
||||
|
||||
// Import address cohort states - all must succeed
|
||||
debug!("importing address cohort states at height {}", consistent_height);
|
||||
debug!(
|
||||
"importing address cohort states at height {}",
|
||||
consistent_height
|
||||
);
|
||||
if !address_cohorts.import_separate_states(consistent_height) {
|
||||
warn!("Address cohort state import failed at height {}", consistent_height);
|
||||
warn!(
|
||||
"Address cohort state import failed at height {}",
|
||||
consistent_height
|
||||
);
|
||||
return Ok(RecoveredState {
|
||||
starting_height: Height::ZERO,
|
||||
});
|
||||
@@ -163,16 +175,25 @@ fn rollback_states(
|
||||
return Height::ZERO;
|
||||
};
|
||||
let chain_height = Height::from(s).incremented();
|
||||
debug!("chain_state rolled back to stamp {:?}, height {}", s, chain_height);
|
||||
debug!(
|
||||
"chain_state rolled back to stamp {:?}, height {}",
|
||||
s, chain_height
|
||||
);
|
||||
heights.insert(chain_height);
|
||||
|
||||
let Ok(stamps) = address_indexes_rollbacks else {
|
||||
warn!("address_indexes rollback failed: {:?}", address_indexes_rollbacks);
|
||||
warn!(
|
||||
"address_indexes rollback failed: {:?}",
|
||||
address_indexes_rollbacks
|
||||
);
|
||||
return Height::ZERO;
|
||||
};
|
||||
for (i, s) in stamps.iter().enumerate() {
|
||||
let h = Height::from(*s).incremented();
|
||||
debug!("address_indexes[{}] rolled back to stamp {:?}, height {}", i, s, h);
|
||||
debug!(
|
||||
"address_indexes[{}] rolled back to stamp {:?}, height {}",
|
||||
i, s, h
|
||||
);
|
||||
heights.insert(h);
|
||||
}
|
||||
|
||||
@@ -182,7 +203,10 @@ fn rollback_states(
|
||||
};
|
||||
for (i, s) in stamps.iter().enumerate() {
|
||||
let h = Height::from(*s).incremented();
|
||||
debug!("address_data[{}] rolled back to stamp {:?}, height {}", i, s, h);
|
||||
debug!(
|
||||
"address_data[{}] rolled back to stamp {:?}, height {}",
|
||||
i, s, h
|
||||
);
|
||||
heights.insert(h);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_error::Result;
|
||||
use brk_types::{EmptyAddressData, FundedAddressData, Height};
|
||||
use rayon::prelude::*;
|
||||
use tracing::info;
|
||||
use vecdb::{AnyStoredVec, AnyVec, VecIndex, WritableVec, Stamp};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Stamp, VecIndex, WritableVec};
|
||||
|
||||
use crate::distribution::{
|
||||
Vecs,
|
||||
@@ -65,8 +65,8 @@ pub(crate) fn write(
|
||||
|
||||
// Incremental supply_state write: only rewrite from the earliest modified height
|
||||
let supply_state_len = vecs.supply_state.len();
|
||||
let truncate_to = min_supply_modified
|
||||
.map_or(supply_state_len, |h| h.to_usize().min(supply_state_len));
|
||||
let truncate_to =
|
||||
min_supply_modified.map_or(supply_state_len, |h| h.to_usize().min(supply_state_len));
|
||||
vecs.supply_state
|
||||
.truncate_if_needed(Height::from(truncate_to))?;
|
||||
for block_state in &chain_state[truncate_to..] {
|
||||
|
||||
@@ -2,7 +2,9 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
@@ -51,7 +53,8 @@ impl ActivityMetrics {
|
||||
cfg.version,
|
||||
)?,
|
||||
|
||||
coinblocks_destroyed: cfg.import_cumulative_sum("coinblocks_destroyed", Version::ZERO)?,
|
||||
coinblocks_destroyed: cfg
|
||||
.import_cumulative_sum("coinblocks_destroyed", Version::ZERO)?,
|
||||
coindays_destroyed: cfg.import_cumulative_sum("coindays_destroyed", Version::ZERO)?,
|
||||
})
|
||||
}
|
||||
@@ -151,25 +154,27 @@ impl ActivityMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.coinblocks_destroyed.compute(starting_indexes.height, &window_starts, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.satblocks_destroyed,
|
||||
|(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.coinblocks_destroyed
|
||||
.compute(starting_indexes.height, &window_starts, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.satblocks_destroyed,
|
||||
|(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.coindays_destroyed.compute(starting_indexes.height, &window_starts, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.satdays_destroyed,
|
||||
|(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.coindays_destroyed
|
||||
.compute(starting_indexes.height, &window_starts, exit, |v| {
|
||||
v.compute_transform(
|
||||
starting_indexes.height,
|
||||
&self.satdays_destroyed,
|
||||
|(i, v, ..)| (i, StoredF64::from(Bitcoin::from(v))),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -28,19 +28,45 @@ pub struct AdjustedCohortMetrics<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for AdjustedCohortMetrics {
|
||||
fn filter(&self) -> &Filter { &self.filter }
|
||||
fn supply(&self) -> &SupplyMetrics { &self.supply }
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics { &mut self.supply }
|
||||
fn outputs(&self) -> &OutputsMetrics { &self.outputs }
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics { &mut self.outputs }
|
||||
fn activity(&self) -> &ActivityMetrics { &self.activity }
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics { &mut self.activity }
|
||||
fn realized_base(&self) -> &RealizedBase { &self.realized }
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase { &mut self.realized }
|
||||
fn unrealized_base(&self) -> &UnrealizedBase { &self.unrealized }
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.unrealized }
|
||||
fn cost_basis_base(&self) -> &CostBasisBase { &self.cost_basis }
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { &mut self.cost_basis }
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.filter
|
||||
}
|
||||
fn supply(&self) -> &SupplyMetrics {
|
||||
&self.supply
|
||||
}
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics {
|
||||
&mut self.supply
|
||||
}
|
||||
fn outputs(&self) -> &OutputsMetrics {
|
||||
&self.outputs
|
||||
}
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics {
|
||||
&mut self.outputs
|
||||
}
|
||||
fn activity(&self) -> &ActivityMetrics {
|
||||
&self.activity
|
||||
}
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics {
|
||||
&mut self.activity
|
||||
}
|
||||
fn realized_base(&self) -> &RealizedBase {
|
||||
&self.realized
|
||||
}
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase {
|
||||
&mut self.realized
|
||||
}
|
||||
fn unrealized_base(&self) -> &UnrealizedBase {
|
||||
&self.unrealized
|
||||
}
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase {
|
||||
&mut self.unrealized
|
||||
}
|
||||
fn cost_basis_base(&self) -> &CostBasisBase {
|
||||
&self.cost_basis
|
||||
}
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase {
|
||||
&mut self.cost_basis
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.supply.validate_computed_versions(base_version)?;
|
||||
@@ -48,7 +74,10 @@ impl CohortMetricsBase for AdjustedCohortMetrics {
|
||||
Ok(())
|
||||
}
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self, height: Height, height_price: Cents, state: &mut CohortState,
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
state: &mut CohortState,
|
||||
) -> Result<()> {
|
||||
state.apply_pending();
|
||||
self.cost_basis.truncate_push_minmax(height, state)?;
|
||||
@@ -69,9 +98,7 @@ impl CohortMetricsBase for AdjustedCohortMetrics {
|
||||
}
|
||||
|
||||
impl AdjustedCohortMetrics {
|
||||
pub(crate) fn forced_import(
|
||||
cfg: &ImportConfig,
|
||||
) -> Result<Self> {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedWithAdjusted::forced_import(cfg)?;
|
||||
@@ -125,5 +152,4 @@ impl AdjustedCohortMetrics {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,8 +84,7 @@ impl CohortMetricsBase for AllCohortMetrics {
|
||||
state.apply_pending();
|
||||
self.cost_basis.truncate_push_minmax(height, state)?;
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.unrealized
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
self.unrealized.truncate_push(height, &unrealized_state)?;
|
||||
self.cost_basis
|
||||
.extended
|
||||
.truncate_push_percentiles(height, state, height_price)?;
|
||||
|
||||
@@ -8,8 +8,8 @@ use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{blocks, distribution::state::CohortState, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityMetrics, CohortMetricsBase, CostBasisBase, ImportConfig, OutputsMetrics,
|
||||
RealizedBase, RelativeWithRelToAll, SupplyMetrics, UnrealizedBase,
|
||||
ActivityMetrics, CohortMetricsBase, CostBasisBase, ImportConfig, OutputsMetrics, RealizedBase,
|
||||
RelativeWithRelToAll, SupplyMetrics, UnrealizedBase,
|
||||
};
|
||||
|
||||
/// Basic cohort metrics: no extensions, with relative (rel_to_all).
|
||||
@@ -28,26 +28,55 @@ pub struct BasicCohortMetrics<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for BasicCohortMetrics {
|
||||
fn filter(&self) -> &Filter { &self.filter }
|
||||
fn supply(&self) -> &SupplyMetrics { &self.supply }
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics { &mut self.supply }
|
||||
fn outputs(&self) -> &OutputsMetrics { &self.outputs }
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics { &mut self.outputs }
|
||||
fn activity(&self) -> &ActivityMetrics { &self.activity }
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics { &mut self.activity }
|
||||
fn realized_base(&self) -> &RealizedBase { &self.realized }
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase { &mut self.realized }
|
||||
fn unrealized_base(&self) -> &UnrealizedBase { &self.unrealized }
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.unrealized }
|
||||
fn cost_basis_base(&self) -> &CostBasisBase { &self.cost_basis }
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { &mut self.cost_basis }
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.filter
|
||||
}
|
||||
fn supply(&self) -> &SupplyMetrics {
|
||||
&self.supply
|
||||
}
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics {
|
||||
&mut self.supply
|
||||
}
|
||||
fn outputs(&self) -> &OutputsMetrics {
|
||||
&self.outputs
|
||||
}
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics {
|
||||
&mut self.outputs
|
||||
}
|
||||
fn activity(&self) -> &ActivityMetrics {
|
||||
&self.activity
|
||||
}
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics {
|
||||
&mut self.activity
|
||||
}
|
||||
fn realized_base(&self) -> &RealizedBase {
|
||||
&self.realized
|
||||
}
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase {
|
||||
&mut self.realized
|
||||
}
|
||||
fn unrealized_base(&self) -> &UnrealizedBase {
|
||||
&self.unrealized
|
||||
}
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase {
|
||||
&mut self.unrealized
|
||||
}
|
||||
fn cost_basis_base(&self) -> &CostBasisBase {
|
||||
&self.cost_basis
|
||||
}
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase {
|
||||
&mut self.cost_basis
|
||||
}
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.supply.validate_computed_versions(base_version)?;
|
||||
self.activity.validate_computed_versions(base_version)?;
|
||||
Ok(())
|
||||
}
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self, height: Height, height_price: Cents, state: &mut CohortState,
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
state: &mut CohortState,
|
||||
) -> Result<()> {
|
||||
state.apply_pending();
|
||||
self.cost_basis.truncate_push_minmax(height, state)?;
|
||||
@@ -68,9 +97,7 @@ impl CohortMetricsBase for BasicCohortMetrics {
|
||||
}
|
||||
|
||||
impl BasicCohortMetrics {
|
||||
pub(crate) fn forced_import(
|
||||
cfg: &ImportConfig,
|
||||
) -> Result<Self> {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedBase::forced_import(cfg)?;
|
||||
|
||||
@@ -29,19 +29,45 @@ pub struct ExtendedCohortMetrics<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for ExtendedCohortMetrics {
|
||||
fn filter(&self) -> &Filter { &self.filter }
|
||||
fn supply(&self) -> &SupplyMetrics { &self.supply }
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics { &mut self.supply }
|
||||
fn outputs(&self) -> &OutputsMetrics { &self.outputs }
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics { &mut self.outputs }
|
||||
fn activity(&self) -> &ActivityMetrics { &self.activity }
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics { &mut self.activity }
|
||||
fn realized_base(&self) -> &RealizedBase { &self.realized }
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase { &mut self.realized }
|
||||
fn unrealized_base(&self) -> &UnrealizedBase { &self.unrealized }
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.unrealized }
|
||||
fn cost_basis_base(&self) -> &CostBasisBase { &self.cost_basis }
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { &mut self.cost_basis }
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.filter
|
||||
}
|
||||
fn supply(&self) -> &SupplyMetrics {
|
||||
&self.supply
|
||||
}
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics {
|
||||
&mut self.supply
|
||||
}
|
||||
fn outputs(&self) -> &OutputsMetrics {
|
||||
&self.outputs
|
||||
}
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics {
|
||||
&mut self.outputs
|
||||
}
|
||||
fn activity(&self) -> &ActivityMetrics {
|
||||
&self.activity
|
||||
}
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics {
|
||||
&mut self.activity
|
||||
}
|
||||
fn realized_base(&self) -> &RealizedBase {
|
||||
&self.realized
|
||||
}
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase {
|
||||
&mut self.realized
|
||||
}
|
||||
fn unrealized_base(&self) -> &UnrealizedBase {
|
||||
&self.unrealized
|
||||
}
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase {
|
||||
&mut self.unrealized
|
||||
}
|
||||
fn cost_basis_base(&self) -> &CostBasisBase {
|
||||
&self.cost_basis
|
||||
}
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase {
|
||||
&mut self.cost_basis
|
||||
}
|
||||
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.supply.validate_computed_versions(base_version)?;
|
||||
@@ -50,13 +76,18 @@ impl CohortMetricsBase for ExtendedCohortMetrics {
|
||||
Ok(())
|
||||
}
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self, height: Height, height_price: Cents, state: &mut CohortState,
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
state: &mut CohortState,
|
||||
) -> Result<()> {
|
||||
state.apply_pending();
|
||||
self.cost_basis.truncate_push_minmax(height, state)?;
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.unrealized.truncate_push(height, &unrealized_state)?;
|
||||
self.cost_basis.extended.truncate_push_percentiles(height, state, height_price)?;
|
||||
self.cost_basis
|
||||
.extended
|
||||
.truncate_push_percentiles(height, state, height_price)?;
|
||||
Ok(())
|
||||
}
|
||||
fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
@@ -73,9 +104,7 @@ impl CohortMetricsBase for ExtendedCohortMetrics {
|
||||
}
|
||||
|
||||
impl ExtendedCohortMetrics {
|
||||
pub(crate) fn forced_import(
|
||||
cfg: &ImportConfig,
|
||||
) -> Result<Self> {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedWithExtended::forced_import(cfg)?;
|
||||
@@ -125,5 +154,4 @@ impl ExtendedCohortMetrics {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,19 +29,45 @@ pub struct ExtendedAdjustedCohortMetrics<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
|
||||
fn filter(&self) -> &Filter { &self.filter }
|
||||
fn supply(&self) -> &SupplyMetrics { &self.supply }
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics { &mut self.supply }
|
||||
fn outputs(&self) -> &OutputsMetrics { &self.outputs }
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics { &mut self.outputs }
|
||||
fn activity(&self) -> &ActivityMetrics { &self.activity }
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics { &mut self.activity }
|
||||
fn realized_base(&self) -> &RealizedBase { &self.realized }
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase { &mut self.realized }
|
||||
fn unrealized_base(&self) -> &UnrealizedBase { &self.unrealized }
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.unrealized }
|
||||
fn cost_basis_base(&self) -> &CostBasisBase { &self.cost_basis }
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { &mut self.cost_basis }
|
||||
fn filter(&self) -> &Filter {
|
||||
&self.filter
|
||||
}
|
||||
fn supply(&self) -> &SupplyMetrics {
|
||||
&self.supply
|
||||
}
|
||||
fn supply_mut(&mut self) -> &mut SupplyMetrics {
|
||||
&mut self.supply
|
||||
}
|
||||
fn outputs(&self) -> &OutputsMetrics {
|
||||
&self.outputs
|
||||
}
|
||||
fn outputs_mut(&mut self) -> &mut OutputsMetrics {
|
||||
&mut self.outputs
|
||||
}
|
||||
fn activity(&self) -> &ActivityMetrics {
|
||||
&self.activity
|
||||
}
|
||||
fn activity_mut(&mut self) -> &mut ActivityMetrics {
|
||||
&mut self.activity
|
||||
}
|
||||
fn realized_base(&self) -> &RealizedBase {
|
||||
&self.realized
|
||||
}
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase {
|
||||
&mut self.realized
|
||||
}
|
||||
fn unrealized_base(&self) -> &UnrealizedBase {
|
||||
&self.unrealized
|
||||
}
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase {
|
||||
&mut self.unrealized
|
||||
}
|
||||
fn cost_basis_base(&self) -> &CostBasisBase {
|
||||
&self.cost_basis
|
||||
}
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase {
|
||||
&mut self.cost_basis
|
||||
}
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||
self.supply.validate_computed_versions(base_version)?;
|
||||
self.activity.validate_computed_versions(base_version)?;
|
||||
@@ -49,13 +75,18 @@ impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
|
||||
Ok(())
|
||||
}
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self, height: Height, height_price: Cents, state: &mut CohortState,
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_price: Cents,
|
||||
state: &mut CohortState,
|
||||
) -> Result<()> {
|
||||
state.apply_pending();
|
||||
self.cost_basis.truncate_push_minmax(height, state)?;
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.unrealized.truncate_push(height, &unrealized_state)?;
|
||||
self.cost_basis.extended.truncate_push_percentiles(height, state, height_price)?;
|
||||
self.cost_basis
|
||||
.extended
|
||||
.truncate_push_percentiles(height, state, height_price)?;
|
||||
Ok(())
|
||||
}
|
||||
fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
@@ -72,9 +103,7 @@ impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
|
||||
}
|
||||
|
||||
impl ExtendedAdjustedCohortMetrics {
|
||||
pub(crate) fn forced_import(
|
||||
cfg: &ImportConfig,
|
||||
) -> Result<Self> {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedWithExtendedAdjusted::forced_import(cfg)?;
|
||||
@@ -129,5 +158,4 @@ impl ExtendedAdjustedCohortMetrics {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use vecdb::{BytesVec, BytesVecValue, Database, ImportableVec};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
CentsType, ComputedFromHeight, ComputedFromHeightCumulative, ComputedFromHeightCumulativeSum,
|
||||
ComputedFromHeightRatio, FiatFromHeight, NumericValue, PercentFromHeight,
|
||||
PercentRollingEmas1w1m, PercentRollingWindows, Price, RollingEmas1w1m, RollingEmas2w,
|
||||
RollingWindows, ValueFromHeight, ValueFromHeightChange, ValueFromHeightCumulative,
|
||||
CentsType, ComputedFromHeight, ComputedFromHeightCumulative,
|
||||
ComputedFromHeightCumulativeSum, ComputedFromHeightRatio, FiatFromHeight, NumericValue,
|
||||
PercentFromHeight, PercentRollingEmas1w1m, PercentRollingWindows, Price, RollingEmas1w1m,
|
||||
RollingEmas2w, RollingWindows, ValueFromHeight, ValueFromHeightChange,
|
||||
ValueFromHeightCumulative,
|
||||
},
|
||||
};
|
||||
|
||||
/// Configuration for importing metrics.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ImportConfig<'a> {
|
||||
pub db: &'a Database,
|
||||
@@ -25,7 +25,6 @@ pub struct ImportConfig<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ImportConfig<'a> {
|
||||
/// Get full metric name with filter prefix.
|
||||
pub(crate) fn name(&self, suffix: &str) -> String {
|
||||
if self.full_name.is_empty() {
|
||||
suffix.to_string()
|
||||
@@ -36,14 +35,17 @@ impl<'a> ImportConfig<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Computed types ---
|
||||
|
||||
pub(crate) fn import_computed<T: NumericValue + JsonSchema>(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<ComputedFromHeight<T>> {
|
||||
ComputedFromHeight::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
ComputedFromHeight::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_cumulative<T: NumericValue + JsonSchema>(
|
||||
@@ -51,7 +53,12 @@ impl<'a> ImportConfig<'a> {
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<ComputedFromHeightCumulative<T>> {
|
||||
ComputedFromHeightCumulative::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
ComputedFromHeightCumulative::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_cumulative_sum<T: NumericValue + JsonSchema>(
|
||||
@@ -59,17 +66,25 @@ impl<'a> ImportConfig<'a> {
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<ComputedFromHeightCumulativeSum<T>> {
|
||||
ComputedFromHeightCumulativeSum::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
ComputedFromHeightCumulativeSum::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
// --- Percent types ---
|
||||
|
||||
pub(crate) fn import_percent_bp16(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<PercentFromHeight<BasisPoints16>> {
|
||||
PercentFromHeight::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
PercentFromHeight::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_percent_bps16(
|
||||
@@ -77,62 +92,158 @@ impl<'a> ImportConfig<'a> {
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<PercentFromHeight<BasisPointsSigned16>> {
|
||||
PercentFromHeight::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
PercentFromHeight::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
// --- Value types ---
|
||||
|
||||
pub(crate) fn import_fiat<C: CentsType>(&self, suffix: &str, offset: Version) -> Result<FiatFromHeight<C>> {
|
||||
FiatFromHeight::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_fiat<C: CentsType>(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<FiatFromHeight<C>> {
|
||||
FiatFromHeight::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_value(&self, suffix: &str, offset: Version) -> Result<ValueFromHeight> {
|
||||
ValueFromHeight::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
ValueFromHeight::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_value_cumulative(&self, suffix: &str, offset: Version) -> Result<ValueFromHeightCumulative> {
|
||||
ValueFromHeightCumulative::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_value_cumulative(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<ValueFromHeightCumulative> {
|
||||
ValueFromHeightCumulative::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_value_change(&self, suffix: &str, offset: Version) -> Result<ValueFromHeightChange> {
|
||||
ValueFromHeightChange::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_value_change(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<ValueFromHeightChange> {
|
||||
ValueFromHeightChange::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
// --- Price and ratio ---
|
||||
|
||||
pub(crate) fn import_price(&self, suffix: &str, offset: Version) -> Result<Price<ComputedFromHeight<Cents>>> {
|
||||
Price::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_price(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<Price<ComputedFromHeight<Cents>>> {
|
||||
Price::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_ratio(&self, suffix: &str, offset: Version) -> Result<ComputedFromHeightRatio> {
|
||||
ComputedFromHeightRatio::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_ratio(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<ComputedFromHeightRatio> {
|
||||
ComputedFromHeightRatio::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
// --- Bytes ---
|
||||
|
||||
pub(crate) fn import_bytes<T: BytesVecValue>(&self, suffix: &str, offset: Version) -> Result<BytesVec<Height, T>> {
|
||||
Ok(BytesVec::forced_import(self.db, &self.name(suffix), self.version + offset)?)
|
||||
pub(crate) fn import_bytes<T: BytesVecValue>(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<BytesVec<Height, T>> {
|
||||
Ok(BytesVec::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
)?)
|
||||
}
|
||||
|
||||
// --- Rolling ---
|
||||
|
||||
pub(crate) fn import_rolling<T: NumericValue + JsonSchema>(&self, suffix: &str, offset: Version) -> Result<RollingWindows<T>> {
|
||||
RollingWindows::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_rolling<T: NumericValue + JsonSchema>(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<RollingWindows<T>> {
|
||||
RollingWindows::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_percent_rolling_bp16(&self, suffix: &str, offset: Version) -> Result<PercentRollingWindows<BasisPoints16>> {
|
||||
PercentRollingWindows::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_percent_rolling_bp16(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<PercentRollingWindows<BasisPoints16>> {
|
||||
PercentRollingWindows::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_emas_1w_1m<T: NumericValue + JsonSchema>(&self, suffix: &str, offset: Version) -> Result<RollingEmas1w1m<T>> {
|
||||
RollingEmas1w1m::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_emas_1w_1m<T: NumericValue + JsonSchema>(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<RollingEmas1w1m<T>> {
|
||||
RollingEmas1w1m::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_percent_emas_1w_1m_bp16(&self, suffix: &str, offset: Version) -> Result<PercentRollingEmas1w1m<BasisPoints16>> {
|
||||
PercentRollingEmas1w1m::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
pub(crate) fn import_percent_emas_1w_1m_bp16(
|
||||
&self,
|
||||
suffix: &str,
|
||||
offset: Version,
|
||||
) -> Result<PercentRollingEmas1w1m<BasisPoints16>> {
|
||||
PercentRollingEmas1w1m::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn import_emas_2w(&self, suffix: &str, offset: Version) -> Result<RollingEmas2w> {
|
||||
RollingEmas2w::forced_import(self.db, &self.name(suffix), self.version + offset, self.indexes)
|
||||
RollingEmas2w::forced_import(
|
||||
self.db,
|
||||
&self.name(suffix),
|
||||
self.version + offset,
|
||||
self.indexes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +69,18 @@ impl CostBasisBase {
|
||||
) -> Result<()> {
|
||||
self.min.cents.height.compute_min_of_others(
|
||||
starting_indexes.height,
|
||||
&others.iter().map(|v| &v.min.cents.height).collect::<Vec<_>>(),
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.min.cents.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.max.cents.height.compute_max_of_others(
|
||||
starting_indexes.height,
|
||||
&others.iter().map(|v| &v.max.cents.height).collect::<Vec<_>>(),
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.max.cents.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -5,10 +5,7 @@ use vecdb::{AnyStoredVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
distribution::state::CohortState,
|
||||
internal::{
|
||||
PERCENTILES_LEN, PercentFromHeight, PercentilesVecs,
|
||||
compute_spot_percentile_rank,
|
||||
},
|
||||
internal::{PERCENTILES_LEN, PercentFromHeight, PercentilesVecs, compute_spot_percentile_rank},
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
@@ -44,8 +41,10 @@ impl CostBasisExtended {
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
spot_cost_basis_percentile: cfg.import_percent_bp16("spot_cost_basis_percentile", Version::ZERO)?,
|
||||
spot_invested_capital_percentile: cfg.import_percent_bp16("spot_invested_capital_percentile", Version::ZERO)?,
|
||||
spot_cost_basis_percentile: cfg
|
||||
.import_percent_bp16("spot_cost_basis_percentile", Version::ZERO)?,
|
||||
spot_invested_capital_percentile: cfg
|
||||
.import_percent_bp16("spot_invested_capital_percentile", Version::ZERO)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,6 @@ use vecdb::{AnyStoredVec, Exit};
|
||||
|
||||
use crate::{blocks, distribution::state::CohortState, prices};
|
||||
|
||||
/// Trait defining the interface for cohort metrics containers.
|
||||
///
|
||||
/// Provides typed accessor methods for base sub-metric components, default
|
||||
/// implementations for shared operations that only use base fields, and
|
||||
/// required methods for operations that vary by extension level.
|
||||
pub trait CohortMetricsBase: Send + Sync {
|
||||
fn filter(&self) -> &Filter;
|
||||
fn supply(&self) -> &SupplyMetrics;
|
||||
@@ -45,14 +40,8 @@ pub trait CohortMetricsBase: Send + Sync {
|
||||
fn cost_basis_base(&self) -> &CostBasisBase;
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase;
|
||||
|
||||
// === Required methods (vary by extension level) ===
|
||||
|
||||
/// Validate computed versions against base version.
|
||||
/// Extended types also validate cost_basis extended versions.
|
||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||
|
||||
/// Compute and push unrealized states.
|
||||
/// Extended types also push cost_basis percentiles.
|
||||
fn compute_then_truncate_push_unrealized_states(
|
||||
&mut self,
|
||||
height: Height,
|
||||
@@ -60,12 +49,8 @@ pub trait CohortMetricsBase: Send + Sync {
|
||||
state: &mut CohortState,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Collect all stored vecs for parallel writing.
|
||||
fn collect_all_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec>;
|
||||
|
||||
// === Default methods (shared across all cohort metric types, use base fields only) ===
|
||||
|
||||
/// Get minimum length across height-indexed vectors written in block loop.
|
||||
fn min_stateful_height_len(&self) -> usize {
|
||||
self.supply()
|
||||
.min_len()
|
||||
@@ -76,7 +61,6 @@ pub trait CohortMetricsBase: Send + Sync {
|
||||
.min(self.cost_basis_base().min_stateful_height_len())
|
||||
}
|
||||
|
||||
/// Push state values to height-indexed vectors.
|
||||
fn truncate_push(&mut self, height: Height, state: &CohortState) -> Result<()> {
|
||||
self.supply_mut()
|
||||
.truncate_push(height, state.supply.value)?;
|
||||
@@ -225,12 +209,18 @@ pub trait CohortMetricsBase: Send + Sync {
|
||||
)?;
|
||||
self.unrealized_base_mut().compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| v.unrealized_base()).collect::<Vec<_>>(),
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| v.unrealized_base())
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.cost_basis_base_mut().compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| v.cost_basis_base()).collect::<Vec<_>>(),
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| v.cost_basis_base())
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
|
||||
@@ -10,18 +10,14 @@ use crate::{
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
/// Adjusted realized metrics (only for adjusted cohorts: all, sth, max_age).
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedAdjusted<M: StorageMode = Rw> {
|
||||
// === Adjusted Value (computed: cohort - up_to_1h) ===
|
||||
pub adjusted_value_created: ComputedFromHeight<Cents, M>,
|
||||
pub adjusted_value_destroyed: ComputedFromHeight<Cents, M>,
|
||||
|
||||
// === Adjusted Value Created/Destroyed Rolling Sums ===
|
||||
pub adjusted_value_created_sum: RollingWindows<Cents, M>,
|
||||
pub adjusted_value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
|
||||
// === Adjusted SOPR (rolling window ratios) ===
|
||||
pub adjusted_sopr: RollingWindows<StoredF64, M>,
|
||||
pub adjusted_sopr_ema: RollingEmas1w1m<StoredF64, M>,
|
||||
}
|
||||
@@ -30,9 +26,12 @@ impl RealizedAdjusted {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(RealizedAdjusted {
|
||||
adjusted_value_created: cfg.import_computed("adjusted_value_created", Version::ZERO)?,
|
||||
adjusted_value_destroyed: cfg.import_computed("adjusted_value_destroyed", Version::ZERO)?,
|
||||
adjusted_value_created_sum: cfg.import_rolling("adjusted_value_created", Version::ONE)?,
|
||||
adjusted_value_destroyed_sum: cfg.import_rolling("adjusted_value_destroyed", Version::ONE)?,
|
||||
adjusted_value_destroyed: cfg
|
||||
.import_computed("adjusted_value_destroyed", Version::ZERO)?,
|
||||
adjusted_value_created_sum: cfg
|
||||
.import_rolling("adjusted_value_created", Version::ONE)?,
|
||||
adjusted_value_destroyed_sum: cfg
|
||||
.import_rolling("adjusted_value_destroyed", Version::ONE)?,
|
||||
adjusted_sopr: cfg.import_rolling("adjusted_sopr", Version::ONE)?,
|
||||
adjusted_sopr_ema: cfg.import_emas_1w_1m("adjusted_sopr_24h", Version::ONE)?,
|
||||
})
|
||||
@@ -66,19 +65,31 @@ impl RealizedAdjusted {
|
||||
// Adjusted value created/destroyed rolling sums
|
||||
let window_starts = blocks.count.window_starts();
|
||||
self.adjusted_value_created_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &self.adjusted_value_created.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.adjusted_value_created.height,
|
||||
exit,
|
||||
)?;
|
||||
self.adjusted_value_destroyed_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &self.adjusted_value_destroyed.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.adjusted_value_destroyed.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// SOPR ratios from rolling sums
|
||||
for ((sopr, vc), vd) in self.adjusted_sopr.as_mut_array().into_iter()
|
||||
for ((sopr, vc), vd) in self
|
||||
.adjusted_sopr
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.adjusted_value_created_sum.as_array())
|
||||
.zip(self.adjusted_value_destroyed_sum.as_array())
|
||||
{
|
||||
sopr.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height, &vc.height, &vd.height, exit,
|
||||
starting_indexes.height,
|
||||
&vc.height,
|
||||
&vd.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,56 +1,49 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
BasisPoints16, BasisPoints32, BasisPointsSigned16,
|
||||
Bitcoin, Cents, CentsSats, CentsSigned, CentsSquaredSats, Dollars, Height, Indexes, Sats, StoredF32, StoredF64, Version,
|
||||
BasisPoints16, BasisPoints32, BasisPointsSigned16, Bitcoin, Cents, CentsSats, CentsSigned,
|
||||
CentsSquaredSats, Dollars, Height, Indexes, Sats, StoredF32, StoredF64, Version,
|
||||
};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, BytesVec, Exit, ReadableCloneableVec,
|
||||
ReadableVec, Rw, StorageMode, WritableVec,
|
||||
AnyStoredVec, AnyVec, BytesVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
blocks,
|
||||
distribution::state::RealizedState,
|
||||
internal::{
|
||||
CentsPlus, CentsUnsignedToDollars, ComputedFromHeightCumulative, ComputedFromHeight,
|
||||
ComputedFromHeightRatio, FiatFromHeight, NegCentsUnsignedToDollars, PercentFromHeight,
|
||||
PercentRollingEmas1w1m, PercentRollingWindows, ValueFromHeightCumulative, LazyFromHeight,
|
||||
Price,
|
||||
RatioCentsBp16, RatioCentsSignedCentsBps16, RatioCentsSignedDollarsBps16, RatioCents64,
|
||||
RollingEmas1w1m, RollingEmas2w, RollingWindows, Identity,
|
||||
CentsPlus, CentsUnsignedToDollars, ComputedFromHeight, ComputedFromHeightCumulative,
|
||||
ComputedFromHeightRatio, FiatFromHeight, Identity, LazyFromHeight,
|
||||
NegCentsUnsignedToDollars, PercentFromHeight, PercentRollingEmas1w1m,
|
||||
PercentRollingWindows, Price, RatioCents64, RatioCentsBp16, RatioCentsSignedCentsBps16,
|
||||
RatioCentsSignedDollarsBps16, RollingEmas1w1m, RollingEmas2w, RollingWindows,
|
||||
ValueFromHeightCumulative,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
/// Base realized metrics (always computed).
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedBase<M: StorageMode = Rw> {
|
||||
// === Realized Cap ===
|
||||
pub realized_cap_cents: ComputedFromHeight<Cents, M>,
|
||||
pub realized_cap: LazyFromHeight<Dollars, Cents>,
|
||||
pub realized_price: Price<ComputedFromHeight<Cents, M>>,
|
||||
pub realized_price_ratio: ComputedFromHeightRatio<M>,
|
||||
pub realized_cap_change_1m: ComputedFromHeight<CentsSigned, M>,
|
||||
|
||||
// === Investor Price ===
|
||||
pub investor_price: Price<ComputedFromHeight<Cents, M>>,
|
||||
pub investor_price_ratio: ComputedFromHeightRatio<M>,
|
||||
|
||||
// === Floor/Ceiling Price Bands ===
|
||||
pub lower_price_band: Price<ComputedFromHeight<Cents, M>>,
|
||||
pub upper_price_band: Price<ComputedFromHeight<Cents, M>>,
|
||||
|
||||
// === Raw values for aggregation ===
|
||||
pub cap_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub investor_cap_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
|
||||
// === MVRV ===
|
||||
pub mvrv: LazyFromHeight<StoredF32>,
|
||||
|
||||
// === Realized Profit/Loss ===
|
||||
pub realized_profit: ComputedFromHeightCumulative<Cents, M>,
|
||||
pub realized_profit_ema_1w: ComputedFromHeight<Cents, M>,
|
||||
pub realized_loss: ComputedFromHeightCumulative<Cents, M>,
|
||||
@@ -60,50 +53,38 @@ pub struct RealizedBase<M: StorageMode = Rw> {
|
||||
pub net_realized_pnl_ema_1w: ComputedFromHeight<CentsSigned, M>,
|
||||
pub gross_pnl: FiatFromHeight<Cents, M>,
|
||||
|
||||
// === Realized vs Realized Cap Ratios ===
|
||||
pub realized_profit_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub realized_loss_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub net_realized_pnl_rel_to_realized_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
|
||||
// === Value Created/Destroyed Splits (stored) ===
|
||||
pub profit_value_created: ComputedFromHeight<Cents, M>,
|
||||
pub profit_value_destroyed: ComputedFromHeight<Cents, M>,
|
||||
pub loss_value_created: ComputedFromHeight<Cents, M>,
|
||||
pub loss_value_destroyed: ComputedFromHeight<Cents, M>,
|
||||
|
||||
// === Value Created/Destroyed Totals ===
|
||||
pub value_created: ComputedFromHeight<Cents, M>,
|
||||
pub value_destroyed: ComputedFromHeight<Cents, M>,
|
||||
|
||||
// === Capitulation/Profit Flow (lazy aliases) ===
|
||||
pub capitulation_flow: LazyFromHeight<Dollars, Cents>,
|
||||
pub profit_flow: LazyFromHeight<Dollars, Cents>,
|
||||
|
||||
// === Value Created/Destroyed Rolling Sums ===
|
||||
pub value_created_sum: RollingWindows<Cents, M>,
|
||||
pub value_destroyed_sum: RollingWindows<Cents, M>,
|
||||
|
||||
// === SOPR (rolling window ratios) ===
|
||||
pub sopr: RollingWindows<StoredF64, M>,
|
||||
pub sopr_24h_ema: RollingEmas1w1m<StoredF64, M>,
|
||||
|
||||
// === Sell Side Risk ===
|
||||
pub gross_pnl_sum: RollingWindows<Cents, M>,
|
||||
pub sell_side_risk_ratio: PercentRollingWindows<BasisPoints16, M>,
|
||||
pub sell_side_risk_ratio_24h_ema: PercentRollingEmas1w1m<BasisPoints16, M>,
|
||||
|
||||
// === Net Realized PnL Deltas ===
|
||||
pub net_pnl_change_1m: ComputedFromHeight<CentsSigned, M>,
|
||||
pub net_pnl_change_1m_rel_to_realized_cap:
|
||||
PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_pnl_change_1m_rel_to_market_cap:
|
||||
PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_pnl_change_1m_rel_to_realized_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_pnl_change_1m_rel_to_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
|
||||
// === Peak Regret ===
|
||||
pub peak_regret: ComputedFromHeightCumulative<Cents, M>,
|
||||
pub peak_regret_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
|
||||
// === Sent in Profit/Loss ===
|
||||
pub sent_in_profit: ValueFromHeightCumulative<M>,
|
||||
pub sent_in_profit_ema: RollingEmas2w<M>,
|
||||
pub sent_in_loss: ValueFromHeightCumulative<M>,
|
||||
@@ -111,15 +92,16 @@ pub struct RealizedBase<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
impl RealizedBase {
|
||||
/// Import realized base metrics from database.
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let v1 = Version::ONE;
|
||||
|
||||
let realized_cap_cents = cfg.import_computed("realized_cap_cents", v0)?;
|
||||
let realized_cap = LazyFromHeight::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("realized_cap"), cfg.version,
|
||||
realized_cap_cents.height.read_only_boxed_clone(), &realized_cap_cents,
|
||||
&cfg.name("realized_cap"),
|
||||
cfg.version,
|
||||
realized_cap_cents.height.read_only_boxed_clone(),
|
||||
&realized_cap_cents,
|
||||
);
|
||||
|
||||
let realized_profit = cfg.import_cumulative("realized_profit", v0)?;
|
||||
@@ -128,8 +110,10 @@ impl RealizedBase {
|
||||
let realized_loss_ema_1w = cfg.import_computed("realized_loss_ema_1w", v0)?;
|
||||
|
||||
let neg_realized_loss = LazyFromHeight::from_height_source::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_realized_loss"), cfg.version + Version::ONE,
|
||||
realized_loss.height.read_only_boxed_clone(), cfg.indexes,
|
||||
&cfg.name("neg_realized_loss"),
|
||||
cfg.version + Version::ONE,
|
||||
realized_loss.height.read_only_boxed_clone(),
|
||||
cfg.indexes,
|
||||
);
|
||||
|
||||
let net_realized_pnl = cfg.import_cumulative("net_realized_pnl", v0)?;
|
||||
@@ -161,17 +145,23 @@ impl RealizedBase {
|
||||
let value_destroyed = cfg.import_computed("value_destroyed", v0)?;
|
||||
|
||||
let capitulation_flow = LazyFromHeight::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("capitulation_flow"), cfg.version,
|
||||
loss_value_destroyed.height.read_only_boxed_clone(), &loss_value_destroyed,
|
||||
&cfg.name("capitulation_flow"),
|
||||
cfg.version,
|
||||
loss_value_destroyed.height.read_only_boxed_clone(),
|
||||
&loss_value_destroyed,
|
||||
);
|
||||
let profit_flow = LazyFromHeight::from_computed::<CentsUnsignedToDollars>(
|
||||
&cfg.name("profit_flow"), cfg.version,
|
||||
profit_value_destroyed.height.read_only_boxed_clone(), &profit_value_destroyed,
|
||||
&cfg.name("profit_flow"),
|
||||
cfg.version,
|
||||
profit_value_destroyed.height.read_only_boxed_clone(),
|
||||
&profit_value_destroyed,
|
||||
);
|
||||
|
||||
let realized_price_ratio = cfg.import_ratio("realized_price", v1)?;
|
||||
let mvrv = LazyFromHeight::from_lazy::<Identity<StoredF32>, BasisPoints32>(
|
||||
&cfg.name("mvrv"), cfg.version, &realized_price_ratio.ratio,
|
||||
&cfg.name("mvrv"),
|
||||
cfg.version,
|
||||
&realized_price_ratio.ratio,
|
||||
);
|
||||
|
||||
// Rolling windows
|
||||
@@ -183,7 +173,8 @@ impl RealizedBase {
|
||||
|
||||
// EMAs
|
||||
let sopr_24h_ema = cfg.import_emas_1w_1m("sopr_24h", v1)?;
|
||||
let sell_side_risk_ratio_24h_ema = cfg.import_percent_emas_1w_1m_bp16("sell_side_risk_ratio_24h", v1)?;
|
||||
let sell_side_risk_ratio_24h_ema =
|
||||
cfg.import_percent_emas_1w_1m_bp16("sell_side_risk_ratio_24h", v1)?;
|
||||
|
||||
let peak_regret_rel_to_realized_cap =
|
||||
cfg.import_percent_bp16("realized_peak_regret_rel_to_realized_cap", v1)?;
|
||||
@@ -228,10 +219,10 @@ impl RealizedBase {
|
||||
sell_side_risk_ratio,
|
||||
sell_side_risk_ratio_24h_ema,
|
||||
net_pnl_change_1m: cfg.import_computed("net_pnl_change_1m", Version::new(3))?,
|
||||
net_pnl_change_1m_rel_to_realized_cap:
|
||||
cfg.import_percent_bps16("net_pnl_change_1m_rel_to_realized_cap", Version::new(3))?,
|
||||
net_pnl_change_1m_rel_to_market_cap:
|
||||
cfg.import_percent_bps16("net_pnl_change_1m_rel_to_market_cap", Version::new(3))?,
|
||||
net_pnl_change_1m_rel_to_realized_cap: cfg
|
||||
.import_percent_bps16("net_pnl_change_1m_rel_to_realized_cap", Version::new(3))?,
|
||||
net_pnl_change_1m_rel_to_market_cap: cfg
|
||||
.import_percent_bps16("net_pnl_change_1m_rel_to_market_cap", Version::new(3))?,
|
||||
peak_regret,
|
||||
peak_regret_rel_to_realized_cap,
|
||||
sent_in_profit: cfg.import_value_cumulative("sent_in_profit", v0)?,
|
||||
@@ -241,7 +232,6 @@ impl RealizedBase {
|
||||
})
|
||||
}
|
||||
|
||||
/// Get minimum length across height-indexed vectors written in block loop.
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.realized_cap
|
||||
.height
|
||||
@@ -260,7 +250,6 @@ impl RealizedBase {
|
||||
.min(self.sent_in_loss.base.sats.height.len())
|
||||
}
|
||||
|
||||
/// Push realized state values to height-indexed vectors.
|
||||
pub(crate) fn truncate_push(&mut self, height: Height, state: &RealizedState) -> Result<()> {
|
||||
self.realized_cap_cents
|
||||
.height
|
||||
@@ -271,7 +260,8 @@ impl RealizedBase {
|
||||
self.realized_loss
|
||||
.height
|
||||
.truncate_push(height, state.loss())?;
|
||||
self.investor_price.cents
|
||||
self.investor_price
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, state.investor_price())?;
|
||||
self.cap_raw.truncate_push(height, state.cap_raw())?;
|
||||
@@ -306,7 +296,6 @@ impl RealizedBase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a Vec of mutable references to all stored vecs for parallel writing.
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.realized_cap_cents.height as &mut dyn AnyStoredVec,
|
||||
@@ -325,7 +314,6 @@ impl RealizedBase {
|
||||
]
|
||||
}
|
||||
|
||||
/// Compute aggregate values from separate cohorts.
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
@@ -362,7 +350,8 @@ impl RealizedBase {
|
||||
.iter()
|
||||
.map(|o| o.investor_price.cents.height.version())
|
||||
.fold(vecdb::Version::ZERO, |acc, v| acc + v);
|
||||
self.investor_price.cents
|
||||
self.investor_price
|
||||
.cents
|
||||
.height
|
||||
.validate_computed_version_or_reset(investor_price_dep_version)?;
|
||||
|
||||
@@ -404,7 +393,8 @@ impl RealizedBase {
|
||||
} else {
|
||||
Cents::new((sum_investor_cap / sum_cap.inner()) as u64)
|
||||
};
|
||||
self.investor_price.cents
|
||||
self.investor_price
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, investor_price)?;
|
||||
}
|
||||
@@ -474,7 +464,6 @@ impl RealizedBase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// First phase of computed metrics (indexes from height).
|
||||
pub(crate) fn compute_rest_part1(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
@@ -492,7 +481,10 @@ impl RealizedBase {
|
||||
&self.realized_profit.height,
|
||||
&self.realized_loss.height,
|
||||
|(i, profit, loss, ..)| {
|
||||
(i, CentsSigned::new(profit.inner() as i64 - loss.inner() as i64))
|
||||
(
|
||||
i,
|
||||
CentsSigned::new(profit.inner() as i64 - loss.inner() as i64),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
@@ -512,7 +504,6 @@ impl RealizedBase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Second phase of computed metrics (base-only parts: realized price, rolling sums, EMAs).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute_rest_part2_base(
|
||||
&mut self,
|
||||
@@ -608,34 +599,54 @@ impl RealizedBase {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// === Rolling sum intermediates ===
|
||||
let window_starts = blocks.count.window_starts();
|
||||
self.value_created_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &self.value_created.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.value_created.height,
|
||||
exit,
|
||||
)?;
|
||||
self.value_destroyed_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &self.value_destroyed.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.value_destroyed.height,
|
||||
exit,
|
||||
)?;
|
||||
self.gross_pnl_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &self.gross_pnl.cents.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.gross_pnl.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Compute SOPR from rolling sums
|
||||
for ((sopr, vc), vd) in self.sopr.as_mut_array().into_iter()
|
||||
for ((sopr, vc), vd) in self
|
||||
.sopr
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.value_created_sum.as_array())
|
||||
.zip(self.value_destroyed_sum.as_array())
|
||||
{
|
||||
sopr.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height, &vc.height, &vd.height, exit,
|
||||
starting_indexes.height,
|
||||
&vc.height,
|
||||
&vd.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Compute sell-side risk ratios
|
||||
for (ssrr, rv) in self.sell_side_risk_ratio.as_mut_array().into_iter()
|
||||
for (ssrr, rv) in self
|
||||
.sell_side_risk_ratio
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.gross_pnl_sum.as_array())
|
||||
{
|
||||
ssrr.compute_binary::<Cents, Cents, RatioCentsBp16>(
|
||||
starting_indexes.height, &rv.height, &self.realized_cap_cents.height, exit,
|
||||
starting_indexes.height,
|
||||
&rv.height,
|
||||
&self.realized_cap_cents.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -652,14 +663,12 @@ impl RealizedBase {
|
||||
&self.realized_loss.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_realized_pnl_ema_1w
|
||||
.height
|
||||
.compute_rolling_ema(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1w_ago,
|
||||
&self.net_realized_pnl.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_realized_pnl_ema_1w.height.compute_rolling_ema(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1w_ago,
|
||||
&self.net_realized_pnl.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 14-day EMA of sent in profit/loss
|
||||
self.sent_in_profit_ema.compute(
|
||||
@@ -726,14 +735,12 @@ impl RealizedBase {
|
||||
)?;
|
||||
|
||||
// Net realized PnL cumulative 30d delta
|
||||
self.net_pnl_change_1m
|
||||
.height
|
||||
.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.net_realized_pnl.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_change_1m.height.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.net_realized_pnl.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.net_pnl_change_1m_rel_to_realized_cap
|
||||
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps16>(
|
||||
|
||||
@@ -6,8 +6,8 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{
|
||||
blocks,
|
||||
internal::{
|
||||
ComputedFromHeightRatioExtension, PercentFromHeight,
|
||||
RatioCents64, RatioDollarsBp16, RollingWindows,
|
||||
ComputedFromHeightRatioExtension, PercentFromHeight, RatioCents64, RatioDollarsBp16,
|
||||
RollingWindows,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,19 +15,15 @@ use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::RealizedBase;
|
||||
|
||||
/// Extended realized metrics (only for extended cohorts: all, sth, lth, age_range).
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedExtended<M: StorageMode = Rw> {
|
||||
pub realized_cap_rel_to_own_market_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
|
||||
// === Realized Profit/Loss Rolling Sums ===
|
||||
pub realized_profit_sum: RollingWindows<Cents, M>,
|
||||
pub realized_loss_sum: RollingWindows<Cents, M>,
|
||||
|
||||
// === Realized Profit to Loss Ratio (from rolling sums) ===
|
||||
pub realized_profit_to_loss_ratio: RollingWindows<StoredF64, M>,
|
||||
|
||||
// === Extended ratio metrics for realized/investor price ===
|
||||
pub realized_price_ratio_ext: ComputedFromHeightRatioExtension<M>,
|
||||
pub investor_price_ratio_ext: ComputedFromHeightRatioExtension<M>,
|
||||
}
|
||||
@@ -35,15 +31,23 @@ pub struct RealizedExtended<M: StorageMode = Rw> {
|
||||
impl RealizedExtended {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(RealizedExtended {
|
||||
realized_cap_rel_to_own_market_cap: cfg.import_percent_bp16("realized_cap_rel_to_own_market_cap", Version::ZERO)?,
|
||||
realized_cap_rel_to_own_market_cap: cfg
|
||||
.import_percent_bp16("realized_cap_rel_to_own_market_cap", Version::ZERO)?,
|
||||
realized_profit_sum: cfg.import_rolling("realized_profit", Version::ONE)?,
|
||||
realized_loss_sum: cfg.import_rolling("realized_loss", Version::ONE)?,
|
||||
realized_profit_to_loss_ratio: cfg.import_rolling("realized_profit_to_loss_ratio", Version::ONE)?,
|
||||
realized_profit_to_loss_ratio: cfg
|
||||
.import_rolling("realized_profit_to_loss_ratio", Version::ONE)?,
|
||||
realized_price_ratio_ext: ComputedFromHeightRatioExtension::forced_import(
|
||||
cfg.db, &cfg.name("realized_price"), cfg.version + Version::ONE, cfg.indexes,
|
||||
cfg.db,
|
||||
&cfg.name("realized_price"),
|
||||
cfg.version + Version::ONE,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
investor_price_ratio_ext: ComputedFromHeightRatioExtension::forced_import(
|
||||
cfg.db, &cfg.name("investor_price"), cfg.version, cfg.indexes,
|
||||
cfg.db,
|
||||
&cfg.name("investor_price"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
@@ -60,10 +64,16 @@ impl RealizedExtended {
|
||||
// Realized profit/loss rolling sums
|
||||
let window_starts = blocks.count.window_starts();
|
||||
self.realized_profit_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &base.realized_profit.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&base.realized_profit.height,
|
||||
exit,
|
||||
)?;
|
||||
self.realized_loss_sum.compute_rolling_sum(
|
||||
starting_indexes.height, &window_starts, &base.realized_loss.height, exit,
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&base.realized_loss.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Realized cap relative to own market cap
|
||||
@@ -76,12 +86,18 @@ impl RealizedExtended {
|
||||
)?;
|
||||
|
||||
// Realized profit to loss ratios
|
||||
for ((ratio, profit), loss) in self.realized_profit_to_loss_ratio.as_mut_array().into_iter()
|
||||
for ((ratio, profit), loss) in self
|
||||
.realized_profit_to_loss_ratio
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.realized_profit_sum.as_array())
|
||||
.zip(self.realized_loss_sum.as_array())
|
||||
{
|
||||
ratio.compute_binary::<Cents, Cents, RatioCents64>(
|
||||
starting_indexes.height, &profit.height, &loss.height, exit,
|
||||
starting_indexes.height,
|
||||
&profit.height,
|
||||
&loss.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,29 +4,23 @@ use brk_types::{BasisPoints16, BasisPointsSigned16, Dollars, Height, Sats, Store
|
||||
use vecdb::{Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{
|
||||
Bps16ToFloat, LazyFromHeight,
|
||||
NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps16, RatioSatsBp16,
|
||||
Bps16ToFloat, LazyFromHeight, NegRatioDollarsBps16, PercentFromHeight, RatioDollarsBp16,
|
||||
RatioDollarsBps16, RatioSatsBp16,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
|
||||
/// Base relative metrics (always computed when relative is enabled).
|
||||
/// All fields are non-Optional - market_cap and realized_cap are always
|
||||
/// available when relative metrics are enabled.
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeBase<M: StorageMode = Rw> {
|
||||
// === Supply in Profit/Loss Relative to Own Supply ===
|
||||
pub supply_in_profit_rel_to_own_supply: PercentFromHeight<BasisPoints16, M>,
|
||||
pub supply_in_loss_rel_to_own_supply: PercentFromHeight<BasisPoints16, M>,
|
||||
|
||||
// === Unrealized vs Market Cap ===
|
||||
pub unrealized_profit_rel_to_market_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub unrealized_loss_rel_to_market_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub neg_unrealized_loss_rel_to_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_unrealized_pnl_rel_to_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub nupl: LazyFromHeight<StoredF32, BasisPointsSigned16>,
|
||||
|
||||
// === Invested Capital in Profit/Loss as % of Realized Cap ===
|
||||
pub invested_capital_in_profit_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub invested_capital_in_loss_rel_to_realized_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
}
|
||||
@@ -42,27 +36,34 @@ impl RelativeBase {
|
||||
let nupl = LazyFromHeight::from_computed::<Bps16ToFloat>(
|
||||
&cfg.name("nupl"),
|
||||
cfg.version + v2,
|
||||
net_unrealized_pnl_rel_to_market_cap.bps.height.read_only_boxed_clone(),
|
||||
net_unrealized_pnl_rel_to_market_cap
|
||||
.bps
|
||||
.height
|
||||
.read_only_boxed_clone(),
|
||||
&net_unrealized_pnl_rel_to_market_cap.bps,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
supply_in_profit_rel_to_own_supply:
|
||||
cfg.import_percent_bp16("supply_in_profit_rel_to_own_supply", v1)?,
|
||||
supply_in_loss_rel_to_own_supply:
|
||||
cfg.import_percent_bp16("supply_in_loss_rel_to_own_supply", v1)?,
|
||||
unrealized_profit_rel_to_market_cap:
|
||||
cfg.import_percent_bp16("unrealized_profit_rel_to_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_market_cap:
|
||||
cfg.import_percent_bp16("unrealized_loss_rel_to_market_cap", v2)?,
|
||||
neg_unrealized_loss_rel_to_market_cap:
|
||||
cfg.import_percent_bps16("neg_unrealized_loss_rel_to_market_cap", v2)?,
|
||||
supply_in_profit_rel_to_own_supply: cfg
|
||||
.import_percent_bp16("supply_in_profit_rel_to_own_supply", v1)?,
|
||||
supply_in_loss_rel_to_own_supply: cfg
|
||||
.import_percent_bp16("supply_in_loss_rel_to_own_supply", v1)?,
|
||||
unrealized_profit_rel_to_market_cap: cfg
|
||||
.import_percent_bp16("unrealized_profit_rel_to_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_market_cap: cfg
|
||||
.import_percent_bp16("unrealized_loss_rel_to_market_cap", v2)?,
|
||||
neg_unrealized_loss_rel_to_market_cap: cfg
|
||||
.import_percent_bps16("neg_unrealized_loss_rel_to_market_cap", v2)?,
|
||||
net_unrealized_pnl_rel_to_market_cap,
|
||||
nupl,
|
||||
invested_capital_in_profit_rel_to_realized_cap:
|
||||
cfg.import_percent_bp16("invested_capital_in_profit_rel_to_realized_cap", Version::ZERO)?,
|
||||
invested_capital_in_loss_rel_to_realized_cap:
|
||||
cfg.import_percent_bp16("invested_capital_in_loss_rel_to_realized_cap", Version::ZERO)?,
|
||||
invested_capital_in_profit_rel_to_realized_cap: cfg.import_percent_bp16(
|
||||
"invested_capital_in_profit_rel_to_realized_cap",
|
||||
Version::ZERO,
|
||||
)?,
|
||||
invested_capital_in_loss_rel_to_realized_cap: cfg.import_percent_bp16(
|
||||
"invested_capital_in_loss_rel_to_realized_cap",
|
||||
Version::ZERO,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -77,35 +78,59 @@ impl RelativeBase {
|
||||
) -> Result<()> {
|
||||
self.supply_in_profit_rel_to_own_supply
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from, &unrealized.supply_in_profit.sats.height, supply_total_sats, exit,
|
||||
max_from,
|
||||
&unrealized.supply_in_profit.sats.height,
|
||||
supply_total_sats,
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_loss_rel_to_own_supply
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from, &unrealized.supply_in_loss.sats.height, supply_total_sats, exit,
|
||||
max_from,
|
||||
&unrealized.supply_in_loss.sats.height,
|
||||
supply_total_sats,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_profit_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.unrealized_profit.usd.height, market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_profit.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.unrealized_loss.usd.height, market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_loss.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.neg_unrealized_loss_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, NegRatioDollarsBps16>(
|
||||
max_from, &unrealized.unrealized_loss.usd.height, market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_loss.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.net_unrealized_pnl_rel_to_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBps16>(
|
||||
max_from, &unrealized.net_unrealized_pnl.usd.height, market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.net_unrealized_pnl.usd.height,
|
||||
market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.invested_capital_in_profit_rel_to_realized_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.invested_capital_in_profit.usd.height, &realized.realized_cap.height, exit,
|
||||
max_from,
|
||||
&unrealized.invested_capital_in_profit.usd.height,
|
||||
&realized.realized_cap.height,
|
||||
exit,
|
||||
)?;
|
||||
self.invested_capital_in_loss_rel_to_realized_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.invested_capital_in_loss.usd.height, &realized.realized_cap.height, exit,
|
||||
max_from,
|
||||
&unrealized.invested_capital_in_loss.usd.height,
|
||||
&realized.realized_cap.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -12,14 +12,10 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
/// Extended relative metrics for own market cap (extended && rel_to_all).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeExtendedOwnMarketCap<M: StorageMode = Rw> {
|
||||
pub unrealized_profit_rel_to_own_market_cap:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub unrealized_loss_rel_to_own_market_cap:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub neg_unrealized_loss_rel_to_own_market_cap:
|
||||
PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap:
|
||||
PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub unrealized_profit_rel_to_own_market_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub unrealized_loss_rel_to_own_market_cap: PercentFromHeight<BasisPoints16, M>,
|
||||
pub neg_unrealized_loss_rel_to_own_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
}
|
||||
|
||||
impl RelativeExtendedOwnMarketCap {
|
||||
@@ -27,14 +23,14 @@ impl RelativeExtendedOwnMarketCap {
|
||||
let v2 = Version::new(2);
|
||||
|
||||
Ok(Self {
|
||||
unrealized_profit_rel_to_own_market_cap:
|
||||
cfg.import_percent_bp16("unrealized_profit_rel_to_own_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_own_market_cap:
|
||||
cfg.import_percent_bp16("unrealized_loss_rel_to_own_market_cap", v2)?,
|
||||
neg_unrealized_loss_rel_to_own_market_cap:
|
||||
cfg.import_percent_bps16("neg_unrealized_loss_rel_to_own_market_cap", v2)?,
|
||||
net_unrealized_pnl_rel_to_own_market_cap:
|
||||
cfg.import_percent_bps16("net_unrealized_pnl_rel_to_own_market_cap", v2)?,
|
||||
unrealized_profit_rel_to_own_market_cap: cfg
|
||||
.import_percent_bp16("unrealized_profit_rel_to_own_market_cap", v2)?,
|
||||
unrealized_loss_rel_to_own_market_cap: cfg
|
||||
.import_percent_bp16("unrealized_loss_rel_to_own_market_cap", v2)?,
|
||||
neg_unrealized_loss_rel_to_own_market_cap: cfg
|
||||
.import_percent_bps16("neg_unrealized_loss_rel_to_own_market_cap", v2)?,
|
||||
net_unrealized_pnl_rel_to_own_market_cap: cfg
|
||||
.import_percent_bps16("net_unrealized_pnl_rel_to_own_market_cap", v2)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,19 +43,31 @@ impl RelativeExtendedOwnMarketCap {
|
||||
) -> Result<()> {
|
||||
self.unrealized_profit_rel_to_own_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.unrealized_profit.usd.height, own_market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_profit.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_own_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.unrealized_loss.usd.height, own_market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_loss.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.neg_unrealized_loss_rel_to_own_market_cap
|
||||
.compute_binary::<Dollars, Dollars, NegRatioDollarsBps16>(
|
||||
max_from, &unrealized.unrealized_loss.usd.height, own_market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_loss.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
self.net_unrealized_pnl_rel_to_own_market_cap
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBps16>(
|
||||
max_from, &unrealized.net_unrealized_pnl.usd.height, own_market_cap, exit,
|
||||
max_from,
|
||||
&unrealized.net_unrealized_pnl.usd.height,
|
||||
own_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -12,14 +12,10 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
/// Extended relative metrics for own total unrealized PnL (extended only).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeExtendedOwnPnl<M: StorageMode = Rw> {
|
||||
pub unrealized_profit_rel_to_own_gross_pnl:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub unrealized_loss_rel_to_own_gross_pnl:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub neg_unrealized_loss_rel_to_own_gross_pnl:
|
||||
PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_unrealized_pnl_rel_to_own_gross_pnl:
|
||||
PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub unrealized_profit_rel_to_own_gross_pnl: PercentFromHeight<BasisPoints16, M>,
|
||||
pub unrealized_loss_rel_to_own_gross_pnl: PercentFromHeight<BasisPoints16, M>,
|
||||
pub neg_unrealized_loss_rel_to_own_gross_pnl: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
pub net_unrealized_pnl_rel_to_own_gross_pnl: PercentFromHeight<BasisPointsSigned16, M>,
|
||||
}
|
||||
|
||||
impl RelativeExtendedOwnPnl {
|
||||
@@ -28,14 +24,14 @@ impl RelativeExtendedOwnPnl {
|
||||
let v2 = Version::new(2);
|
||||
|
||||
Ok(Self {
|
||||
unrealized_profit_rel_to_own_gross_pnl:
|
||||
cfg.import_percent_bp16("unrealized_profit_rel_to_own_gross_pnl", v1)?,
|
||||
unrealized_loss_rel_to_own_gross_pnl:
|
||||
cfg.import_percent_bp16("unrealized_loss_rel_to_own_gross_pnl", v1)?,
|
||||
neg_unrealized_loss_rel_to_own_gross_pnl:
|
||||
cfg.import_percent_bps16("neg_unrealized_loss_rel_to_own_gross_pnl", v1)?,
|
||||
net_unrealized_pnl_rel_to_own_gross_pnl:
|
||||
cfg.import_percent_bps16("net_unrealized_pnl_rel_to_own_gross_pnl", v2)?,
|
||||
unrealized_profit_rel_to_own_gross_pnl: cfg
|
||||
.import_percent_bp16("unrealized_profit_rel_to_own_gross_pnl", v1)?,
|
||||
unrealized_loss_rel_to_own_gross_pnl: cfg
|
||||
.import_percent_bp16("unrealized_loss_rel_to_own_gross_pnl", v1)?,
|
||||
neg_unrealized_loss_rel_to_own_gross_pnl: cfg
|
||||
.import_percent_bps16("neg_unrealized_loss_rel_to_own_gross_pnl", v1)?,
|
||||
net_unrealized_pnl_rel_to_own_gross_pnl: cfg
|
||||
.import_percent_bps16("net_unrealized_pnl_rel_to_own_gross_pnl", v2)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,19 +43,31 @@ impl RelativeExtendedOwnPnl {
|
||||
) -> Result<()> {
|
||||
self.unrealized_profit_rel_to_own_gross_pnl
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.unrealized_profit.usd.height, &unrealized.gross_pnl.usd.height, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_profit.usd.height,
|
||||
&unrealized.gross_pnl.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss_rel_to_own_gross_pnl
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBp16>(
|
||||
max_from, &unrealized.unrealized_loss.usd.height, &unrealized.gross_pnl.usd.height, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_loss.usd.height,
|
||||
&unrealized.gross_pnl.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
self.neg_unrealized_loss_rel_to_own_gross_pnl
|
||||
.compute_binary::<Dollars, Dollars, NegRatioDollarsBps16>(
|
||||
max_from, &unrealized.unrealized_loss.usd.height, &unrealized.gross_pnl.usd.height, exit,
|
||||
max_from,
|
||||
&unrealized.unrealized_loss.usd.height,
|
||||
&unrealized.gross_pnl.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_unrealized_pnl_rel_to_own_gross_pnl
|
||||
.compute_binary::<Dollars, Dollars, RatioDollarsBps16>(
|
||||
max_from, &unrealized.net_unrealized_pnl.usd.height, &unrealized.gross_pnl.usd.height, exit,
|
||||
max_from,
|
||||
&unrealized.net_unrealized_pnl.usd.height,
|
||||
&unrealized.gross_pnl.usd.height,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,23 +10,20 @@ use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
/// Relative-to-all metrics (not present for the "all" cohort itself).
|
||||
#[derive(Traversable)]
|
||||
pub struct RelativeToAll<M: StorageMode = Rw> {
|
||||
pub supply_rel_to_circulating_supply:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub supply_in_profit_rel_to_circulating_supply:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub supply_in_loss_rel_to_circulating_supply:
|
||||
PercentFromHeight<BasisPoints16, M>,
|
||||
pub supply_rel_to_circulating_supply: PercentFromHeight<BasisPoints16, M>,
|
||||
pub supply_in_profit_rel_to_circulating_supply: PercentFromHeight<BasisPoints16, M>,
|
||||
pub supply_in_loss_rel_to_circulating_supply: PercentFromHeight<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
impl RelativeToAll {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
supply_rel_to_circulating_supply:
|
||||
cfg.import_percent_bp16("supply_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_in_profit_rel_to_circulating_supply:
|
||||
cfg.import_percent_bp16("supply_in_profit_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_in_loss_rel_to_circulating_supply:
|
||||
cfg.import_percent_bp16("supply_in_loss_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_rel_to_circulating_supply: cfg
|
||||
.import_percent_bp16("supply_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_in_profit_rel_to_circulating_supply: cfg
|
||||
.import_percent_bp16("supply_in_profit_rel_to_circulating_supply", Version::ONE)?,
|
||||
supply_in_loss_rel_to_circulating_supply: cfg
|
||||
.import_percent_bp16("supply_in_loss_rel_to_circulating_supply", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -40,15 +37,24 @@ impl RelativeToAll {
|
||||
) -> Result<()> {
|
||||
self.supply_rel_to_circulating_supply
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from, supply_total_sats, all_supply_sats, exit,
|
||||
max_from,
|
||||
supply_total_sats,
|
||||
all_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_profit_rel_to_circulating_supply
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from, &unrealized.supply_in_profit.sats.height, all_supply_sats, exit,
|
||||
max_from,
|
||||
&unrealized.supply_in_profit.sats.height,
|
||||
all_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_loss_rel_to_circulating_supply
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from, &unrealized.supply_in_loss.sats.height, all_supply_sats, exit,
|
||||
max_from,
|
||||
&unrealized.supply_in_loss.sats.height,
|
||||
all_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
|
||||
use super::{
|
||||
RelativeBase, RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl,
|
||||
RelativeToAll,
|
||||
};
|
||||
use super::{RelativeBase, RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeToAll};
|
||||
|
||||
/// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl).
|
||||
/// Used by: sth, lth, age_range cohorts.
|
||||
|
||||
@@ -7,8 +7,8 @@ use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::internal::{
|
||||
HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin,
|
||||
LazyValueFromHeight, ValueFromHeightChange, ValueFromHeight,
|
||||
HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyValueFromHeight, ValueFromHeight,
|
||||
ValueFromHeightChange,
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
|
||||
@@ -2,8 +2,8 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, CentsSats, CentsSigned, CentsSquaredSats, Height, Indexes, Version};
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, BytesVec, Exit, ReadableCloneableVec, ReadableVec,
|
||||
Rw, StorageMode, WritableVec,
|
||||
AnyStoredVec, AnyVec, BytesVec, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -19,36 +19,28 @@ use brk_types::Dollars;
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
/// Base unrealized profit/loss metrics (always computed).
|
||||
#[derive(Traversable)]
|
||||
pub struct UnrealizedBase<M: StorageMode = Rw> {
|
||||
// === Supply in Profit/Loss ===
|
||||
pub supply_in_profit: ValueFromHeight<M>,
|
||||
pub supply_in_loss: ValueFromHeight<M>,
|
||||
|
||||
// === Unrealized Profit/Loss ===
|
||||
pub unrealized_profit: FiatFromHeight<Cents, M>,
|
||||
pub unrealized_loss: FiatFromHeight<Cents, M>,
|
||||
|
||||
// === Invested Capital in Profit/Loss ===
|
||||
pub invested_capital_in_profit: FiatFromHeight<Cents, M>,
|
||||
pub invested_capital_in_loss: FiatFromHeight<Cents, M>,
|
||||
|
||||
// === Raw values for precise aggregation (used to compute pain/greed indices) ===
|
||||
pub invested_capital_in_profit_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub invested_capital_in_loss_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
|
||||
// === Pain/Greed Indices ===
|
||||
pub pain_index: FiatFromHeight<Cents, M>,
|
||||
pub greed_index: FiatFromHeight<Cents, M>,
|
||||
pub net_sentiment: FiatFromHeight<CentsSigned, M>,
|
||||
|
||||
// === Negated ===
|
||||
pub neg_unrealized_loss: LazyFromHeight<Dollars, Cents>,
|
||||
|
||||
// === Net and Total ===
|
||||
pub net_unrealized_pnl: FiatFromHeight<CentsSigned, M>,
|
||||
pub gross_pnl: FiatFromHeight<Cents, M>,
|
||||
}
|
||||
@@ -65,7 +57,8 @@ impl UnrealizedBase {
|
||||
let invested_capital_in_profit = cfg.import_fiat("invested_capital_in_profit", v0)?;
|
||||
let invested_capital_in_loss = cfg.import_fiat("invested_capital_in_loss", v0)?;
|
||||
|
||||
let invested_capital_in_profit_raw = cfg.import_bytes("invested_capital_in_profit_raw", v0)?;
|
||||
let invested_capital_in_profit_raw =
|
||||
cfg.import_bytes("invested_capital_in_profit_raw", v0)?;
|
||||
let invested_capital_in_loss_raw = cfg.import_bytes("invested_capital_in_loss_raw", v0)?;
|
||||
let investor_cap_in_profit_raw = cfg.import_bytes("investor_cap_in_profit_raw", v0)?;
|
||||
let investor_cap_in_loss_raw = cfg.import_bytes("investor_cap_in_loss_raw", v0)?;
|
||||
@@ -193,39 +186,30 @@ impl UnrealizedBase {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply_in_profit
|
||||
.sats
|
||||
.height
|
||||
.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.supply_in_profit.sats.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_loss
|
||||
.sats
|
||||
.height
|
||||
.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.supply_in_loss.sats.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_profit
|
||||
.cents
|
||||
.height
|
||||
.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.unrealized_profit.cents.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_profit.sats.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.supply_in_profit.sats.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.supply_in_loss.sats.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.supply_in_loss.sats.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_profit.cents.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.unrealized_profit.cents.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_loss.cents.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
@@ -273,7 +257,10 @@ impl UnrealizedBase {
|
||||
// Pre-collect all cohort data to avoid per-element BytesVec reads in nested loop
|
||||
let invested_profit_ranges: Vec<Vec<CentsSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.invested_capital_in_profit_raw.collect_range_at(start, end))
|
||||
.map(|o| {
|
||||
o.invested_capital_in_profit_raw
|
||||
.collect_range_at(start, end)
|
||||
})
|
||||
.collect();
|
||||
let invested_loss_ranges: Vec<Vec<CentsSats>> = others
|
||||
.iter()
|
||||
@@ -336,10 +323,7 @@ impl UnrealizedBase {
|
||||
}
|
||||
let investor_price_losers = investor_cap.inner() / invested_cap.inner();
|
||||
let spot_u128 = spot.as_u128();
|
||||
(
|
||||
h,
|
||||
Cents::new((investor_price_losers - spot_u128) as u64),
|
||||
)
|
||||
(h, Cents::new((investor_price_losers - spot_u128) as u64))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
@@ -356,10 +340,7 @@ impl UnrealizedBase {
|
||||
}
|
||||
let investor_price_winners = investor_cap.inner() / invested_cap.inner();
|
||||
let spot_u128 = spot.as_u128();
|
||||
(
|
||||
h,
|
||||
Cents::new((spot_u128 - investor_price_winners) as u64),
|
||||
)
|
||||
(h, Cents::new((spot_u128 - investor_price_winners) as u64))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -18,8 +18,7 @@ impl AddressCohortState {
|
||||
pub(crate) fn new(path: &Path, name: &str) -> Self {
|
||||
Self {
|
||||
addr_count: 0,
|
||||
inner: CohortState::new(path, name)
|
||||
.with_price_rounding(COST_BASIS_PRICE_DIGITS),
|
||||
inner: CohortState::new(path, name).with_price_rounding(COST_BASIS_PRICE_DIGITS),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,5 +135,4 @@ impl AddressCohortState {
|
||||
|
||||
self.inner.decrement_snapshot(&snapshot);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_types::{Age, CentsSats, Cents, CostBasisSnapshot, Height, Sats, SupplyState};
|
||||
use brk_types::{Age, Cents, CentsSats, CostBasisSnapshot, Height, Sats, SupplyState};
|
||||
|
||||
use super::super::cost_basis::{CostBasisData, Percentiles, RealizedState, UnrealizedState};
|
||||
|
||||
@@ -256,8 +256,7 @@ impl CohortState {
|
||||
}
|
||||
|
||||
pub(crate) fn compute_unrealized_state(&mut self, height_price: Cents) -> UnrealizedState {
|
||||
self.cost_basis_data
|
||||
.compute_unrealized_state(height_price)
|
||||
self.cost_basis_data.compute_unrealized_state(height_price)
|
||||
}
|
||||
|
||||
pub(crate) fn write(&mut self, height: Height, cleanup: bool) -> Result<()> {
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_types::{
|
||||
CentsCompact, CentsSats, CentsSquaredSats, Cents, CostBasisDistribution, Height, Sats,
|
||||
Cents, CentsCompact, CentsSats, CentsSquaredSats, CostBasisDistribution, Height, Sats,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::Bytes;
|
||||
@@ -95,7 +95,13 @@ impl CostBasisData {
|
||||
|
||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (CentsCompact, &Sats)> {
|
||||
self.assert_pending_empty();
|
||||
self.state.as_ref().unwrap().base.map.iter().map(|(&k, v)| (k, v))
|
||||
self.state
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.base
|
||||
.map
|
||||
.iter()
|
||||
.map(|(&k, v)| (k, v))
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
@@ -105,7 +111,8 @@ impl CostBasisData {
|
||||
pub(crate) fn first_key_value(&self) -> Option<(CentsCompact, &Sats)> {
|
||||
self.assert_pending_empty();
|
||||
self.state
|
||||
.as_ref().unwrap()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.base
|
||||
.map
|
||||
.first_key_value()
|
||||
@@ -115,7 +122,8 @@ impl CostBasisData {
|
||||
pub(crate) fn last_key_value(&self) -> Option<(CentsCompact, &Sats)> {
|
||||
self.assert_pending_empty();
|
||||
self.state
|
||||
.as_ref().unwrap()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.base
|
||||
.map
|
||||
.last_key_value()
|
||||
@@ -179,7 +187,14 @@ impl CostBasisData {
|
||||
self.percentiles_dirty = true;
|
||||
}
|
||||
for (cents, (inc, dec)) in self.pending.drain() {
|
||||
let entry = self.state.as_mut().unwrap().base.map.entry(cents).or_default();
|
||||
let entry = self
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.base
|
||||
.map
|
||||
.entry(cents)
|
||||
.or_default();
|
||||
*entry += inc;
|
||||
if *entry < dec {
|
||||
panic!(
|
||||
@@ -322,7 +337,10 @@ impl CostBasisData {
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(self.path_state(height), self.state.as_ref().unwrap().serialize()?)?;
|
||||
fs::write(
|
||||
self.path_state(height),
|
||||
self.state.as_ref().unwrap().serialize()?,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use brk_types::{CentsSats, CentsSquaredSats, Cents, Sats};
|
||||
use brk_types::{Cents, CentsSats, CentsSquaredSats, Sats};
|
||||
|
||||
/// Realized state using u128 for raw cent*sat values internally.
|
||||
/// This avoids overflow and defers division to output time for efficiency.
|
||||
@@ -156,14 +156,22 @@ impl RealizedState {
|
||||
|
||||
/// Increment using pre-computed snapshot values (for address path)
|
||||
#[inline]
|
||||
pub(crate) fn increment_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats) {
|
||||
pub(crate) fn increment_snapshot(
|
||||
&mut self,
|
||||
price_sats: CentsSats,
|
||||
investor_cap: CentsSquaredSats,
|
||||
) {
|
||||
self.cap_raw += price_sats.as_u128();
|
||||
self.investor_cap_raw += investor_cap;
|
||||
}
|
||||
|
||||
/// Decrement using pre-computed snapshot values (for address path)
|
||||
#[inline]
|
||||
pub(crate) fn decrement_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats) {
|
||||
pub(crate) fn decrement_snapshot(
|
||||
&mut self,
|
||||
price_sats: CentsSats,
|
||||
investor_cap: CentsSquaredSats,
|
||||
) {
|
||||
self.cap_raw -= price_sats.as_u128();
|
||||
self.investor_cap_raw -= investor_cap;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ impl UnrealizedState {
|
||||
invested_capital_in_profit_raw: 0,
|
||||
invested_capital_in_loss_raw: 0,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/// Internal cache state using u128 for raw cent*sat values.
|
||||
@@ -279,5 +278,4 @@ impl CachedUnrealizedState {
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Cents, EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex,
|
||||
Height, Indexes, SupplyState, Timestamp, TxIndex, Version,
|
||||
Cents, EmptyAddressData, EmptyAddressIndex, FundedAddressData, FundedAddressIndex, Height,
|
||||
Indexes, SupplyState, Timestamp, TxIndex, Version,
|
||||
};
|
||||
use tracing::{debug, info};
|
||||
use vecdb::{
|
||||
AnyVec, BytesVec, Database, Exit, ImportableVec, LazyVecFrom1, PAGE_SIZE, ReadOnlyClone,
|
||||
AnyVec, BytesVec, Database, Exit, ImportableVec, LazyVecFrom1, ReadOnlyClone,
|
||||
ReadableCloneableVec, ReadableVec, Rw, Stamp, StorageMode, WritableVec,
|
||||
};
|
||||
|
||||
@@ -22,7 +22,9 @@ use crate::{
|
||||
},
|
||||
state::BlockState,
|
||||
},
|
||||
indexes, inputs, outputs, prices, transactions,
|
||||
indexes, inputs,
|
||||
internal::{finalize_db, open_db},
|
||||
outputs, prices, transactions,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -34,8 +36,6 @@ use super::{
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::new(22);
|
||||
|
||||
/// Main struct holding all computed vectors and state for stateful computation.
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
@@ -95,8 +95,7 @@ impl Vecs {
|
||||
let db_path = parent.join(super::DB_NAME);
|
||||
let states_path = db_path.join("states");
|
||||
|
||||
let db = Database::open(&db_path)?;
|
||||
db.set_min_len(PAGE_SIZE * 20_000_000)?;
|
||||
let db = open_db(parent, super::DB_NAME, 20_000_000)?;
|
||||
db.set_min_regions(50_000)?;
|
||||
|
||||
let version = parent_version + VERSION;
|
||||
@@ -139,8 +138,7 @@ impl Vecs {
|
||||
let total_addr_count = TotalAddrCountVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
// Per-block delta of total (global + per-type)
|
||||
let new_addr_count =
|
||||
NewAddrCountVecs::forced_import(&db, version, indexes)?;
|
||||
let new_addr_count = NewAddrCountVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
// Growth rate: new / addr_count (global + per-type)
|
||||
let growth_rate = GrowthRateVecs::forced_import(&db, version, indexes)?;
|
||||
@@ -180,13 +178,7 @@ impl Vecs {
|
||||
states_path,
|
||||
};
|
||||
|
||||
this.db.retain_regions(
|
||||
this.iter_any_exportable()
|
||||
.flat_map(|v| v.region_names())
|
||||
.collect(),
|
||||
)?;
|
||||
this.db.compact()?;
|
||||
|
||||
finalize_db(&this.db, &this)?;
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
@@ -308,7 +300,10 @@ impl Vecs {
|
||||
Height::ZERO
|
||||
} else if chain_state.len() == usize::from(recovered_height) {
|
||||
// Normal resume: chain_state already matches, reuse as-is
|
||||
debug!("reusing in-memory chain_state ({} entries)", chain_state.len());
|
||||
debug!(
|
||||
"reusing in-memory chain_state ({} entries)",
|
||||
chain_state.len()
|
||||
);
|
||||
recovered_height
|
||||
} else {
|
||||
debug!("rebuilding chain_state from stored values");
|
||||
@@ -359,8 +354,7 @@ impl Vecs {
|
||||
|
||||
let cached_prices = std::mem::take(&mut self.cached_prices);
|
||||
let cached_timestamps = std::mem::take(&mut self.cached_timestamps);
|
||||
let cached_price_range_max =
|
||||
std::mem::take(&mut self.cached_price_range_max);
|
||||
let cached_price_range_max = std::mem::take(&mut self.cached_price_range_max);
|
||||
|
||||
process_blocks(
|
||||
self,
|
||||
@@ -424,8 +418,12 @@ impl Vecs {
|
||||
|
||||
self.address_activity
|
||||
.compute_rest(starting_indexes.height, &window_starts, exit)?;
|
||||
self.new_addr_count
|
||||
.compute(starting_indexes.height, &window_starts, &self.total_addr_count, exit)?;
|
||||
self.new_addr_count.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.total_addr_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 6e. Compute growth_rate = new_addr_count / addr_count
|
||||
self.growth_rate.compute(
|
||||
|
||||
@@ -6,7 +6,7 @@ use brk_types::{
|
||||
P2PKHBytes, P2SHAddressIndex, P2SHBytes, P2TRAddressIndex, P2TRBytes, P2WPKHAddressIndex,
|
||||
P2WPKHBytes, P2WSHAddressIndex, P2WSHBytes, TxIndex, UnknownOutputIndex, Version,
|
||||
};
|
||||
use vecdb::{ReadableCloneableVec, LazyVecFrom1};
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
@@ -26,12 +26,14 @@ pub struct Vecs {
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2PK33Vecs {
|
||||
pub identity: LazyVecFrom1<P2PK33AddressIndex, P2PK33AddressIndex, P2PK33AddressIndex, P2PK33Bytes>,
|
||||
pub identity:
|
||||
LazyVecFrom1<P2PK33AddressIndex, P2PK33AddressIndex, P2PK33AddressIndex, P2PK33Bytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2PK65Vecs {
|
||||
pub identity: LazyVecFrom1<P2PK65AddressIndex, P2PK65AddressIndex, P2PK65AddressIndex, P2PK65Bytes>,
|
||||
pub identity:
|
||||
LazyVecFrom1<P2PK65AddressIndex, P2PK65AddressIndex, P2PK65AddressIndex, P2PK65Bytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
@@ -51,7 +53,8 @@ pub struct P2TRVecs {
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct P2WPKHVecs {
|
||||
pub identity: LazyVecFrom1<P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHBytes>,
|
||||
pub identity:
|
||||
LazyVecFrom1<P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHAddressIndex, P2WPKHBytes>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
@@ -163,7 +166,11 @@ impl Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"emptyoutputindex",
|
||||
version,
|
||||
indexer.vecs.scripts.empty_to_txindex.read_only_boxed_clone(),
|
||||
indexer
|
||||
.vecs
|
||||
.scripts
|
||||
.empty_to_txindex
|
||||
.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
},
|
||||
@@ -171,7 +178,11 @@ impl Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"unknownoutputindex",
|
||||
version,
|
||||
indexer.vecs.scripts.unknown_to_txindex.read_only_boxed_clone(),
|
||||
indexer
|
||||
.vecs
|
||||
.scripts
|
||||
.unknown_to_txindex
|
||||
.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
},
|
||||
@@ -179,7 +190,11 @@ impl Vecs {
|
||||
identity: LazyVecFrom1::init(
|
||||
"opreturnindex",
|
||||
version,
|
||||
indexer.vecs.scripts.opreturn_to_txindex.read_only_boxed_clone(),
|
||||
indexer
|
||||
.vecs
|
||||
.scripts
|
||||
.opreturn_to_txindex
|
||||
.read_only_boxed_clone(),
|
||||
|index, _| index,
|
||||
),
|
||||
},
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, Year10, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4,
|
||||
Minute10, Minute30, Month1, Month3, Month6, StoredU64, Version, Week1,
|
||||
Year1,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute10, Minute30,
|
||||
Month1, Month3, Month6, StoredU64, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user