mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: fmt
This commit is contained in:
@@ -108,7 +108,13 @@ fn fill_mixed_empty_field_parts(
|
|||||||
// Recurse first (bottom-up)
|
// Recurse first (bottom-up)
|
||||||
for (field_name, child_node) in children {
|
for (field_name, child_node) in children {
|
||||||
let child_path = build_child_path(path, field_name);
|
let child_path = build_child_path(path, field_name);
|
||||||
fill_mixed_empty_field_parts(child_node, &child_path, pattern_lookup, patterns, node_bases);
|
fill_mixed_empty_field_parts(
|
||||||
|
child_node,
|
||||||
|
&child_path,
|
||||||
|
pattern_lookup,
|
||||||
|
patterns,
|
||||||
|
node_bases,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this node has mixed empty/non-empty field_parts
|
// Check if this node has mixed empty/non-empty field_parts
|
||||||
@@ -351,16 +357,18 @@ fn try_embedded_disc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Strategy 2: suffix discriminator (e.g., all field_parts differ by `_4y` suffix)
|
/// Strategy 2: suffix discriminator (e.g., all field_parts differ by `_4y` suffix)
|
||||||
fn try_suffix_disc(
|
fn try_suffix_disc(majority: &[&InstanceAnalysis], fields: &[PatternField]) -> Option<PatternMode> {
|
||||||
majority: &[&InstanceAnalysis],
|
|
||||||
fields: &[PatternField],
|
|
||||||
) -> Option<PatternMode> {
|
|
||||||
let first = &majority[0];
|
let first = &majority[0];
|
||||||
|
|
||||||
// Use a non-empty field to detect the suffix
|
// Use a non-empty field to detect the suffix
|
||||||
let ref_field = fields
|
let ref_field = fields
|
||||||
.iter()
|
.iter()
|
||||||
.find(|f| first.field_parts.get(&f.name).is_some_and(|v| !v.is_empty()))
|
.find(|f| {
|
||||||
|
first
|
||||||
|
.field_parts
|
||||||
|
.get(&f.name)
|
||||||
|
.is_some_and(|v| !v.is_empty())
|
||||||
|
})
|
||||||
.map(|f| &f.name)?;
|
.map(|f| &f.name)?;
|
||||||
let ref_first = first.field_parts.get(ref_field)?;
|
let ref_first = first.field_parts.get(ref_field)?;
|
||||||
|
|
||||||
@@ -763,19 +771,51 @@ mod tests {
|
|||||||
fn test_embedded_disc_percentile_bands() {
|
fn test_embedded_disc_percentile_bands() {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "bps".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "price".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "bps".into(),
|
||||||
PatternField { name: "ratio".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "price".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "ratio".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let pct99 = InstanceAnalysis {
|
let pct99 = InstanceAnalysis {
|
||||||
base: "realized_price".into(),
|
base: "realized_price".into(),
|
||||||
field_parts: [("bps".into(), "ratio_pct99_bps".into()), ("price".into(), "pct99".into()), ("ratio".into(), "ratio_pct99".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("bps".into(), "ratio_pct99_bps".into()),
|
||||||
|
("price".into(), "pct99".into()),
|
||||||
|
("ratio".into(), "ratio_pct99".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let pct1 = InstanceAnalysis {
|
let pct1 = InstanceAnalysis {
|
||||||
base: "realized_price".into(),
|
base: "realized_price".into(),
|
||||||
field_parts: [("bps".into(), "ratio_pct1_bps".into()), ("price".into(), "pct1".into()), ("ratio".into(), "ratio_pct1".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("bps".into(), "ratio_pct1_bps".into()),
|
||||||
|
("price".into(), "pct1".into()),
|
||||||
|
("ratio".into(), "ratio_pct1".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[pct99, pct1], &fields);
|
let mode = determine_pattern_mode(&[pct99, pct1], &fields);
|
||||||
assert!(mode.is_some());
|
assert!(mode.is_some());
|
||||||
@@ -793,19 +833,51 @@ mod tests {
|
|||||||
fn test_suffix_disc_period_windows() {
|
fn test_suffix_disc_period_windows() {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "p1sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "p1sd".into(),
|
||||||
PatternField { name: "zscore".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "sd".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "zscore".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let all_time = InstanceAnalysis {
|
let all_time = InstanceAnalysis {
|
||||||
base: "realized_price".into(),
|
base: "realized_price".into(),
|
||||||
field_parts: [("p1sd".into(), "p1sd".into()), ("sd".into(), "ratio_sd".into()), ("zscore".into(), "ratio_zscore".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("p1sd".into(), "p1sd".into()),
|
||||||
|
("sd".into(), "ratio_sd".into()),
|
||||||
|
("zscore".into(), "ratio_zscore".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let four_year = InstanceAnalysis {
|
let four_year = InstanceAnalysis {
|
||||||
base: "realized_price".into(),
|
base: "realized_price".into(),
|
||||||
field_parts: [("p1sd".into(), "p1sd_4y".into()), ("sd".into(), "ratio_sd_4y".into()), ("zscore".into(), "ratio_zscore_4y".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("p1sd".into(), "p1sd_4y".into()),
|
||||||
|
("sd".into(), "ratio_sd_4y".into()),
|
||||||
|
("zscore".into(), "ratio_zscore_4y".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[all_time, four_year], &fields);
|
let mode = determine_pattern_mode(&[all_time, four_year], &fields);
|
||||||
assert!(mode.is_some());
|
assert!(mode.is_some());
|
||||||
@@ -823,18 +895,39 @@ mod tests {
|
|||||||
fn test_suffix_disc_with_empty_fields() {
|
fn test_suffix_disc_with_empty_fields() {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "band".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "band".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "sd".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let all_time = InstanceAnalysis {
|
let all_time = InstanceAnalysis {
|
||||||
base: "price".into(),
|
base: "price".into(),
|
||||||
field_parts: [("band".into(), "".into()), ("sd".into(), "ratio_sd".into())].into_iter().collect(),
|
field_parts: [("band".into(), "".into()), ("sd".into(), "ratio_sd".into())]
|
||||||
is_suffix_mode: true, has_outlier: false,
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let four_year = InstanceAnalysis {
|
let four_year = InstanceAnalysis {
|
||||||
base: "price".into(),
|
base: "price".into(),
|
||||||
field_parts: [("band".into(), "".into()), ("sd".into(), "ratio_sd_4y".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("band".into(), "".into()),
|
||||||
|
("sd".into(), "ratio_sd_4y".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[all_time, four_year], &fields);
|
let mode = determine_pattern_mode(&[all_time, four_year], &fields);
|
||||||
assert!(mode.is_some());
|
assert!(mode.is_some());
|
||||||
@@ -851,18 +944,39 @@ mod tests {
|
|||||||
fn test_suffix_disc_empty_to_nonempty() {
|
fn test_suffix_disc_empty_to_nonempty() {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "all".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "sth".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "all".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "sth".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let regular = InstanceAnalysis {
|
let regular = InstanceAnalysis {
|
||||||
base: "supply".into(),
|
base: "supply".into(),
|
||||||
field_parts: [("all".into(), "".into()), ("sth".into(), "sth_".into())].into_iter().collect(),
|
field_parts: [("all".into(), "".into()), ("sth".into(), "sth_".into())]
|
||||||
is_suffix_mode: true, has_outlier: false,
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let profitability = InstanceAnalysis {
|
let profitability = InstanceAnalysis {
|
||||||
base: "utxos_in_profit".into(),
|
base: "utxos_in_profit".into(),
|
||||||
field_parts: [("all".into(), "supply".into()), ("sth".into(), "sth_supply".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("all".into(), "supply".into()),
|
||||||
|
("sth".into(), "sth_supply".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[regular, profitability], &fields);
|
let mode = determine_pattern_mode(&[regular, profitability], &fields);
|
||||||
assert!(mode.is_some());
|
assert!(mode.is_some());
|
||||||
@@ -879,43 +993,91 @@ mod tests {
|
|||||||
fn test_outlier_rejects_pattern() {
|
fn test_outlier_rejects_pattern() {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "ratio".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "value".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "ratio".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "value".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
// SOPR case: one instance has outlier naming (no common prefix)
|
// SOPR case: one instance has outlier naming (no common prefix)
|
||||||
let normal = InstanceAnalysis {
|
let normal = InstanceAnalysis {
|
||||||
base: "series".into(),
|
base: "series".into(),
|
||||||
field_parts: [("ratio".into(), "ratio".into()), ("value".into(), "value".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: false,
|
("ratio".into(), "ratio".into()),
|
||||||
|
("value".into(), "value".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let outlier = InstanceAnalysis {
|
let outlier = InstanceAnalysis {
|
||||||
base: "".into(),
|
base: "".into(),
|
||||||
field_parts: [("ratio".into(), "asopr".into()), ("value".into(), "adj_value".into())].into_iter().collect(),
|
field_parts: [
|
||||||
is_suffix_mode: true, has_outlier: true,
|
("ratio".into(), "asopr".into()),
|
||||||
|
("value".into(), "adj_value".into()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: true,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[normal, outlier], &fields);
|
let mode = determine_pattern_mode(&[normal, outlier], &fields);
|
||||||
assert!(mode.is_some(), "Outlier should be filtered out, leaving a valid pattern from non-outlier instances");
|
assert!(
|
||||||
|
mode.is_some(),
|
||||||
|
"Outlier should be filtered out, leaving a valid pattern from non-outlier instances"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unanimity_rejects_disagreeing_instances() {
|
fn test_unanimity_rejects_disagreeing_instances() {
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "a".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "b".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "a".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "b".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let inst1 = InstanceAnalysis {
|
let inst1 = InstanceAnalysis {
|
||||||
base: "x".into(),
|
base: "x".into(),
|
||||||
field_parts: [("a".into(), "foo".into()), ("b".into(), "bar".into())].into_iter().collect(),
|
field_parts: [("a".into(), "foo".into()), ("b".into(), "bar".into())]
|
||||||
is_suffix_mode: true, has_outlier: false,
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let inst2 = InstanceAnalysis {
|
let inst2 = InstanceAnalysis {
|
||||||
base: "y".into(),
|
base: "y".into(),
|
||||||
field_parts: [("a".into(), "baz".into()), ("b".into(), "qux".into())].into_iter().collect(),
|
field_parts: [("a".into(), "baz".into()), ("b".into(), "qux".into())]
|
||||||
is_suffix_mode: true, has_outlier: false,
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[inst1, inst2], &fields);
|
let mode = determine_pattern_mode(&[inst1, inst2], &fields);
|
||||||
assert!(mode.is_none(), "Should be non-parameterizable when no pattern detected");
|
assert!(
|
||||||
|
mode.is_none(),
|
||||||
|
"Should be non-parameterizable when no pattern detected"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -925,20 +1087,43 @@ mod tests {
|
|||||||
// Should keep identity (empty parts) so both children receive acc unchanged.
|
// Should keep identity (empty parts) so both children receive acc unchanged.
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![
|
||||||
PatternField { name: "absolute".into(), rust_type: "TypeA".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "rate".into(), rust_type: "TypeB".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "absolute".into(),
|
||||||
|
rust_type: "TypeA".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "rate".into(),
|
||||||
|
rust_type: "TypeB".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
let inst = InstanceAnalysis {
|
let inst = InstanceAnalysis {
|
||||||
base: "supply_delta".into(),
|
base: "supply_delta".into(),
|
||||||
field_parts: [("absolute".into(), "".into()), ("rate".into(), "".into())].into_iter().collect(),
|
field_parts: [("absolute".into(), "".into()), ("rate".into(), "".into())]
|
||||||
is_suffix_mode: true, has_outlier: false,
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: false,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[inst], &fields);
|
let mode = determine_pattern_mode(&[inst], &fields);
|
||||||
assert!(mode.is_some());
|
assert!(mode.is_some());
|
||||||
match mode.unwrap() {
|
match mode.unwrap() {
|
||||||
PatternMode::Suffix { relatives } => {
|
PatternMode::Suffix { relatives } => {
|
||||||
assert_eq!(relatives.get("absolute"), Some(&"".to_string()), "absolute should be identity");
|
assert_eq!(
|
||||||
assert_eq!(relatives.get("rate"), Some(&"".to_string()), "rate should be identity");
|
relatives.get("absolute"),
|
||||||
|
Some(&"".to_string()),
|
||||||
|
"absolute should be identity"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
relatives.get("rate"),
|
||||||
|
Some(&"".to_string()),
|
||||||
|
"rate should be identity"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
other => panic!("Expected Suffix with identity, got {:?}", other),
|
other => panic!("Expected Suffix with identity, got {:?}", other),
|
||||||
}
|
}
|
||||||
@@ -975,16 +1160,26 @@ mod tests {
|
|||||||
// Parent patterns containing non-parameterizable children should also
|
// Parent patterns containing non-parameterizable children should also
|
||||||
// be detected via metadata.is_parameterizable (recursive check).
|
// be detected via metadata.is_parameterizable (recursive check).
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
let fields = vec![
|
let fields = vec![PatternField {
|
||||||
PatternField { name: "a".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "a".into(),
|
||||||
];
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
}];
|
||||||
let inst = InstanceAnalysis {
|
let inst = InstanceAnalysis {
|
||||||
base: "".into(),
|
base: "".into(),
|
||||||
field_parts: [("a".into(), "standalone_name".into())].into_iter().collect(),
|
field_parts: [("a".into(), "standalone_name".into())]
|
||||||
is_suffix_mode: true, has_outlier: true,
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
is_suffix_mode: true,
|
||||||
|
has_outlier: true,
|
||||||
};
|
};
|
||||||
let mode = determine_pattern_mode(&[inst], &fields);
|
let mode = determine_pattern_mode(&[inst], &fields);
|
||||||
assert!(mode.is_none(), "Pattern with outlier should be non-parameterizable");
|
assert!(
|
||||||
|
mode.is_none(),
|
||||||
|
"Pattern with outlier should be non-parameterizable"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -998,9 +1193,27 @@ mod tests {
|
|||||||
let pattern = StructuralPattern {
|
let pattern = StructuralPattern {
|
||||||
name: "TestPattern".into(),
|
name: "TestPattern".into(),
|
||||||
fields: vec![
|
fields: vec![
|
||||||
PatternField { name: "_0sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
PatternField {
|
||||||
PatternField { name: "p1sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
name: "_0sd".into(),
|
||||||
PatternField { name: "sd".into(), rust_type: "T".into(), json_type: "n".into(), indexes: BTreeSet::new(), type_param: None },
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "p1sd".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
|
PatternField {
|
||||||
|
name: "sd".into(),
|
||||||
|
rust_type: "T".into(),
|
||||||
|
json_type: "n".into(),
|
||||||
|
indexes: BTreeSet::new(),
|
||||||
|
type_param: None,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
mode: Some(PatternMode::Templated {
|
mode: Some(PatternMode::Templated {
|
||||||
templates: [
|
templates: [
|
||||||
@@ -1059,9 +1272,15 @@ mod tests {
|
|||||||
assert_eq!(analysis.field_parts.get("loss"), Some(&"".to_string()));
|
assert_eq!(analysis.field_parts.get("loss"), Some(&"".to_string()));
|
||||||
assert_eq!(analysis.field_parts.get("supply"), Some(&"".to_string()));
|
assert_eq!(analysis.field_parts.get("supply"), Some(&"".to_string()));
|
||||||
// others should be non-empty
|
// others should be non-empty
|
||||||
assert_eq!(analysis.field_parts.get("cap"), Some(&"realized_cap".to_string()));
|
assert_eq!(
|
||||||
|
analysis.field_parts.get("cap"),
|
||||||
|
Some(&"realized_cap".to_string())
|
||||||
|
);
|
||||||
assert_eq!(analysis.field_parts.get("mvrv"), Some(&"mvrv".to_string()));
|
assert_eq!(analysis.field_parts.get("mvrv"), Some(&"mvrv".to_string()));
|
||||||
assert_eq!(analysis.field_parts.get("price"), Some(&"realized_price".to_string()));
|
assert_eq!(
|
||||||
|
analysis.field_parts.get("price"),
|
||||||
|
Some(&"realized_price".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1111,12 +1330,20 @@ mod tests {
|
|||||||
&mut path_to_pattern,
|
&mut path_to_pattern,
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = node_bases.get("test").expect("should have node_bases entry");
|
let result = node_bases
|
||||||
|
.get("test")
|
||||||
|
.expect("should have node_bases entry");
|
||||||
assert_eq!(result.base, "utxos");
|
assert_eq!(result.base, "utxos");
|
||||||
assert!(!result.has_outlier);
|
assert!(!result.has_outlier);
|
||||||
assert_eq!(result.field_parts.get("cap"), Some(&"realized_cap".to_string()));
|
assert_eq!(
|
||||||
|
result.field_parts.get("cap"),
|
||||||
|
Some(&"realized_cap".to_string())
|
||||||
|
);
|
||||||
assert_eq!(result.field_parts.get("mvrv"), Some(&"mvrv".to_string()));
|
assert_eq!(result.field_parts.get("mvrv"), Some(&"mvrv".to_string()));
|
||||||
// loss branch returns base "utxos_realized_loss" which yields field_part "realized_loss"
|
// loss branch returns base "utxos_realized_loss" which yields field_part "realized_loss"
|
||||||
assert_eq!(result.field_parts.get("loss"), Some(&"realized_loss".to_string()));
|
assert_eq!(
|
||||||
|
result.field_parts.get("loss"),
|
||||||
|
Some(&"realized_loss".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use brk_cohort::{
|
use brk_cohort::{
|
||||||
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, EPOCH_NAMES, LOSS_NAMES,
|
AGE_RANGE_NAMES, AMOUNT_RANGE_NAMES, CLASS_NAMES, EPOCH_NAMES, LOSS_NAMES, OVER_AGE_NAMES,
|
||||||
OVER_AGE_NAMES, OVER_AMOUNT_NAMES, PROFITABILITY_RANGE_NAMES, PROFIT_NAMES,
|
OVER_AMOUNT_NAMES, PROFIT_NAMES, PROFITABILITY_RANGE_NAMES, SPENDABLE_TYPE_NAMES, TERM_NAMES,
|
||||||
SPENDABLE_TYPE_NAMES, TERM_NAMES, UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
|
UNDER_AGE_NAMES, UNDER_AMOUNT_NAMES,
|
||||||
};
|
};
|
||||||
use brk_types::{Index, PoolSlug, pools};
|
use brk_types::{Index, PoolSlug, pools};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -64,7 +64,10 @@ impl CohortConstants {
|
|||||||
("AMOUNT_RANGE_NAMES", to_value(&AMOUNT_RANGE_NAMES)),
|
("AMOUNT_RANGE_NAMES", to_value(&AMOUNT_RANGE_NAMES)),
|
||||||
("OVER_AMOUNT_NAMES", to_value(&OVER_AMOUNT_NAMES)),
|
("OVER_AMOUNT_NAMES", to_value(&OVER_AMOUNT_NAMES)),
|
||||||
("UNDER_AMOUNT_NAMES", to_value(&UNDER_AMOUNT_NAMES)),
|
("UNDER_AMOUNT_NAMES", to_value(&UNDER_AMOUNT_NAMES)),
|
||||||
("PROFITABILITY_RANGE_NAMES", to_value(&PROFITABILITY_RANGE_NAMES)),
|
(
|
||||||
|
"PROFITABILITY_RANGE_NAMES",
|
||||||
|
to_value(&PROFITABILITY_RANGE_NAMES),
|
||||||
|
),
|
||||||
("PROFIT_NAMES", to_value(&PROFIT_NAMES)),
|
("PROFIT_NAMES", to_value(&PROFIT_NAMES)),
|
||||||
("LOSS_NAMES", to_value(&LOSS_NAMES)),
|
("LOSS_NAMES", to_value(&LOSS_NAMES)),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ use std::fmt::Write;
|
|||||||
|
|
||||||
use brk_types::SeriesLeafWithSchema;
|
use brk_types::SeriesLeafWithSchema;
|
||||||
|
|
||||||
use crate::{ClientMetadata, LanguageSyntax, PatternBaseResult, PatternField, PatternMode, StructuralPattern};
|
use crate::{
|
||||||
|
ClientMetadata, LanguageSyntax, PatternBaseResult, PatternField, PatternMode, StructuralPattern,
|
||||||
|
};
|
||||||
|
|
||||||
/// Create a path suffix from a name.
|
/// Create a path suffix from a name.
|
||||||
fn path_suffix(name: &str) -> String {
|
fn path_suffix(name: &str) -> String {
|
||||||
@@ -33,9 +35,7 @@ fn compute_parameterized_value<S: LanguageSyntax>(
|
|||||||
if let Some(child_pattern) = metadata.find_pattern(&field.rust_type)
|
if let Some(child_pattern) = metadata.find_pattern(&field.rust_type)
|
||||||
&& child_pattern.is_templated()
|
&& child_pattern.is_templated()
|
||||||
{
|
{
|
||||||
let disc_template = pattern
|
let disc_template = pattern.get_field_part(&field.name).unwrap_or(&field.name);
|
||||||
.get_field_part(&field.name)
|
|
||||||
.unwrap_or(&field.name);
|
|
||||||
let disc_arg = syntax.disc_arg_expr(disc_template);
|
let disc_arg = syntax.disc_arg_expr(disc_template);
|
||||||
let acc_arg = syntax.owned_expr("acc");
|
let acc_arg = syntax.owned_expr("acc");
|
||||||
return syntax.constructor(&field.rust_type, &format!("{acc_arg}, {disc_arg}"));
|
return syntax.constructor(&field.rust_type, &format!("{acc_arg}, {disc_arg}"));
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ pub fn prepare_tree_node<'a>(
|
|||||||
p.is_suffix_mode() == base_result.is_suffix_mode
|
p.is_suffix_mode() == base_result.is_suffix_mode
|
||||||
&& p.field_parts_match(&base_result.field_parts)
|
&& p.field_parts_match(&base_result.field_parts)
|
||||||
});
|
});
|
||||||
let is_parameterizable = matching_pattern
|
let is_parameterizable =
|
||||||
.is_none_or(|p| metadata.is_parameterizable(&p.name));
|
matching_pattern.is_none_or(|p| metadata.is_parameterizable(&p.name));
|
||||||
|
|
||||||
// should_inline determines if we generate an inline struct type
|
// should_inline determines if we generate an inline struct type
|
||||||
let should_inline = !is_leaf
|
let should_inline = !is_leaf
|
||||||
|
|||||||
@@ -726,7 +726,12 @@ pub fn generate_structural_patterns(
|
|||||||
writeln!(output, " */").unwrap();
|
writeln!(output, " */").unwrap();
|
||||||
|
|
||||||
if pattern.is_templated() {
|
if pattern.is_templated() {
|
||||||
writeln!(output, "function create{}(client, acc, disc) {{", pattern.name).unwrap();
|
writeln!(
|
||||||
|
output,
|
||||||
|
"function create{}(client, acc, disc) {{",
|
||||||
|
pattern.name
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
writeln!(output, "function create{}(client, acc) {{", pattern.name).unwrap();
|
writeln!(output, "function create{}(client, acc) {{", pattern.name).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,7 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
writeln!(output, " \"\"\"").unwrap();
|
writeln!(output, " \"\"\"").unwrap();
|
||||||
writeln!(
|
writeln!(output, " return SeriesEndpoint(self, series, index)").unwrap();
|
||||||
output,
|
|
||||||
" return SeriesEndpoint(self, series, index)"
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
writeln!(output).unwrap();
|
writeln!(output).unwrap();
|
||||||
|
|
||||||
// Generate helper methods
|
// Generate helper methods
|
||||||
|
|||||||
@@ -684,7 +684,6 @@ pub fn generate_structural_patterns(
|
|||||||
writeln!(output, "# Reusable structural pattern classes\n").unwrap();
|
writeln!(output, "# Reusable structural pattern classes\n").unwrap();
|
||||||
|
|
||||||
for pattern in patterns {
|
for pattern in patterns {
|
||||||
|
|
||||||
// Generate class
|
// Generate class
|
||||||
if pattern.is_generic {
|
if pattern.is_generic {
|
||||||
writeln!(output, "class {}(Generic[T]):", pattern.name).unwrap();
|
writeln!(output, "class {}(Generic[T]):", pattern.name).unwrap();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -75,7 +75,8 @@ impl<T> AddrGroups<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_overlapping_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
pub fn iter_overlapping_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||||
self.under_amount.iter_mut().chain(self.over_amount.iter_mut())
|
self.under_amount
|
||||||
|
.iter_mut()
|
||||||
|
.chain(self.over_amount.iter_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,29 @@ impl<T> AgeRange<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_array(arr: [T; 21]) -> Self {
|
pub fn from_array(arr: [T; 21]) -> Self {
|
||||||
let [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20] = arr;
|
let [
|
||||||
|
a0,
|
||||||
|
a1,
|
||||||
|
a2,
|
||||||
|
a3,
|
||||||
|
a4,
|
||||||
|
a5,
|
||||||
|
a6,
|
||||||
|
a7,
|
||||||
|
a8,
|
||||||
|
a9,
|
||||||
|
a10,
|
||||||
|
a11,
|
||||||
|
a12,
|
||||||
|
a13,
|
||||||
|
a14,
|
||||||
|
a15,
|
||||||
|
a16,
|
||||||
|
a17,
|
||||||
|
a18,
|
||||||
|
a19,
|
||||||
|
a20,
|
||||||
|
] = arr;
|
||||||
Self {
|
Self {
|
||||||
under_1h: a0,
|
under_1h: a0,
|
||||||
_1h_to_1d: a1,
|
_1h_to_1d: a1,
|
||||||
|
|||||||
@@ -84,7 +84,11 @@ pub const AMOUNT_RANGE_NAMES: AmountRange<CohortName> = AmountRange {
|
|||||||
_10sats_to_100sats: CohortName::new("10sats_to_100sats", "10-100 sats", "10-100 Sats"),
|
_10sats_to_100sats: CohortName::new("10sats_to_100sats", "10-100 sats", "10-100 Sats"),
|
||||||
_100sats_to_1k_sats: CohortName::new("100sats_to_1k_sats", "100-1k sats", "100-1K Sats"),
|
_100sats_to_1k_sats: CohortName::new("100sats_to_1k_sats", "100-1k sats", "100-1K Sats"),
|
||||||
_1k_sats_to_10k_sats: CohortName::new("1k_sats_to_10k_sats", "1k-10k sats", "1K-10K Sats"),
|
_1k_sats_to_10k_sats: CohortName::new("1k_sats_to_10k_sats", "1k-10k sats", "1K-10K Sats"),
|
||||||
_10k_sats_to_100k_sats: CohortName::new("10k_sats_to_100k_sats", "10k-100k sats", "10K-100K Sats"),
|
_10k_sats_to_100k_sats: CohortName::new(
|
||||||
|
"10k_sats_to_100k_sats",
|
||||||
|
"10k-100k sats",
|
||||||
|
"10K-100K Sats",
|
||||||
|
),
|
||||||
_100k_sats_to_1m_sats: CohortName::new("100k_sats_to_1m_sats", "100k-1M sats", "100K-1M Sats"),
|
_100k_sats_to_1m_sats: CohortName::new("100k_sats_to_1m_sats", "100k-1M sats", "100K-1M Sats"),
|
||||||
_1m_sats_to_10m_sats: CohortName::new("1m_sats_to_10m_sats", "1M-10M sats", "1M-10M Sats"),
|
_1m_sats_to_10m_sats: CohortName::new("1m_sats_to_10m_sats", "1M-10M sats", "1M-10M Sats"),
|
||||||
_10m_sats_to_1btc: CohortName::new("10m_sats_to_1btc", "0.1-1 BTC", "0.1-1 BTC"),
|
_10m_sats_to_1btc: CohortName::new("10m_sats_to_1btc", "0.1-1 BTC", "0.1-1 BTC"),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
mod addr;
|
mod addr;
|
||||||
mod amount_filter;
|
|
||||||
mod age_range;
|
mod age_range;
|
||||||
|
mod amount_filter;
|
||||||
mod amount_range;
|
mod amount_range;
|
||||||
mod by_addr_type;
|
mod by_addr_type;
|
||||||
mod by_any_addr;
|
mod by_any_addr;
|
||||||
@@ -30,8 +30,8 @@ mod utxo;
|
|||||||
pub use brk_types::{Age, Term};
|
pub use brk_types::{Age, Term};
|
||||||
|
|
||||||
pub use addr::*;
|
pub use addr::*;
|
||||||
pub use amount_filter::*;
|
|
||||||
pub use age_range::*;
|
pub use age_range::*;
|
||||||
|
pub use amount_filter::*;
|
||||||
pub use amount_range::*;
|
pub use amount_range::*;
|
||||||
pub use by_addr_type::*;
|
pub use by_addr_type::*;
|
||||||
pub use by_any_addr::*;
|
pub use by_any_addr::*;
|
||||||
|
|||||||
@@ -43,19 +43,31 @@ pub const OVER_AMOUNT_NAMES: OverAmount<CohortName> = OverAmount {
|
|||||||
pub const OVER_AMOUNT_FILTERS: OverAmount<Filter> = OverAmount {
|
pub const OVER_AMOUNT_FILTERS: OverAmount<Filter> = OverAmount {
|
||||||
_1sat: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1sat)),
|
_1sat: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1sat)),
|
||||||
_10sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10sats)),
|
_10sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10sats)),
|
||||||
_100sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._100sats)),
|
_100sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
_1k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1k_sats)),
|
OVER_AMOUNT_THRESHOLDS._100sats,
|
||||||
_10k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10k_sats)),
|
)),
|
||||||
|
_1k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
|
OVER_AMOUNT_THRESHOLDS._1k_sats,
|
||||||
|
)),
|
||||||
|
_10k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
|
OVER_AMOUNT_THRESHOLDS._10k_sats,
|
||||||
|
)),
|
||||||
_100k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
_100k_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
OVER_AMOUNT_THRESHOLDS._100k_sats,
|
OVER_AMOUNT_THRESHOLDS._100k_sats,
|
||||||
)),
|
)),
|
||||||
_1m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1m_sats)),
|
_1m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
_10m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10m_sats)),
|
OVER_AMOUNT_THRESHOLDS._1m_sats,
|
||||||
|
)),
|
||||||
|
_10m_sats: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
|
OVER_AMOUNT_THRESHOLDS._10m_sats,
|
||||||
|
)),
|
||||||
_1btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1btc)),
|
_1btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1btc)),
|
||||||
_10btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10btc)),
|
_10btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10btc)),
|
||||||
_100btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._100btc)),
|
_100btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._100btc)),
|
||||||
_1k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1k_btc)),
|
_1k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._1k_btc)),
|
||||||
_10k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(OVER_AMOUNT_THRESHOLDS._10k_btc)),
|
_10k_btc: Filter::Amount(AmountFilter::GreaterOrEqual(
|
||||||
|
OVER_AMOUNT_THRESHOLDS._10k_btc,
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable, Serialize)]
|
#[derive(Default, Clone, Traversable, Serialize)]
|
||||||
|
|||||||
@@ -16,10 +16,26 @@ pub const PROFIT_NAMES: Profit<CohortName> = Profit {
|
|||||||
_70pct: CohortName::new("utxos_over_70pct_in_profit", ">=70%", "Over 70% in Profit"),
|
_70pct: CohortName::new("utxos_over_70pct_in_profit", ">=70%", "Over 70% in Profit"),
|
||||||
_80pct: CohortName::new("utxos_over_80pct_in_profit", ">=80%", "Over 80% in Profit"),
|
_80pct: CohortName::new("utxos_over_80pct_in_profit", ">=80%", "Over 80% in Profit"),
|
||||||
_90pct: CohortName::new("utxos_over_90pct_in_profit", ">=90%", "Over 90% in Profit"),
|
_90pct: CohortName::new("utxos_over_90pct_in_profit", ">=90%", "Over 90% in Profit"),
|
||||||
_100pct: CohortName::new("utxos_over_100pct_in_profit", ">=100%", "Over 100% in Profit"),
|
_100pct: CohortName::new(
|
||||||
_200pct: CohortName::new("utxos_over_200pct_in_profit", ">=200%", "Over 200% in Profit"),
|
"utxos_over_100pct_in_profit",
|
||||||
_300pct: CohortName::new("utxos_over_300pct_in_profit", ">=300%", "Over 300% in Profit"),
|
">=100%",
|
||||||
_500pct: CohortName::new("utxos_over_500pct_in_profit", ">=500%", "Over 500% in Profit"),
|
"Over 100% in Profit",
|
||||||
|
),
|
||||||
|
_200pct: CohortName::new(
|
||||||
|
"utxos_over_200pct_in_profit",
|
||||||
|
">=200%",
|
||||||
|
"Over 200% in Profit",
|
||||||
|
),
|
||||||
|
_300pct: CohortName::new(
|
||||||
|
"utxos_over_300pct_in_profit",
|
||||||
|
">=300%",
|
||||||
|
"Over 300% in Profit",
|
||||||
|
),
|
||||||
|
_500pct: CohortName::new(
|
||||||
|
"utxos_over_500pct_in_profit",
|
||||||
|
">=500%",
|
||||||
|
"Over 500% in Profit",
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Number of profit thresholds.
|
/// Number of profit thresholds.
|
||||||
|
|||||||
@@ -83,31 +83,131 @@ pub fn compute_profitability_boundaries(spot: Cents) -> [Cents; PROFITABILITY_BO
|
|||||||
|
|
||||||
/// Profitability range names (25 ranges, from most profitable to most in loss)
|
/// Profitability range names (25 ranges, from most profitable to most in loss)
|
||||||
pub const PROFITABILITY_RANGE_NAMES: ProfitabilityRange<CohortName> = ProfitabilityRange {
|
pub const PROFITABILITY_RANGE_NAMES: ProfitabilityRange<CohortName> = ProfitabilityRange {
|
||||||
over_1000pct_in_profit: CohortName::new("utxos_over_1000pct_in_profit", "+>1000%", "Over 1000% in Profit"),
|
over_1000pct_in_profit: CohortName::new(
|
||||||
_500pct_to_1000pct_in_profit: CohortName::new("utxos_500pct_to_1000pct_in_profit", "+500-1000%", "500-1000% in Profit"),
|
"utxos_over_1000pct_in_profit",
|
||||||
_300pct_to_500pct_in_profit: CohortName::new("utxos_300pct_to_500pct_in_profit", "+300-500%", "300-500% in Profit"),
|
"+>1000%",
|
||||||
_200pct_to_300pct_in_profit: CohortName::new("utxos_200pct_to_300pct_in_profit", "+200-300%", "200-300% in Profit"),
|
"Over 1000% in Profit",
|
||||||
_100pct_to_200pct_in_profit: CohortName::new("utxos_100pct_to_200pct_in_profit", "+100-200%", "100-200% in Profit"),
|
),
|
||||||
_90pct_to_100pct_in_profit: CohortName::new("utxos_90pct_to_100pct_in_profit", "+90-100%", "90-100% in Profit"),
|
_500pct_to_1000pct_in_profit: CohortName::new(
|
||||||
_80pct_to_90pct_in_profit: CohortName::new("utxos_80pct_to_90pct_in_profit", "+80-90%", "80-90% in Profit"),
|
"utxos_500pct_to_1000pct_in_profit",
|
||||||
_70pct_to_80pct_in_profit: CohortName::new("utxos_70pct_to_80pct_in_profit", "+70-80%", "70-80% in Profit"),
|
"+500-1000%",
|
||||||
_60pct_to_70pct_in_profit: CohortName::new("utxos_60pct_to_70pct_in_profit", "+60-70%", "60-70% in Profit"),
|
"500-1000% in Profit",
|
||||||
_50pct_to_60pct_in_profit: CohortName::new("utxos_50pct_to_60pct_in_profit", "+50-60%", "50-60% in Profit"),
|
),
|
||||||
_40pct_to_50pct_in_profit: CohortName::new("utxos_40pct_to_50pct_in_profit", "+40-50%", "40-50% in Profit"),
|
_300pct_to_500pct_in_profit: CohortName::new(
|
||||||
_30pct_to_40pct_in_profit: CohortName::new("utxos_30pct_to_40pct_in_profit", "+30-40%", "30-40% in Profit"),
|
"utxos_300pct_to_500pct_in_profit",
|
||||||
_20pct_to_30pct_in_profit: CohortName::new("utxos_20pct_to_30pct_in_profit", "+20-30%", "20-30% in Profit"),
|
"+300-500%",
|
||||||
_10pct_to_20pct_in_profit: CohortName::new("utxos_10pct_to_20pct_in_profit", "+10-20%", "10-20% in Profit"),
|
"300-500% in Profit",
|
||||||
_0pct_to_10pct_in_profit: CohortName::new("utxos_0pct_to_10pct_in_profit", "+0-10%", "0-10% in Profit"),
|
),
|
||||||
_0pct_to_10pct_in_loss: CohortName::new("utxos_0pct_to_10pct_in_loss", "-0-10%", "0-10% in Loss"),
|
_200pct_to_300pct_in_profit: CohortName::new(
|
||||||
_10pct_to_20pct_in_loss: CohortName::new("utxos_10pct_to_20pct_in_loss", "-10-20%", "10-20% in Loss"),
|
"utxos_200pct_to_300pct_in_profit",
|
||||||
_20pct_to_30pct_in_loss: CohortName::new("utxos_20pct_to_30pct_in_loss", "-20-30%", "20-30% in Loss"),
|
"+200-300%",
|
||||||
_30pct_to_40pct_in_loss: CohortName::new("utxos_30pct_to_40pct_in_loss", "-30-40%", "30-40% in Loss"),
|
"200-300% in Profit",
|
||||||
_40pct_to_50pct_in_loss: CohortName::new("utxos_40pct_to_50pct_in_loss", "-40-50%", "40-50% in Loss"),
|
),
|
||||||
_50pct_to_60pct_in_loss: CohortName::new("utxos_50pct_to_60pct_in_loss", "-50-60%", "50-60% in Loss"),
|
_100pct_to_200pct_in_profit: CohortName::new(
|
||||||
_60pct_to_70pct_in_loss: CohortName::new("utxos_60pct_to_70pct_in_loss", "-60-70%", "60-70% in Loss"),
|
"utxos_100pct_to_200pct_in_profit",
|
||||||
_70pct_to_80pct_in_loss: CohortName::new("utxos_70pct_to_80pct_in_loss", "-70-80%", "70-80% in Loss"),
|
"+100-200%",
|
||||||
_80pct_to_90pct_in_loss: CohortName::new("utxos_80pct_to_90pct_in_loss", "-80-90%", "80-90% in Loss"),
|
"100-200% in Profit",
|
||||||
_90pct_to_100pct_in_loss: CohortName::new("utxos_90pct_to_100pct_in_loss", "-90-100%", "90-100% in Loss"),
|
),
|
||||||
|
_90pct_to_100pct_in_profit: CohortName::new(
|
||||||
|
"utxos_90pct_to_100pct_in_profit",
|
||||||
|
"+90-100%",
|
||||||
|
"90-100% in Profit",
|
||||||
|
),
|
||||||
|
_80pct_to_90pct_in_profit: CohortName::new(
|
||||||
|
"utxos_80pct_to_90pct_in_profit",
|
||||||
|
"+80-90%",
|
||||||
|
"80-90% in Profit",
|
||||||
|
),
|
||||||
|
_70pct_to_80pct_in_profit: CohortName::new(
|
||||||
|
"utxos_70pct_to_80pct_in_profit",
|
||||||
|
"+70-80%",
|
||||||
|
"70-80% in Profit",
|
||||||
|
),
|
||||||
|
_60pct_to_70pct_in_profit: CohortName::new(
|
||||||
|
"utxos_60pct_to_70pct_in_profit",
|
||||||
|
"+60-70%",
|
||||||
|
"60-70% in Profit",
|
||||||
|
),
|
||||||
|
_50pct_to_60pct_in_profit: CohortName::new(
|
||||||
|
"utxos_50pct_to_60pct_in_profit",
|
||||||
|
"+50-60%",
|
||||||
|
"50-60% in Profit",
|
||||||
|
),
|
||||||
|
_40pct_to_50pct_in_profit: CohortName::new(
|
||||||
|
"utxos_40pct_to_50pct_in_profit",
|
||||||
|
"+40-50%",
|
||||||
|
"40-50% in Profit",
|
||||||
|
),
|
||||||
|
_30pct_to_40pct_in_profit: CohortName::new(
|
||||||
|
"utxos_30pct_to_40pct_in_profit",
|
||||||
|
"+30-40%",
|
||||||
|
"30-40% in Profit",
|
||||||
|
),
|
||||||
|
_20pct_to_30pct_in_profit: CohortName::new(
|
||||||
|
"utxos_20pct_to_30pct_in_profit",
|
||||||
|
"+20-30%",
|
||||||
|
"20-30% in Profit",
|
||||||
|
),
|
||||||
|
_10pct_to_20pct_in_profit: CohortName::new(
|
||||||
|
"utxos_10pct_to_20pct_in_profit",
|
||||||
|
"+10-20%",
|
||||||
|
"10-20% in Profit",
|
||||||
|
),
|
||||||
|
_0pct_to_10pct_in_profit: CohortName::new(
|
||||||
|
"utxos_0pct_to_10pct_in_profit",
|
||||||
|
"+0-10%",
|
||||||
|
"0-10% in Profit",
|
||||||
|
),
|
||||||
|
_0pct_to_10pct_in_loss: CohortName::new(
|
||||||
|
"utxos_0pct_to_10pct_in_loss",
|
||||||
|
"-0-10%",
|
||||||
|
"0-10% in Loss",
|
||||||
|
),
|
||||||
|
_10pct_to_20pct_in_loss: CohortName::new(
|
||||||
|
"utxos_10pct_to_20pct_in_loss",
|
||||||
|
"-10-20%",
|
||||||
|
"10-20% in Loss",
|
||||||
|
),
|
||||||
|
_20pct_to_30pct_in_loss: CohortName::new(
|
||||||
|
"utxos_20pct_to_30pct_in_loss",
|
||||||
|
"-20-30%",
|
||||||
|
"20-30% in Loss",
|
||||||
|
),
|
||||||
|
_30pct_to_40pct_in_loss: CohortName::new(
|
||||||
|
"utxos_30pct_to_40pct_in_loss",
|
||||||
|
"-30-40%",
|
||||||
|
"30-40% in Loss",
|
||||||
|
),
|
||||||
|
_40pct_to_50pct_in_loss: CohortName::new(
|
||||||
|
"utxos_40pct_to_50pct_in_loss",
|
||||||
|
"-40-50%",
|
||||||
|
"40-50% in Loss",
|
||||||
|
),
|
||||||
|
_50pct_to_60pct_in_loss: CohortName::new(
|
||||||
|
"utxos_50pct_to_60pct_in_loss",
|
||||||
|
"-50-60%",
|
||||||
|
"50-60% in Loss",
|
||||||
|
),
|
||||||
|
_60pct_to_70pct_in_loss: CohortName::new(
|
||||||
|
"utxos_60pct_to_70pct_in_loss",
|
||||||
|
"-60-70%",
|
||||||
|
"60-70% in Loss",
|
||||||
|
),
|
||||||
|
_70pct_to_80pct_in_loss: CohortName::new(
|
||||||
|
"utxos_70pct_to_80pct_in_loss",
|
||||||
|
"-70-80%",
|
||||||
|
"70-80% in Loss",
|
||||||
|
),
|
||||||
|
_80pct_to_90pct_in_loss: CohortName::new(
|
||||||
|
"utxos_80pct_to_90pct_in_loss",
|
||||||
|
"-80-90%",
|
||||||
|
"80-90% in Loss",
|
||||||
|
),
|
||||||
|
_90pct_to_100pct_in_loss: CohortName::new(
|
||||||
|
"utxos_90pct_to_100pct_in_loss",
|
||||||
|
"-90-100%",
|
||||||
|
"90-100% in Loss",
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ProfitabilityRange<CohortName> {
|
impl ProfitabilityRange<CohortName> {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use brk_traversable::Traversable;
|
|||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AgeRange, AmountRange, ByEpoch, OverAmount, UnderAmount, UnderAge, OverAge,
|
AgeRange, AmountRange, ByEpoch, ByTerm, Class, Filter, OverAge, OverAmount, SpendableType,
|
||||||
Class, SpendableType, ByTerm, Filter,
|
UnderAge, UnderAmount,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Clone, Traversable)]
|
#[derive(Default, Clone, Traversable)]
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ impl Vecs {
|
|||||||
let r1 = s.spawn(|| count.compute(indexer, starting_indexes, exit));
|
let r1 = s.spawn(|| count.compute(indexer, starting_indexes, exit));
|
||||||
let r2 = s.spawn(|| interval.compute(indexer, starting_indexes, exit));
|
let r2 = s.spawn(|| interval.compute(indexer, starting_indexes, exit));
|
||||||
let r3 = s.spawn(|| weight.compute(indexer, starting_indexes, exit));
|
let r3 = s.spawn(|| weight.compute(indexer, starting_indexes, exit));
|
||||||
let r4 =
|
let r4 = s.spawn(|| difficulty.compute(indexer, indexes, starting_indexes, exit));
|
||||||
s.spawn(|| difficulty.compute(indexer, indexes, starting_indexes, exit));
|
|
||||||
let r5 = s.spawn(|| halving.compute(indexes, starting_indexes, exit));
|
let r5 = s.spawn(|| halving.compute(indexes, starting_indexes, exit));
|
||||||
size.compute(indexer, &*lookback, starting_indexes, exit)?;
|
size.compute(indexer, &*lookback, starting_indexes, exit)?;
|
||||||
r1.join().unwrap()?;
|
r1.join().unwrap()?;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use super::Vecs;
|
|||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{
|
internal::{
|
||||||
BlockCountTarget24h, BlockCountTarget1w, BlockCountTarget1m, BlockCountTarget1y,
|
BlockCountTarget1m, BlockCountTarget1w, BlockCountTarget1y, BlockCountTarget24h,
|
||||||
CachedWindowStarts, PerBlockCumulativeRolling, ConstantVecs, Windows,
|
CachedWindowStarts, ConstantVecs, PerBlockCumulativeRolling, Windows,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,10 +20,26 @@ impl Vecs {
|
|||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
target: Windows {
|
target: Windows {
|
||||||
_24h: ConstantVecs::new::<BlockCountTarget24h>("block_count_target_24h", version, indexes),
|
_24h: ConstantVecs::new::<BlockCountTarget24h>(
|
||||||
_1w: ConstantVecs::new::<BlockCountTarget1w>("block_count_target_1w", version, indexes),
|
"block_count_target_24h",
|
||||||
_1m: ConstantVecs::new::<BlockCountTarget1m>("block_count_target_1m", version, indexes),
|
version,
|
||||||
_1y: ConstantVecs::new::<BlockCountTarget1y>("block_count_target_1y", version, indexes),
|
indexes,
|
||||||
|
),
|
||||||
|
_1w: ConstantVecs::new::<BlockCountTarget1w>(
|
||||||
|
"block_count_target_1w",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
),
|
||||||
|
_1m: ConstantVecs::new::<BlockCountTarget1m>(
|
||||||
|
"block_count_target_1m",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
),
|
||||||
|
_1y: ConstantVecs::new::<BlockCountTarget1y>(
|
||||||
|
"block_count_target_1y",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
total: PerBlockCumulativeRolling::forced_import(
|
total: PerBlockCumulativeRolling::forced_import(
|
||||||
db,
|
db,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
|
|||||||
use brk_types::{StoredU32, StoredU64};
|
use brk_types::{StoredU32, StoredU64};
|
||||||
use vecdb::{Rw, StorageMode};
|
use vecdb::{Rw, StorageMode};
|
||||||
|
|
||||||
use crate::internal::{PerBlockCumulativeRolling, ConstantVecs, Windows};
|
use crate::internal::{ConstantVecs, PerBlockCumulativeRolling, Windows};
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct Vecs<M: StorageMode = Rw> {
|
pub struct Vecs<M: StorageMode = Rw> {
|
||||||
|
|||||||
@@ -27,12 +27,8 @@ impl Vecs {
|
|||||||
indexes,
|
indexes,
|
||||||
);
|
);
|
||||||
|
|
||||||
let blocks_to_retarget = PerBlock::forced_import(
|
let blocks_to_retarget =
|
||||||
db,
|
PerBlock::forced_import(db, "blocks_to_retarget", version + v2, indexes)?;
|
||||||
"blocks_to_retarget",
|
|
||||||
version + v2,
|
|
||||||
indexes,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let days_to_retarget = LazyPerBlock::from_computed::<BlocksToDaysF32>(
|
let days_to_retarget = LazyPerBlock::from_computed::<BlocksToDaysF32>(
|
||||||
"days_to_retarget",
|
"days_to_retarget",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use brk_traversable::Traversable;
|
|||||||
use brk_types::{BasisPointsSigned32, Epoch, StoredF32, StoredF64, StoredU32};
|
use brk_types::{BasisPointsSigned32, Epoch, StoredF32, StoredF64, StoredU32};
|
||||||
use vecdb::{Rw, StorageMode};
|
use vecdb::{Rw, StorageMode};
|
||||||
|
|
||||||
use crate::internal::{LazyPerBlock, PerBlock, Resolutions, PercentPerBlock};
|
use crate::internal::{LazyPerBlock, PerBlock, PercentPerBlock, Resolutions};
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct Vecs<M: StorageMode = Rw> {
|
pub struct Vecs<M: StorageMode = Rw> {
|
||||||
pub value: Resolutions<StoredF64>,
|
pub value: Resolutions<StoredF64>,
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ impl Vecs {
|
|||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let v2 = Version::TWO;
|
let v2 = Version::TWO;
|
||||||
|
|
||||||
let blocks_to_halving = PerBlock::forced_import(
|
let blocks_to_halving =
|
||||||
db, "blocks_to_halving", version + v2, indexes,
|
PerBlock::forced_import(db, "blocks_to_halving", version + v2, indexes)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let days_to_halving = LazyPerBlock::from_computed::<BlocksToDaysF32>(
|
let days_to_halving = LazyPerBlock::from_computed::<BlocksToDaysF32>(
|
||||||
"days_to_halving",
|
"days_to_halving",
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, LookbackVecs, SizeVecs, Vecs,
|
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, LookbackVecs, SizeVecs, Vecs, WeightVecs,
|
||||||
WeightVecs,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
|
|||||||
@@ -13,27 +13,26 @@ impl Vecs {
|
|||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut prev_timestamp = None;
|
let mut prev_timestamp = None;
|
||||||
self.0
|
self.0.compute(starting_indexes.height, exit, |vec| {
|
||||||
.compute(starting_indexes.height, exit, |vec| {
|
vec.compute_transform(
|
||||||
vec.compute_transform(
|
starting_indexes.height,
|
||||||
starting_indexes.height,
|
&indexer.vecs.blocks.timestamp,
|
||||||
&indexer.vecs.blocks.timestamp,
|
|(h, timestamp, ..)| {
|
||||||
|(h, timestamp, ..)| {
|
let interval = if let Some(prev_h) = h.decremented() {
|
||||||
let interval = if let Some(prev_h) = h.decremented() {
|
let prev = prev_timestamp.unwrap_or_else(|| {
|
||||||
let prev = prev_timestamp.unwrap_or_else(|| {
|
indexer.vecs.blocks.timestamp.collect_one(prev_h).unwrap()
|
||||||
indexer.vecs.blocks.timestamp.collect_one(prev_h).unwrap()
|
});
|
||||||
});
|
timestamp.checked_sub(prev).unwrap_or(Timestamp::ZERO)
|
||||||
timestamp.checked_sub(prev).unwrap_or(Timestamp::ZERO)
|
} else {
|
||||||
} else {
|
Timestamp::ZERO
|
||||||
Timestamp::ZERO
|
};
|
||||||
};
|
prev_timestamp = Some(timestamp);
|
||||||
prev_timestamp = Some(timestamp);
|
(h, interval)
|
||||||
(h, interval)
|
},
|
||||||
},
|
exit,
|
||||||
exit,
|
)?;
|
||||||
)?;
|
Ok(())
|
||||||
Ok(())
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use brk_types::Version;
|
|||||||
use vecdb::Database;
|
use vecdb::Database;
|
||||||
|
|
||||||
use super::Vecs;
|
use super::Vecs;
|
||||||
use crate::{indexes, internal::{CachedWindowStarts, PerBlockRollingAverage}};
|
use crate::{
|
||||||
|
indexes,
|
||||||
|
internal::{CachedWindowStarts, PerBlockRollingAverage},
|
||||||
|
};
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
pub(crate) fn forced_import(
|
pub(crate) fn forced_import(
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Height, Indexes, Timestamp, Version};
|
use brk_types::{Height, Indexes, Timestamp, Version};
|
||||||
use vecdb::{AnyVec, CachedVec, Cursor, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex};
|
use vecdb::{
|
||||||
|
AnyVec, CachedVec, Cursor, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw,
|
||||||
|
StorageMode, VecIndex,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{CachedWindowStarts, Windows, WindowStarts},
|
internal::{CachedWindowStarts, WindowStarts, Windows},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
@@ -112,10 +115,49 @@ impl Vecs {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cached_window_starts,
|
cached_window_starts,
|
||||||
_1h, _24h, _3d, _1w, _8d, _9d, _12d, _13d, _2w, _21d, _26d,
|
_1h,
|
||||||
_1m, _34d, _55d, _2m, _9w, _12w, _89d, _3m, _14w, _111d, _144d,
|
_24h,
|
||||||
_6m, _26w, _200d, _9m, _350d, _12m, _1y, _14m, _2y, _26m, _3y,
|
_3d,
|
||||||
_200w, _4y, _5y, _6y, _8y, _9y, _10y, _12y, _14y, _26y,
|
_1w,
|
||||||
|
_8d,
|
||||||
|
_9d,
|
||||||
|
_12d,
|
||||||
|
_13d,
|
||||||
|
_2w,
|
||||||
|
_21d,
|
||||||
|
_26d,
|
||||||
|
_1m,
|
||||||
|
_34d,
|
||||||
|
_55d,
|
||||||
|
_2m,
|
||||||
|
_9w,
|
||||||
|
_12w,
|
||||||
|
_89d,
|
||||||
|
_3m,
|
||||||
|
_14w,
|
||||||
|
_111d,
|
||||||
|
_144d,
|
||||||
|
_6m,
|
||||||
|
_26w,
|
||||||
|
_200d,
|
||||||
|
_9m,
|
||||||
|
_350d,
|
||||||
|
_12m,
|
||||||
|
_1y,
|
||||||
|
_14m,
|
||||||
|
_2y,
|
||||||
|
_26m,
|
||||||
|
_3y,
|
||||||
|
_200w,
|
||||||
|
_4y,
|
||||||
|
_5y,
|
||||||
|
_6y,
|
||||||
|
_8y,
|
||||||
|
_9y,
|
||||||
|
_10y,
|
||||||
|
_12y,
|
||||||
|
_14y,
|
||||||
|
_26y,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +170,6 @@ impl Vecs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn start_vec(&self, days: usize) -> &EagerVec<PcoVec<Height, Height>> {
|
pub fn start_vec(&self, days: usize) -> &EagerVec<PcoVec<Height, Height>> {
|
||||||
match days {
|
match days {
|
||||||
1 => &self._24h,
|
1 => &self._24h,
|
||||||
@@ -183,9 +224,7 @@ impl Vecs {
|
|||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.compute_rolling_start_hours(indexes, starting_indexes, exit, 1, |s| {
|
self.compute_rolling_start_hours(indexes, starting_indexes, exit, 1, |s| &mut s._1h)?;
|
||||||
&mut s._1h
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 1, |s| &mut s._24h)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 1, |s| &mut s._24h)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 3, |s| &mut s._3d)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 3, |s| &mut s._3d)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 7, |s| &mut s._1w)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 7, |s| &mut s._1w)?;
|
||||||
@@ -205,47 +244,29 @@ impl Vecs {
|
|||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 89, |s| &mut s._89d)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 89, |s| &mut s._89d)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 90, |s| &mut s._3m)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 90, |s| &mut s._3m)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 98, |s| &mut s._14w)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 98, |s| &mut s._14w)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 111, |s| {
|
self.compute_rolling_start(indexes, starting_indexes, exit, 111, |s| &mut s._111d)?;
|
||||||
&mut s._111d
|
self.compute_rolling_start(indexes, starting_indexes, exit, 144, |s| &mut s._144d)?;
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 144, |s| {
|
|
||||||
&mut s._144d
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 180, |s| &mut s._6m)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 180, |s| &mut s._6m)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 182, |s| &mut s._26w)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 182, |s| &mut s._26w)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 200, |s| {
|
self.compute_rolling_start(indexes, starting_indexes, exit, 200, |s| &mut s._200d)?;
|
||||||
&mut s._200d
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 270, |s| &mut s._9m)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 270, |s| &mut s._9m)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 350, |s| {
|
self.compute_rolling_start(indexes, starting_indexes, exit, 350, |s| &mut s._350d)?;
|
||||||
&mut s._350d
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 360, |s| &mut s._12m)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 360, |s| &mut s._12m)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 365, |s| &mut s._1y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 365, |s| &mut s._1y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 420, |s| &mut s._14m)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 420, |s| &mut s._14m)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 730, |s| &mut s._2y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 730, |s| &mut s._2y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 780, |s| &mut s._26m)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 780, |s| &mut s._26m)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 1095, |s| &mut s._3y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 1095, |s| &mut s._3y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 1400, |s| {
|
self.compute_rolling_start(indexes, starting_indexes, exit, 1400, |s| &mut s._200w)?;
|
||||||
&mut s._200w
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 1460, |s| &mut s._4y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 1460, |s| &mut s._4y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 1825, |s| &mut s._5y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 1825, |s| &mut s._5y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 2190, |s| &mut s._6y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 2190, |s| &mut s._6y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 2920, |s| &mut s._8y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 2920, |s| &mut s._8y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 3285, |s| &mut s._9y)?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 3285, |s| &mut s._9y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 3650, |s| {
|
self.compute_rolling_start(indexes, starting_indexes, exit, 3650, |s| &mut s._10y)?;
|
||||||
&mut s._10y
|
self.compute_rolling_start(indexes, starting_indexes, exit, 4380, |s| &mut s._12y)?;
|
||||||
})?;
|
self.compute_rolling_start(indexes, starting_indexes, exit, 5110, |s| &mut s._14y)?;
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 4380, |s| {
|
self.compute_rolling_start(indexes, starting_indexes, exit, 9490, |s| &mut s._26y)?;
|
||||||
&mut s._12y
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 5110, |s| {
|
|
||||||
&mut s._14y
|
|
||||||
})?;
|
|
||||||
self.compute_rolling_start(indexes, starting_indexes, exit, 9490, |s| {
|
|
||||||
&mut s._26y
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -261,9 +282,13 @@ impl Vecs {
|
|||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
||||||
{
|
{
|
||||||
self.compute_rolling_start_inner(indexes, starting_indexes, exit, get_field, |t, prev_ts| {
|
self.compute_rolling_start_inner(
|
||||||
t.difference_in_days_between(prev_ts) >= days
|
indexes,
|
||||||
})
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
get_field,
|
||||||
|
|t, prev_ts| t.difference_in_days_between(prev_ts) >= days,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_rolling_start_hours<F>(
|
fn compute_rolling_start_hours<F>(
|
||||||
@@ -277,9 +302,13 @@ impl Vecs {
|
|||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
|
||||||
{
|
{
|
||||||
self.compute_rolling_start_inner(indexes, starting_indexes, exit, get_field, |t, prev_ts| {
|
self.compute_rolling_start_inner(
|
||||||
t.difference_in_hours_between(prev_ts) >= hours
|
indexes,
|
||||||
})
|
starting_indexes,
|
||||||
|
exit,
|
||||||
|
get_field,
|
||||||
|
|t, prev_ts| t.difference_in_hours_between(prev_ts) >= hours,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_rolling_start_inner<F, D>(
|
fn compute_rolling_start_inner<F, D>(
|
||||||
|
|||||||
@@ -28,10 +28,18 @@ impl Vecs {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
coinblocks_created: PerBlockCumulativeRolling::forced_import(
|
coinblocks_created: PerBlockCumulativeRolling::forced_import(
|
||||||
db, "coinblocks_created", version, indexes, cached_starts,
|
db,
|
||||||
|
"coinblocks_created",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
cached_starts,
|
||||||
)?,
|
)?,
|
||||||
coinblocks_stored: PerBlockCumulativeRolling::forced_import(
|
coinblocks_stored: PerBlockCumulativeRolling::forced_import(
|
||||||
db, "coinblocks_stored", version, indexes, cached_starts,
|
db,
|
||||||
|
"coinblocks_stored",
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
cached_starts,
|
||||||
)?,
|
)?,
|
||||||
liveliness,
|
liveliness,
|
||||||
vaultedness,
|
vaultedness,
|
||||||
|
|||||||
@@ -22,11 +22,8 @@ impl Vecs {
|
|||||||
let circulating_supply = &all_metrics.supply.total.btc.height;
|
let circulating_supply = &all_metrics.supply.total.btc.height;
|
||||||
let realized_price = &all_metrics.realized.price.cents.height;
|
let realized_price = &all_metrics.realized.price.cents.height;
|
||||||
|
|
||||||
self.vaulted.compute_all(
|
self.vaulted
|
||||||
prices,
|
.compute_all(prices, starting_indexes, exit, |v| {
|
||||||
starting_indexes,
|
|
||||||
exit,
|
|
||||||
|v| {
|
|
||||||
Ok(v.compute_transform2(
|
Ok(v.compute_transform2(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
realized_price,
|
realized_price,
|
||||||
@@ -36,14 +33,10 @@ impl Vecs {
|
|||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?)
|
)?)
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
self.active.compute_all(
|
self.active
|
||||||
prices,
|
.compute_all(prices, starting_indexes, exit, |v| {
|
||||||
starting_indexes,
|
|
||||||
exit,
|
|
||||||
|v| {
|
|
||||||
Ok(v.compute_transform2(
|
Ok(v.compute_transform2(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
realized_price,
|
realized_price,
|
||||||
@@ -53,14 +46,10 @@ impl Vecs {
|
|||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?)
|
)?)
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
self.true_market_mean.compute_all(
|
self.true_market_mean
|
||||||
prices,
|
.compute_all(prices, starting_indexes, exit, |v| {
|
||||||
starting_indexes,
|
|
||||||
exit,
|
|
||||||
|v| {
|
|
||||||
Ok(v.compute_transform2(
|
Ok(v.compute_transform2(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
&cap.investor.cents.height,
|
&cap.investor.cents.height,
|
||||||
@@ -70,14 +59,10 @@ impl Vecs {
|
|||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?)
|
)?)
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
self.cointime.compute_all(
|
self.cointime
|
||||||
prices,
|
.compute_all(prices, starting_indexes, exit, |v| {
|
||||||
starting_indexes,
|
|
||||||
exit,
|
|
||||||
|v| {
|
|
||||||
Ok(v.compute_transform2(
|
Ok(v.compute_transform2(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
&cap.cointime.cents.height,
|
&cap.cointime.cents.height,
|
||||||
@@ -87,8 +72,7 @@ impl Vecs {
|
|||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
)?)
|
)?)
|
||||||
},
|
})?;
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ use brk_types::Version;
|
|||||||
use vecdb::Database;
|
use vecdb::Database;
|
||||||
|
|
||||||
use super::Vecs;
|
use super::Vecs;
|
||||||
use crate::{
|
use crate::{indexes, internal::PriceWithRatioExtendedPerBlock};
|
||||||
indexes,
|
|
||||||
internal::PriceWithRatioExtendedPerBlock,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
pub(crate) fn forced_import(
|
pub(crate) fn forced_import(
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ impl Vecs {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.vaulted.compute(prices, starting_indexes.height, exit)?;
|
self.vaulted
|
||||||
|
.compute(prices, starting_indexes.height, exit)?;
|
||||||
self.active.compute(prices, starting_indexes.height, exit)?;
|
self.active.compute(prices, starting_indexes.height, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -12,12 +12,7 @@ impl Vecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
vaulted: AmountPerBlock::forced_import(
|
vaulted: AmountPerBlock::forced_import(db, "vaulted_supply", version, indexes)?,
|
||||||
db,
|
|
||||||
"vaulted_supply",
|
|
||||||
version,
|
|
||||||
indexes,
|
|
||||||
)?,
|
|
||||||
active: AmountPerBlock::forced_import(db, "active_supply", version, indexes)?,
|
active: AmountPerBlock::forced_import(db, "active_supply", version, indexes)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,52 +31,49 @@ impl Vecs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.created
|
self.created.compute(starting_indexes.height, exit, |vec| {
|
||||||
.compute(starting_indexes.height, exit, |vec| {
|
vec.compute_multiply(
|
||||||
vec.compute_multiply(
|
starting_indexes.height,
|
||||||
starting_indexes.height,
|
&prices.spot.usd.height,
|
||||||
&prices.spot.usd.height,
|
&activity.coinblocks_created.block,
|
||||||
&activity.coinblocks_created.block,
|
exit,
|
||||||
exit,
|
)?;
|
||||||
)?;
|
Ok(())
|
||||||
Ok(())
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
self.stored
|
self.stored.compute(starting_indexes.height, exit, |vec| {
|
||||||
.compute(starting_indexes.height, exit, |vec| {
|
vec.compute_multiply(
|
||||||
vec.compute_multiply(
|
starting_indexes.height,
|
||||||
starting_indexes.height,
|
&prices.spot.usd.height,
|
||||||
&prices.spot.usd.height,
|
&activity.coinblocks_stored.block,
|
||||||
&activity.coinblocks_stored.block,
|
exit,
|
||||||
exit,
|
)?;
|
||||||
)?;
|
Ok(())
|
||||||
Ok(())
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
// VOCDD: Value of Coin Days Destroyed = price × (CDD / circulating_supply)
|
// VOCDD: Value of Coin Days Destroyed = price × (CDD / circulating_supply)
|
||||||
// Supply-adjusted to account for growing supply over time
|
// Supply-adjusted to account for growing supply over time
|
||||||
// This is a key input for Reserve Risk / HODL Bank calculation
|
// This is a key input for Reserve Risk / HODL Bank calculation
|
||||||
self.vocdd
|
self.vocdd.compute(starting_indexes.height, exit, |vec| {
|
||||||
.compute(starting_indexes.height, exit, |vec| {
|
vec.compute_transform3(
|
||||||
vec.compute_transform3(
|
starting_indexes.height,
|
||||||
starting_indexes.height,
|
&prices.spot.usd.height,
|
||||||
&prices.spot.usd.height,
|
&coindays_destroyed.block,
|
||||||
&coindays_destroyed.block,
|
circulating_supply,
|
||||||
circulating_supply,
|
|(i, price, cdd, supply, _): (_, Dollars, StoredF64, Bitcoin, _)| {
|
||||||
|(i, price, cdd, supply, _): (_, Dollars, StoredF64, Bitcoin, _)| {
|
let supply_f64 = f64::from(supply);
|
||||||
let supply_f64 = f64::from(supply);
|
if supply_f64 == 0.0 {
|
||||||
if supply_f64 == 0.0 {
|
(i, StoredF64::from(0.0))
|
||||||
(i, StoredF64::from(0.0))
|
} else {
|
||||||
} else {
|
// VOCDD = price × (CDD / supply)
|
||||||
// VOCDD = price × (CDD / supply)
|
let vocdd = f64::from(price) * f64::from(cdd) / supply_f64;
|
||||||
let vocdd = f64::from(price) * f64::from(cdd) / supply_f64;
|
(i, StoredF64::from(vocdd))
|
||||||
(i, StoredF64::from(vocdd))
|
}
|
||||||
}
|
},
|
||||||
},
|
exit,
|
||||||
exit,
|
)?;
|
||||||
)?;
|
Ok(())
|
||||||
Ok(())
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,11 +148,7 @@ impl ActivityCountVecs {
|
|||||||
self.both.block.push(counts.both.into());
|
self.both.block.push(counts.both.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_rest(
|
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
max_from: Height,
|
|
||||||
exit: &Exit,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.reactivated.compute_rest(max_from, exit)?;
|
self.reactivated.compute_rest(max_from, exit)?;
|
||||||
self.sending.compute_rest(max_from, exit)?;
|
self.sending.compute_rest(max_from, exit)?;
|
||||||
self.receiving.compute_rest(max_from, exit)?;
|
self.receiving.compute_rest(max_from, exit)?;
|
||||||
@@ -180,8 +176,8 @@ impl AddrTypeToActivityCountVecs {
|
|||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
cached_starts: &CachedWindowStarts,
|
cached_starts: &CachedWindowStarts,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
Ok(Self::from(
|
Ok(Self::from(ByAddrType::<ActivityCountVecs>::new_with_name(
|
||||||
ByAddrType::<ActivityCountVecs>::new_with_name(|type_name| {
|
|type_name| {
|
||||||
ActivityCountVecs::forced_import(
|
ActivityCountVecs::forced_import(
|
||||||
db,
|
db,
|
||||||
&format!("{type_name}_{name}"),
|
&format!("{type_name}_{name}"),
|
||||||
@@ -189,8 +185,8 @@ impl AddrTypeToActivityCountVecs {
|
|||||||
indexes,
|
indexes,
|
||||||
cached_starts,
|
cached_starts,
|
||||||
)
|
)
|
||||||
})?,
|
},
|
||||||
))
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||||
@@ -221,11 +217,7 @@ impl AddrTypeToActivityCountVecs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_rest(
|
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
max_from: Height,
|
|
||||||
exit: &Exit,
|
|
||||||
) -> Result<()> {
|
|
||||||
for type_vecs in self.0.values_mut() {
|
for type_vecs in self.0.values_mut() {
|
||||||
type_vecs.compute_rest(max_from, exit)?;
|
type_vecs.compute_rest(max_from, exit)?;
|
||||||
}
|
}
|
||||||
@@ -259,7 +251,11 @@ impl AddrActivityVecs {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
all: ActivityCountVecs::forced_import(db, name, version, indexes, cached_starts)?,
|
all: ActivityCountVecs::forced_import(db, name, version, indexes, cached_starts)?,
|
||||||
by_addr_type: AddrTypeToActivityCountVecs::forced_import(
|
by_addr_type: AddrTypeToActivityCountVecs::forced_import(
|
||||||
db, name, version, indexes, cached_starts,
|
db,
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
indexes,
|
||||||
|
cached_starts,
|
||||||
)?,
|
)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -284,11 +280,7 @@ impl AddrActivityVecs {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_rest(
|
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
max_from: Height,
|
|
||||||
exit: &Exit,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.all.compute_rest(max_from, exit)?;
|
self.all.compute_rest(max_from, exit)?;
|
||||||
self.by_addr_type.compute_rest(max_from, exit)?;
|
self.by_addr_type.compute_rest(max_from, exit)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ use vecdb::{
|
|||||||
use crate::{indexes, internal::PerBlock};
|
use crate::{indexes, internal::PerBlock};
|
||||||
|
|
||||||
#[derive(Deref, DerefMut, Traversable)]
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
pub struct AddrCountVecs<M: StorageMode = Rw>(
|
pub struct AddrCountVecs<M: StorageMode = Rw>(#[traversable(flatten)] pub PerBlock<StoredU64, M>);
|
||||||
#[traversable(flatten)] pub PerBlock<StoredU64, M>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl AddrCountVecs {
|
impl AddrCountVecs {
|
||||||
pub(crate) fn forced_import(
|
pub(crate) fn forced_import(
|
||||||
@@ -23,9 +21,7 @@ impl AddrCountVecs {
|
|||||||
version: Version,
|
version: Version,
|
||||||
indexes: &indexes::Vecs,
|
indexes: &indexes::Vecs,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
Ok(Self(PerBlock::forced_import(
|
Ok(Self(PerBlock::forced_import(db, name, version, indexes)?))
|
||||||
db, name, version, indexes,
|
|
||||||
)?))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,42 +53,17 @@ impl From<(&AddrTypeToAddrCountVecs, Height)> for AddrTypeToAddrCount {
|
|||||||
.collect_one(prev_height)
|
.collect_one(prev_height)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into(),
|
.into(),
|
||||||
p2pkh: groups
|
p2pkh: groups.p2pkh.height.collect_one(prev_height).unwrap().into(),
|
||||||
.p2pkh
|
p2sh: groups.p2sh.height.collect_one(prev_height).unwrap().into(),
|
||||||
.height
|
|
||||||
.collect_one(prev_height)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
p2sh: groups
|
|
||||||
.p2sh
|
|
||||||
.height
|
|
||||||
.collect_one(prev_height)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
p2wpkh: groups
|
p2wpkh: groups
|
||||||
.p2wpkh
|
.p2wpkh
|
||||||
.height
|
.height
|
||||||
.collect_one(prev_height)
|
.collect_one(prev_height)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into(),
|
.into(),
|
||||||
p2wsh: groups
|
p2wsh: groups.p2wsh.height.collect_one(prev_height).unwrap().into(),
|
||||||
.p2wsh
|
p2tr: groups.p2tr.height.collect_one(prev_height).unwrap().into(),
|
||||||
.height
|
p2a: groups.p2a.height.collect_one(prev_height).unwrap().into(),
|
||||||
.collect_one(prev_height)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
p2tr: groups
|
|
||||||
.p2tr
|
|
||||||
.height
|
|
||||||
.collect_one(prev_height)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
p2a: groups
|
|
||||||
.p2a
|
|
||||||
.height
|
|
||||||
.collect_one(prev_height)
|
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
@@ -177,7 +148,10 @@ impl AddrCountsVecs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||||
self.all.height.len().min(self.by_addr_type.min_stateful_len())
|
self.all
|
||||||
|
.height
|
||||||
|
.len()
|
||||||
|
.min(self.by_addr_type.min_stateful_len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn par_iter_height_mut(
|
pub(crate) fn par_iter_height_mut(
|
||||||
@@ -199,11 +173,7 @@ impl AddrCountsVecs {
|
|||||||
self.by_addr_type.push_height(addr_counts);
|
self.by_addr_type.push_height(addr_counts);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_rest(
|
pub(crate) fn compute_rest(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
starting_indexes: &Indexes,
|
|
||||||
exit: &Exit,
|
|
||||||
) -> Result<()> {
|
|
||||||
let sources = self.by_addr_type.by_height();
|
let sources = self.by_addr_type.by_height();
|
||||||
self.all
|
self.all
|
||||||
.height
|
.height
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height};
|
||||||
EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, Height,
|
|
||||||
};
|
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use vecdb::{AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec};
|
use vecdb::{AnyStoredVec, BytesVec, Rw, Stamp, StorageMode, WritableVec};
|
||||||
|
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ impl DeltaVecs {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self { all, by_addr_type }
|
||||||
all,
|
|
||||||
by_addr_type,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use brk_error::{Error, Result};
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
AnyAddrIndex, Height, OutputType, P2AAddrIndex, P2PK33AddrIndex, P2PK65AddrIndex,
|
AnyAddrIndex, Height, OutputType, P2AAddrIndex, P2PK33AddrIndex, P2PK65AddrIndex,
|
||||||
P2PKHAddrIndex, P2SHAddrIndex, P2TRAddrIndex, P2WPKHAddrIndex, P2WSHAddrIndex,
|
P2PKHAddrIndex, P2SHAddrIndex, P2TRAddrIndex, P2WPKHAddrIndex, P2WSHAddrIndex, TypeIndex,
|
||||||
TypeIndex, Version,
|
Version,
|
||||||
};
|
};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|||||||
@@ -44,10 +44,7 @@ impl NewAddrCountVecs {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { all, by_addr_type })
|
||||||
all,
|
|
||||||
by_addr_type,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute(
|
pub(crate) fn compute(
|
||||||
|
|||||||
@@ -24,20 +24,11 @@ impl TotalAddrCountVecs {
|
|||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let all = PerBlock::forced_import(db, "total_addr_count", version, indexes)?;
|
let all = PerBlock::forced_import(db, "total_addr_count", version, indexes)?;
|
||||||
|
|
||||||
let by_addr_type: ByAddrType<PerBlock<StoredU64>> =
|
let by_addr_type: ByAddrType<PerBlock<StoredU64>> = ByAddrType::new_with_name(|name| {
|
||||||
ByAddrType::new_with_name(|name| {
|
PerBlock::forced_import(db, &format!("{name}_total_addr_count"), version, indexes)
|
||||||
PerBlock::forced_import(
|
})?;
|
||||||
db,
|
|
||||||
&format!("{name}_total_addr_count"),
|
|
||||||
version,
|
|
||||||
indexes,
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self { all, by_addr_type })
|
||||||
all,
|
|
||||||
by_addr_type,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Eagerly compute total = addr_count + empty_addr_count.
|
/// Eagerly compute total = addr_count + empty_addr_count.
|
||||||
|
|||||||
@@ -103,9 +103,7 @@ pub(crate) fn load_uncached_addr_data(
|
|||||||
// Check if this is a new address (type_index >= first for this height)
|
// Check if this is a new address (type_index >= first for this height)
|
||||||
let first = *first_addr_indexes.get(addr_type).unwrap();
|
let first = *first_addr_indexes.get(addr_type).unwrap();
|
||||||
if first <= type_index {
|
if first <= type_index {
|
||||||
return Ok(Some(WithAddrDataSource::New(
|
return Ok(Some(WithAddrDataSource::New(FundedAddrData::default())));
|
||||||
FundedAddrData::default(),
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if already in cache
|
// Skip if already in cache
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ impl<'a> AddrLookup<'a> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
output_type: OutputType,
|
output_type: OutputType,
|
||||||
type_index: TypeIndex,
|
type_index: TypeIndex,
|
||||||
) -> (
|
) -> (&mut WithAddrDataSource<FundedAddrData>, TrackingStatus) {
|
||||||
&mut WithAddrDataSource<FundedAddrData>,
|
|
||||||
TrackingStatus,
|
|
||||||
) {
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
let map = self.funded.get_mut(output_type).unwrap();
|
let map = self.funded.get_mut(output_type).unwrap();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
AnyAddrIndex, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex,
|
AnyAddrIndex, EmptyAddrData, EmptyAddrIndex, FundedAddrData, FundedAddrIndex, OutputType,
|
||||||
OutputType, TypeIndex,
|
TypeIndex,
|
||||||
};
|
};
|
||||||
use vecdb::AnyVec;
|
use vecdb::AnyVec;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ pub(crate) fn process_received(
|
|||||||
empty_addr_count: &mut ByAddrType<u64>,
|
empty_addr_count: &mut ByAddrType<u64>,
|
||||||
activity_counts: &mut AddrTypeToActivityCounts,
|
activity_counts: &mut AddrTypeToActivityCounts,
|
||||||
) {
|
) {
|
||||||
let max_type_len = received_data.iter().map(|(_, v)| v.len()).max().unwrap_or(0);
|
let max_type_len = received_data
|
||||||
|
.iter()
|
||||||
|
.map(|(_, v)| v.len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0);
|
||||||
let mut aggregated: FxHashMap<TypeIndex, AggregatedReceive> =
|
let mut aggregated: FxHashMap<TypeIndex, AggregatedReceive> =
|
||||||
FxHashMap::with_capacity_and_hasher(max_type_len, Default::default());
|
FxHashMap::with_capacity_and_hasher(max_type_len, Default::default());
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,7 @@ pub(crate) fn update_tx_counts(
|
|||||||
.get_mut(&type_index)
|
.get_mut(&type_index)
|
||||||
{
|
{
|
||||||
addr_data.tx_count += tx_count;
|
addr_data.tx_count += tx_count;
|
||||||
} else if let Some(addr_data) = empty_cache
|
} else if let Some(addr_data) = empty_cache.get_mut(addr_type).unwrap().get_mut(&type_index)
|
||||||
.get_mut(addr_type)
|
|
||||||
.unwrap()
|
|
||||||
.get_mut(&type_index)
|
|
||||||
{
|
{
|
||||||
addr_data.tx_count += tx_count;
|
addr_data.tx_count += tx_count;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,9 +90,7 @@ pub(crate) fn process_inputs(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let items: Vec<_> = if input_count < 128 {
|
let items: Vec<_> = if input_count < 128 {
|
||||||
(0..input_count)
|
(0..input_count).map(map_fn).collect::<Result<Vec<_>>>()?
|
||||||
.map(map_fn)
|
|
||||||
.collect::<Result<Vec<_>>>()?
|
|
||||||
} else {
|
} else {
|
||||||
(0..input_count)
|
(0..input_count)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
@@ -109,10 +107,9 @@ pub(crate) fn process_inputs(
|
|||||||
Default::default(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
let mut sent_data = HeightToAddrTypeToVec::with_capacity(estimated_unique_heights);
|
let mut sent_data = HeightToAddrTypeToVec::with_capacity(estimated_unique_heights);
|
||||||
let mut addr_data =
|
let mut addr_data = AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
|
||||||
AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
|
estimated_per_type,
|
||||||
estimated_per_type,
|
);
|
||||||
);
|
|
||||||
let mut tx_index_vecs =
|
let mut tx_index_vecs =
|
||||||
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ use rayon::prelude::*;
|
|||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::distribution::{
|
use crate::distribution::{
|
||||||
addr::{
|
addr::{AddrTypeToTypeIndexMap, AddrTypeToVec, AddrsDataVecs, AnyAddrIndexesVecs},
|
||||||
AddrTypeToTypeIndexMap, AddrTypeToVec, AddrsDataVecs, AnyAddrIndexesVecs,
|
|
||||||
},
|
|
||||||
compute::{TxOutData, VecsReaders},
|
compute::{TxOutData, VecsReaders},
|
||||||
state::Transacted,
|
state::Transacted,
|
||||||
};
|
};
|
||||||
@@ -79,9 +77,7 @@ pub(crate) fn process_outputs(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let items: Vec<_> = if output_count < 128 {
|
let items: Vec<_> = if output_count < 128 {
|
||||||
(0..output_count)
|
(0..output_count).map(map_fn).collect::<Result<Vec<_>>>()?
|
||||||
.map(map_fn)
|
|
||||||
.collect::<Result<Vec<_>>>()?
|
|
||||||
} else {
|
} else {
|
||||||
(0..output_count)
|
(0..output_count)
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
@@ -93,10 +89,9 @@ pub(crate) fn process_outputs(
|
|||||||
let estimated_per_type = (output_count / 8).max(8);
|
let estimated_per_type = (output_count / 8).max(8);
|
||||||
let mut transacted = Transacted::default();
|
let mut transacted = Transacted::default();
|
||||||
let mut received_data = AddrTypeToVec::with_capacity(estimated_per_type);
|
let mut received_data = AddrTypeToVec::with_capacity(estimated_per_type);
|
||||||
let mut addr_data =
|
let mut addr_data = AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
|
||||||
AddrTypeToTypeIndexMap::<WithAddrDataSource<FundedAddrData>>::with_capacity(
|
estimated_per_type,
|
||||||
estimated_per_type,
|
);
|
||||||
);
|
|
||||||
let mut tx_index_vecs =
|
let mut tx_index_vecs =
|
||||||
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
AddrTypeToTypeIndexMap::<SmallVec<[TxIndex; 4]>>::with_capacity(estimated_per_type);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use brk_cohort::{
|
use brk_cohort::{AddrGroups, AmountRange, Filter, Filtered, OverAmount, UnderAmount};
|
||||||
AddrGroups, AmountRange, OverAmount, UnderAmount, Filter, Filtered,
|
|
||||||
};
|
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Height, Indexes, StoredU64, Version};
|
use brk_types::{Height, Indexes, StoredU64, Version};
|
||||||
|
|||||||
@@ -161,9 +161,7 @@ impl DynCohortVecs for AddrCohortVecs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(state) = self.state.as_ref() {
|
if let Some(state) = self.state.as_ref() {
|
||||||
self.addr_count
|
self.addr_count.height.push(state.addr_count.into());
|
||||||
.height
|
|
||||||
.push(state.addr_count.into());
|
|
||||||
self.metrics.supply.push_state(&state.inner);
|
self.metrics.supply.push_state(&state.inner);
|
||||||
self.metrics.outputs.push_state(&state.inner);
|
self.metrics.outputs.push_state(&state.inner);
|
||||||
self.metrics.activity.push_state(&state.inner);
|
self.metrics.activity.push_state(&state.inner);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use brk_types::{CostBasisSnapshot, Cents, Height, Timestamp};
|
use brk_types::{Cents, CostBasisSnapshot, Height, Timestamp};
|
||||||
use vecdb::Rw;
|
use vecdb::Rw;
|
||||||
|
|
||||||
use crate::distribution::state::Transacted;
|
use crate::distribution::state::Transacted;
|
||||||
@@ -34,10 +34,16 @@ impl UTXOCohorts<Rw> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.receive_utxo_snapshot(&supply_state, &snapshot);
|
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||||
if let Some(v) = self.epoch.mut_vec_from_height(height) {
|
if let Some(v) = self.epoch.mut_vec_from_height(height) {
|
||||||
v.state.as_mut().unwrap().receive_utxo_snapshot(&supply_state, &snapshot);
|
v.state
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||||
}
|
}
|
||||||
if let Some(v) = self.class.mut_vec_from_timestamp(timestamp) {
|
if let Some(v) = self.class.mut_vec_from_timestamp(timestamp) {
|
||||||
v.state.as_mut().unwrap().receive_utxo_snapshot(&supply_state, &snapshot);
|
v.state
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update output type cohorts (skip types with no outputs this block)
|
// Update output type cohorts (skip types with no outputs this block)
|
||||||
|
|||||||
@@ -64,10 +64,16 @@ impl UTXOCohorts<Rw> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||||
if let Some(v) = self.epoch.mut_vec_from_height(receive_height) {
|
if let Some(v) = self.epoch.mut_vec_from_height(receive_height) {
|
||||||
v.state.as_mut().unwrap().send_utxo_precomputed(&sent.spendable_supply, &pre);
|
v.state
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||||
}
|
}
|
||||||
if let Some(v) = self.class.mut_vec_from_timestamp(block_state.timestamp) {
|
if let Some(v) = self.class.mut_vec_from_timestamp(block_state.timestamp) {
|
||||||
v.state.as_mut().unwrap().send_utxo_precomputed(&sent.spendable_supply, &pre);
|
v.state
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||||
}
|
}
|
||||||
} else if sent.spendable_supply.utxo_count > 0 {
|
} else if sent.spendable_supply.utxo_count > 0 {
|
||||||
// Zero-value UTXOs: just subtract supply
|
// Zero-value UTXOs: just subtract supply
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ impl UTXOCohorts<Rw> {
|
|||||||
idx
|
idx
|
||||||
} else {
|
} else {
|
||||||
let mut idx = cached[boundary_idx];
|
let mut idx = cached[boundary_idx];
|
||||||
while idx < chain_state.len()
|
while idx < chain_state.len() && *chain_state[idx].timestamp <= lower_timestamp {
|
||||||
&& *chain_state[idx].timestamp <= lower_timestamp
|
|
||||||
{
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
cached[boundary_idx] = idx;
|
cached[boundary_idx] = idx;
|
||||||
@@ -84,8 +82,7 @@ impl UTXOCohorts<Rw> {
|
|||||||
|
|
||||||
// Move supply from younger cohort to older cohort
|
// Move supply from younger cohort to older cohort
|
||||||
for block_state in &chain_state[start_idx..end_idx] {
|
for block_state in &chain_state[start_idx..end_idx] {
|
||||||
let snapshot =
|
let snapshot = CostBasisSnapshot::from_utxo(block_state.price, &block_state.supply);
|
||||||
CostBasisSnapshot::from_utxo(block_state.price, &block_state.supply);
|
|
||||||
if let Some(state) = age_cohorts[boundary_idx].as_mut() {
|
if let Some(state) = age_cohorts[boundary_idx].as_mut() {
|
||||||
state.decrement_snapshot(&snapshot);
|
state.decrement_snapshot(&snapshot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use brk_error::Result;
|
|||||||
use brk_types::{Cents, Height, Indexes, Version};
|
use brk_types::{Cents, Height, Indexes, Version};
|
||||||
use vecdb::{Exit, ReadableVec};
|
use vecdb::{Exit, ReadableVec};
|
||||||
|
|
||||||
use crate::{distribution::{cohorts::traits::DynCohortVecs, metrics::CoreCohortMetrics}, prices};
|
use crate::{
|
||||||
|
distribution::{cohorts::traits::DynCohortVecs, metrics::CoreCohortMetrics},
|
||||||
|
prices,
|
||||||
|
};
|
||||||
|
|
||||||
use super::UTXOCohortVecs;
|
use super::UTXOCohortVecs;
|
||||||
|
|
||||||
@@ -45,12 +48,8 @@ impl DynCohortVecs for UTXOCohortVecs<CoreCohortMetrics> {
|
|||||||
if let Some(state) = self.state.as_mut() {
|
if let Some(state) = self.state.as_mut() {
|
||||||
state.apply_pending();
|
state.apply_pending();
|
||||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||||
self.metrics
|
self.metrics.unrealized.push_state(&unrealized_state);
|
||||||
.unrealized
|
self.metrics.supply.push_profitability(&unrealized_state);
|
||||||
.push_state(&unrealized_state);
|
|
||||||
self.metrics
|
|
||||||
.supply
|
|
||||||
.push_profitability(&unrealized_state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ use brk_error::Result;
|
|||||||
use brk_types::{Cents, Height, Indexes, Version};
|
use brk_types::{Cents, Height, Indexes, Version};
|
||||||
use vecdb::{Exit, ReadableVec};
|
use vecdb::{Exit, ReadableVec};
|
||||||
|
|
||||||
use crate::{distribution::cohorts::traits::DynCohortVecs, distribution::metrics::TypeCohortMetrics, prices};
|
use crate::{
|
||||||
|
distribution::cohorts::traits::DynCohortVecs, distribution::metrics::TypeCohortMetrics, prices,
|
||||||
|
};
|
||||||
|
|
||||||
use super::UTXOCohortVecs;
|
use super::UTXOCohortVecs;
|
||||||
|
|
||||||
@@ -45,12 +47,8 @@ impl DynCohortVecs for UTXOCohortVecs<TypeCohortMetrics> {
|
|||||||
if let Some(state) = self.state.as_mut() {
|
if let Some(state) = self.state.as_mut() {
|
||||||
state.apply_pending();
|
state.apply_pending();
|
||||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||||
self.metrics
|
self.metrics.unrealized.push_state(&unrealized_state);
|
||||||
.unrealized
|
self.metrics.supply.push_profitability(&unrealized_state);
|
||||||
.push_state(&unrealized_state);
|
|
||||||
self.metrics
|
|
||||||
.supply
|
|
||||||
.push_profitability(&unrealized_state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -144,42 +144,50 @@ pub(crate) fn process_blocks(
|
|||||||
let first_p2a_vec = indexer
|
let first_p2a_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2a.first_index
|
.p2a
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2pk33_vec = indexer
|
let first_p2pk33_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2pk33.first_index
|
.p2pk33
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2pk65_vec = indexer
|
let first_p2pk65_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2pk65.first_index
|
.p2pk65
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2pkh_vec = indexer
|
let first_p2pkh_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2pkh.first_index
|
.p2pkh
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2sh_vec = indexer
|
let first_p2sh_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2sh.first_index
|
.p2sh
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2tr_vec = indexer
|
let first_p2tr_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2tr.first_index
|
.p2tr
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2wpkh_vec = indexer
|
let first_p2wpkh_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2wpkh.first_index
|
.p2wpkh
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
let first_p2wsh_vec = indexer
|
let first_p2wsh_vec = indexer
|
||||||
.vecs
|
.vecs
|
||||||
.addrs
|
.addrs
|
||||||
.p2wsh.first_index
|
.p2wsh
|
||||||
|
.first_index
|
||||||
.collect_range_at(start_usize, end_usize);
|
.collect_range_at(start_usize, end_usize);
|
||||||
|
|
||||||
// Track running totals - recover from previous height if resuming
|
// Track running totals - recover from previous height if resuming
|
||||||
@@ -187,10 +195,8 @@ pub(crate) fn process_blocks(
|
|||||||
let (mut addr_counts, mut empty_addr_counts) = if starting_height > Height::ZERO {
|
let (mut addr_counts, mut empty_addr_counts) = if starting_height > Height::ZERO {
|
||||||
let addr_counts =
|
let addr_counts =
|
||||||
AddrTypeToAddrCount::from((&vecs.addrs.funded.by_addr_type, starting_height));
|
AddrTypeToAddrCount::from((&vecs.addrs.funded.by_addr_type, starting_height));
|
||||||
let empty_addr_counts = AddrTypeToAddrCount::from((
|
let empty_addr_counts =
|
||||||
&vecs.addrs.empty.by_addr_type,
|
AddrTypeToAddrCount::from((&vecs.addrs.empty.by_addr_type, starting_height));
|
||||||
starting_height,
|
|
||||||
));
|
|
||||||
(addr_counts, empty_addr_counts)
|
(addr_counts, empty_addr_counts)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -274,12 +280,18 @@ pub(crate) fn process_blocks(
|
|||||||
// processing closures so outputs and inputs collection overlap each other
|
// processing closures so outputs and inputs collection overlap each other
|
||||||
// and tick-tock, instead of running sequentially before the join.
|
// and tick-tock, instead of running sequentially before the join.
|
||||||
let (matured, oi_result) = rayon::join(
|
let (matured, oi_result) = rayon::join(
|
||||||
|| vecs.utxo_cohorts.tick_tock_next_block(chain_state, timestamp),
|
|| {
|
||||||
|
vecs.utxo_cohorts
|
||||||
|
.tick_tock_next_block(chain_state, timestamp)
|
||||||
|
},
|
||||||
|| -> Result<_> {
|
|| -> Result<_> {
|
||||||
let (outputs_result, inputs_result) = rayon::join(
|
let (outputs_result, inputs_result) = rayon::join(
|
||||||
|| {
|
|| {
|
||||||
let txout_index_to_tx_index = txout_to_tx_index_buf
|
let txout_index_to_tx_index = txout_to_tx_index_buf.build(
|
||||||
.build(first_tx_index, tx_count, tx_index_to_output_count);
|
first_tx_index,
|
||||||
|
tx_count,
|
||||||
|
tx_index_to_output_count,
|
||||||
|
);
|
||||||
let txout_data_vec =
|
let txout_data_vec =
|
||||||
txout_iters.collect_block_outputs(first_txout_index, output_count);
|
txout_iters.collect_block_outputs(first_txout_index, output_count);
|
||||||
process_outputs(
|
process_outputs(
|
||||||
@@ -294,14 +306,21 @@ pub(crate) fn process_blocks(
|
|||||||
},
|
},
|
||||||
|| -> Result<_> {
|
|| -> Result<_> {
|
||||||
if input_count > 1 {
|
if input_count > 1 {
|
||||||
let txin_index_to_tx_index = txin_to_tx_index_buf
|
let txin_index_to_tx_index = txin_to_tx_index_buf.build(
|
||||||
.build(first_tx_index, tx_count, tx_index_to_input_count);
|
first_tx_index,
|
||||||
let (input_values, input_prev_heights, input_output_types, input_type_indexes) =
|
tx_count,
|
||||||
txin_iters.collect_block_inputs(
|
tx_index_to_input_count,
|
||||||
first_txin_index + 1,
|
);
|
||||||
input_count - 1,
|
let (
|
||||||
height,
|
input_values,
|
||||||
);
|
input_prev_heights,
|
||||||
|
input_output_types,
|
||||||
|
input_type_indexes,
|
||||||
|
) = txin_iters.collect_block_inputs(
|
||||||
|
first_txin_index + 1,
|
||||||
|
input_count - 1,
|
||||||
|
height,
|
||||||
|
);
|
||||||
process_inputs(
|
process_inputs(
|
||||||
input_count - 1,
|
input_count - 1,
|
||||||
&txin_index_to_tx_index[1..],
|
&txin_index_to_tx_index[1..],
|
||||||
@@ -380,9 +399,9 @@ pub(crate) fn process_blocks(
|
|||||||
blocks_old as u128 * u64::from(sent.spendable_supply.value) as u128
|
blocks_old as u128 * u64::from(sent.spendable_supply.value) as u128
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
vecs.coinblocks_destroyed.block.push(
|
vecs.coinblocks_destroyed.block.push(StoredF64::from(
|
||||||
StoredF64::from(total_satblocks as f64 / Sats::ONE_BTC_U128 as f64),
|
total_satblocks as f64 / Sats::ONE_BTC_U128 as f64,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record maturation (sats crossing age boundaries)
|
// Record maturation (sats crossing age boundaries)
|
||||||
@@ -451,9 +470,11 @@ pub(crate) fn process_blocks(
|
|||||||
vecs.utxo_cohorts.update_fenwick_from_pending();
|
vecs.utxo_cohorts.update_fenwick_from_pending();
|
||||||
|
|
||||||
// Push to height-indexed vectors
|
// Push to height-indexed vectors
|
||||||
vecs.addrs.funded
|
vecs.addrs
|
||||||
|
.funded
|
||||||
.push_height(addr_counts.sum(), &addr_counts);
|
.push_height(addr_counts.sum(), &addr_counts);
|
||||||
vecs.addrs.empty
|
vecs.addrs
|
||||||
|
.empty
|
||||||
.push_height(empty_addr_counts.sum(), &empty_addr_counts);
|
.push_height(empty_addr_counts.sum(), &empty_addr_counts);
|
||||||
vecs.addrs.activity.push_height(&activity_counts);
|
vecs.addrs.activity.push_height(&activity_counts);
|
||||||
|
|
||||||
@@ -467,11 +488,8 @@ pub(crate) fn process_blocks(
|
|||||||
block_price,
|
block_price,
|
||||||
);
|
);
|
||||||
|
|
||||||
vecs.utxo_cohorts.push_aggregate_percentiles(
|
vecs.utxo_cohorts
|
||||||
block_price,
|
.push_aggregate_percentiles(block_price, date_opt, &vecs.states_path)?;
|
||||||
date_opt,
|
|
||||||
&vecs.states_path,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Periodic checkpoint flush
|
// Periodic checkpoint flush
|
||||||
if height != last_height
|
if height != last_height
|
||||||
@@ -534,20 +552,16 @@ fn push_cohort_states(
|
|||||||
// Phase 1: push + unrealized (no reset yet — states still needed for aggregation)
|
// Phase 1: push + unrealized (no reset yet — states still needed for aggregation)
|
||||||
rayon::join(
|
rayon::join(
|
||||||
|| {
|
|| {
|
||||||
utxo_cohorts
|
utxo_cohorts.par_iter_separate_mut().for_each(|v| {
|
||||||
.par_iter_separate_mut()
|
v.push_state(height);
|
||||||
.for_each(|v| {
|
v.push_unrealized_state(height_price);
|
||||||
v.push_state(height);
|
})
|
||||||
v.push_unrealized_state(height_price);
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|| {
|
|| {
|
||||||
addr_cohorts
|
addr_cohorts.par_iter_separate_mut().for_each(|v| {
|
||||||
.par_iter_separate_mut()
|
v.push_state(height);
|
||||||
.for_each(|v| {
|
v.push_unrealized_state(height_price);
|
||||||
v.push_state(height);
|
})
|
||||||
v.push_unrealized_state(height_price);
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -116,11 +116,10 @@ impl<'a> TxInReaders<'a> {
|
|||||||
current_height: Height,
|
current_height: Height,
|
||||||
) -> (&[Sats], &[Height], &[OutputType], &[TypeIndex]) {
|
) -> (&[Sats], &[Height], &[OutputType], &[TypeIndex]) {
|
||||||
let end = first_txin_index + input_count;
|
let end = first_txin_index + input_count;
|
||||||
self.txins.spent.value.collect_range_into_at(
|
self.txins
|
||||||
first_txin_index,
|
.spent
|
||||||
end,
|
.value
|
||||||
&mut self.values_buf,
|
.collect_range_into_at(first_txin_index, end, &mut self.values_buf);
|
||||||
);
|
|
||||||
self.indexer.vecs.inputs.outpoint.collect_range_into_at(
|
self.indexer.vecs.inputs.outpoint.collect_range_into_at(
|
||||||
first_txin_index,
|
first_txin_index,
|
||||||
end,
|
end,
|
||||||
@@ -138,8 +137,8 @@ impl<'a> TxInReaders<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.prev_heights_buf.clear();
|
self.prev_heights_buf.clear();
|
||||||
self.prev_heights_buf.extend(
|
self.prev_heights_buf
|
||||||
self.outpoints_buf.iter().map(|outpoint| {
|
.extend(self.outpoints_buf.iter().map(|outpoint| {
|
||||||
if outpoint.is_coinbase() {
|
if outpoint.is_coinbase() {
|
||||||
current_height
|
current_height
|
||||||
} else {
|
} else {
|
||||||
@@ -147,8 +146,7 @@ impl<'a> TxInReaders<'a> {
|
|||||||
.get(outpoint.tx_index())
|
.get(outpoint.tx_index())
|
||||||
.unwrap_or(current_height)
|
.unwrap_or(current_height)
|
||||||
}
|
}
|
||||||
}),
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
(
|
(
|
||||||
&self.values_buf,
|
&self.values_buf,
|
||||||
@@ -166,10 +164,7 @@ pub struct VecsReaders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VecsReaders {
|
impl VecsReaders {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(any_addr_indexes: &AnyAddrIndexesVecs, addrs_data: &AddrsDataVecs) -> Self {
|
||||||
any_addr_indexes: &AnyAddrIndexesVecs,
|
|
||||||
addrs_data: &AddrsDataVecs,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
addr_type_index_to_any_addr_index: ByAddrType {
|
addr_type_index_to_any_addr_index: ByAddrType {
|
||||||
p2a: any_addr_indexes.p2a.create_reader(),
|
p2a: any_addr_indexes.p2a.create_reader(),
|
||||||
|
|||||||
@@ -185,10 +185,7 @@ fn rollback_states(
|
|||||||
heights.insert(chain_height);
|
heights.insert(chain_height);
|
||||||
|
|
||||||
let Ok(stamps) = addr_indexes_rollbacks else {
|
let Ok(stamps) = addr_indexes_rollbacks else {
|
||||||
warn!(
|
warn!("addr_indexes rollback failed: {:?}", addr_indexes_rollbacks);
|
||||||
"addr_indexes rollback failed: {:?}",
|
|
||||||
addr_indexes_rollbacks
|
|
||||||
);
|
|
||||||
return Height::ZERO;
|
return Height::ZERO;
|
||||||
};
|
};
|
||||||
for (i, s) in stamps.iter().enumerate() {
|
for (i, s) in stamps.iter().enumerate() {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ use derive_more::{Deref, DerefMut};
|
|||||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
|
distribution::{
|
||||||
|
metrics::ImportConfig,
|
||||||
|
state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
|
},
|
||||||
internal::{AmountPerBlockCumulativeRolling, PerBlockCumulativeRolling},
|
internal::{AmountPerBlockCumulativeRolling, PerBlockCumulativeRolling},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
@@ -46,19 +49,18 @@ impl ActivityCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_state(
|
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
|
||||||
&mut self,
|
|
||||||
state: &CohortState<impl RealizedOps, impl CostBasisOps>,
|
|
||||||
) {
|
|
||||||
self.minimal.push_state(state);
|
self.minimal.push_state(state);
|
||||||
self.coindays_destroyed.block.push(
|
self.coindays_destroyed
|
||||||
StoredF64::from(Bitcoin::from(state.satdays_destroyed)),
|
.block
|
||||||
);
|
.push(StoredF64::from(Bitcoin::from(state.satdays_destroyed)));
|
||||||
self.transfer_volume_in_profit
|
self.transfer_volume_in_profit
|
||||||
.block.sats
|
.block
|
||||||
|
.sats
|
||||||
.push(state.realized.sent_in_profit());
|
.push(state.realized.sent_in_profit());
|
||||||
self.transfer_volume_in_loss
|
self.transfer_volume_in_loss
|
||||||
.block.sats
|
.block
|
||||||
|
.sats
|
||||||
.push(state.realized.sent_in_loss());
|
.push(state.realized.sent_in_loss());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ use vecdb::{AnyStoredVec, Exit, ReadableCloneableVec, Rw, StorageMode};
|
|||||||
use crate::internal::{Identity, LazyPerBlock, PerBlock, Windows};
|
use crate::internal::{Identity, LazyPerBlock, PerBlock, Windows};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
|
distribution::{
|
||||||
|
metrics::ImportConfig,
|
||||||
|
state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
|
},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +36,12 @@ impl ActivityFull {
|
|||||||
let coinyears_destroyed = LazyPerBlock::from_height_source::<Identity<StoredF64>>(
|
let coinyears_destroyed = LazyPerBlock::from_height_source::<Identity<StoredF64>>(
|
||||||
&cfg.name("coinyears_destroyed"),
|
&cfg.name("coinyears_destroyed"),
|
||||||
cfg.version + v1,
|
cfg.version + v1,
|
||||||
inner.coindays_destroyed.sum._1y.height.read_only_boxed_clone(),
|
inner
|
||||||
|
.coindays_destroyed
|
||||||
|
.sum
|
||||||
|
._1y
|
||||||
|
.height
|
||||||
|
.read_only_boxed_clone(),
|
||||||
cfg.indexes,
|
cfg.indexes,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -89,7 +97,8 @@ impl ActivityFull {
|
|||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.inner.compute_rest_part1(prices, starting_indexes, exit)?;
|
self.inner
|
||||||
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
|
|
||||||
for ((dormancy, cdd_sum), tv_sum) in self
|
for ((dormancy, cdd_sum), tv_sum) in self
|
||||||
.dormancy
|
.dormancy
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ use brk_types::{Indexes, Version};
|
|||||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::{metrics::ImportConfig, state::{CohortState, CostBasisOps, RealizedOps}},
|
distribution::{
|
||||||
|
metrics::ImportConfig,
|
||||||
|
state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
|
},
|
||||||
internal::AmountPerBlockCumulativeRolling,
|
internal::AmountPerBlockCumulativeRolling,
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
@@ -23,16 +26,11 @@ impl ActivityMinimal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn min_len(&self) -> usize {
|
pub(crate) fn min_len(&self) -> usize {
|
||||||
self.transfer_volume
|
self.transfer_volume.block.sats.len()
|
||||||
.block.sats
|
|
||||||
.len()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_state(
|
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
|
||||||
&mut self,
|
|
||||||
state: &CohortState<impl RealizedOps, impl CostBasisOps>,
|
|
||||||
) {
|
|
||||||
self.transfer_volume.block.sats.push(state.sent);
|
self.transfer_volume.block.sats.push(state.sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,7 @@ pub trait ActivityLike: Send + Sync {
|
|||||||
fn as_core(&self) -> &ActivityCore;
|
fn as_core(&self) -> &ActivityCore;
|
||||||
fn as_core_mut(&mut self) -> &mut ActivityCore;
|
fn as_core_mut(&mut self) -> &mut ActivityCore;
|
||||||
fn min_len(&self) -> usize;
|
fn min_len(&self) -> usize;
|
||||||
fn push_state<R: RealizedOps>(
|
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>);
|
||||||
&mut self,
|
|
||||||
state: &CohortState<R, impl CostBasisOps>,
|
|
||||||
);
|
|
||||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()>;
|
||||||
fn compute_from_stateful(
|
fn compute_from_stateful(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -39,37 +36,69 @@ pub trait ActivityLike: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ActivityLike for ActivityCore {
|
impl ActivityLike for ActivityCore {
|
||||||
fn as_core(&self) -> &ActivityCore { self }
|
fn as_core(&self) -> &ActivityCore {
|
||||||
fn as_core_mut(&mut self) -> &mut ActivityCore { self }
|
self
|
||||||
fn min_len(&self) -> usize { self.min_len() }
|
}
|
||||||
|
fn as_core_mut(&mut self) -> &mut ActivityCore {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn min_len(&self) -> usize {
|
||||||
|
self.min_len()
|
||||||
|
}
|
||||||
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>) {
|
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>) {
|
||||||
self.push_state(state);
|
self.push_state(state);
|
||||||
}
|
}
|
||||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||||
self.validate_computed_versions(base_version)
|
self.validate_computed_versions(base_version)
|
||||||
}
|
}
|
||||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
|
fn compute_from_stateful(
|
||||||
|
&mut self,
|
||||||
|
starting_indexes: &Indexes,
|
||||||
|
others: &[&ActivityCore],
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
self.compute_from_stateful(starting_indexes, others, exit)
|
self.compute_from_stateful(starting_indexes, others, exit)
|
||||||
}
|
}
|
||||||
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
fn compute_rest_part1(
|
||||||
|
&mut self,
|
||||||
|
prices: &prices::Vecs,
|
||||||
|
starting_indexes: &Indexes,
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
self.compute_rest_part1(prices, starting_indexes, exit)
|
self.compute_rest_part1(prices, starting_indexes, exit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActivityLike for ActivityFull {
|
impl ActivityLike for ActivityFull {
|
||||||
fn as_core(&self) -> &ActivityCore { &self.inner }
|
fn as_core(&self) -> &ActivityCore {
|
||||||
fn as_core_mut(&mut self) -> &mut ActivityCore { &mut self.inner }
|
&self.inner
|
||||||
fn min_len(&self) -> usize { self.full_min_len() }
|
}
|
||||||
|
fn as_core_mut(&mut self) -> &mut ActivityCore {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
fn min_len(&self) -> usize {
|
||||||
|
self.full_min_len()
|
||||||
|
}
|
||||||
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>) {
|
fn push_state<R: RealizedOps>(&mut self, state: &CohortState<R, impl CostBasisOps>) {
|
||||||
self.full_push_state(state);
|
self.full_push_state(state);
|
||||||
}
|
}
|
||||||
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
fn validate_computed_versions(&mut self, base_version: Version) -> Result<()> {
|
||||||
self.inner.validate_computed_versions(base_version)
|
self.inner.validate_computed_versions(base_version)
|
||||||
}
|
}
|
||||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&ActivityCore], exit: &Exit) -> Result<()> {
|
fn compute_from_stateful(
|
||||||
|
&mut self,
|
||||||
|
starting_indexes: &Indexes,
|
||||||
|
others: &[&ActivityCore],
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
self.compute_from_stateful(starting_indexes, others, exit)
|
self.compute_from_stateful(starting_indexes, others, exit)
|
||||||
}
|
}
|
||||||
fn compute_rest_part1(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
fn compute_rest_part1(
|
||||||
|
&mut self,
|
||||||
|
prices: &prices::Vecs,
|
||||||
|
starting_indexes: &Indexes,
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
self.compute_rest_part1(prices, starting_indexes, exit)
|
self.compute_rest_part1(prices, starting_indexes, exit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
use brk_cohort::Filter;
|
use brk_cohort::Filter;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{Cents, Dollars, Height, Indexes, Version};
|
||||||
Cents, Dollars, Height, Indexes, Version,
|
|
||||||
};
|
|
||||||
use vecdb::AnyStoredVec;
|
use vecdb::AnyStoredVec;
|
||||||
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
|
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
@@ -136,11 +134,8 @@ impl AllCohortMetrics {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let all_utxo_count = self.outputs.unspent_count.height.read_only_clone();
|
let all_utxo_count = self.outputs.unspent_count.height.read_only_clone();
|
||||||
self.outputs.compute_part2(
|
self.outputs
|
||||||
starting_indexes.height,
|
.compute_part2(starting_indexes.height, &all_utxo_count, exit)?;
|
||||||
&all_utxo_count,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.cost_basis.compute_prices(
|
self.cost_basis.compute_prices(
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
@@ -154,11 +149,8 @@ impl AllCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.unrealized.compute_sentiment(
|
self.unrealized
|
||||||
starting_indexes,
|
.compute_sentiment(starting_indexes, &prices.spot.cents.height, exit)?;
|
||||||
&prices.spot.cents.height,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.relative.compute(
|
self.relative.compute(
|
||||||
starting_indexes.height,
|
starting_indexes.height,
|
||||||
|
|||||||
@@ -87,16 +87,12 @@ impl BasicCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.relative.compute(
|
self.relative
|
||||||
starting_indexes.height,
|
.compute(starting_indexes.height, &self.supply, all_supply_sats, exit)?;
|
||||||
&self.supply,
|
|
||||||
all_supply_sats,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
self.outputs
|
||||||
|
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,10 @@ impl CoreCohortMetrics {
|
|||||||
)?;
|
)?;
|
||||||
self.unrealized.compute_from_stateful(
|
self.unrealized.compute_from_stateful(
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
&others.iter().map(|v| v.unrealized_core()).collect::<Vec<_>>(),
|
&others
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.unrealized_core())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -105,16 +108,14 @@ impl CoreCohortMetrics {
|
|||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.supply
|
self.supply.compute(prices, starting_indexes.height, exit)?;
|
||||||
.compute(prices, starting_indexes.height, exit)?;
|
|
||||||
|
|
||||||
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
||||||
|
|
||||||
self.activity
|
self.activity
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
|
|
||||||
self.realized
|
self.realized.compute_rest_part1(starting_indexes, exit)?;
|
||||||
.compute_rest_part1(starting_indexes, exit)?;
|
|
||||||
|
|
||||||
self.unrealized.compute_rest(starting_indexes, exit)?;
|
self.unrealized.compute_rest(starting_indexes, exit)?;
|
||||||
|
|
||||||
@@ -144,14 +145,11 @@ impl CoreCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.relative.compute(
|
self.relative
|
||||||
starting_indexes.height,
|
.compute(starting_indexes.height, &self.supply, all_supply_sats, exit)?;
|
||||||
&self.supply,
|
|
||||||
all_supply_sats,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
self.outputs
|
||||||
|
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,8 +104,7 @@ impl MinimalCohortMetrics {
|
|||||||
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.activity
|
self.activity
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
self.realized
|
self.realized.compute_rest_part1(starting_indexes, exit)?;
|
||||||
.compute_rest_part1(starting_indexes, exit)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +129,8 @@ impl MinimalCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
self.outputs
|
||||||
|
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,8 +66,7 @@ impl TypeCohortMetrics {
|
|||||||
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.activity
|
self.activity
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
self.realized
|
self.realized.compute_rest_part1(starting_indexes, exit)?;
|
||||||
.compute_rest_part1(starting_indexes, exit)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +91,8 @@ impl TypeCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
self.outputs
|
||||||
|
.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ use brk_types::CentsSquaredSats;
|
|||||||
use brk_types::{BasisPoints16, Cents, Height, Indexes, Sats, Version};
|
use brk_types::{BasisPoints16, Cents, Height, Indexes, Sats, Version};
|
||||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::internal::{
|
use crate::internal::{PERCENTILES_LEN, PerBlock, PercentPerBlock, PercentilesVecs, Price};
|
||||||
PERCENTILES_LEN, PerBlock, PercentPerBlock, PercentilesVecs, Price,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ImportConfig;
|
use super::ImportConfig;
|
||||||
|
|
||||||
@@ -33,12 +31,32 @@ impl CostBasis {
|
|||||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
in_profit: CostBasisSide {
|
in_profit: CostBasisSide {
|
||||||
per_coin: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_profit_per_coin"), cfg.version + Version::ONE, cfg.indexes)?,
|
per_coin: Price::forced_import(
|
||||||
per_dollar: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_profit_per_dollar"), cfg.version + Version::ONE, cfg.indexes)?,
|
cfg.db,
|
||||||
|
&cfg.name("cost_basis_in_profit_per_coin"),
|
||||||
|
cfg.version + Version::ONE,
|
||||||
|
cfg.indexes,
|
||||||
|
)?,
|
||||||
|
per_dollar: Price::forced_import(
|
||||||
|
cfg.db,
|
||||||
|
&cfg.name("cost_basis_in_profit_per_dollar"),
|
||||||
|
cfg.version + Version::ONE,
|
||||||
|
cfg.indexes,
|
||||||
|
)?,
|
||||||
},
|
},
|
||||||
in_loss: CostBasisSide {
|
in_loss: CostBasisSide {
|
||||||
per_coin: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_loss_per_coin"), cfg.version + Version::ONE, cfg.indexes)?,
|
per_coin: Price::forced_import(
|
||||||
per_dollar: Price::forced_import(cfg.db, &cfg.name("cost_basis_in_loss_per_dollar"), cfg.version + Version::ONE, cfg.indexes)?,
|
cfg.db,
|
||||||
|
&cfg.name("cost_basis_in_loss_per_coin"),
|
||||||
|
cfg.version + Version::ONE,
|
||||||
|
cfg.indexes,
|
||||||
|
)?,
|
||||||
|
per_dollar: Price::forced_import(
|
||||||
|
cfg.db,
|
||||||
|
&cfg.name("cost_basis_in_loss_per_dollar"),
|
||||||
|
cfg.version + Version::ONE,
|
||||||
|
cfg.indexes,
|
||||||
|
)?,
|
||||||
},
|
},
|
||||||
min: cfg.import("cost_basis_min", Version::ZERO)?,
|
min: cfg.import("cost_basis_min", Version::ZERO)?,
|
||||||
max: cfg.import("cost_basis_max", Version::ZERO)?,
|
max: cfg.import("cost_basis_max", Version::ZERO)?,
|
||||||
|
|||||||
@@ -51,7 +51,12 @@ macro_rules! impl_cohort_accessors {
|
|||||||
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs {
|
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs {
|
||||||
&mut self.unrealized
|
&mut self.unrealized
|
||||||
}
|
}
|
||||||
fn supply_and_unrealized_mut(&mut self) -> (&$crate::distribution::metrics::SupplyCore, &mut Self::UnrealizedVecs) {
|
fn supply_and_unrealized_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> (
|
||||||
|
&$crate::distribution::metrics::SupplyCore,
|
||||||
|
&mut Self::UnrealizedVecs,
|
||||||
|
) {
|
||||||
(&*self.supply, &mut self.unrealized)
|
(&*self.supply, &mut self.unrealized)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -94,7 +99,12 @@ macro_rules! impl_cohort_accessors_inner {
|
|||||||
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs {
|
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs {
|
||||||
&mut self.inner.unrealized
|
&mut self.inner.unrealized
|
||||||
}
|
}
|
||||||
fn supply_and_unrealized_mut(&mut self) -> (&$crate::distribution::metrics::SupplyCore, &mut Self::UnrealizedVecs) {
|
fn supply_and_unrealized_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> (
|
||||||
|
&$crate::distribution::metrics::SupplyCore,
|
||||||
|
&mut Self::UnrealizedVecs,
|
||||||
|
) {
|
||||||
(&*self.inner.supply, &mut self.inner.unrealized)
|
(&*self.inner.supply, &mut self.inner.unrealized)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -125,8 +135,7 @@ pub use realized::{
|
|||||||
pub use relative::{RelativeForAll, RelativeToAll, RelativeWithExtended};
|
pub use relative::{RelativeForAll, RelativeToAll, RelativeWithExtended};
|
||||||
pub use supply::{SupplyBase, SupplyCore};
|
pub use supply::{SupplyBase, SupplyCore};
|
||||||
pub use unrealized::{
|
pub use unrealized::{
|
||||||
UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike,
|
UnrealizedBasic, UnrealizedCore, UnrealizedFull, UnrealizedLike, UnrealizedMinimal,
|
||||||
UnrealizedMinimal,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use brk_cohort::Filter;
|
use brk_cohort::Filter;
|
||||||
@@ -250,10 +259,7 @@ pub trait CohortMetricsBase:
|
|||||||
.min(self.unrealized().min_stateful_len())
|
.min(self.unrealized().min_stateful_len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_state(
|
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
|
||||||
&mut self,
|
|
||||||
state: &CohortState<RealizedState, CostBasisData<WithCapital>>,
|
|
||||||
) {
|
|
||||||
self.supply_mut().push_state(state);
|
self.supply_mut().push_state(state);
|
||||||
self.outputs_mut().push_state(state);
|
self.outputs_mut().push_state(state);
|
||||||
self.activity_mut().push_state(state);
|
self.activity_mut().push_state(state);
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{AmountPerBlock, AmountPerBlockWithDeltas, CachedWindowStarts, PerBlock, RatioPerBlock},
|
internal::{
|
||||||
|
AmountPerBlock, AmountPerBlockWithDeltas, CachedWindowStarts, PerBlock, RatioPerBlock,
|
||||||
|
},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -183,22 +185,34 @@ impl ProfitabilityBucket {
|
|||||||
|
|
||||||
self.supply.all.sats.height.compute_sum_of_others(
|
self.supply.all.sats.height.compute_sum_of_others(
|
||||||
max_from,
|
max_from,
|
||||||
&sources.iter().map(|s| &s.supply.all.sats.height).collect::<Vec<_>>(),
|
&sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| &s.supply.all.sats.height)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.supply.sth.sats.height.compute_sum_of_others(
|
self.supply.sth.sats.height.compute_sum_of_others(
|
||||||
max_from,
|
max_from,
|
||||||
&sources.iter().map(|s| &s.supply.sth.sats.height).collect::<Vec<_>>(),
|
&sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| &s.supply.sth.sats.height)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.realized_cap.all.height.compute_sum_of_others(
|
self.realized_cap.all.height.compute_sum_of_others(
|
||||||
max_from,
|
max_from,
|
||||||
&sources.iter().map(|s| &s.realized_cap.all.height).collect::<Vec<_>>(),
|
&sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| &s.realized_cap.all.height)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.realized_cap.sth.height.compute_sum_of_others(
|
self.realized_cap.sth.height.compute_sum_of_others(
|
||||||
max_from,
|
max_from,
|
||||||
&sources.iter().map(|s| &s.realized_cap.sth.height).collect::<Vec<_>>(),
|
&sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| &s.realized_cap.sth.height)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Dollars, Height, Indexes, StoredF64, Version};
|
use brk_types::{
|
||||||
|
BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Dollars, Height, Indexes, StoredF64, Version,
|
||||||
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use vecdb::{
|
use vecdb::{
|
||||||
AnyStoredVec, Exit, LazyVecFrom1, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
|
AnyStoredVec, Exit, LazyVecFrom1, ReadableCloneableVec, ReadableVec, Rw, StorageMode,
|
||||||
@@ -42,7 +44,8 @@ pub struct RealizedCore<M: StorageMode = Rw> {
|
|||||||
|
|
||||||
#[traversable(wrap = "loss", rename = "negative")]
|
#[traversable(wrap = "loss", rename = "negative")]
|
||||||
pub neg_loss: NegRealizedLoss,
|
pub neg_loss: NegRealizedLoss,
|
||||||
pub net_pnl: FiatPerBlockCumulativeWithSumsAndDeltas<CentsSigned, CentsSigned, BasisPointsSigned32, M>,
|
pub net_pnl:
|
||||||
|
FiatPerBlockCumulativeWithSumsAndDeltas<CentsSigned, CentsSigned, BasisPointsSigned32, M>,
|
||||||
pub sopr: RealizedSoprCore<M>,
|
pub sopr: RealizedSoprCore<M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +110,10 @@ impl RealizedCore {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
|
pub(crate) fn push_state(&mut self, state: &CohortState<impl RealizedOps, impl CostBasisOps>) {
|
||||||
self.minimal.push_state(state);
|
self.minimal.push_state(state);
|
||||||
self.sopr.value_destroyed.block.push(state.realized.value_destroyed());
|
self.sopr
|
||||||
|
.value_destroyed
|
||||||
|
.block
|
||||||
|
.push(state.realized.value_destroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||||
@@ -135,8 +141,7 @@ impl RealizedCore {
|
|||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.minimal
|
self.minimal.compute_rest_part1(starting_indexes, exit)?;
|
||||||
.compute_rest_part1(starting_indexes, exit)?;
|
|
||||||
|
|
||||||
self.sopr
|
self.sopr
|
||||||
.value_destroyed
|
.value_destroyed
|
||||||
@@ -169,8 +174,7 @@ impl RealizedCore {
|
|||||||
self.minimal
|
self.minimal
|
||||||
.compute_rest_part2(prices, starting_indexes, height_to_supply, exit)?;
|
.compute_rest_part2(prices, starting_indexes, height_to_supply, exit)?;
|
||||||
|
|
||||||
self.net_pnl
|
self.net_pnl.compute_rest(starting_indexes.height, exit)?;
|
||||||
.compute_rest(starting_indexes.height, exit)?;
|
|
||||||
|
|
||||||
self.sopr
|
self.sopr
|
||||||
.ratio
|
.ratio
|
||||||
|
|||||||
@@ -178,7 +178,8 @@ impl RealizedFull {
|
|||||||
.push(state.realized.investor_cap_raw());
|
.push(state.realized.investor_cap_raw());
|
||||||
self.peak_regret
|
self.peak_regret
|
||||||
.value
|
.value
|
||||||
.block.cents
|
.block
|
||||||
|
.cents
|
||||||
.push(state.realized.peak_regret());
|
.push(state.realized.peak_regret());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,10 +219,7 @@ impl RealizedFull {
|
|||||||
};
|
};
|
||||||
self.investor.price.cents.height.push(investor_price);
|
self.investor.price.cents.height.push(investor_price);
|
||||||
|
|
||||||
self.peak_regret
|
self.peak_regret.value.block.cents.push(accum.peak_regret());
|
||||||
.value
|
|
||||||
.block.cents
|
|
||||||
.push(accum.peak_regret());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_rest_part1(
|
pub(crate) fn compute_rest_part1(
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
BasisPoints32, BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Height, Indexes, Sats, StoredF32,
|
BasisPoints32, BasisPointsSigned32, Bitcoin, Cents, CentsSigned, Height, Indexes, Sats,
|
||||||
Version,
|
StoredF32, Version,
|
||||||
};
|
|
||||||
use vecdb::{
|
|
||||||
AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec,
|
|
||||||
};
|
};
|
||||||
|
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::state::{CohortState, CostBasisOps, RealizedOps},
|
distribution::state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
internal::{
|
internal::{
|
||||||
FiatPerBlockCumulativeWithSums,
|
FiatPerBlockCumulativeWithSums, FiatPerBlockWithDeltas, Identity, LazyPerBlock,
|
||||||
FiatPerBlockWithDeltas, Identity, LazyPerBlock, PriceWithRatioPerBlock,
|
PriceWithRatioPerBlock,
|
||||||
},
|
},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
@@ -111,23 +109,24 @@ impl RealizedMinimal {
|
|||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let cap = &self.cap.cents.height;
|
let cap = &self.cap.cents.height;
|
||||||
self.price.compute_all(prices, starting_indexes, exit, |v| {
|
self.price
|
||||||
Ok(v.compute_transform2(
|
.compute_all(prices, starting_indexes, exit, |v| {
|
||||||
starting_indexes.height,
|
Ok(v.compute_transform2(
|
||||||
cap,
|
starting_indexes.height,
|
||||||
height_to_supply,
|
cap,
|
||||||
|(i, cap_cents, supply, ..)| {
|
height_to_supply,
|
||||||
let cap = cap_cents.as_u128();
|
|(i, cap_cents, supply, ..)| {
|
||||||
let supply_sats = Sats::from(supply).as_u128();
|
let cap = cap_cents.as_u128();
|
||||||
if supply_sats == 0 {
|
let supply_sats = Sats::from(supply).as_u128();
|
||||||
(i, Cents::ZERO)
|
if supply_sats == 0 {
|
||||||
} else {
|
(i, Cents::ZERO)
|
||||||
(i, Cents::from(cap * Sats::ONE_BTC_U128 / supply_sats))
|
} else {
|
||||||
}
|
(i, Cents::from(cap * Sats::ONE_BTC_U128 / supply_sats))
|
||||||
},
|
}
|
||||||
exit,
|
},
|
||||||
)?)
|
exit,
|
||||||
})?;
|
)?)
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ mod core;
|
|||||||
mod full;
|
mod full;
|
||||||
mod minimal;
|
mod minimal;
|
||||||
|
|
||||||
pub use adjusted::AdjustedSopr;
|
|
||||||
pub use self::core::RealizedCore;
|
pub use self::core::RealizedCore;
|
||||||
|
pub use adjusted::AdjustedSopr;
|
||||||
pub use full::{RealizedFull, RealizedFullAccum};
|
pub use full::{RealizedFull, RealizedFullAccum};
|
||||||
pub use minimal::RealizedMinimal;
|
pub use minimal::RealizedMinimal;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ use brk_error::Result;
|
|||||||
use brk_types::Indexes;
|
use brk_types::Indexes;
|
||||||
use vecdb::Exit;
|
use vecdb::Exit;
|
||||||
|
|
||||||
use crate::distribution::state::{WithCapital, CohortState, CostBasisData, RealizedState};
|
use crate::distribution::state::{CohortState, CostBasisData, RealizedState, WithCapital};
|
||||||
|
|
||||||
pub trait RealizedLike: Send + Sync {
|
pub trait RealizedLike: Send + Sync {
|
||||||
fn as_core(&self) -> &RealizedCore;
|
fn as_core(&self) -> &RealizedCore;
|
||||||
@@ -29,9 +29,15 @@ pub trait RealizedLike: Send + Sync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RealizedLike for RealizedCore {
|
impl RealizedLike for RealizedCore {
|
||||||
fn as_core(&self) -> &RealizedCore { self }
|
fn as_core(&self) -> &RealizedCore {
|
||||||
fn as_core_mut(&mut self) -> &mut RealizedCore { self }
|
self
|
||||||
fn min_stateful_len(&self) -> usize { self.min_stateful_len() }
|
}
|
||||||
|
fn as_core_mut(&mut self) -> &mut RealizedCore {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn min_stateful_len(&self) -> usize {
|
||||||
|
self.min_stateful_len()
|
||||||
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
|
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
|
||||||
self.push_state(state)
|
self.push_state(state)
|
||||||
@@ -39,15 +45,26 @@ impl RealizedLike for RealizedCore {
|
|||||||
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||||
self.compute_rest_part1(starting_indexes, exit)
|
self.compute_rest_part1(starting_indexes, exit)
|
||||||
}
|
}
|
||||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
|
fn compute_from_stateful(
|
||||||
|
&mut self,
|
||||||
|
starting_indexes: &Indexes,
|
||||||
|
others: &[&RealizedCore],
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
self.compute_from_stateful(starting_indexes, others, exit)
|
self.compute_from_stateful(starting_indexes, others, exit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RealizedLike for RealizedFull {
|
impl RealizedLike for RealizedFull {
|
||||||
fn as_core(&self) -> &RealizedCore { &self.core }
|
fn as_core(&self) -> &RealizedCore {
|
||||||
fn as_core_mut(&mut self) -> &mut RealizedCore { &mut self.core }
|
&self.core
|
||||||
fn min_stateful_len(&self) -> usize { self.min_stateful_len() }
|
}
|
||||||
|
fn as_core_mut(&mut self) -> &mut RealizedCore {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
fn min_stateful_len(&self) -> usize {
|
||||||
|
self.min_stateful_len()
|
||||||
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
|
fn push_state(&mut self, state: &CohortState<RealizedState, CostBasisData<WithCapital>>) {
|
||||||
self.push_state(state)
|
self.push_state(state)
|
||||||
@@ -55,7 +72,12 @@ impl RealizedLike for RealizedFull {
|
|||||||
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
fn compute_rest_part1(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||||
self.compute_rest_part1(starting_indexes, exit)
|
self.compute_rest_part1(starting_indexes, exit)
|
||||||
}
|
}
|
||||||
fn compute_from_stateful(&mut self, starting_indexes: &Indexes, others: &[&RealizedCore], exit: &Exit) -> Result<()> {
|
fn compute_from_stateful(
|
||||||
|
&mut self,
|
||||||
|
starting_indexes: &Indexes,
|
||||||
|
others: &[&RealizedCore],
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
self.compute_from_stateful(starting_indexes, others, exit)
|
self.compute_from_stateful(starting_indexes, others, exit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ impl RelativeExtendedOwnMarketCap {
|
|||||||
let v2 = Version::new(2);
|
let v2 = Version::new(2);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
unrealized_profit_to_own_mcap: cfg
|
unrealized_profit_to_own_mcap: cfg.import("unrealized_profit_to_own_mcap", v2)?,
|
||||||
.import("unrealized_profit_to_own_mcap", v2)?,
|
|
||||||
unrealized_loss_to_own_mcap: cfg
|
unrealized_loss_to_own_mcap: cfg
|
||||||
.import("unrealized_loss_to_own_mcap", Version::new(3))?,
|
.import("unrealized_loss_to_own_mcap", Version::new(3))?,
|
||||||
net_unrealized_pnl_to_own_mcap: cfg
|
net_unrealized_pnl_to_own_mcap: cfg
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ pub struct RelativeExtendedOwnPnl<M: StorageMode = Rw> {
|
|||||||
pub unrealized_loss_to_own_gross_pnl: PercentPerBlock<BasisPoints16, M>,
|
pub unrealized_loss_to_own_gross_pnl: PercentPerBlock<BasisPoints16, M>,
|
||||||
#[traversable(wrap = "unrealized/net_pnl", rename = "to_own_gross_pnl")]
|
#[traversable(wrap = "unrealized/net_pnl", rename = "to_own_gross_pnl")]
|
||||||
pub net_unrealized_pnl_to_own_gross_pnl: PercentPerBlock<BasisPointsSigned32, M>,
|
pub net_unrealized_pnl_to_own_gross_pnl: PercentPerBlock<BasisPointsSigned32, M>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelativeExtendedOwnPnl {
|
impl RelativeExtendedOwnPnl {
|
||||||
@@ -26,8 +25,7 @@ impl RelativeExtendedOwnPnl {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
unrealized_profit_to_own_gross_pnl: cfg
|
unrealized_profit_to_own_gross_pnl: cfg
|
||||||
.import("unrealized_profit_to_own_gross_pnl", v1)?,
|
.import("unrealized_profit_to_own_gross_pnl", v1)?,
|
||||||
unrealized_loss_to_own_gross_pnl: cfg
|
unrealized_loss_to_own_gross_pnl: cfg.import("unrealized_loss_to_own_gross_pnl", v1)?,
|
||||||
.import("unrealized_loss_to_own_gross_pnl", v1)?,
|
|
||||||
net_unrealized_pnl_to_own_gross_pnl: cfg
|
net_unrealized_pnl_to_own_gross_pnl: cfg
|
||||||
.import("net_unrealized_pnl_to_own_gross_pnl", Version::new(3))?,
|
.import("net_unrealized_pnl_to_own_gross_pnl", Version::new(3))?,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
|||||||
|
|
||||||
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
|
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
|
||||||
|
|
||||||
use super::{RelativeFull, RelativeExtendedOwnPnl};
|
use super::{RelativeExtendedOwnPnl, RelativeFull};
|
||||||
|
|
||||||
/// Relative metrics for the "all" cohort (base + own_pnl, NO rel_to_all).
|
/// Relative metrics for the "all" cohort (base + own_pnl, NO rel_to_all).
|
||||||
#[derive(Deref, DerefMut, Traversable)]
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
@@ -35,15 +35,14 @@ impl RelativeForAll {
|
|||||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.base.compute(
|
self.base
|
||||||
|
.compute(max_from, supply, &unrealized.inner.basic, market_cap, exit)?;
|
||||||
|
self.extended_own_pnl.compute(
|
||||||
max_from,
|
max_from,
|
||||||
supply,
|
&unrealized.inner,
|
||||||
&unrealized.inner.basic,
|
&unrealized.gross_pnl.usd.height,
|
||||||
market_cap,
|
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
self.extended_own_pnl
|
|
||||||
.compute(max_from, &unrealized.inner, &unrealized.gross_pnl.usd.height, exit)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,13 +28,10 @@ impl RelativeFull {
|
|||||||
let v2 = Version::new(2);
|
let v2 = Version::new(2);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
supply_in_profit_to_own: cfg
|
supply_in_profit_to_own: cfg.import("supply_in_profit_to_own", v1)?,
|
||||||
.import("supply_in_profit_to_own", v1)?,
|
|
||||||
supply_in_loss_to_own: cfg.import("supply_in_loss_to_own", v1)?,
|
supply_in_loss_to_own: cfg.import("supply_in_loss_to_own", v1)?,
|
||||||
unrealized_profit_to_mcap: cfg
|
unrealized_profit_to_mcap: cfg.import("unrealized_profit_to_mcap", v2)?,
|
||||||
.import("unrealized_profit_to_mcap", v2)?,
|
unrealized_loss_to_mcap: cfg.import("unrealized_loss_to_mcap", v2)?,
|
||||||
unrealized_loss_to_mcap: cfg
|
|
||||||
.import("unrealized_loss_to_mcap", v2)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ pub struct RelativeToAll<M: StorageMode = Rw> {
|
|||||||
impl RelativeToAll {
|
impl RelativeToAll {
|
||||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
supply_to_circulating: cfg
|
supply_to_circulating: cfg.import("supply_to_circulating", Version::ONE)?,
|
||||||
.import("supply_to_circulating", Version::ONE)?,
|
|
||||||
supply_in_profit_to_circulating: cfg
|
supply_in_profit_to_circulating: cfg
|
||||||
.import("supply_in_profit_to_circulating", Version::ONE)?,
|
.import("supply_in_profit_to_circulating", Version::ONE)?,
|
||||||
supply_in_loss_to_circulating: cfg
|
supply_in_loss_to_circulating: cfg
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
|||||||
|
|
||||||
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
|
use crate::distribution::metrics::{ImportConfig, SupplyCore, UnrealizedFull};
|
||||||
|
|
||||||
use super::{RelativeFull, RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeToAll};
|
use super::{RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeFull, RelativeToAll};
|
||||||
|
|
||||||
/// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl).
|
/// Full extended relative metrics (base + rel_to_all + own_market_cap + own_pnl).
|
||||||
/// Used by: sth, lth cohorts.
|
/// Used by: sth, lth cohorts.
|
||||||
@@ -45,23 +45,18 @@ impl RelativeWithExtended {
|
|||||||
own_market_cap: &impl ReadableVec<Height, Dollars>,
|
own_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.base.compute(
|
self.base
|
||||||
max_from,
|
.compute(max_from, supply, &unrealized.inner.basic, market_cap, exit)?;
|
||||||
supply,
|
self.rel_to_all
|
||||||
&unrealized.inner.basic,
|
.compute(max_from, supply, all_supply_sats, exit)?;
|
||||||
market_cap,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
self.rel_to_all.compute(
|
|
||||||
max_from,
|
|
||||||
supply,
|
|
||||||
all_supply_sats,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
self.extended_own_market_cap
|
self.extended_own_market_cap
|
||||||
.compute(max_from, &unrealized.inner, own_market_cap, exit)?;
|
.compute(max_from, &unrealized.inner, own_market_cap, exit)?;
|
||||||
self.extended_own_pnl
|
self.extended_own_pnl.compute(
|
||||||
.compute(max_from, &unrealized.inner, &unrealized.gross_pnl.usd.height, exit)?;
|
max_from,
|
||||||
|
&unrealized.inner,
|
||||||
|
&unrealized.gross_pnl.usd.height,
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ use brk_traversable::Traversable;
|
|||||||
use brk_types::{BasisPointsSigned32, Height, Indexes, Sats, SatsSigned, Version};
|
use brk_types::{BasisPointsSigned32, Height, Indexes, Sats, SatsSigned, Version};
|
||||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::{distribution::state::{CohortState, CostBasisOps, RealizedOps}, prices};
|
use crate::{
|
||||||
|
distribution::state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
use crate::internal::{
|
prices,
|
||||||
AmountPerBlock, LazyRollingDeltasFromHeight,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::internal::{AmountPerBlock, LazyRollingDeltasFromHeight};
|
||||||
|
|
||||||
use crate::distribution::metrics::ImportConfig;
|
use crate::distribution::metrics::ImportConfig;
|
||||||
|
|
||||||
/// Base supply metrics: total supply only (2 stored vecs).
|
/// Base supply metrics: total supply only (2 stored vecs).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mod base;
|
mod base;
|
||||||
mod core;
|
mod core;
|
||||||
|
|
||||||
pub use base::SupplyBase;
|
|
||||||
pub use self::core::SupplyCore;
|
pub use self::core::SupplyCore;
|
||||||
|
pub use base::SupplyBase;
|
||||||
|
|||||||
@@ -54,14 +54,8 @@ impl UnrealizedBasic {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_state(&mut self, state: &UnrealizedState) {
|
pub(crate) fn push_state(&mut self, state: &UnrealizedState) {
|
||||||
self.profit
|
self.profit.cents.height.push(state.unrealized_profit);
|
||||||
.cents
|
self.loss.cents.height.push(state.unrealized_loss);
|
||||||
.height
|
|
||||||
.push(state.unrealized_profit);
|
|
||||||
self.loss
|
|
||||||
.cents
|
|
||||||
.height
|
|
||||||
.push(state.unrealized_loss);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ use derive_more::{Deref, DerefMut};
|
|||||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::{
|
distribution::{metrics::ImportConfig, state::UnrealizedState},
|
||||||
metrics::ImportConfig,
|
|
||||||
state::UnrealizedState,
|
|
||||||
},
|
|
||||||
internal::{CentsSubtractToCentsSigned, FiatPerBlock},
|
internal::{CentsSubtractToCentsSigned, FiatPerBlock},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,11 +57,7 @@ impl UnrealizedCore {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_rest(
|
pub(crate) fn compute_rest(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
starting_indexes: &Indexes,
|
|
||||||
exit: &Exit,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.net_pnl
|
self.net_pnl
|
||||||
.cents
|
.cents
|
||||||
.height
|
.height
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ impl UnrealizedLike for UnrealizedFull {
|
|||||||
supply_in_loss_sats: &(impl ReadableVec<Height, Sats> + Sync),
|
supply_in_loss_sats: &(impl ReadableVec<Height, Sats> + Sync),
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.compute_rest_all(prices, starting_indexes, supply_in_profit_sats, supply_in_loss_sats, exit)
|
self.compute_rest_all(
|
||||||
|
prices,
|
||||||
|
starting_indexes,
|
||||||
|
supply_in_profit_sats,
|
||||||
|
supply_in_loss_sats,
|
||||||
|
exit,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_types::{Age, Cents, CentsCompact, CentsSats, CentsSquaredSats, CostBasisSnapshot, Height, Sats, SupplyState};
|
use brk_types::{
|
||||||
|
Age, Cents, CentsCompact, CentsSats, CentsSquaredSats, CostBasisSnapshot, Height, Sats,
|
||||||
|
SupplyState,
|
||||||
|
};
|
||||||
|
|
||||||
use super::super::{cost_basis::{Accumulate, CostBasisData, CostBasisOps, RealizedOps, UnrealizedState}, pending::PendingDelta};
|
use super::super::{
|
||||||
|
cost_basis::{Accumulate, CostBasisData, CostBasisOps, RealizedOps, UnrealizedState},
|
||||||
|
pending::PendingDelta,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SendPrecomputed {
|
pub struct SendPrecomputed {
|
||||||
pub sats: Sats,
|
pub sats: Sats,
|
||||||
@@ -193,11 +199,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_utxo_precomputed(
|
pub(crate) fn send_utxo_precomputed(&mut self, supply: &SupplyState, pre: &SendPrecomputed) {
|
||||||
&mut self,
|
|
||||||
supply: &SupplyState,
|
|
||||||
pre: &SendPrecomputed,
|
|
||||||
) {
|
|
||||||
self.supply -= supply;
|
self.supply -= supply;
|
||||||
self.sent += pre.sats;
|
self.sent += pre.sats;
|
||||||
self.spent_utxo_count += supply.utxo_count;
|
self.spent_utxo_count += supply.utxo_count;
|
||||||
@@ -205,8 +207,13 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
|
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.realized
|
self.realized.send(
|
||||||
.send(pre.sats, pre.current_ps, pre.prev_ps, pre.ath_ps, pre.prev_investor_cap);
|
pre.sats,
|
||||||
|
pre.current_ps,
|
||||||
|
pre.prev_ps,
|
||||||
|
pre.ath_ps,
|
||||||
|
pre.prev_investor_cap,
|
||||||
|
);
|
||||||
|
|
||||||
self.cost_basis
|
self.cost_basis
|
||||||
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_investor_cap);
|
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_investor_cap);
|
||||||
|
|||||||
@@ -370,7 +370,11 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
let (base, rest) = CostBasisDistribution::deserialize_with_rest(&data)?;
|
let (base, rest) = CostBasisDistribution::deserialize_with_rest(&data)?;
|
||||||
self.map = Some(base);
|
self.map = Some(base);
|
||||||
self.raw.state = Some(RawState::deserialize(rest)?);
|
self.raw.state = Some(RawState::deserialize(rest)?);
|
||||||
debug_assert!(rest.len() >= 32, "CostBasisData state too short: {} bytes", rest.len());
|
debug_assert!(
|
||||||
|
rest.len() >= 32,
|
||||||
|
"CostBasisData state too short: {} bytes",
|
||||||
|
rest.len()
|
||||||
|
);
|
||||||
self.investor_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
self.investor_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
||||||
self.pending.clear();
|
self.pending.clear();
|
||||||
self.raw.pending_cap = PendingCapDelta::default();
|
self.raw.pending_cap = PendingCapDelta::default();
|
||||||
@@ -435,7 +439,9 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
|||||||
Path: {:?}\n\
|
Path: {:?}\n\
|
||||||
Current (after increments): {:?}\n\
|
Current (after increments): {:?}\n\
|
||||||
Trying to decrement by: {:?}",
|
Trying to decrement by: {:?}",
|
||||||
self.raw.pathbuf, self.investor_cap_raw, self.pending_investor_cap.dec
|
self.raw.pathbuf,
|
||||||
|
self.investor_cap_raw,
|
||||||
|
self.pending_investor_cap.dec
|
||||||
);
|
);
|
||||||
self.investor_cap_raw -= self.pending_investor_cap.dec;
|
self.investor_cap_raw -= self.pending_investor_cap.dec;
|
||||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ pub use data::*;
|
|||||||
pub use realized::*;
|
pub use realized::*;
|
||||||
pub use unrealized::UnrealizedState;
|
pub use unrealized::UnrealizedState;
|
||||||
|
|
||||||
pub(crate) use unrealized::{Accumulate, WithoutCapital, WithCapital};
|
pub(crate) use unrealized::{Accumulate, WithCapital, WithoutCapital};
|
||||||
|
|
||||||
// Internal use only
|
// Internal use only
|
||||||
pub(super) use unrealized::CachedUnrealizedState;
|
pub(super) use unrealized::CachedUnrealizedState;
|
||||||
|
|||||||
@@ -58,10 +58,18 @@ pub trait Accumulate: Default + Clone + Send + Sync + 'static {
|
|||||||
fn core(&self) -> &WithoutCapital;
|
fn core(&self) -> &WithoutCapital;
|
||||||
fn core_mut(&mut self) -> &mut WithoutCapital;
|
fn core_mut(&mut self) -> &mut WithoutCapital;
|
||||||
|
|
||||||
fn supply_in_profit(&self) -> Sats { self.core().supply_in_profit }
|
fn supply_in_profit(&self) -> Sats {
|
||||||
fn supply_in_loss(&self) -> Sats { self.core().supply_in_loss }
|
self.core().supply_in_profit
|
||||||
fn unrealized_profit(&mut self) -> &mut u128 { &mut self.core_mut().unrealized_profit }
|
}
|
||||||
fn unrealized_loss(&mut self) -> &mut u128 { &mut self.core_mut().unrealized_loss }
|
fn supply_in_loss(&self) -> Sats {
|
||||||
|
self.core().supply_in_loss
|
||||||
|
}
|
||||||
|
fn unrealized_profit(&mut self) -> &mut u128 {
|
||||||
|
&mut self.core_mut().unrealized_profit
|
||||||
|
}
|
||||||
|
fn unrealized_loss(&mut self) -> &mut u128 {
|
||||||
|
&mut self.core_mut().unrealized_loss
|
||||||
|
}
|
||||||
|
|
||||||
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats);
|
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats);
|
||||||
fn accumulate_loss(&mut self, price_u128: u128, sats: Sats);
|
fn accumulate_loss(&mut self, price_u128: u128, sats: Sats);
|
||||||
@@ -80,8 +88,12 @@ impl Accumulate for WithoutCapital {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn core(&self) -> &WithoutCapital { self }
|
fn core(&self) -> &WithoutCapital {
|
||||||
fn core_mut(&mut self) -> &mut WithoutCapital { self }
|
self
|
||||||
|
}
|
||||||
|
fn core_mut(&mut self) -> &mut WithoutCapital {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn accumulate_profit(&mut self, _price_u128: u128, sats: Sats) {
|
fn accumulate_profit(&mut self, _price_u128: u128, sats: Sats) {
|
||||||
@@ -110,8 +122,12 @@ impl Accumulate for WithCapital {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn core(&self) -> &WithoutCapital { &self.core }
|
fn core(&self) -> &WithoutCapital {
|
||||||
fn core_mut(&mut self) -> &mut WithoutCapital { &mut self.core }
|
&self.core
|
||||||
|
}
|
||||||
|
fn core_mut(&mut self) -> &mut WithoutCapital {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use brk_indexer::Indexer;
|
use brk_indexer::Indexer;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
Addr, AddrBytes, EmptyOutputIndex, OpReturnIndex, P2AAddrIndex, P2ABytes,
|
Addr, AddrBytes, EmptyOutputIndex, OpReturnIndex, P2AAddrIndex, P2ABytes, P2MSOutputIndex,
|
||||||
P2MSOutputIndex, P2PK33AddrIndex, P2PK33Bytes, P2PK65AddrIndex, P2PK65Bytes,
|
P2PK33AddrIndex, P2PK33Bytes, P2PK65AddrIndex, P2PK65Bytes, P2PKHAddrIndex, P2PKHBytes,
|
||||||
P2PKHAddrIndex, P2PKHBytes, P2SHAddrIndex, P2SHBytes, P2TRAddrIndex, P2TRBytes,
|
P2SHAddrIndex, P2SHBytes, P2TRAddrIndex, P2TRBytes, P2WPKHAddrIndex, P2WPKHBytes,
|
||||||
P2WPKHAddrIndex, P2WPKHBytes, P2WSHAddrIndex, P2WSHBytes, TxIndex, UnknownOutputIndex,
|
P2WSHAddrIndex, P2WSHBytes, TxIndex, UnknownOutputIndex, Version,
|
||||||
Version,
|
|
||||||
};
|
};
|
||||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
use vecdb::{LazyVecFrom1, ReadableCloneableVec};
|
||||||
|
|
||||||
@@ -27,15 +26,13 @@ pub struct Vecs {
|
|||||||
|
|
||||||
#[derive(Clone, Traversable)]
|
#[derive(Clone, Traversable)]
|
||||||
pub struct P2PK33Vecs {
|
pub struct P2PK33Vecs {
|
||||||
pub identity:
|
pub identity: LazyVecFrom1<P2PK33AddrIndex, P2PK33AddrIndex, P2PK33AddrIndex, P2PK33Bytes>,
|
||||||
LazyVecFrom1<P2PK33AddrIndex, P2PK33AddrIndex, P2PK33AddrIndex, P2PK33Bytes>,
|
|
||||||
pub addr: LazyVecFrom1<P2PK33AddrIndex, Addr, P2PK33AddrIndex, P2PK33Bytes>,
|
pub addr: LazyVecFrom1<P2PK33AddrIndex, Addr, P2PK33AddrIndex, P2PK33Bytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Traversable)]
|
#[derive(Clone, Traversable)]
|
||||||
pub struct P2PK65Vecs {
|
pub struct P2PK65Vecs {
|
||||||
pub identity:
|
pub identity: LazyVecFrom1<P2PK65AddrIndex, P2PK65AddrIndex, P2PK65AddrIndex, P2PK65Bytes>,
|
||||||
LazyVecFrom1<P2PK65AddrIndex, P2PK65AddrIndex, P2PK65AddrIndex, P2PK65Bytes>,
|
|
||||||
pub addr: LazyVecFrom1<P2PK65AddrIndex, Addr, P2PK65AddrIndex, P2PK65Bytes>,
|
pub addr: LazyVecFrom1<P2PK65AddrIndex, Addr, P2PK65AddrIndex, P2PK65Bytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +56,7 @@ pub struct P2TRVecs {
|
|||||||
|
|
||||||
#[derive(Clone, Traversable)]
|
#[derive(Clone, Traversable)]
|
||||||
pub struct P2WPKHVecs {
|
pub struct P2WPKHVecs {
|
||||||
pub identity:
|
pub identity: LazyVecFrom1<P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHBytes>,
|
||||||
LazyVecFrom1<P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHAddrIndex, P2WPKHBytes>,
|
|
||||||
pub addr: LazyVecFrom1<P2WPKHAddrIndex, Addr, P2WPKHAddrIndex, P2WPKHBytes>,
|
pub addr: LazyVecFrom1<P2WPKHAddrIndex, Addr, P2WPKHAddrIndex, P2WPKHBytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +211,12 @@ impl Vecs {
|
|||||||
identity: LazyVecFrom1::init(
|
identity: LazyVecFrom1::init(
|
||||||
"p2ms_output_index",
|
"p2ms_output_index",
|
||||||
version,
|
version,
|
||||||
indexer.vecs.scripts.p2ms.to_tx_index.read_only_boxed_clone(),
|
indexer
|
||||||
|
.vecs
|
||||||
|
.scripts
|
||||||
|
.p2ms
|
||||||
|
.to_tx_index
|
||||||
|
.read_only_boxed_clone(),
|
||||||
|index, _| index,
|
|index, _| index,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -226,7 +227,8 @@ impl Vecs {
|
|||||||
indexer
|
indexer
|
||||||
.vecs
|
.vecs
|
||||||
.scripts
|
.scripts
|
||||||
.empty.to_tx_index
|
.empty
|
||||||
|
.to_tx_index
|
||||||
.read_only_boxed_clone(),
|
.read_only_boxed_clone(),
|
||||||
|index, _| index,
|
|index, _| index,
|
||||||
),
|
),
|
||||||
@@ -238,7 +240,8 @@ impl Vecs {
|
|||||||
indexer
|
indexer
|
||||||
.vecs
|
.vecs
|
||||||
.scripts
|
.scripts
|
||||||
.unknown.to_tx_index
|
.unknown
|
||||||
|
.to_tx_index
|
||||||
.read_only_boxed_clone(),
|
.read_only_boxed_clone(),
|
||||||
|index, _| index,
|
|index, _| index,
|
||||||
),
|
),
|
||||||
@@ -250,7 +253,8 @@ impl Vecs {
|
|||||||
indexer
|
indexer
|
||||||
.vecs
|
.vecs
|
||||||
.scripts
|
.scripts
|
||||||
.op_return.to_tx_index
|
.op_return
|
||||||
|
.to_tx_index
|
||||||
.read_only_boxed_clone(),
|
.read_only_boxed_clone(),
|
||||||
|index, _| index,
|
|index, _| index,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30,
|
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30, Month1, Month3,
|
||||||
Month1, Month3, Month6, StoredU64, Version, Week1, Year1, Year10,
|
Month6, StoredU64, Version, Week1, Year1, Year10,
|
||||||
};
|
};
|
||||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};
|
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, Rw, StorageMode};
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ use vecdb::{CachedVec, Database, Exit, ReadableVec, Rw, StorageMode};
|
|||||||
use crate::internal::db_utils::{finalize_db, open_db};
|
use crate::internal::db_utils::{finalize_db, open_db};
|
||||||
|
|
||||||
pub use addr::Vecs as AddrVecs;
|
pub use addr::Vecs as AddrVecs;
|
||||||
pub use timestamp::Timestamps;
|
|
||||||
pub use cached_mappings::CachedMappings;
|
pub use cached_mappings::CachedMappings;
|
||||||
pub use day1::Vecs as Day1Vecs;
|
pub use day1::Vecs as Day1Vecs;
|
||||||
pub use day3::Vecs as Day3Vecs;
|
pub use day3::Vecs as Day3Vecs;
|
||||||
@@ -50,6 +49,7 @@ pub use minute30::Vecs as Minute30Vecs;
|
|||||||
pub use month1::Vecs as Month1Vecs;
|
pub use month1::Vecs as Month1Vecs;
|
||||||
pub use month3::Vecs as Month3Vecs;
|
pub use month3::Vecs as Month3Vecs;
|
||||||
pub use month6::Vecs as Month6Vecs;
|
pub use month6::Vecs as Month6Vecs;
|
||||||
|
pub use timestamp::Timestamps;
|
||||||
pub use tx_index::Vecs as TxIndexVecs;
|
pub use tx_index::Vecs as TxIndexVecs;
|
||||||
pub use txin_index::Vecs as TxInIndexVecs;
|
pub use txin_index::Vecs as TxInIndexVecs;
|
||||||
pub use txout_index::Vecs as TxOutIndexVecs;
|
pub use txout_index::Vecs as TxOutIndexVecs;
|
||||||
@@ -190,20 +190,10 @@ impl Vecs {
|
|||||||
|
|
||||||
self.compute_timestamp_mappings(&starting_indexes, exit)?;
|
self.compute_timestamp_mappings(&starting_indexes, exit)?;
|
||||||
|
|
||||||
let starting_day1 = self.compute_calendar_mappings(
|
let starting_day1 =
|
||||||
indexer,
|
self.compute_calendar_mappings(indexer, &starting_indexes, prev_height, exit)?;
|
||||||
&starting_indexes,
|
|
||||||
prev_height,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.compute_period_vecs(
|
self.compute_period_vecs(indexer, &starting_indexes, prev_height, starting_day1, exit)?;
|
||||||
indexer,
|
|
||||||
&starting_indexes,
|
|
||||||
prev_height,
|
|
||||||
starting_day1,
|
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.timestamp.compute_per_resolution(
|
self.timestamp.compute_per_resolution(
|
||||||
indexer,
|
indexer,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use brk_error::Result;
|
|||||||
use brk_types::{Bitcoin, Dollars, Indexes, StoredF32};
|
use brk_types::{Bitcoin, Dollars, Indexes, StoredF32};
|
||||||
use vecdb::Exit;
|
use vecdb::Exit;
|
||||||
|
|
||||||
use super::{gini, Vecs};
|
use super::{Vecs, gini};
|
||||||
use crate::{distribution, internal::RatioDollarsBp32, market, mining, transactions};
|
use crate::{distribution, internal::RatioDollarsBp32, market, mining, transactions};
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
@@ -126,22 +126,20 @@ impl Vecs {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Supply-Adjusted Dormancy = dormancy / circulating_supply_btc
|
// Supply-Adjusted Dormancy = dormancy / circulating_supply_btc
|
||||||
self.dormancy.supply_adjusted
|
self.dormancy.supply_adjusted.height.compute_transform2(
|
||||||
.height
|
starting_indexes.height,
|
||||||
.compute_transform2(
|
&all_activity.dormancy._24h.height,
|
||||||
starting_indexes.height,
|
supply_total_sats,
|
||||||
&all_activity.dormancy._24h.height,
|
|(i, dormancy, supply_sats, ..)| {
|
||||||
supply_total_sats,
|
let supply = f64::from(Bitcoin::from(supply_sats));
|
||||||
|(i, dormancy, supply_sats, ..)| {
|
if supply == 0.0 {
|
||||||
let supply = f64::from(Bitcoin::from(supply_sats));
|
(i, StoredF32::from(0.0f32))
|
||||||
if supply == 0.0 {
|
} else {
|
||||||
(i, StoredF32::from(0.0f32))
|
(i, StoredF32::from((f64::from(dormancy) / supply) as f32))
|
||||||
} else {
|
}
|
||||||
(i, StoredF32::from((f64::from(dormancy) / supply) as f32))
|
},
|
||||||
}
|
exit,
|
||||||
},
|
)?;
|
||||||
exit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Stock-to-Flow: supply / annual_issuance
|
// Stock-to-Flow: supply / annual_issuance
|
||||||
// annual_issuance ≈ subsidy_per_block × 52560 (blocks/year)
|
// annual_issuance ≈ subsidy_per_block × 52560 (blocks/year)
|
||||||
@@ -154,9 +152,10 @@ impl Vecs {
|
|||||||
if annual_flow == 0.0 {
|
if annual_flow == 0.0 {
|
||||||
(i, StoredF32::from(0.0f32))
|
(i, StoredF32::from(0.0f32))
|
||||||
} else {
|
} else {
|
||||||
(i, StoredF32::from(
|
(
|
||||||
(supply_sats.as_u128() as f64 / annual_flow) as f32,
|
i,
|
||||||
))
|
StoredF32::from((supply_sats.as_u128() as f64 / annual_flow) as f32),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exit,
|
exit,
|
||||||
@@ -180,26 +179,25 @@ impl Vecs {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Seller Exhaustion Constant: % supply_in_profit × 30d_volatility
|
// Seller Exhaustion Constant: % supply_in_profit × 30d_volatility
|
||||||
self.seller_exhaustion
|
self.seller_exhaustion.height.compute_transform3(
|
||||||
.height
|
starting_indexes.height,
|
||||||
.compute_transform3(
|
&all_metrics.supply.in_profit.sats.height,
|
||||||
starting_indexes.height,
|
&market.volatility._1m.height,
|
||||||
&all_metrics.supply.in_profit.sats.height,
|
supply_total_sats,
|
||||||
&market.volatility._1m.height,
|
|(i, profit_sats, volatility, total_sats, ..)| {
|
||||||
supply_total_sats,
|
let total = total_sats.as_u128() as f64;
|
||||||
|(i, profit_sats, volatility, total_sats, ..)| {
|
if total == 0.0 {
|
||||||
let total = total_sats.as_u128() as f64;
|
(i, StoredF32::from(0.0f32))
|
||||||
if total == 0.0 {
|
} else {
|
||||||
(i, StoredF32::from(0.0f32))
|
let pct_in_profit = profit_sats.as_u128() as f64 / total;
|
||||||
} else {
|
(
|
||||||
let pct_in_profit = profit_sats.as_u128() as f64 / total;
|
i,
|
||||||
(i, StoredF32::from(
|
StoredF32::from((pct_in_profit * f64::from(volatility)) as f32),
|
||||||
(pct_in_profit * f64::from(volatility)) as f32,
|
)
|
||||||
))
|
}
|
||||||
}
|
},
|
||||||
},
|
exit,
|
||||||
exit,
|
)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let exit = exit.clone();
|
let exit = exit.clone();
|
||||||
self.db.run_bg(move |db| {
|
self.db.run_bg(move |db| {
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ use brk_types::Version;
|
|||||||
use super::{Vecs, realized_envelope::RealizedEnvelope};
|
use super::{Vecs, realized_envelope::RealizedEnvelope};
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{PerBlock, PercentPerBlock, RatioPerBlock, db_utils::{finalize_db, open_db}},
|
internal::{
|
||||||
|
PerBlock, PercentPerBlock, RatioPerBlock,
|
||||||
|
db_utils::{finalize_db, open_db},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const VERSION: Version = Version::new(1);
|
const VERSION: Version = Version::new(1);
|
||||||
@@ -35,8 +38,7 @@ impl Vecs {
|
|||||||
flow: PerBlock::forced_import(&db, "dormancy_flow", v, indexes)?,
|
flow: PerBlock::forced_import(&db, "dormancy_flow", v, indexes)?,
|
||||||
};
|
};
|
||||||
let stock_to_flow = PerBlock::forced_import(&db, "stock_to_flow", v, indexes)?;
|
let stock_to_flow = PerBlock::forced_import(&db, "stock_to_flow", v, indexes)?;
|
||||||
let seller_exhaustion =
|
let seller_exhaustion = PerBlock::forced_import(&db, "seller_exhaustion", v, indexes)?;
|
||||||
PerBlock::forced_import(&db, "seller_exhaustion", v, indexes)?;
|
|
||||||
|
|
||||||
let realized_envelope = RealizedEnvelope::forced_import(&db, v, indexes)?;
|
let realized_envelope = RealizedEnvelope::forced_import(&db, v, indexes)?;
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ use brk_types::{Cents, Height, Indexes, StoredI8, Version};
|
|||||||
use vecdb::{AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, WritableVec};
|
use vecdb::{AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cointime,
|
cointime, distribution, indexes,
|
||||||
distribution,
|
|
||||||
indexes,
|
|
||||||
internal::{PerBlock, Price, RatioPerBlockPercentiles},
|
internal::{PerBlock, Price, RatioPerBlockPercentiles},
|
||||||
prices,
|
prices,
|
||||||
};
|
};
|
||||||
@@ -82,16 +80,48 @@ impl RealizedEnvelope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lower percentiles: max across all models (tightest lower bound)
|
// Lower percentiles: max across all models (tightest lower bound)
|
||||||
self.pct0_5.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct0_5), exit)?;
|
self.pct0_5.cents.height.compute_max_of_others(
|
||||||
self.pct1.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct1), exit)?;
|
starting_indexes.height,
|
||||||
self.pct2.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct2), exit)?;
|
&sources!(pct0_5),
|
||||||
self.pct5.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct5), exit)?;
|
exit,
|
||||||
|
)?;
|
||||||
|
self.pct1.cents.height.compute_max_of_others(
|
||||||
|
starting_indexes.height,
|
||||||
|
&sources!(pct1),
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
self.pct2.cents.height.compute_max_of_others(
|
||||||
|
starting_indexes.height,
|
||||||
|
&sources!(pct2),
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
self.pct5.cents.height.compute_max_of_others(
|
||||||
|
starting_indexes.height,
|
||||||
|
&sources!(pct5),
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Upper percentiles: min across all models (tightest upper bound)
|
// Upper percentiles: min across all models (tightest upper bound)
|
||||||
self.pct95.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct95), exit)?;
|
self.pct95.cents.height.compute_min_of_others(
|
||||||
self.pct98.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct98), exit)?;
|
starting_indexes.height,
|
||||||
self.pct99.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct99), exit)?;
|
&sources!(pct95),
|
||||||
self.pct99_5.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct99_5), exit)?;
|
exit,
|
||||||
|
)?;
|
||||||
|
self.pct98.cents.height.compute_min_of_others(
|
||||||
|
starting_indexes.height,
|
||||||
|
&sources!(pct98),
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
self.pct99.cents.height.compute_min_of_others(
|
||||||
|
starting_indexes.height,
|
||||||
|
&sources!(pct99),
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
self.pct99_5.cents.height.compute_min_of_others(
|
||||||
|
starting_indexes.height,
|
||||||
|
&sources!(pct99_5),
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
|
||||||
let spot = &prices.spot.cents.height;
|
let spot = &prices.spot.cents.height;
|
||||||
|
|
||||||
@@ -121,10 +151,15 @@ impl RealizedEnvelope {
|
|||||||
&self.pct99_5.cents.height,
|
&self.pct99_5.cents.height,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dep_version: Version = bands.iter().map(|b| b.version()).sum::<Version>() + spot.version();
|
let dep_version: Version =
|
||||||
|
bands.iter().map(|b| b.version()).sum::<Version>() + spot.version();
|
||||||
|
|
||||||
self.index.height.validate_computed_version_or_reset(dep_version)?;
|
self.index
|
||||||
self.index.height.truncate_if_needed(starting_indexes.height)?;
|
.height
|
||||||
|
.validate_computed_version_or_reset(dep_version)?;
|
||||||
|
self.index
|
||||||
|
.height
|
||||||
|
.truncate_if_needed(starting_indexes.height)?;
|
||||||
|
|
||||||
self.index.height.repeat_until_complete(exit, |vec| {
|
self.index.height.repeat_until_complete(exit, |vec| {
|
||||||
let skip = vec.len();
|
let skip = vec.len();
|
||||||
@@ -142,14 +177,30 @@ impl RealizedEnvelope {
|
|||||||
let price = spot_batch[j];
|
let price = spot_batch[j];
|
||||||
let mut score: i8 = 0;
|
let mut score: i8 = 0;
|
||||||
|
|
||||||
if price < b[3][j] { score -= 1; }
|
if price < b[3][j] {
|
||||||
if price < b[2][j] { score -= 1; }
|
score -= 1;
|
||||||
if price < b[1][j] { score -= 1; }
|
}
|
||||||
if price < b[0][j] { score -= 1; }
|
if price < b[2][j] {
|
||||||
if price > b[4][j] { score += 1; }
|
score -= 1;
|
||||||
if price > b[5][j] { score += 1; }
|
}
|
||||||
if price > b[6][j] { score += 1; }
|
if price < b[1][j] {
|
||||||
if price > b[7][j] { score += 1; }
|
score -= 1;
|
||||||
|
}
|
||||||
|
if price < b[0][j] {
|
||||||
|
score -= 1;
|
||||||
|
}
|
||||||
|
if price > b[4][j] {
|
||||||
|
score += 1;
|
||||||
|
}
|
||||||
|
if price > b[5][j] {
|
||||||
|
score += 1;
|
||||||
|
}
|
||||||
|
if price > b[6][j] {
|
||||||
|
score += 1;
|
||||||
|
}
|
||||||
|
if price > b[7][j] {
|
||||||
|
score += 1;
|
||||||
|
}
|
||||||
|
|
||||||
vec.push(StoredI8::new(score));
|
vec.push(StoredI8::new(score));
|
||||||
}
|
}
|
||||||
@@ -182,8 +233,12 @@ impl RealizedEnvelope {
|
|||||||
.sum::<Version>()
|
.sum::<Version>()
|
||||||
+ spot.version();
|
+ spot.version();
|
||||||
|
|
||||||
self.score.height.validate_computed_version_or_reset(dep_version)?;
|
self.score
|
||||||
self.score.height.truncate_if_needed(starting_indexes.height)?;
|
.height
|
||||||
|
.validate_computed_version_or_reset(dep_version)?;
|
||||||
|
self.score
|
||||||
|
.height
|
||||||
|
.truncate_if_needed(starting_indexes.height)?;
|
||||||
|
|
||||||
self.score.height.repeat_until_complete(exit, |vec| {
|
self.score.height.repeat_until_complete(exit, |vec| {
|
||||||
let skip = vec.len();
|
let skip = vec.len();
|
||||||
@@ -233,14 +288,30 @@ impl RealizedEnvelope {
|
|||||||
let mut total: i8 = 0;
|
let mut total: i8 = 0;
|
||||||
|
|
||||||
for model in &bands {
|
for model in &bands {
|
||||||
if price < model[3][j] { total -= 1; }
|
if price < model[3][j] {
|
||||||
if price < model[2][j] { total -= 1; }
|
total -= 1;
|
||||||
if price < model[1][j] { total -= 1; }
|
}
|
||||||
if price < model[0][j] { total -= 1; }
|
if price < model[2][j] {
|
||||||
if price > model[4][j] { total += 1; }
|
total -= 1;
|
||||||
if price > model[5][j] { total += 1; }
|
}
|
||||||
if price > model[6][j] { total += 1; }
|
if price < model[1][j] {
|
||||||
if price > model[7][j] { total += 1; }
|
total -= 1;
|
||||||
|
}
|
||||||
|
if price < model[0][j] {
|
||||||
|
total -= 1;
|
||||||
|
}
|
||||||
|
if price > model[4][j] {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
if price > model[5][j] {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
if price > model[6][j] {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
if price > model[7][j] {
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec.push(StoredI8::new(total));
|
vec.push(StoredI8::new(total));
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ impl Vecs {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.db.sync_bg_tasks()?;
|
self.db.sync_bg_tasks()?;
|
||||||
|
|
||||||
self.spent
|
self.spent.compute(indexer, starting_indexes, exit)?;
|
||||||
.compute(indexer, starting_indexes, exit)?;
|
|
||||||
self.count
|
self.count
|
||||||
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
|
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ use brk_types::Version;
|
|||||||
use vecdb::Database;
|
use vecdb::Database;
|
||||||
|
|
||||||
use super::Vecs;
|
use super::Vecs;
|
||||||
use crate::{indexes, internal::{CachedWindowStarts, PerBlockAggregated}};
|
use crate::{
|
||||||
|
indexes,
|
||||||
|
internal::{CachedWindowStarts, PerBlockAggregated},
|
||||||
|
};
|
||||||
|
|
||||||
impl Vecs {
|
impl Vecs {
|
||||||
pub(crate) fn forced_import(
|
pub(crate) fn forced_import(
|
||||||
|
|||||||
@@ -7,6 +7,4 @@ use vecdb::{Rw, StorageMode};
|
|||||||
use crate::internal::PerBlockAggregated;
|
use crate::internal::PerBlockAggregated;
|
||||||
|
|
||||||
#[derive(Deref, DerefMut, Traversable)]
|
#[derive(Deref, DerefMut, Traversable)]
|
||||||
pub struct Vecs<M: StorageMode = Rw>(
|
pub struct Vecs<M: StorageMode = Rw>(#[traversable(flatten)] pub PerBlockAggregated<StoredU64, M>);
|
||||||
#[traversable(flatten)] pub PerBlockAggregated<StoredU64, M>,
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ use brk_types::Version;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
indexes,
|
indexes,
|
||||||
internal::{CachedWindowStarts, db_utils::{finalize_db, open_db}},
|
internal::{
|
||||||
|
CachedWindowStarts,
|
||||||
|
db_utils::{finalize_db, open_db},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{CountVecs, SpentVecs, Vecs};
|
use super::{CountVecs, SpentVecs, Vecs};
|
||||||
|
|||||||
@@ -146,5 +146,4 @@ mod tests {
|
|||||||
assert_eq!(ep.count(), 0);
|
assert_eq!(ep.count(), 0);
|
||||||
assert_eq!(quantile(&ep, 0.5), 0);
|
assert_eq!(quantile(&ep, 0.5), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,8 +137,7 @@ where
|
|||||||
} else {
|
} else {
|
||||||
min_out.push(T::from(window.min()));
|
min_out.push(T::from(window.min()));
|
||||||
max_out.push(T::from(window.max()));
|
max_out.push(T::from(window.max()));
|
||||||
let [p10, p25, p50, p75, p90] =
|
let [p10, p25, p50, p75, p90] = window.percentiles(&[0.10, 0.25, 0.50, 0.75, 0.90]);
|
||||||
window.percentiles(&[0.10, 0.25, 0.50, 0.75, 0.90]);
|
|
||||||
p10_out.push(T::from(p10));
|
p10_out.push(T::from(p10));
|
||||||
p25_out.push(T::from(p25));
|
p25_out.push(T::from(p25));
|
||||||
median_out.push(T::from(p50));
|
median_out.push(T::from(p50));
|
||||||
@@ -165,13 +164,7 @@ where
|
|||||||
// Final flush
|
// Final flush
|
||||||
let _lock = exit.lock();
|
let _lock = exit.lock();
|
||||||
for v in [
|
for v in [
|
||||||
min_out,
|
min_out, max_out, p10_out, p25_out, median_out, p75_out, p90_out,
|
||||||
max_out,
|
|
||||||
p10_out,
|
|
||||||
p25_out,
|
|
||||||
median_out,
|
|
||||||
p75_out,
|
|
||||||
p90_out,
|
|
||||||
] {
|
] {
|
||||||
v.write()?;
|
v.write()?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30,
|
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Minute10, Minute30, Month1, Month3,
|
||||||
Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
Month6, Version, Week1, Year1, Year10,
|
||||||
};
|
};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user