diff --git a/crates/brk_bindgen/src/analysis/patterns.rs b/crates/brk_bindgen/src/analysis/patterns.rs index 1518c8f41..b64cf18a5 100644 --- a/crates/brk_bindgen/src/analysis/patterns.rs +++ b/crates/brk_bindgen/src/analysis/patterns.rs @@ -3,7 +3,7 @@ //! This module detects repeating tree structures and analyzes them //! using the bottom-up name deconstruction algorithm. -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use brk_types::{TreeNode, extract_json_type}; @@ -13,25 +13,25 @@ use crate::{PatternBaseResult, PatternField, StructuralPattern, to_pascal_case}; /// Context for pattern detection, holding all intermediate state. struct PatternContext { /// Maps field signatures to pattern names - signature_to_pattern: HashMap, String>, + signature_to_pattern: BTreeMap, String>, /// Counts how many times each signature appears - signature_counts: HashMap, usize>, + signature_counts: BTreeMap, usize>, /// Maps normalized signatures to pattern names (for naming consistency) - normalized_to_name: HashMap, String>, + normalized_to_name: BTreeMap, String>, /// Counts pattern name usage (for unique naming) - name_counts: HashMap, + name_counts: BTreeMap, /// Maps signatures to their child field lists - signature_to_child_fields: HashMap, Vec>>, + signature_to_child_fields: BTreeMap, Vec>>, } impl PatternContext { fn new() -> Self { Self { - signature_to_pattern: HashMap::new(), - signature_counts: HashMap::new(), - normalized_to_name: HashMap::new(), - name_counts: HashMap::new(), - signature_to_child_fields: HashMap::new(), + signature_to_pattern: BTreeMap::new(), + signature_counts: BTreeMap::new(), + normalized_to_name: BTreeMap::new(), + name_counts: BTreeMap::new(), + signature_to_child_fields: BTreeMap::new(), } } } @@ -45,9 +45,9 @@ pub fn detect_structural_patterns( tree: &TreeNode, ) -> ( Vec, - HashMap, String>, - HashMap, String>, - HashMap, + BTreeMap, String>, + BTreeMap, String>, + BTreeMap, ) { let mut ctx = PatternContext::new(); resolve_branch_patterns(tree, "root", &mut ctx); @@ -90,7 +90,7 @@ pub fn detect_structural_patterns( patterns.extend(generic_patterns); // Build pattern lookup for mode analysis (patterns appearing 2+ times) - let mut pattern_lookup: HashMap, String> = HashMap::new(); + let mut pattern_lookup: BTreeMap, String> = BTreeMap::new(); for (sig, name) in &ctx.signature_to_pattern { if ctx.signature_counts.get(sig).copied().unwrap_or(0) >= 2 { pattern_lookup.insert(sig.clone(), name.clone()); @@ -110,29 +110,30 @@ pub fn detect_structural_patterns( /// Detect generic patterns by grouping signatures by their normalized form. fn detect_generic_patterns( - signature_to_pattern: &HashMap, String>, + signature_to_pattern: &BTreeMap, String>, ) -> ( Vec, - HashMap, String>, - HashMap, String>, + BTreeMap, String>, + BTreeMap, String>, ) { - let mut normalized_groups: HashMap< + let mut normalized_groups: BTreeMap< Vec, Vec<(Vec, String, String)>, - > = HashMap::new(); + > = BTreeMap::new(); for (fields, name) in signature_to_pattern { if let Some((normalized, extracted_type)) = normalize_fields_for_generic(fields) { - normalized_groups - .entry(normalized) - .or_default() - .push((fields.clone(), name.clone(), extracted_type)); + normalized_groups.entry(normalized).or_default().push(( + fields.clone(), + name.clone(), + extracted_type, + )); } } let mut patterns = Vec::new(); - let mut pattern_mappings: HashMap, String> = HashMap::new(); - let mut type_mappings: HashMap, String> = HashMap::new(); + let mut pattern_mappings: BTreeMap, String> = BTreeMap::new(); + let mut type_mappings: BTreeMap, String> = BTreeMap::new(); for (normalized_fields, group) in normalized_groups { if group.len() >= 2 { @@ -205,7 +206,10 @@ fn normalize_fields_for_generic(fields: &[PatternField]) -> Option<(Vec Vec { } /// Generate a unique pattern name. -fn generate_pattern_name(field_name: &str, name_counts: &mut HashMap) -> String { +fn generate_pattern_name(field_name: &str, name_counts: &mut BTreeMap) -> String { let pascal = to_pascal_case(field_name); let sanitized = if pascal.chars().next().is_some_and(|c| c.is_ascii_digit()) { format!("_{}", pascal) diff --git a/crates/brk_bindgen/src/analysis/positions.rs b/crates/brk_bindgen/src/analysis/positions.rs index 54690d777..d70f2b483 100644 --- a/crates/brk_bindgen/src/analysis/positions.rs +++ b/crates/brk_bindgen/src/analysis/positions.rs @@ -4,7 +4,7 @@ //! suffix mode (fields append to acc) or prefix mode (fields prepend to acc), //! and extracts the field parts (relatives or prefixes) for code generation. -use std::collections::HashMap; +use std::collections::BTreeMap; use brk_types::TreeNode; @@ -18,7 +18,7 @@ struct InstanceAnalysis { base: String, /// For suffix mode: field -> relative name /// For prefix mode: field -> prefix - field_parts: HashMap, + field_parts: BTreeMap, /// Whether this instance appears to be suffix mode is_suffix_mode: bool, } @@ -34,12 +34,12 @@ struct InstanceAnalysis { pub fn analyze_pattern_modes( tree: &TreeNode, patterns: &mut [StructuralPattern], - pattern_lookup: &HashMap, String>, -) -> HashMap { + pattern_lookup: &BTreeMap, String>, +) -> BTreeMap { // Collect analyses from all instances, keyed by pattern name - let mut all_analyses: HashMap> = HashMap::new(); + let mut all_analyses: BTreeMap> = BTreeMap::new(); // Also collect base results for each node, keyed by tree path - let mut node_bases: HashMap = HashMap::new(); + let mut node_bases: BTreeMap = BTreeMap::new(); // Bottom-up traversal collect_instance_analyses(tree, "", pattern_lookup, &mut all_analyses, &mut node_bases); @@ -61,9 +61,9 @@ pub fn analyze_pattern_modes( fn collect_instance_analyses( node: &TreeNode, path: &str, - pattern_lookup: &HashMap, String>, - all_analyses: &mut HashMap>, - node_bases: &mut HashMap, + pattern_lookup: &BTreeMap, String>, + all_analyses: &mut BTreeMap>, + node_bases: &mut BTreeMap, ) -> Option { match node { TreeNode::Leaf(leaf) => { @@ -72,7 +72,7 @@ fn collect_instance_analyses( } TreeNode::Branch(children) => { // First, process all children recursively (bottom-up) - let mut child_bases: HashMap = HashMap::new(); + let mut child_bases: BTreeMap = BTreeMap::new(); for (field_name, child_node) in children { let child_path = build_child_path(path, field_name); if let Some(base) = collect_instance_analyses( @@ -122,13 +122,13 @@ fn collect_instance_analyses( } /// Analyze a single pattern instance from its child bases. -fn analyze_instance(child_bases: &HashMap) -> InstanceAnalysis { +fn analyze_instance(child_bases: &BTreeMap) -> InstanceAnalysis { let bases: Vec<&str> = child_bases.values().map(|s| s.as_str()).collect(); // Try suffix mode first: look for common prefix among children if let Some(common_prefix) = find_common_prefix(&bases) { let base = common_prefix.trim_end_matches('_').to_string(); - let mut field_parts = HashMap::new(); + let mut field_parts = BTreeMap::new(); for (field_name, child_base) in child_bases { // Relative = child_base with common prefix stripped @@ -154,7 +154,7 @@ fn analyze_instance(child_bases: &HashMap) -> InstanceAnalysis { // Try prefix mode: look for common suffix among children if let Some(common_suffix) = find_common_suffix(&bases) { let base = common_suffix.trim_start_matches('_').to_string(); - let mut field_parts = HashMap::new(); + let mut field_parts = BTreeMap::new(); for (field_name, child_base) in child_bases { // Prefix = child_base with common suffix stripped, normalized to end with _ @@ -214,8 +214,8 @@ fn determine_pattern_mode( } // Find the most common field_parts within the majority group - // Convert to sorted Vec for comparison since HashMap isn't hashable - let mut parts_counts: HashMap, usize> = HashMap::new(); + // Convert to sorted Vec for comparison since BTreeMap isn't hashable + let mut parts_counts: BTreeMap, usize> = BTreeMap::new(); for analysis in &majority_instances { let mut sorted: Vec<_> = analysis .field_parts @@ -227,7 +227,7 @@ fn determine_pattern_mode( } let (best_parts_vec, _count) = parts_counts.into_iter().max_by_key(|(_, count)| *count)?; - let best_parts: HashMap = best_parts_vec.into_iter().collect(); + let best_parts: BTreeMap = best_parts_vec.into_iter().collect(); // Verify all required fields have parts for field in fields { @@ -255,7 +255,7 @@ mod tests { #[test] fn test_analyze_instance_suffix_mode() { - let mut child_bases = HashMap::new(); + let mut child_bases = BTreeMap::new(); child_bases.insert("max".to_string(), "lth_cost_basis_max".to_string()); child_bases.insert("min".to_string(), "lth_cost_basis_min".to_string()); child_bases.insert("percentiles".to_string(), "lth_cost_basis".to_string()); @@ -276,7 +276,7 @@ mod tests { fn test_analyze_instance_prefix_mode() { // Period-prefixed metrics like "1y_lump_sum_stack", "1m_lump_sum_stack" // share a common suffix "_lump_sum_stack" with different period prefixes - let mut child_bases = HashMap::new(); + let mut child_bases = BTreeMap::new(); child_bases.insert("_1y".to_string(), "1y_lump_sum_stack".to_string()); child_bases.insert("_1m".to_string(), "1m_lump_sum_stack".to_string()); child_bases.insert("_1w".to_string(), "1w_lump_sum_stack".to_string()); @@ -293,7 +293,7 @@ mod tests { #[test] fn test_analyze_instance_root_suffix() { // At root level with suffix naming convention - let mut child_bases = HashMap::new(); + let mut child_bases = BTreeMap::new(); child_bases.insert("max".to_string(), "cost_basis_max".to_string()); child_bases.insert("min".to_string(), "cost_basis_min".to_string()); child_bases.insert("percentiles".to_string(), "cost_basis".to_string()); diff --git a/crates/brk_bindgen/src/analysis/tree.rs b/crates/brk_bindgen/src/analysis/tree.rs index cc22e089a..ecfee4a5b 100644 --- a/crates/brk_bindgen/src/analysis/tree.rs +++ b/crates/brk_bindgen/src/analysis/tree.rs @@ -3,7 +3,7 @@ //! This module provides utilities for working with the TreeNode structure, //! including leaf name extraction and index pattern detection. -use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use brk_types::{Index, TreeNode, extract_json_type}; @@ -28,7 +28,7 @@ fn get_shortest_leaf_name(node: &TreeNode) -> Option { /// Get the field signature for a branch node's children. pub fn get_node_fields( children: &BTreeMap, - pattern_lookup: &HashMap, String>, + pattern_lookup: &BTreeMap, String>, ) -> Vec { let mut fields: Vec = children .iter() @@ -117,7 +117,7 @@ pub struct PatternBaseResult { pub is_suffix_mode: bool, /// The field parts (suffix in suffix mode, prefix in prefix mode) for each field. /// Used to check if instance field parts match the pattern's field parts. - pub field_parts: HashMap, + pub field_parts: BTreeMap, } impl PatternBaseResult { @@ -128,7 +128,7 @@ impl PatternBaseResult { base: String::new(), has_outlier: true, is_suffix_mode: true, - field_parts: HashMap::new(), + field_parts: BTreeMap::new(), } } @@ -139,7 +139,7 @@ impl PatternBaseResult { base: String::new(), has_outlier: false, is_suffix_mode: true, - field_parts: HashMap::new(), + field_parts: BTreeMap::new(), } } } @@ -200,7 +200,7 @@ struct FindBaseResult { base: String, has_outlier: bool, is_suffix_mode: bool, - field_parts: HashMap, + field_parts: BTreeMap, } /// Try to find a common base from child names using prefix/suffix detection. @@ -214,7 +214,7 @@ fn try_find_base( // Try common prefix first (suffix mode) if let Some(prefix) = find_common_prefix(&leaf_names) { let base = prefix.trim_end_matches('_').to_string(); - let mut field_parts = HashMap::new(); + let mut field_parts = BTreeMap::new(); for (field_name, leaf_name) in child_names { // Compute the suffix part for this field let suffix = if leaf_name == &base { @@ -238,7 +238,7 @@ fn try_find_base( // Try common suffix (prefix mode) if let Some(suffix) = find_common_suffix(&leaf_names) { let base = suffix.trim_start_matches('_').to_string(); - let mut field_parts = HashMap::new(); + let mut field_parts = BTreeMap::new(); for (field_name, leaf_name) in child_names { // Compute the prefix part for this field, normalized to end with _ let prefix_part = leaf_name @@ -300,7 +300,7 @@ pub fn infer_accumulated_name(parent_acc: &str, field_name: &str, descendant_lea pub fn get_fields_with_child_info( children: &BTreeMap, parent_name: &str, - pattern_lookup: &HashMap, String>, + pattern_lookup: &BTreeMap, String>, ) -> Vec<(PatternField, Option>)> { children .iter() diff --git a/crates/brk_bindgen/src/generate/tree.rs b/crates/brk_bindgen/src/generate/tree.rs index 0df4a1942..0571b7595 100644 --- a/crates/brk_bindgen/src/generate/tree.rs +++ b/crates/brk_bindgen/src/generate/tree.rs @@ -1,10 +1,12 @@ //! Shared tree generation helpers. -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; use brk_types::TreeNode; -use crate::{ClientMetadata, PatternBaseResult, PatternField, child_type_name, get_fields_with_child_info}; +use crate::{ + ClientMetadata, PatternBaseResult, PatternField, child_type_name, get_fields_with_child_info, +}; /// Build a child path by appending a child name to a parent path. /// Uses "/" as separator. If parent is empty, returns just the child name. @@ -53,9 +55,9 @@ pub fn prepare_tree_node<'a>( node: &'a TreeNode, name: &str, path: &str, - pattern_lookup: &HashMap, String>, + pattern_lookup: &BTreeMap, String>, metadata: &ClientMetadata, - generated: &mut HashSet, + generated: &mut BTreeSet, ) -> Option> { let TreeNode::Branch(branch_children) = node else { return None; @@ -127,8 +129,8 @@ pub fn prepare_tree_node<'a>( // should_inline determines if we generate an inline struct type // We inline if: it's a branch AND (doesn't match any pattern OR pattern incompatible OR has outlier) - let should_inline = - !is_leaf && (!matches_any_pattern || !pattern_compatible || base_result.has_outlier); + let should_inline = !is_leaf + && (!matches_any_pattern || !pattern_compatible || base_result.has_outlier); // Inline type name (only used when should_inline is true) let inline_type_name = if should_inline { diff --git a/crates/brk_bindgen/src/generators/javascript/mod.rs b/crates/brk_bindgen/src/generators/javascript/mod.rs index 3c6afce97..4b5625405 100644 --- a/crates/brk_bindgen/src/generators/javascript/mod.rs +++ b/crates/brk_bindgen/src/generators/javascript/mod.rs @@ -11,6 +11,7 @@ use std::{fmt::Write, fs, io, path::Path}; use serde_json::json; +use super::write_if_changed; use crate::{ClientMetadata, Endpoint, TypeSchemas, VERSION}; /// Generate JavaScript + JSDoc client from metadata and OpenAPI endpoints. @@ -34,7 +35,7 @@ pub fn generate_javascript_client( tree::generate_tree_typedefs(&mut output, &metadata.catalog, metadata); tree::generate_main_client(&mut output, &metadata.catalog, metadata, endpoints); - fs::write(output_path, output)?; + write_if_changed(output_path, &output)?; // Update package.json version if it exists in the same directory if let Some(parent) = output_path.parent() { @@ -59,7 +60,7 @@ fn update_package_json_version(package_json_path: &Path) -> io::Result<()> { let updated = serde_json::to_string_pretty(&package) .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - fs::write(package_json_path, updated + "\n")?; + write_if_changed(package_json_path, &(updated + "\n"))?; Ok(()) } diff --git a/crates/brk_bindgen/src/generators/javascript/tree.rs b/crates/brk_bindgen/src/generators/javascript/tree.rs index 49e2bae4f..b56a7fafb 100644 --- a/crates/brk_bindgen/src/generators/javascript/tree.rs +++ b/crates/brk_bindgen/src/generators/javascript/tree.rs @@ -1,6 +1,6 @@ //! JavaScript tree structure generation. -use std::collections::HashSet; +use std::collections::BTreeSet; use std::fmt::Write; use brk_types::TreeNode; @@ -18,7 +18,7 @@ pub fn generate_tree_typedefs(output: &mut String, catalog: &TreeNode, metadata: writeln!(output, "// Catalog tree typedefs\n").unwrap(); let pattern_lookup = metadata.pattern_lookup(); - let mut generated = HashSet::new(); + let mut generated = BTreeSet::new(); generate_tree_typedef( output, "MetricsTree", @@ -35,9 +35,9 @@ fn generate_tree_typedef( name: &str, path: &str, node: &TreeNode, - pattern_lookup: &std::collections::HashMap, String>, + pattern_lookup: &std::collections::BTreeMap, String>, metadata: &ClientMetadata, - generated: &mut HashSet, + generated: &mut BTreeSet, ) { let Some(ctx) = prepare_tree_node(node, name, path, pattern_lookup, metadata, generated) else { return; @@ -124,7 +124,7 @@ pub fn generate_main_client( writeln!(output, " */").unwrap(); writeln!(output, " _buildTree(basePath) {{").unwrap(); writeln!(output, " return {{").unwrap(); - let mut generated = HashSet::new(); + let mut generated = BTreeSet::new(); generate_tree_initializer( output, catalog, @@ -178,9 +178,9 @@ fn generate_tree_initializer( name: &str, path: &str, indent: usize, - pattern_lookup: &std::collections::HashMap, String>, + pattern_lookup: &std::collections::BTreeMap, String>, metadata: &ClientMetadata, - generated: &mut HashSet, + generated: &mut BTreeSet, ) { let indent_str = " ".repeat(indent); diff --git a/crates/brk_bindgen/src/generators/mod.rs b/crates/brk_bindgen/src/generators/mod.rs index ccfda6f91..abed3ccc7 100644 --- a/crates/brk_bindgen/src/generators/mod.rs +++ b/crates/brk_bindgen/src/generators/mod.rs @@ -7,7 +7,7 @@ //! - `api.rs` - API method generation //! - `mod.rs` - Entry point -use std::fmt::Write; +use std::{fmt::Write, fs, io, path::Path}; pub mod javascript; pub mod python; @@ -41,3 +41,14 @@ pub fn normalize_return_type(return_type: &str) -> String { } result } + +/// Write content to a file only if it differs from existing content. +/// Preserves mtime when unchanged, avoiding unnecessary cargo rebuilds. +pub fn write_if_changed(path: &Path, content: &str) -> io::Result<()> { + if let Ok(existing) = fs::read_to_string(path) + && existing == content + { + return Ok(()); + } + fs::write(path, content) +} diff --git a/crates/brk_bindgen/src/generators/python/mod.rs b/crates/brk_bindgen/src/generators/python/mod.rs index 44c3538cc..b1c5bcb1c 100644 --- a/crates/brk_bindgen/src/generators/python/mod.rs +++ b/crates/brk_bindgen/src/generators/python/mod.rs @@ -7,8 +7,9 @@ pub mod client; pub mod tree; pub mod types; -use std::{fmt::Write, fs, io, path::Path}; +use std::{fmt::Write, io, path::Path}; +use super::write_if_changed; use crate::{ClientMetadata, Endpoint, TypeSchemas}; /// Generate Python client from metadata and OpenAPI endpoints. @@ -48,7 +49,7 @@ pub fn generate_python_client( tree::generate_tree_classes(&mut output, &metadata.catalog, metadata); api::generate_main_client(&mut output, endpoints); - fs::write(output_path, output)?; + write_if_changed(output_path, &output)?; Ok(()) } diff --git a/crates/brk_bindgen/src/generators/python/tree.rs b/crates/brk_bindgen/src/generators/python/tree.rs index 0fa7f3eae..2e64be0f5 100644 --- a/crates/brk_bindgen/src/generators/python/tree.rs +++ b/crates/brk_bindgen/src/generators/python/tree.rs @@ -1,6 +1,6 @@ //! Python tree structure generation. -use std::collections::HashSet; +use std::collections::BTreeSet; use std::fmt::Write; use brk_types::TreeNode; @@ -15,7 +15,7 @@ pub fn generate_tree_classes(output: &mut String, catalog: &TreeNode, metadata: writeln!(output, "# Metrics tree classes\n").unwrap(); let pattern_lookup = metadata.pattern_lookup(); - let mut generated = HashSet::new(); + let mut generated = BTreeSet::new(); generate_tree_class( output, "MetricsTree", @@ -33,9 +33,9 @@ fn generate_tree_class( name: &str, path: &str, node: &TreeNode, - pattern_lookup: &std::collections::HashMap, String>, + pattern_lookup: &std::collections::BTreeMap, String>, metadata: &ClientMetadata, - generated: &mut HashSet, + generated: &mut BTreeSet, ) { let Some(ctx) = prepare_tree_node(node, name, path, pattern_lookup, metadata, generated) else { return; @@ -74,7 +74,9 @@ fn generate_tree_class( if child.is_leaf { if let TreeNode::Leaf(leaf) = child.node { - generate_leaf_field(output, &syntax, "client", child.name, leaf, metadata, " "); + generate_leaf_field( + output, &syntax, "client", child.name, leaf, metadata, " ", + ); } } else if child.should_inline { // Inline class diff --git a/crates/brk_bindgen/src/generators/python/types.rs b/crates/brk_bindgen/src/generators/python/types.rs index de546e808..5a61a7b57 100644 --- a/crates/brk_bindgen/src/generators/python/types.rs +++ b/crates/brk_bindgen/src/generators/python/types.rs @@ -1,11 +1,14 @@ //! Python type definitions generation. -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Write; use serde_json::Value; -use crate::{TypeSchemas, escape_python_keyword, generators::MANUAL_GENERIC_TYPES, get_union_variants, ref_to_type_name}; +use crate::{ + TypeSchemas, escape_python_keyword, generators::MANUAL_GENERIC_TYPES, get_union_variants, + ref_to_type_name, +}; /// Generate type definitions from schemas. pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { @@ -32,7 +35,7 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { // Generate simple type aliases first // Quote references to TypedDicts since they're defined after - let typed_dict_set: HashSet<_> = typed_dicts.iter().cloned().collect(); + let typed_dict_set: BTreeSet<_> = typed_dicts.iter().cloned().collect(); for name in type_aliases { let schema = &schemas[&name]; let type_desc = schema.get("description").and_then(|d| d.as_str()); @@ -49,7 +52,10 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { for name in typed_dicts { let schema = &schemas[&name]; let type_desc = schema.get("description").and_then(|d| d.as_str()); - let props = schema.get("properties").and_then(|p| p.as_object()).unwrap(); + let props = schema + .get("properties") + .and_then(|p| p.as_object()) + .unwrap(); writeln!(output, "class {}(TypedDict):", name).unwrap(); @@ -100,9 +106,9 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { /// Types that reference other types (via $ref) must be defined after their dependencies. fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec { // Build dependency graph - let mut deps: HashMap> = HashMap::new(); + let mut deps: BTreeMap> = BTreeMap::new(); for (name, schema) in schemas { - let mut type_deps = HashSet::new(); + let mut type_deps = BTreeSet::new(); collect_schema_refs(schema, &mut type_deps); // Only keep deps that are in our schemas type_deps.retain(|d| schemas.contains_key(d)); @@ -110,7 +116,7 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec { } // Kahn's algorithm for topological sort - let mut in_degree: HashMap = HashMap::new(); + let mut in_degree: BTreeMap = BTreeMap::new(); for name in schemas.keys() { in_degree.insert(name.clone(), 0); } @@ -148,7 +154,7 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec { result.reverse(); // Add any types that weren't processed (e.g., due to circular refs or other edge cases) - let result_set: HashSet<_> = result.iter().cloned().collect(); + let result_set: BTreeSet<_> = result.iter().cloned().collect(); let mut missing: Vec<_> = schemas .keys() .filter(|k| !result_set.contains(*k)) @@ -161,7 +167,7 @@ fn topological_sort_schemas(schemas: &TypeSchemas) -> Vec { } /// Collect all type references ($ref) from a schema -fn collect_schema_refs(schema: &Value, refs: &mut HashSet) { +fn collect_schema_refs(schema: &Value, refs: &mut BTreeSet) { match schema { Value::Object(map) => { if let Some(ref_path) = map.get("$ref").and_then(|r| r.as_str()) @@ -215,7 +221,7 @@ fn json_type_to_python(ty: &str, schema: &Value, current_type: Option<&str>) -> pub fn schema_to_python_type( schema: &Value, current_type: Option<&str>, - quote_types: Option<&HashSet>, + quote_types: Option<&BTreeSet>, ) -> String { if let Some(all_of) = schema.get("allOf").and_then(|v| v.as_array()) { for item in all_of { @@ -230,8 +236,8 @@ pub fn schema_to_python_type( if let Some(ref_path) = schema.get("$ref").and_then(|r| r.as_str()) { let type_name = ref_to_type_name(ref_path).unwrap_or("Any"); // Quote self-references or types in quote_types set - let should_quote = current_type == Some(type_name) - || quote_types.is_some_and(|qt| qt.contains(type_name)); + let should_quote = + current_type == Some(type_name) || quote_types.is_some_and(|qt| qt.contains(type_name)); if should_quote { return format!("\"{}\"", type_name); } diff --git a/crates/brk_bindgen/src/generators/rust/mod.rs b/crates/brk_bindgen/src/generators/rust/mod.rs index 81a191164..174108018 100644 --- a/crates/brk_bindgen/src/generators/rust/mod.rs +++ b/crates/brk_bindgen/src/generators/rust/mod.rs @@ -7,8 +7,9 @@ pub mod client; pub mod tree; mod types; -use std::{fmt::Write, fs, io, path::Path}; +use std::{fmt::Write, io, path::Path}; +use super::write_if_changed; use crate::{ClientMetadata, Endpoint}; /// Generate Rust client from metadata and OpenAPI endpoints. @@ -38,7 +39,7 @@ pub fn generate_rust_client( tree::generate_tree(&mut output, &metadata.catalog, metadata); api::generate_main_client(&mut output, endpoints); - fs::write(output_path, output)?; + write_if_changed(output_path, &output)?; Ok(()) } diff --git a/crates/brk_bindgen/src/generators/rust/tree.rs b/crates/brk_bindgen/src/generators/rust/tree.rs index 1f2b6da01..aba64e61e 100644 --- a/crates/brk_bindgen/src/generators/rust/tree.rs +++ b/crates/brk_bindgen/src/generators/rust/tree.rs @@ -1,6 +1,6 @@ //! Rust tree structure generation. -use std::collections::HashSet; +use std::collections::BTreeSet; use std::fmt::Write; use brk_types::TreeNode; @@ -15,7 +15,7 @@ pub fn generate_tree(output: &mut String, catalog: &TreeNode, metadata: &ClientM writeln!(output, "// Metrics tree\n").unwrap(); let pattern_lookup = metadata.pattern_lookup(); - let mut generated = HashSet::new(); + let mut generated = BTreeSet::new(); generate_tree_node( output, "MetricsTree", @@ -32,9 +32,9 @@ fn generate_tree_node( name: &str, path: &str, node: &TreeNode, - pattern_lookup: &std::collections::HashMap, String>, + pattern_lookup: &std::collections::BTreeMap, String>, metadata: &ClientMetadata, - generated: &mut HashSet, + generated: &mut BTreeSet, ) { let Some(ctx) = prepare_tree_node(node, name, path, pattern_lookup, metadata, generated) else { return; diff --git a/crates/brk_bindgen/src/types/metadata.rs b/crates/brk_bindgen/src/types/metadata.rs index 6f55e396f..ca37e46d8 100644 --- a/crates/brk_bindgen/src/types/metadata.rs +++ b/crates/brk_bindgen/src/types/metadata.rs @@ -1,6 +1,6 @@ //! Client metadata extracted from brk_query. -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use brk_query::Vecs; use brk_types::{Index, MetricLeafWithSchema}; @@ -18,11 +18,11 @@ pub struct ClientMetadata { /// Index set patterns - sets of indexes that appear together on metrics pub index_set_patterns: Vec, /// Maps concrete field signatures to pattern names - concrete_to_pattern: HashMap, String>, + concrete_to_pattern: BTreeMap, String>, /// Maps concrete field signatures to their type parameter (for generic patterns) - concrete_to_type_param: HashMap, String>, + concrete_to_type_param: BTreeMap, String>, /// Maps tree paths to their computed PatternBaseResult - node_bases: HashMap, + node_bases: BTreeMap, } impl ClientMetadata { @@ -134,7 +134,7 @@ impl ClientMetadata { } /// Build a lookup map from field signatures to pattern names. - pub fn pattern_lookup(&self) -> HashMap, String> { + pub fn pattern_lookup(&self) -> BTreeMap, String> { let mut lookup = self.concrete_to_pattern.clone(); for p in &self.structural_patterns { lookup.insert(p.fields.clone(), p.name.clone()); diff --git a/crates/brk_bindgen/src/types/positions.rs b/crates/brk_bindgen/src/types/positions.rs index b4e65346b..25ff34219 100644 --- a/crates/brk_bindgen/src/types/positions.rs +++ b/crates/brk_bindgen/src/types/positions.rs @@ -4,7 +4,7 @@ //! - Suffix mode: `_m(acc, relative)` → `acc_relative` or just `relative` if acc empty //! - Prefix mode: `_p(prefix, acc)` → `prefix_acc` or just `acc` if prefix empty -use std::collections::HashMap; +use std::collections::BTreeMap; /// How a pattern constructs metric names from the accumulator. #[derive(Debug, Clone, PartialEq, Eq)] @@ -14,13 +14,13 @@ pub enum PatternMode { /// Example: `_m("lth", "max_cost_basis")` → `"lth_max_cost_basis"` Suffix { /// Maps field name to its relative name (full metric name when acc = "") - relatives: HashMap, + relatives: BTreeMap, }, /// Fields prepend their prefix to acc. /// Formula: `_p(prefix, acc)` → `{prefix}_{acc}` or `{acc}` if prefix empty /// Example: `_p("cumulative", "lth_realized_loss")` → `"cumulative_lth_realized_loss"` Prefix { /// Maps field name to its prefix (empty string for identity) - prefixes: HashMap, + prefixes: BTreeMap, }, } diff --git a/crates/brk_bindgen/src/types/structs.rs b/crates/brk_bindgen/src/types/structs.rs index 36c693363..d2530526d 100644 --- a/crates/brk_bindgen/src/types/structs.rs +++ b/crates/brk_bindgen/src/types/structs.rs @@ -1,6 +1,6 @@ //! Structural pattern and field types. -use std::collections::{BTreeSet, HashMap}; +use std::collections::{BTreeMap, BTreeSet}; use brk_types::Index; @@ -37,7 +37,9 @@ impl StructuralPattern { /// Get the field part (relative name or prefix) for a given field. pub fn get_field_part(&self, field_name: &str) -> Option<&str> { match &self.mode { - Some(PatternMode::Suffix { relatives }) => relatives.get(field_name).map(|s| s.as_str()), + Some(PatternMode::Suffix { relatives }) => { + relatives.get(field_name).map(|s| s.as_str()) + } Some(PatternMode::Prefix { prefixes }) => prefixes.get(field_name).map(|s| s.as_str()), None => None, } @@ -50,7 +52,7 @@ impl StructuralPattern { /// Check if the given instance field parts match this pattern's field parts. /// Returns true if all field parts in the pattern match the instance's field parts. - pub fn field_parts_match(&self, instance_field_parts: &HashMap) -> bool { + pub fn field_parts_match(&self, instance_field_parts: &BTreeMap) -> bool { match &self.mode { Some(PatternMode::Suffix { relatives }) => { // For each field in the pattern, check if the instance has the same suffix diff --git a/crates/brk_bindgen/tests/catalog_test.rs b/crates/brk_bindgen/tests/catalog_test.rs index 96e96d53e..fbe7b53fb 100644 --- a/crates/brk_bindgen/tests/catalog_test.rs +++ b/crates/brk_bindgen/tests/catalog_test.rs @@ -1,6 +1,6 @@ //! Tests that verify pattern analysis using the real catalog. -use std::collections::HashSet; +use std::collections::{BTreeMap, BTreeSet}; use std::fmt::Write; use brk_bindgen::ClientMetadata; @@ -26,7 +26,7 @@ fn load_metadata() -> ClientMetadata { } /// Collect all leaf metric names from a tree. -fn collect_leaf_names(node: &TreeNode, names: &mut HashSet) { +fn collect_leaf_names(node: &TreeNode, names: &mut BTreeSet) { match node { TreeNode::Leaf(leaf) => { names.insert(leaf.name().to_string()); @@ -63,7 +63,7 @@ fn test_catalog_loads() { #[test] fn test_all_leaves_have_names() { let catalog = load_catalog(); - let mut names = HashSet::new(); + let mut names = BTreeSet::new(); collect_leaf_names(&catalog, &mut names); println!("Catalog has {} unique metric names", names.len()); @@ -246,11 +246,11 @@ fn test_parameterizable_patterns_have_mode() { // Verify all parameterizable patterns have valid modes with all fields for pattern in ¶meterizable { let mode = pattern.mode.as_ref().unwrap(); - let field_names: HashSet<_> = pattern.fields.iter().map(|f| f.name.clone()).collect(); + let field_names: BTreeSet<_> = pattern.fields.iter().map(|f| f.name.clone()).collect(); match mode { brk_bindgen::PatternMode::Suffix { relatives } => { - let mode_fields: HashSet<_> = relatives.keys().cloned().collect(); + let mode_fields: BTreeSet<_> = relatives.keys().cloned().collect(); assert_eq!( field_names, mode_fields, "Pattern {} suffix mode should have all fields", @@ -258,7 +258,7 @@ fn test_parameterizable_patterns_have_mode() { ); } brk_bindgen::PatternMode::Prefix { prefixes } => { - let mode_fields: HashSet<_> = prefixes.keys().cloned().collect(); + let mode_fields: BTreeSet<_> = prefixes.keys().cloned().collect(); assert_eq!( field_names, mode_fields, "Pattern {} prefix mode should have all fields", @@ -333,7 +333,7 @@ fn test_generated_rust_output() { let metadata = ClientMetadata::from_catalog(catalog.clone()); // Collect all metric names from the catalog - let mut all_metrics = HashSet::new(); + let mut all_metrics = BTreeSet::new(); collect_leaf_names(&catalog, &mut all_metrics); // Generate Rust client output @@ -416,7 +416,7 @@ fn test_generated_javascript_output() { let metadata = ClientMetadata::from_catalog(catalog.clone()); // Collect all metric names from the catalog - let mut all_metrics = HashSet::new(); + let mut all_metrics = BTreeSet::new(); collect_leaf_names(&catalog, &mut all_metrics); // Load schemas from OpenAPI spec only (catalog schemas require runtime data) @@ -507,7 +507,7 @@ fn test_generated_python_output() { let metadata = ClientMetadata::from_catalog(catalog.clone()); // Collect all metric names from the catalog - let mut all_metrics = HashSet::new(); + let mut all_metrics = BTreeSet::new(); collect_leaf_names(&catalog, &mut all_metrics); // Load schemas from OpenAPI spec only (catalog schemas require runtime data) @@ -665,8 +665,8 @@ fn test_cost_basis_relatives() { // For leaves (max, min), the base is the metric name // For branches (percentiles), the base is the common prefix of its children - let mut child_bases: std::collections::HashMap = - std::collections::HashMap::new(); + let mut child_bases: std::collections::BTreeMap = + std::collections::BTreeMap::new(); for (field, metric) in metrics { if field.starts_with("percentiles.") { // This is a percentile metric - compute what the percentiles branch would return @@ -721,20 +721,20 @@ fn test_debug_cost_basis_pattern2_mode() { #[derive(Debug, Clone)] struct DebugInstanceAnalysis { base: String, - field_parts: std::collections::HashMap, + field_parts: std::collections::BTreeMap, is_suffix_mode: bool, } fn collect_debug( node: &TreeNode, - pattern_lookup: &std::collections::HashMap, String>, - all_analyses: &mut std::collections::HashMap>, + pattern_lookup: &std::collections::BTreeMap, String>, + all_analyses: &mut std::collections::BTreeMap>, ) -> Option { match node { TreeNode::Leaf(leaf) => Some(leaf.name().to_string()), TreeNode::Branch(children) => { - let mut child_bases: std::collections::HashMap = - std::collections::HashMap::new(); + let mut child_bases: std::collections::BTreeMap = + std::collections::BTreeMap::new(); for (field_name, child_node) in children { if let Some(base) = collect_debug(child_node, pattern_lookup, all_analyses) { child_bases.insert(field_name.clone(), base); @@ -750,7 +750,7 @@ fn test_debug_cost_basis_pattern2_mode() { let (base, field_parts, is_suffix_mode) = if let Some(common_prefix) = brk_bindgen::find_common_prefix(&bases) { let base = common_prefix.trim_end_matches('_').to_string(); - let mut parts = std::collections::HashMap::new(); + let mut parts = std::collections::BTreeMap::new(); for (field_name, child_base) in &child_bases { let relative = if *child_base == base { String::new() @@ -792,8 +792,7 @@ fn test_debug_cost_basis_pattern2_mode() { } } - let mut all_analyses: std::collections::HashMap> = - std::collections::HashMap::new(); + let mut all_analyses: BTreeMap> = BTreeMap::new(); collect_debug(&catalog, &pattern_lookup, &mut all_analyses); if let Some(analyses) = all_analyses.get("CostBasisPattern2") { diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 22079f4cc..0418290b5 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -1535,18 +1535,18 @@ impl ClassDaysInLossPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - _2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_days_in_profit")), - _2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_days_in_profit")), - _2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_days_in_profit")), - _2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_days_in_profit")), - _2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_days_in_profit")), - _2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_days_in_profit")), - _2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_days_in_profit")), - _2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_days_in_profit")), - _2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_days_in_profit")), - _2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_days_in_profit")), - _2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_days_in_profit")), - _2026: MetricPattern4::new(client.clone(), _m(&acc, "2026_days_in_profit")), + _2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_returns")), + _2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_returns")), + _2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_returns")), + _2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_returns")), + _2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_returns")), + _2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_returns")), + _2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_returns")), + _2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_returns")), + _2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_returns")), + _2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_returns")), + _2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_returns")), + _2026: MetricPattern4::new(client.clone(), _m(&acc, "2026_returns")), } } } @@ -1619,38 +1619,6 @@ impl DollarsPattern { } } -/// Pattern struct for repeated tree structure. -pub struct RelativePattern2 { - pub neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1, - pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1, - pub net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1, - pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1, - pub supply_in_loss_rel_to_own_supply: MetricPattern1, - pub supply_in_profit_rel_to_own_supply: MetricPattern1, - pub unrealized_loss_rel_to_own_market_cap: MetricPattern1, - pub unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1, - pub unrealized_profit_rel_to_own_market_cap: MetricPattern1, - pub unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1, -} - -impl RelativePattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_market_cap")), - neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_total_unrealized_pnl")), - net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_market_cap")), - net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_total_unrealized_pnl")), - supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")), - supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")), - unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_market_cap")), - unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_total_unrealized_pnl")), - unrealized_profit_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_market_cap")), - unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_total_unrealized_pnl")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct RelativePattern { pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1, @@ -1683,6 +1651,38 @@ impl RelativePattern { } } +/// Pattern struct for repeated tree structure. +pub struct RelativePattern2 { + pub neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1, + pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1, + pub net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1, + pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1, + pub supply_in_loss_rel_to_own_supply: MetricPattern1, + pub supply_in_profit_rel_to_own_supply: MetricPattern1, + pub unrealized_loss_rel_to_own_market_cap: MetricPattern1, + pub unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1, + pub unrealized_profit_rel_to_own_market_cap: MetricPattern1, + pub unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1, +} + +impl RelativePattern2 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_market_cap")), + neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_own_total_unrealized_pnl")), + net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_market_cap")), + net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_own_total_unrealized_pnl")), + supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")), + supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")), + unrealized_loss_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_market_cap")), + unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_own_total_unrealized_pnl")), + unrealized_profit_rel_to_own_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_market_cap")), + unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_own_total_unrealized_pnl")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct CountPattern2 { pub average: MetricPattern1, @@ -1833,6 +1833,32 @@ impl _0satsPattern { } } +/// Pattern struct for repeated tree structure. +pub struct PeriodCagrPattern { + pub _10y: MetricPattern4, + pub _2y: MetricPattern4, + pub _3y: MetricPattern4, + pub _4y: MetricPattern4, + pub _5y: MetricPattern4, + pub _6y: MetricPattern4, + pub _8y: MetricPattern4, +} + +impl PeriodCagrPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + _10y: MetricPattern4::new(client.clone(), _p("10y", &acc)), + _2y: MetricPattern4::new(client.clone(), _p("2y", &acc)), + _3y: MetricPattern4::new(client.clone(), _p("3y", &acc)), + _4y: MetricPattern4::new(client.clone(), _p("4y", &acc)), + _5y: MetricPattern4::new(client.clone(), _p("5y", &acc)), + _6y: MetricPattern4::new(client.clone(), _p("6y", &acc)), + _8y: MetricPattern4::new(client.clone(), _p("8y", &acc)), + } + } +} + /// Pattern struct for repeated tree structure. pub struct _100btcPattern { pub activity: ActivityPattern2, @@ -1911,58 +1937,6 @@ impl _10yPattern { } } -/// Pattern struct for repeated tree structure. -pub struct UnrealizedPattern { - pub neg_unrealized_loss: MetricPattern1, - pub net_unrealized_pnl: MetricPattern1, - pub supply_in_loss: ActiveSupplyPattern, - pub supply_in_profit: ActiveSupplyPattern, - pub total_unrealized_pnl: MetricPattern1, - pub unrealized_loss: MetricPattern1, - pub unrealized_profit: MetricPattern1, -} - -impl UnrealizedPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")), - net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")), - supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")), - supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")), - total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")), - unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")), - unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct PeriodCagrPattern { - pub _10y: MetricPattern4, - pub _2y: MetricPattern4, - pub _3y: MetricPattern4, - pub _4y: MetricPattern4, - pub _5y: MetricPattern4, - pub _6y: MetricPattern4, - pub _8y: MetricPattern4, -} - -impl PeriodCagrPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - _10y: MetricPattern4::new(client.clone(), _p("10y", &acc)), - _2y: MetricPattern4::new(client.clone(), _p("2y", &acc)), - _3y: MetricPattern4::new(client.clone(), _p("3y", &acc)), - _4y: MetricPattern4::new(client.clone(), _p("4y", &acc)), - _5y: MetricPattern4::new(client.clone(), _p("5y", &acc)), - _6y: MetricPattern4::new(client.clone(), _p("6y", &acc)), - _8y: MetricPattern4::new(client.clone(), _p("8y", &acc)), - } - } -} - /// Pattern struct for repeated tree structure. pub struct _10yTo12yPattern { pub activity: ActivityPattern2, @@ -1989,6 +1963,32 @@ impl _10yTo12yPattern { } } +/// Pattern struct for repeated tree structure. +pub struct UnrealizedPattern { + pub neg_unrealized_loss: MetricPattern1, + pub net_unrealized_pnl: MetricPattern1, + pub supply_in_loss: ActiveSupplyPattern, + pub supply_in_profit: ActiveSupplyPattern, + pub total_unrealized_pnl: MetricPattern1, + pub unrealized_loss: MetricPattern1, + pub unrealized_profit: MetricPattern1, +} + +impl UnrealizedPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")), + net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")), + supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")), + supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")), + total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")), + unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")), + unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct AllPattern { pub balance_decreased: FullnessPattern, @@ -2056,37 +2056,19 @@ impl SplitPattern2 { } /// Pattern struct for repeated tree structure. -pub struct UnclaimedRewardsPattern { - pub bitcoin: BitcoinPattern2, - pub dollars: BlockCountPattern, - pub sats: BlockCountPattern, +pub struct SegwitAdoptionPattern { + pub base: MetricPattern11, + pub cumulative: MetricPattern2, + pub sum: MetricPattern2, } -impl UnclaimedRewardsPattern { +impl SegwitAdoptionPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - bitcoin: BitcoinPattern2::new(client.clone(), _m(&acc, "btc")), - dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")), - sats: BlockCountPattern::new(client.clone(), acc.clone()), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct _2015Pattern { - pub bitcoin: MetricPattern4, - pub dollars: MetricPattern4, - pub sats: MetricPattern4, -} - -impl _2015Pattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - bitcoin: MetricPattern4::new(client.clone(), _m(&acc, "btc")), - dollars: MetricPattern4::new(client.clone(), _m(&acc, "usd")), - sats: MetricPattern4::new(client.clone(), acc.clone()), + base: MetricPattern11::new(client.clone(), acc.clone()), + cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")), + sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")), } } } @@ -2109,6 +2091,24 @@ impl ActiveSupplyPattern { } } +/// Pattern struct for repeated tree structure. +pub struct _2015Pattern { + pub bitcoin: MetricPattern4, + pub dollars: MetricPattern4, + pub sats: MetricPattern4, +} + +impl _2015Pattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + bitcoin: MetricPattern4::new(client.clone(), _m(&acc, "btc")), + dollars: MetricPattern4::new(client.clone(), _m(&acc, "usd")), + sats: MetricPattern4::new(client.clone(), acc.clone()), + } + } +} + /// Pattern struct for repeated tree structure. pub struct CoinbasePattern { pub bitcoin: BitcoinPattern, @@ -2128,37 +2128,19 @@ impl CoinbasePattern { } /// Pattern struct for repeated tree structure. -pub struct SegwitAdoptionPattern { - pub base: MetricPattern11, - pub cumulative: MetricPattern2, - pub sum: MetricPattern2, +pub struct UnclaimedRewardsPattern { + pub bitcoin: BitcoinPattern2, + pub dollars: BlockCountPattern, + pub sats: BlockCountPattern, } -impl SegwitAdoptionPattern { +impl UnclaimedRewardsPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - base: MetricPattern11::new(client.clone(), acc.clone()), - cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")), - sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct CostBasisPattern2 { - pub max: ActivePricePattern, - pub min: ActivePricePattern, - pub percentiles: PercentilesPattern, -} - -impl CostBasisPattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - max: ActivePricePattern::new(client.clone(), _m(&acc, "max_cost_basis")), - min: ActivePricePattern::new(client.clone(), _m(&acc, "min_cost_basis")), - percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")), + bitcoin: BitcoinPattern2::new(client.clone(), _m(&acc, "btc")), + dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")), + sats: BlockCountPattern::new(client.clone(), acc.clone()), } } } @@ -2182,17 +2164,19 @@ impl CoinbasePattern2 { } /// Pattern struct for repeated tree structure. -pub struct RelativePattern4 { - pub supply_in_loss_rel_to_own_supply: MetricPattern1, - pub supply_in_profit_rel_to_own_supply: MetricPattern1, +pub struct CostBasisPattern2 { + pub max: ActivePricePattern, + pub min: ActivePricePattern, + pub percentiles: PercentilesPattern, } -impl RelativePattern4 { +impl CostBasisPattern2 { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "loss_rel_to_own_supply")), - supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "profit_rel_to_own_supply")), + max: ActivePricePattern::new(client.clone(), _m(&acc, "max_cost_basis")), + min: ActivePricePattern::new(client.clone(), _m(&acc, "min_cost_basis")), + percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")), } } } @@ -2214,17 +2198,17 @@ impl ActivePricePattern { } /// Pattern struct for repeated tree structure. -pub struct _1dReturns1mSdPattern { - pub sd: MetricPattern4, - pub sma: MetricPattern4, +pub struct _0sdUsdPattern { + pub dollars: MetricPattern4, + pub sats: MetricPattern4, } -impl _1dReturns1mSdPattern { +impl _0sdUsdPattern { /// Create a new pattern node with accumulated metric name. pub fn new(client: Arc, acc: String) -> Self { Self { - sd: MetricPattern4::new(client.clone(), _m(&acc, "sd")), - sma: MetricPattern4::new(client.clone(), _m(&acc, "sma")), + dollars: MetricPattern4::new(client.clone(), acc.clone()), + sats: MetricPattern4::new(client.clone(), _m(&acc, "sats")), } } } @@ -2245,22 +2229,6 @@ impl SupplyPattern2 { } } -/// Pattern struct for repeated tree structure. -pub struct _0sdUsdPattern { - pub dollars: MetricPattern4, - pub sats: MetricPattern4, -} - -impl _0sdUsdPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - dollars: MetricPattern4::new(client.clone(), acc.clone()), - sats: MetricPattern4::new(client.clone(), _m(&acc, "sats")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct CostBasisPattern { pub max: ActivePricePattern, @@ -2277,6 +2245,38 @@ impl CostBasisPattern { } } +/// Pattern struct for repeated tree structure. +pub struct _1dReturns1mSdPattern { + pub sd: MetricPattern4, + pub sma: MetricPattern4, +} + +impl _1dReturns1mSdPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + sd: MetricPattern4::new(client.clone(), _m(&acc, "sd")), + sma: MetricPattern4::new(client.clone(), _m(&acc, "sma")), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct RelativePattern4 { + pub supply_in_loss_rel_to_own_supply: MetricPattern1, + pub supply_in_profit_rel_to_own_supply: MetricPattern1, +} + +impl RelativePattern4 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "loss_rel_to_own_supply")), + supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "profit_rel_to_own_supply")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct BlockCountPattern { pub cumulative: MetricPattern1, @@ -2325,20 +2325,6 @@ impl SatsPattern { } } -/// Pattern struct for repeated tree structure. -pub struct OutputsPattern { - pub utxo_count: MetricPattern1, -} - -impl OutputsPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - utxo_count: MetricPattern1::new(client.clone(), acc.clone()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct RealizedPriceExtraPattern { pub ratio: MetricPattern4, @@ -2353,6 +2339,20 @@ impl RealizedPriceExtraPattern { } } +/// Pattern struct for repeated tree structure. +pub struct OutputsPattern { + pub utxo_count: MetricPattern1, +} + +impl OutputsPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + utxo_count: MetricPattern1::new(client.clone(), acc.clone()), + } + } +} + // Metrics tree /// Metrics tree node. @@ -4276,10 +4276,10 @@ impl MetricsTree_Market_Ath { pub struct MetricsTree_Market_Dca { pub class_average_price: MetricsTree_Market_Dca_ClassAveragePrice, pub class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss, - pub class_days_in_profit: ClassDaysInLossPattern, + pub class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit, pub class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown, pub class_max_return: MetricsTree_Market_Dca_ClassMaxReturn, - pub class_returns: MetricsTree_Market_Dca_ClassReturns, + pub class_returns: ClassDaysInLossPattern, pub class_stack: MetricsTree_Market_Dca_ClassStack, pub period_average_price: MetricsTree_Market_Dca_PeriodAveragePrice, pub period_cagr: PeriodCagrPattern, @@ -4302,10 +4302,10 @@ impl MetricsTree_Market_Dca { Self { class_average_price: MetricsTree_Market_Dca_ClassAveragePrice::new(client.clone(), format!("{base_path}_class_average_price")), class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss::new(client.clone(), format!("{base_path}_class_days_in_loss")), - class_days_in_profit: ClassDaysInLossPattern::new(client.clone(), "dca_class".to_string()), + class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit::new(client.clone(), format!("{base_path}_class_days_in_profit")), class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown::new(client.clone(), format!("{base_path}_class_max_drawdown")), class_max_return: MetricsTree_Market_Dca_ClassMaxReturn::new(client.clone(), format!("{base_path}_class_max_return")), - class_returns: MetricsTree_Market_Dca_ClassReturns::new(client.clone(), format!("{base_path}_class_returns")), + class_returns: ClassDaysInLossPattern::new(client.clone(), "dca_class".to_string()), class_stack: MetricsTree_Market_Dca_ClassStack::new(client.clone(), format!("{base_path}_class_stack")), period_average_price: MetricsTree_Market_Dca_PeriodAveragePrice::new(client.clone(), format!("{base_path}_period_average_price")), period_cagr: PeriodCagrPattern::new(client.clone(), "dca_cagr".to_string()), @@ -4395,6 +4395,41 @@ impl MetricsTree_Market_Dca_ClassDaysInLoss { } } +/// Metrics tree node. +pub struct MetricsTree_Market_Dca_ClassDaysInProfit { + pub _2015: MetricPattern4, + pub _2016: MetricPattern4, + pub _2017: MetricPattern4, + pub _2018: MetricPattern4, + pub _2019: MetricPattern4, + pub _2020: MetricPattern4, + pub _2021: MetricPattern4, + pub _2022: MetricPattern4, + pub _2023: MetricPattern4, + pub _2024: MetricPattern4, + pub _2025: MetricPattern4, + pub _2026: MetricPattern4, +} + +impl MetricsTree_Market_Dca_ClassDaysInProfit { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + _2015: MetricPattern4::new(client.clone(), "dca_class_2015_days_in_profit".to_string()), + _2016: MetricPattern4::new(client.clone(), "dca_class_2016_days_in_profit".to_string()), + _2017: MetricPattern4::new(client.clone(), "dca_class_2017_days_in_profit".to_string()), + _2018: MetricPattern4::new(client.clone(), "dca_class_2018_days_in_profit".to_string()), + _2019: MetricPattern4::new(client.clone(), "dca_class_2019_days_in_profit".to_string()), + _2020: MetricPattern4::new(client.clone(), "dca_class_2020_days_in_profit".to_string()), + _2021: MetricPattern4::new(client.clone(), "dca_class_2021_days_in_profit".to_string()), + _2022: MetricPattern4::new(client.clone(), "dca_class_2022_days_in_profit".to_string()), + _2023: MetricPattern4::new(client.clone(), "dca_class_2023_days_in_profit".to_string()), + _2024: MetricPattern4::new(client.clone(), "dca_class_2024_days_in_profit".to_string()), + _2025: MetricPattern4::new(client.clone(), "dca_class_2025_days_in_profit".to_string()), + _2026: MetricPattern4::new(client.clone(), "dca_class_2026_days_in_profit".to_string()), + } + } +} + /// Metrics tree node. pub struct MetricsTree_Market_Dca_ClassMaxDrawdown { pub _2015: MetricPattern4, @@ -4465,41 +4500,6 @@ impl MetricsTree_Market_Dca_ClassMaxReturn { } } -/// Metrics tree node. -pub struct MetricsTree_Market_Dca_ClassReturns { - pub _2015: MetricPattern4, - pub _2016: MetricPattern4, - pub _2017: MetricPattern4, - pub _2018: MetricPattern4, - pub _2019: MetricPattern4, - pub _2020: MetricPattern4, - pub _2021: MetricPattern4, - pub _2022: MetricPattern4, - pub _2023: MetricPattern4, - pub _2024: MetricPattern4, - pub _2025: MetricPattern4, - pub _2026: MetricPattern4, -} - -impl MetricsTree_Market_Dca_ClassReturns { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - _2015: MetricPattern4::new(client.clone(), "dca_class_2015_returns".to_string()), - _2016: MetricPattern4::new(client.clone(), "dca_class_2016_returns".to_string()), - _2017: MetricPattern4::new(client.clone(), "dca_class_2017_returns".to_string()), - _2018: MetricPattern4::new(client.clone(), "dca_class_2018_returns".to_string()), - _2019: MetricPattern4::new(client.clone(), "dca_class_2019_returns".to_string()), - _2020: MetricPattern4::new(client.clone(), "dca_class_2020_returns".to_string()), - _2021: MetricPattern4::new(client.clone(), "dca_class_2021_returns".to_string()), - _2022: MetricPattern4::new(client.clone(), "dca_class_2022_returns".to_string()), - _2023: MetricPattern4::new(client.clone(), "dca_class_2023_returns".to_string()), - _2024: MetricPattern4::new(client.clone(), "dca_class_2024_returns".to_string()), - _2025: MetricPattern4::new(client.clone(), "dca_class_2025_returns".to_string()), - _2026: MetricPattern4::new(client.clone(), "dca_class_2026_returns".to_string()), - } - } -} - /// Metrics tree node. pub struct MetricsTree_Market_Dca_ClassStack { pub _2015: _2015Pattern, @@ -5651,7 +5651,7 @@ pub struct BrkClient { impl BrkClient { /// Client version. - pub const VERSION: &'static str = "v0.1.0"; + pub const VERSION: &'static str = "v0.1.1"; /// Create a new client with the given base URL. pub fn new(base_url: impl Into) -> Self { diff --git a/crates/brk_indexer/src/lib.rs b/crates/brk_indexer/src/lib.rs index 15f63e330..833b1ff28 100644 --- a/crates/brk_indexer/src/lib.rs +++ b/crates/brk_indexer/src/lib.rs @@ -7,7 +7,7 @@ use brk_iterator::Blocks; use brk_rpc::Client; use brk_types::Height; use tracing::{debug, info}; -use vecdb::Exit; +use vecdb::{Exit, TypedVecIterator}; mod constants; mod indexes; mod processor; diff --git a/crates/brk_indexer/src/stores.rs b/crates/brk_indexer/src/stores.rs index fb7b6fcb6..a468af397 100644 --- a/crates/brk_indexer/src/stores.rs +++ b/crates/brk_indexer/src/stores.rs @@ -1,5 +1,7 @@ use std::{fs, path::Path, time::Instant}; +use rustc_hash::FxHashSet; + use brk_cohort::ByAddressType; use brk_error::Result; use brk_store::{AnyStore, Kind, Mode, Store}; @@ -228,6 +230,7 @@ impl Stores { }); // Remove address hashes for all address types starting from rollback height + // (each address only appears once in bytes vec, so no dedup needed) for address_type in [ OutputType::P2PK65, OutputType::P2PK33, @@ -280,6 +283,11 @@ impl Stores { let mut txoutindex_to_outputtype_iter = vecs.outputs.outputtype.iter()?; let mut txoutindex_to_typeindex_iter = vecs.outputs.typeindex.iter()?; + // Collect unique (addresstype, addressindex, txindex) to avoid double deletion + // when same address receives multiple outputs in same transaction + let mut addressindex_txindex_to_remove: FxHashSet<(OutputType, TypeIndex, TxIndex)> = + FxHashSet::default(); + for txoutindex in starting_indexes.txoutindex.to_usize()..vecs.outputs.outputtype.len() { @@ -292,9 +300,7 @@ impl Stores { let addressindex = txoutindex_to_typeindex_iter.get_at_unwrap(txoutindex); let txindex = txoutindex_to_txindex_iter.get_at_unwrap(txoutindex); - self.addresstype_to_addressindex_and_txindex - .get_mut_unwrap(addresstype) - .remove(AddressIndexTxIndex::from((addressindex, txindex))); + addressindex_txindex_to_remove.insert((addresstype, addressindex, txindex)); let vout = Vout::from( txoutindex @@ -304,11 +310,14 @@ impl Stores { ); let outpoint = OutPoint::new(txindex, vout); + // OutPoints are unique per output, no dedup needed self.addresstype_to_addressindex_and_unspentoutpoint .get_mut_unwrap(addresstype) .remove(AddressIndexOutPoint::from((addressindex, outpoint))); } + // Don't remove yet - merge with second loop's set first + // Collect outputs that were spent after the rollback point // We need to: 1) reset their spend status, 2) restore address stores let mut txindex_to_first_txoutindex_iter = @@ -349,21 +358,30 @@ impl Stores { .collect(); // Now process the collected outputs (iterators dropped, can mutate vecs) + // Add spending tx entries to the same set (avoid double deletion when same tx + // both creates output to address A and spends output from address A) for (outpoint, outputtype, typeindex, spending_txindex) in outputs_to_unspend { // Restore address stores if this is an address output if outputtype.is_address() { let addresstype = outputtype; let addressindex = typeindex; - self.addresstype_to_addressindex_and_txindex - .get_mut_unwrap(addresstype) - .remove(AddressIndexTxIndex::from((addressindex, spending_txindex))); + // Add to same set as first loop + addressindex_txindex_to_remove.insert((addresstype, addressindex, spending_txindex)); + // OutPoints are unique, no dedup needed for insert self.addresstype_to_addressindex_and_unspentoutpoint .get_mut_unwrap(addresstype) .insert(AddressIndexOutPoint::from((addressindex, outpoint)), Unit); } } + + // Now remove all deduplicated addressindex_txindex entries (from both loops) + for (addresstype, addressindex, txindex) in addressindex_txindex_to_remove { + self.addresstype_to_addressindex_and_txindex + .get_mut_unwrap(addresstype) + .remove(AddressIndexTxIndex::from((addressindex, txindex))); + } } else { unreachable!(); } diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index cadf5a4e0..ff74ffd5f 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -1953,18 +1953,18 @@ function createPeriodDaysInLossPattern(client, acc) { */ function createClassDaysInLossPattern(client, acc) { return { - _2015: createMetricPattern4(client, _m(acc, '2015_days_in_profit')), - _2016: createMetricPattern4(client, _m(acc, '2016_days_in_profit')), - _2017: createMetricPattern4(client, _m(acc, '2017_days_in_profit')), - _2018: createMetricPattern4(client, _m(acc, '2018_days_in_profit')), - _2019: createMetricPattern4(client, _m(acc, '2019_days_in_profit')), - _2020: createMetricPattern4(client, _m(acc, '2020_days_in_profit')), - _2021: createMetricPattern4(client, _m(acc, '2021_days_in_profit')), - _2022: createMetricPattern4(client, _m(acc, '2022_days_in_profit')), - _2023: createMetricPattern4(client, _m(acc, '2023_days_in_profit')), - _2024: createMetricPattern4(client, _m(acc, '2024_days_in_profit')), - _2025: createMetricPattern4(client, _m(acc, '2025_days_in_profit')), - _2026: createMetricPattern4(client, _m(acc, '2026_days_in_profit')), + _2015: createMetricPattern4(client, _m(acc, '2015_returns')), + _2016: createMetricPattern4(client, _m(acc, '2016_returns')), + _2017: createMetricPattern4(client, _m(acc, '2017_returns')), + _2018: createMetricPattern4(client, _m(acc, '2018_returns')), + _2019: createMetricPattern4(client, _m(acc, '2019_returns')), + _2020: createMetricPattern4(client, _m(acc, '2020_returns')), + _2021: createMetricPattern4(client, _m(acc, '2021_returns')), + _2022: createMetricPattern4(client, _m(acc, '2022_returns')), + _2023: createMetricPattern4(client, _m(acc, '2023_returns')), + _2024: createMetricPattern4(client, _m(acc, '2024_returns')), + _2025: createMetricPattern4(client, _m(acc, '2025_returns')), + _2026: createMetricPattern4(client, _m(acc, '2026_returns')), }; } @@ -2044,41 +2044,6 @@ function createDollarsPattern(client, acc) { }; } -/** - * @typedef {Object} RelativePattern2 - * @property {MetricPattern1} negUnrealizedLossRelToOwnMarketCap - * @property {MetricPattern1} negUnrealizedLossRelToOwnTotalUnrealizedPnl - * @property {MetricPattern1} netUnrealizedPnlRelToOwnMarketCap - * @property {MetricPattern1} netUnrealizedPnlRelToOwnTotalUnrealizedPnl - * @property {MetricPattern1} supplyInLossRelToOwnSupply - * @property {MetricPattern1} supplyInProfitRelToOwnSupply - * @property {MetricPattern1} unrealizedLossRelToOwnMarketCap - * @property {MetricPattern1} unrealizedLossRelToOwnTotalUnrealizedPnl - * @property {MetricPattern1} unrealizedProfitRelToOwnMarketCap - * @property {MetricPattern1} unrealizedProfitRelToOwnTotalUnrealizedPnl - */ - -/** - * Create a RelativePattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {RelativePattern2} - */ -function createRelativePattern2(client, acc) { - return { - negUnrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')), - negUnrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')), - netUnrealizedPnlRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')), - netUnrealizedPnlRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')), - supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')), - supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')), - unrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')), - unrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')), - unrealizedProfitRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')), - unrealizedProfitRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')), - }; -} - /** * @typedef {Object} RelativePattern * @property {MetricPattern1} negUnrealizedLossRelToMarketCap @@ -2114,6 +2079,41 @@ function createRelativePattern(client, acc) { }; } +/** + * @typedef {Object} RelativePattern2 + * @property {MetricPattern1} negUnrealizedLossRelToOwnMarketCap + * @property {MetricPattern1} negUnrealizedLossRelToOwnTotalUnrealizedPnl + * @property {MetricPattern1} netUnrealizedPnlRelToOwnMarketCap + * @property {MetricPattern1} netUnrealizedPnlRelToOwnTotalUnrealizedPnl + * @property {MetricPattern1} supplyInLossRelToOwnSupply + * @property {MetricPattern1} supplyInProfitRelToOwnSupply + * @property {MetricPattern1} unrealizedLossRelToOwnMarketCap + * @property {MetricPattern1} unrealizedLossRelToOwnTotalUnrealizedPnl + * @property {MetricPattern1} unrealizedProfitRelToOwnMarketCap + * @property {MetricPattern1} unrealizedProfitRelToOwnTotalUnrealizedPnl + */ + +/** + * Create a RelativePattern2 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {RelativePattern2} + */ +function createRelativePattern2(client, acc) { + return { + negUnrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')), + negUnrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')), + netUnrealizedPnlRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')), + netUnrealizedPnlRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')), + supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')), + supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')), + unrealizedLossRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')), + unrealizedLossRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')), + unrealizedProfitRelToOwnMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')), + unrealizedProfitRelToOwnTotalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')), + }; +} + /** * @template T * @typedef {Object} CountPattern2 @@ -2285,6 +2285,35 @@ function create_0satsPattern(client, acc) { }; } +/** + * @typedef {Object} PeriodCagrPattern + * @property {MetricPattern4} _10y + * @property {MetricPattern4} _2y + * @property {MetricPattern4} _3y + * @property {MetricPattern4} _4y + * @property {MetricPattern4} _5y + * @property {MetricPattern4} _6y + * @property {MetricPattern4} _8y + */ + +/** + * Create a PeriodCagrPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {PeriodCagrPattern} + */ +function createPeriodCagrPattern(client, acc) { + return { + _10y: createMetricPattern4(client, _p('10y', acc)), + _2y: createMetricPattern4(client, _p('2y', acc)), + _3y: createMetricPattern4(client, _p('3y', acc)), + _4y: createMetricPattern4(client, _p('4y', acc)), + _5y: createMetricPattern4(client, _p('5y', acc)), + _6y: createMetricPattern4(client, _p('6y', acc)), + _8y: createMetricPattern4(client, _p('8y', acc)), + }; +} + /** * @typedef {Object} _100btcPattern * @property {ActivityPattern2} activity @@ -2372,64 +2401,6 @@ function create_10yPattern(client, acc) { }; } -/** - * @typedef {Object} UnrealizedPattern - * @property {MetricPattern1} negUnrealizedLoss - * @property {MetricPattern1} netUnrealizedPnl - * @property {ActiveSupplyPattern} supplyInLoss - * @property {ActiveSupplyPattern} supplyInProfit - * @property {MetricPattern1} totalUnrealizedPnl - * @property {MetricPattern1} unrealizedLoss - * @property {MetricPattern1} unrealizedProfit - */ - -/** - * Create a UnrealizedPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {UnrealizedPattern} - */ -function createUnrealizedPattern(client, acc) { - return { - negUnrealizedLoss: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss')), - netUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl')), - supplyInLoss: createActiveSupplyPattern(client, _m(acc, 'supply_in_loss')), - supplyInProfit: createActiveSupplyPattern(client, _m(acc, 'supply_in_profit')), - totalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'total_unrealized_pnl')), - unrealizedLoss: createMetricPattern1(client, _m(acc, 'unrealized_loss')), - unrealizedProfit: createMetricPattern1(client, _m(acc, 'unrealized_profit')), - }; -} - -/** - * @typedef {Object} PeriodCagrPattern - * @property {MetricPattern4} _10y - * @property {MetricPattern4} _2y - * @property {MetricPattern4} _3y - * @property {MetricPattern4} _4y - * @property {MetricPattern4} _5y - * @property {MetricPattern4} _6y - * @property {MetricPattern4} _8y - */ - -/** - * Create a PeriodCagrPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {PeriodCagrPattern} - */ -function createPeriodCagrPattern(client, acc) { - return { - _10y: createMetricPattern4(client, _p('10y', acc)), - _2y: createMetricPattern4(client, _p('2y', acc)), - _3y: createMetricPattern4(client, _p('3y', acc)), - _4y: createMetricPattern4(client, _p('4y', acc)), - _5y: createMetricPattern4(client, _p('5y', acc)), - _6y: createMetricPattern4(client, _p('6y', acc)), - _8y: createMetricPattern4(client, _p('8y', acc)), - }; -} - /** * @typedef {Object} _10yTo12yPattern * @property {ActivityPattern2} activity @@ -2459,6 +2430,35 @@ function create_10yTo12yPattern(client, acc) { }; } +/** + * @typedef {Object} UnrealizedPattern + * @property {MetricPattern1} negUnrealizedLoss + * @property {MetricPattern1} netUnrealizedPnl + * @property {ActiveSupplyPattern} supplyInLoss + * @property {ActiveSupplyPattern} supplyInProfit + * @property {MetricPattern1} totalUnrealizedPnl + * @property {MetricPattern1} unrealizedLoss + * @property {MetricPattern1} unrealizedProfit + */ + +/** + * Create a UnrealizedPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {UnrealizedPattern} + */ +function createUnrealizedPattern(client, acc) { + return { + negUnrealizedLoss: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss')), + netUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl')), + supplyInLoss: createActiveSupplyPattern(client, _m(acc, 'supply_in_loss')), + supplyInProfit: createActiveSupplyPattern(client, _m(acc, 'supply_in_profit')), + totalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'total_unrealized_pnl')), + unrealizedLoss: createMetricPattern1(client, _m(acc, 'unrealized_loss')), + unrealizedProfit: createMetricPattern1(client, _m(acc, 'unrealized_profit')), + }; +} + /** * @typedef {Object} AllPattern * @property {FullnessPattern} balanceDecreased @@ -2537,44 +2537,23 @@ function createSplitPattern2(client, acc) { } /** - * @typedef {Object} UnclaimedRewardsPattern - * @property {BitcoinPattern2} bitcoin - * @property {BlockCountPattern} dollars - * @property {BlockCountPattern} sats + * @typedef {Object} SegwitAdoptionPattern + * @property {MetricPattern11} base + * @property {MetricPattern2} cumulative + * @property {MetricPattern2} sum */ /** - * Create a UnclaimedRewardsPattern pattern node + * Create a SegwitAdoptionPattern pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {UnclaimedRewardsPattern} + * @returns {SegwitAdoptionPattern} */ -function createUnclaimedRewardsPattern(client, acc) { +function createSegwitAdoptionPattern(client, acc) { return { - bitcoin: createBitcoinPattern2(client, _m(acc, 'btc')), - dollars: createBlockCountPattern(client, _m(acc, 'usd')), - sats: createBlockCountPattern(client, acc), - }; -} - -/** - * @typedef {Object} _2015Pattern - * @property {MetricPattern4} bitcoin - * @property {MetricPattern4} dollars - * @property {MetricPattern4} sats - */ - -/** - * Create a _2015Pattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {_2015Pattern} - */ -function create_2015Pattern(client, acc) { - return { - bitcoin: createMetricPattern4(client, _m(acc, 'btc')), - dollars: createMetricPattern4(client, _m(acc, 'usd')), - sats: createMetricPattern4(client, acc), + base: createMetricPattern11(client, acc), + cumulative: createMetricPattern2(client, _m(acc, 'cumulative')), + sum: createMetricPattern2(client, _m(acc, 'sum')), }; } @@ -2599,6 +2578,27 @@ function createActiveSupplyPattern(client, acc) { }; } +/** + * @typedef {Object} _2015Pattern + * @property {MetricPattern4} bitcoin + * @property {MetricPattern4} dollars + * @property {MetricPattern4} sats + */ + +/** + * Create a _2015Pattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {_2015Pattern} + */ +function create_2015Pattern(client, acc) { + return { + bitcoin: createMetricPattern4(client, _m(acc, 'btc')), + dollars: createMetricPattern4(client, _m(acc, 'usd')), + sats: createMetricPattern4(client, acc), + }; +} + /** * @typedef {Object} CoinbasePattern * @property {BitcoinPattern} bitcoin @@ -2621,44 +2621,23 @@ function createCoinbasePattern(client, acc) { } /** - * @typedef {Object} SegwitAdoptionPattern - * @property {MetricPattern11} base - * @property {MetricPattern2} cumulative - * @property {MetricPattern2} sum + * @typedef {Object} UnclaimedRewardsPattern + * @property {BitcoinPattern2} bitcoin + * @property {BlockCountPattern} dollars + * @property {BlockCountPattern} sats */ /** - * Create a SegwitAdoptionPattern pattern node + * Create a UnclaimedRewardsPattern pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {SegwitAdoptionPattern} + * @returns {UnclaimedRewardsPattern} */ -function createSegwitAdoptionPattern(client, acc) { +function createUnclaimedRewardsPattern(client, acc) { return { - base: createMetricPattern11(client, acc), - cumulative: createMetricPattern2(client, _m(acc, 'cumulative')), - sum: createMetricPattern2(client, _m(acc, 'sum')), - }; -} - -/** - * @typedef {Object} CostBasisPattern2 - * @property {ActivePricePattern} max - * @property {ActivePricePattern} min - * @property {PercentilesPattern} percentiles - */ - -/** - * Create a CostBasisPattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {CostBasisPattern2} - */ -function createCostBasisPattern2(client, acc) { - return { - max: createActivePricePattern(client, _m(acc, 'max_cost_basis')), - min: createActivePricePattern(client, _m(acc, 'min_cost_basis')), - percentiles: createPercentilesPattern(client, _m(acc, 'cost_basis')), + bitcoin: createBitcoinPattern2(client, _m(acc, 'btc')), + dollars: createBlockCountPattern(client, _m(acc, 'usd')), + sats: createBlockCountPattern(client, acc), }; } @@ -2684,21 +2663,23 @@ function createCoinbasePattern2(client, acc) { } /** - * @typedef {Object} RelativePattern4 - * @property {MetricPattern1} supplyInLossRelToOwnSupply - * @property {MetricPattern1} supplyInProfitRelToOwnSupply + * @typedef {Object} CostBasisPattern2 + * @property {ActivePricePattern} max + * @property {ActivePricePattern} min + * @property {PercentilesPattern} percentiles */ /** - * Create a RelativePattern4 pattern node + * Create a CostBasisPattern2 pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {RelativePattern4} + * @returns {CostBasisPattern2} */ -function createRelativePattern4(client, acc) { +function createCostBasisPattern2(client, acc) { return { - supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'loss_rel_to_own_supply')), - supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'profit_rel_to_own_supply')), + max: createActivePricePattern(client, _m(acc, 'max_cost_basis')), + min: createActivePricePattern(client, _m(acc, 'min_cost_basis')), + percentiles: createPercentilesPattern(client, _m(acc, 'cost_basis')), }; } @@ -2722,21 +2703,21 @@ function createActivePricePattern(client, acc) { } /** - * @typedef {Object} _1dReturns1mSdPattern - * @property {MetricPattern4} sd - * @property {MetricPattern4} sma + * @typedef {Object} _0sdUsdPattern + * @property {MetricPattern4} dollars + * @property {MetricPattern4} sats */ /** - * Create a _1dReturns1mSdPattern pattern node + * Create a _0sdUsdPattern pattern node * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {_1dReturns1mSdPattern} + * @returns {_0sdUsdPattern} */ -function create_1dReturns1mSdPattern(client, acc) { +function create_0sdUsdPattern(client, acc) { return { - sd: createMetricPattern4(client, _m(acc, 'sd')), - sma: createMetricPattern4(client, _m(acc, 'sma')), + dollars: createMetricPattern4(client, acc), + sats: createMetricPattern4(client, _m(acc, 'sats')), }; } @@ -2759,25 +2740,6 @@ function createSupplyPattern2(client, acc) { }; } -/** - * @typedef {Object} _0sdUsdPattern - * @property {MetricPattern4} dollars - * @property {MetricPattern4} sats - */ - -/** - * Create a _0sdUsdPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {_0sdUsdPattern} - */ -function create_0sdUsdPattern(client, acc) { - return { - dollars: createMetricPattern4(client, acc), - sats: createMetricPattern4(client, _m(acc, 'sats')), - }; -} - /** * @typedef {Object} CostBasisPattern * @property {ActivePricePattern} max @@ -2797,6 +2759,44 @@ function createCostBasisPattern(client, acc) { }; } +/** + * @typedef {Object} _1dReturns1mSdPattern + * @property {MetricPattern4} sd + * @property {MetricPattern4} sma + */ + +/** + * Create a _1dReturns1mSdPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {_1dReturns1mSdPattern} + */ +function create_1dReturns1mSdPattern(client, acc) { + return { + sd: createMetricPattern4(client, _m(acc, 'sd')), + sma: createMetricPattern4(client, _m(acc, 'sma')), + }; +} + +/** + * @typedef {Object} RelativePattern4 + * @property {MetricPattern1} supplyInLossRelToOwnSupply + * @property {MetricPattern1} supplyInProfitRelToOwnSupply + */ + +/** + * Create a RelativePattern4 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {RelativePattern4} + */ +function createRelativePattern4(client, acc) { + return { + supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'loss_rel_to_own_supply')), + supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'profit_rel_to_own_supply')), + }; +} + /** * @template T * @typedef {Object} BlockCountPattern @@ -2860,23 +2860,6 @@ function createSatsPattern(client, acc) { }; } -/** - * @typedef {Object} OutputsPattern - * @property {MetricPattern1} utxoCount - */ - -/** - * Create a OutputsPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {OutputsPattern} - */ -function createOutputsPattern(client, acc) { - return { - utxoCount: createMetricPattern1(client, acc), - }; -} - /** * @typedef {Object} RealizedPriceExtraPattern * @property {MetricPattern4} ratio @@ -2894,6 +2877,23 @@ function createRealizedPriceExtraPattern(client, acc) { }; } +/** + * @typedef {Object} OutputsPattern + * @property {MetricPattern1} utxoCount + */ + +/** + * Create a OutputsPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {OutputsPattern} + */ +function createOutputsPattern(client, acc) { + return { + utxoCount: createMetricPattern1(client, acc), + }; +} + // Catalog tree typedefs /** @@ -3743,10 +3743,10 @@ function createRealizedPriceExtraPattern(client, acc) { * @typedef {Object} MetricsTree_Market_Dca * @property {MetricsTree_Market_Dca_ClassAveragePrice} classAveragePrice * @property {MetricsTree_Market_Dca_ClassDaysInLoss} classDaysInLoss - * @property {ClassDaysInLossPattern} classDaysInProfit + * @property {MetricsTree_Market_Dca_ClassDaysInProfit} classDaysInProfit * @property {MetricsTree_Market_Dca_ClassMaxDrawdown} classMaxDrawdown * @property {MetricsTree_Market_Dca_ClassMaxReturn} classMaxReturn - * @property {MetricsTree_Market_Dca_ClassReturns} classReturns + * @property {ClassDaysInLossPattern} classReturns * @property {MetricsTree_Market_Dca_ClassStack} classStack * @property {MetricsTree_Market_Dca_PeriodAveragePrice} periodAveragePrice * @property {PeriodCagrPattern} periodCagr @@ -3796,6 +3796,22 @@ function createRealizedPriceExtraPattern(client, acc) { * @property {MetricPattern4} _2026 */ +/** + * @typedef {Object} MetricsTree_Market_Dca_ClassDaysInProfit + * @property {MetricPattern4} _2015 + * @property {MetricPattern4} _2016 + * @property {MetricPattern4} _2017 + * @property {MetricPattern4} _2018 + * @property {MetricPattern4} _2019 + * @property {MetricPattern4} _2020 + * @property {MetricPattern4} _2021 + * @property {MetricPattern4} _2022 + * @property {MetricPattern4} _2023 + * @property {MetricPattern4} _2024 + * @property {MetricPattern4} _2025 + * @property {MetricPattern4} _2026 + */ + /** * @typedef {Object} MetricsTree_Market_Dca_ClassMaxDrawdown * @property {MetricPattern4} _2015 @@ -3828,22 +3844,6 @@ function createRealizedPriceExtraPattern(client, acc) { * @property {MetricPattern4} _2026 */ -/** - * @typedef {Object} MetricsTree_Market_Dca_ClassReturns - * @property {MetricPattern4} _2015 - * @property {MetricPattern4} _2016 - * @property {MetricPattern4} _2017 - * @property {MetricPattern4} _2018 - * @property {MetricPattern4} _2019 - * @property {MetricPattern4} _2020 - * @property {MetricPattern4} _2021 - * @property {MetricPattern4} _2022 - * @property {MetricPattern4} _2023 - * @property {MetricPattern4} _2024 - * @property {MetricPattern4} _2025 - * @property {MetricPattern4} _2026 - */ - /** * @typedef {Object} MetricsTree_Market_Dca_ClassStack * @property {_2015Pattern} _2015 @@ -4371,7 +4371,7 @@ function createRealizedPriceExtraPattern(client, acc) { * @extends BrkClientBase */ class BrkClient extends BrkClientBase { - VERSION = "v0.1.0"; + VERSION = "v0.1.1"; INDEXES = /** @type {const} */ ([ "dateindex", @@ -5906,7 +5906,20 @@ class BrkClient extends BrkClientBase { _2025: createMetricPattern4(this, 'dca_class_2025_days_in_loss'), _2026: createMetricPattern4(this, 'dca_class_2026_days_in_loss'), }, - classDaysInProfit: createClassDaysInLossPattern(this, 'dca_class'), + classDaysInProfit: { + _2015: createMetricPattern4(this, 'dca_class_2015_days_in_profit'), + _2016: createMetricPattern4(this, 'dca_class_2016_days_in_profit'), + _2017: createMetricPattern4(this, 'dca_class_2017_days_in_profit'), + _2018: createMetricPattern4(this, 'dca_class_2018_days_in_profit'), + _2019: createMetricPattern4(this, 'dca_class_2019_days_in_profit'), + _2020: createMetricPattern4(this, 'dca_class_2020_days_in_profit'), + _2021: createMetricPattern4(this, 'dca_class_2021_days_in_profit'), + _2022: createMetricPattern4(this, 'dca_class_2022_days_in_profit'), + _2023: createMetricPattern4(this, 'dca_class_2023_days_in_profit'), + _2024: createMetricPattern4(this, 'dca_class_2024_days_in_profit'), + _2025: createMetricPattern4(this, 'dca_class_2025_days_in_profit'), + _2026: createMetricPattern4(this, 'dca_class_2026_days_in_profit'), + }, classMaxDrawdown: { _2015: createMetricPattern4(this, 'dca_class_2015_max_drawdown'), _2016: createMetricPattern4(this, 'dca_class_2016_max_drawdown'), @@ -5935,20 +5948,7 @@ class BrkClient extends BrkClientBase { _2025: createMetricPattern4(this, 'dca_class_2025_max_return'), _2026: createMetricPattern4(this, 'dca_class_2026_max_return'), }, - classReturns: { - _2015: createMetricPattern4(this, 'dca_class_2015_returns'), - _2016: createMetricPattern4(this, 'dca_class_2016_returns'), - _2017: createMetricPattern4(this, 'dca_class_2017_returns'), - _2018: createMetricPattern4(this, 'dca_class_2018_returns'), - _2019: createMetricPattern4(this, 'dca_class_2019_returns'), - _2020: createMetricPattern4(this, 'dca_class_2020_returns'), - _2021: createMetricPattern4(this, 'dca_class_2021_returns'), - _2022: createMetricPattern4(this, 'dca_class_2022_returns'), - _2023: createMetricPattern4(this, 'dca_class_2023_returns'), - _2024: createMetricPattern4(this, 'dca_class_2024_returns'), - _2025: createMetricPattern4(this, 'dca_class_2025_returns'), - _2026: createMetricPattern4(this, 'dca_class_2026_returns'), - }, + classReturns: createClassDaysInLossPattern(this, 'dca_class'), classStack: { _2015: create_2015Pattern(this, 'dca_class_2015_stack'), _2016: create_2015Pattern(this, 'dca_class_2016_stack'), diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 7149d5090..bbbdcf2cf 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -2047,18 +2047,18 @@ class ClassDaysInLossPattern(Generic[T]): def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_days_in_profit')) - self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_days_in_profit')) - self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_days_in_profit')) - self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_days_in_profit')) - self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_days_in_profit')) - self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_days_in_profit')) - self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_days_in_profit')) - self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_days_in_profit')) - self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_days_in_profit')) - self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_days_in_profit')) - self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_days_in_profit')) - self._2026: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2026_days_in_profit')) + self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_returns')) + self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_returns')) + self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_returns')) + self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_returns')) + self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_returns')) + self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_returns')) + self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_returns')) + self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_returns')) + self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_returns')) + self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_returns')) + self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_returns')) + self._2026: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2026_returns')) class BitcoinPattern: """Pattern struct for repeated tree structure.""" @@ -2094,22 +2094,6 @@ class DollarsPattern(Generic[T]): self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90')) self.sum: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'sum')) -class RelativePattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')) - self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')) - self.net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')) - self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')) - self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')) - self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')) - self.unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')) - self.unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')) - self.unrealized_profit_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')) - self.unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')) - class RelativePattern: """Pattern struct for repeated tree structure.""" @@ -2126,6 +2110,22 @@ class RelativePattern: self.unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap')) self.unrealized_profit_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap')) +class RelativePattern2: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.neg_unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_market_cap')) + self.neg_unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_own_total_unrealized_pnl')) + self.net_unrealized_pnl_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_market_cap')) + self.net_unrealized_pnl_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_own_total_unrealized_pnl')) + self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')) + self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')) + self.unrealized_loss_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_market_cap')) + self.unrealized_loss_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_own_total_unrealized_pnl')) + self.unrealized_profit_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap')) + self.unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl')) + class CountPattern2(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2201,6 +2201,19 @@ class _0satsPattern: self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) +class PeriodCagrPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self._10y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('10y', acc)) + self._2y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('2y', acc)) + self._3y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('3y', acc)) + self._4y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('4y', acc)) + self._5y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('5y', acc)) + self._6y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('6y', acc)) + self._8y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('8y', acc)) + class _100btcPattern: """Pattern struct for repeated tree structure.""" @@ -2240,32 +2253,6 @@ class _10yPattern: self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) -class UnrealizedPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.neg_unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss')) - self.net_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl')) - self.supply_in_loss: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_loss')) - self.supply_in_profit: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_profit')) - self.total_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'total_unrealized_pnl')) - self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss')) - self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit')) - -class PeriodCagrPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self._10y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('10y', acc)) - self._2y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('2y', acc)) - self._3y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('3y', acc)) - self._4y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('4y', acc)) - self._5y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('5y', acc)) - self._6y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('6y', acc)) - self._8y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('8y', acc)) - class _10yTo12yPattern: """Pattern struct for repeated tree structure.""" @@ -2279,6 +2266,19 @@ class _10yTo12yPattern: self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) +class UnrealizedPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.neg_unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss')) + self.net_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl')) + self.supply_in_loss: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_loss')) + self.supply_in_profit: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_profit')) + self.total_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'total_unrealized_pnl')) + self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss')) + self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit')) + class AllPattern: """Pattern struct for repeated tree structure.""" @@ -2312,23 +2312,14 @@ class SplitPattern2(Generic[T]): self.low: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'low')) self.open: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'open')) -class UnclaimedRewardsPattern: +class SegwitAdoptionPattern: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.bitcoin: BitcoinPattern2[Bitcoin] = BitcoinPattern2(client, _m(acc, 'btc')) - self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) - self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) - -class _2015Pattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.bitcoin: MetricPattern4[Bitcoin] = MetricPattern4(client, _m(acc, 'btc')) - self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd')) - self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc) + self.base: MetricPattern11[StoredF32] = MetricPattern11(client, acc) + self.cumulative: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'cumulative')) + self.sum: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'sum')) class ActiveSupplyPattern: """Pattern struct for repeated tree structure.""" @@ -2339,6 +2330,15 @@ class ActiveSupplyPattern: self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc) +class _2015Pattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.bitcoin: MetricPattern4[Bitcoin] = MetricPattern4(client, _m(acc, 'btc')) + self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd')) + self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc) + class CoinbasePattern: """Pattern struct for repeated tree structure.""" @@ -2348,23 +2348,14 @@ class CoinbasePattern: self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd')) self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc) -class SegwitAdoptionPattern: +class UnclaimedRewardsPattern: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.base: MetricPattern11[StoredF32] = MetricPattern11(client, acc) - self.cumulative: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'cumulative')) - self.sum: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'sum')) - -class CostBasisPattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.max: ActivePricePattern = ActivePricePattern(client, _m(acc, 'max_cost_basis')) - self.min: ActivePricePattern = ActivePricePattern(client, _m(acc, 'min_cost_basis')) - self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) + self.bitcoin: BitcoinPattern2[Bitcoin] = BitcoinPattern2(client, _m(acc, 'btc')) + self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) + self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) class CoinbasePattern2: """Pattern struct for repeated tree structure.""" @@ -2375,13 +2366,14 @@ class CoinbasePattern2: self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) -class RelativePattern4: +class CostBasisPattern2: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'loss_rel_to_own_supply')) - self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'profit_rel_to_own_supply')) + self.max: ActivePricePattern = ActivePricePattern(client, _m(acc, 'max_cost_basis')) + self.min: ActivePricePattern = ActivePricePattern(client, _m(acc, 'min_cost_basis')) + self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) class ActivePricePattern: """Pattern struct for repeated tree structure.""" @@ -2391,13 +2383,13 @@ class ActivePricePattern: self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, acc) self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, _m(acc, 'sats')) -class _1dReturns1mSdPattern: +class _0sdUsdPattern: """Pattern struct for repeated tree structure.""" def __init__(self, client: BrkClientBase, acc: str): """Create pattern node with accumulated metric name.""" - self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd')) - self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma')) + self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, acc) + self.sats: MetricPattern4[SatsFract] = MetricPattern4(client, _m(acc, 'sats')) class SupplyPattern2: """Pattern struct for repeated tree structure.""" @@ -2407,14 +2399,6 @@ class SupplyPattern2: self.halved: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'halved')) self.total: ActiveSupplyPattern = ActiveSupplyPattern(client, acc) -class _0sdUsdPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, acc) - self.sats: MetricPattern4[SatsFract] = MetricPattern4(client, _m(acc, 'sats')) - class CostBasisPattern: """Pattern struct for repeated tree structure.""" @@ -2423,6 +2407,22 @@ class CostBasisPattern: self.max: ActivePricePattern = ActivePricePattern(client, _m(acc, 'max_cost_basis')) self.min: ActivePricePattern = ActivePricePattern(client, _m(acc, 'min_cost_basis')) +class _1dReturns1mSdPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd')) + self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma')) + +class RelativePattern4: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'loss_rel_to_own_supply')) + self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'profit_rel_to_own_supply')) + class BlockCountPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2447,13 +2447,6 @@ class SatsPattern(Generic[T]): self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc_sats')) self.split: SplitPattern2[T] = SplitPattern2(client, _m(acc, 'sats')) -class OutputsPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc) - class RealizedPriceExtraPattern: """Pattern struct for repeated tree structure.""" @@ -2461,6 +2454,13 @@ class RealizedPriceExtraPattern: """Create pattern node with accumulated metric name.""" self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc) +class OutputsPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc) + # Metrics tree classes class MetricsTree_Addresses: @@ -3384,6 +3384,23 @@ class MetricsTree_Market_Dca_ClassDaysInLoss: self._2025: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2025_days_in_loss') self._2026: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2026_days_in_loss') +class MetricsTree_Market_Dca_ClassDaysInProfit: + """Metrics tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self._2015: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2015_days_in_profit') + self._2016: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2016_days_in_profit') + self._2017: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2017_days_in_profit') + self._2018: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2018_days_in_profit') + self._2019: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2019_days_in_profit') + self._2020: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2020_days_in_profit') + self._2021: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2021_days_in_profit') + self._2022: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2022_days_in_profit') + self._2023: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2023_days_in_profit') + self._2024: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2024_days_in_profit') + self._2025: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2025_days_in_profit') + self._2026: MetricPattern4[StoredU32] = MetricPattern4(client, 'dca_class_2026_days_in_profit') + class MetricsTree_Market_Dca_ClassMaxDrawdown: """Metrics tree node.""" @@ -3418,23 +3435,6 @@ class MetricsTree_Market_Dca_ClassMaxReturn: self._2025: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2025_max_return') self._2026: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2026_max_return') -class MetricsTree_Market_Dca_ClassReturns: - """Metrics tree node.""" - - def __init__(self, client: BrkClientBase, base_path: str = ''): - self._2015: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2015_returns') - self._2016: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2016_returns') - self._2017: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2017_returns') - self._2018: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2018_returns') - self._2019: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2019_returns') - self._2020: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2020_returns') - self._2021: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2021_returns') - self._2022: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2022_returns') - self._2023: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2023_returns') - self._2024: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2024_returns') - self._2025: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2025_returns') - self._2026: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2026_returns') - class MetricsTree_Market_Dca_ClassStack: """Metrics tree node.""" @@ -3475,10 +3475,10 @@ class MetricsTree_Market_Dca: def __init__(self, client: BrkClientBase, base_path: str = ''): self.class_average_price: MetricsTree_Market_Dca_ClassAveragePrice = MetricsTree_Market_Dca_ClassAveragePrice(client) self.class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss = MetricsTree_Market_Dca_ClassDaysInLoss(client) - self.class_days_in_profit: ClassDaysInLossPattern[StoredU32] = ClassDaysInLossPattern(client, 'dca_class') + self.class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit = MetricsTree_Market_Dca_ClassDaysInProfit(client) self.class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown = MetricsTree_Market_Dca_ClassMaxDrawdown(client) self.class_max_return: MetricsTree_Market_Dca_ClassMaxReturn = MetricsTree_Market_Dca_ClassMaxReturn(client) - self.class_returns: MetricsTree_Market_Dca_ClassReturns = MetricsTree_Market_Dca_ClassReturns(client) + self.class_returns: ClassDaysInLossPattern[StoredF32] = ClassDaysInLossPattern(client, 'dca_class') self.class_stack: MetricsTree_Market_Dca_ClassStack = MetricsTree_Market_Dca_ClassStack(client) self.period_average_price: MetricsTree_Market_Dca_PeriodAveragePrice = MetricsTree_Market_Dca_PeriodAveragePrice(client) self.period_cagr: PeriodCagrPattern = PeriodCagrPattern(client, 'dca_cagr') @@ -4052,7 +4052,7 @@ class MetricsTree: class BrkClient(BrkClientBase): """Main BRK client with metrics tree and API methods.""" - VERSION = "v0.1.0" + VERSION = "v0.1.1" INDEXES = [ "dateindex",