diff --git a/crates/brk_bindgen/.gitignore b/crates/brk_bindgen/.gitignore index 168473d3f..e68118c4f 100644 --- a/crates/brk_bindgen/.gitignore +++ b/crates/brk_bindgen/.gitignore @@ -3,3 +3,4 @@ clients/ /*.js /*.rs /*.py +tests/output/ diff --git a/crates/brk_bindgen/src/generate/fields.rs b/crates/brk_bindgen/src/generate/fields.rs index 9afab1d58..914d77912 100644 --- a/crates/brk_bindgen/src/generate/fields.rs +++ b/crates/brk_bindgen/src/generate/fields.rs @@ -97,33 +97,24 @@ pub fn generate_tree_node_field( let type_ann = metadata.field_type_annotation(field, false, None, syntax.generic_syntax()); let value = if metadata.is_pattern_type(&field.rust_type) { - // Check if this pattern is parameterizable - let pattern = metadata.find_pattern(&field.rust_type); - let is_parameterizable = pattern.is_some_and(|p| p.is_parameterizable()); + // Use metric base only for parameterizable patterns + let use_base = metadata + .find_pattern(&field.rust_type) + .is_some_and(|p| p.is_parameterizable()) + && pattern_base.is_some(); - if is_parameterizable { - if let Some(base) = pattern_base { - // Use the detected metric base - let path = syntax.string_literal(base); - syntax.constructor(&field.rust_type, &path) - } else { - // Fallback to tree path - let path_expr = syntax.path_expr("base_path", &path_suffix(child_name)); - syntax.constructor(&field.rust_type, &path_expr) - } + let path_arg = if use_base { + syntax.string_literal(pattern_base.unwrap()) } else { - let path_expr = syntax.path_expr("base_path", &path_suffix(child_name)); - syntax.constructor(&field.rust_type, &path_expr) - } + syntax.path_expr("base_path", &path_suffix(child_name)) + }; + syntax.constructor(&field.rust_type, &path_arg) } else if let Some(accessor) = metadata.find_index_set_pattern(&field.indexes) { - // Leaf field - use actual metric name if provided - if let Some(metric_name) = pattern_base { - let path = syntax.string_literal(metric_name); - syntax.constructor(&accessor.name, &path) - } else { - let path_expr = syntax.path_expr("base_path", &path_suffix(child_name)); - syntax.constructor(&accessor.name, &path_expr) - } + // Leaf field - use metric name if provided, else tree path + let path_arg = pattern_base + .map(|name| syntax.string_literal(name)) + .unwrap_or_else(|| syntax.path_expr("base_path", &path_suffix(child_name))); + syntax.constructor(&accessor.name, &path_arg) } else if field.is_branch() { // Non-pattern branch - instantiate the nested struct let path_expr = syntax.path_expr("base_path", &path_suffix(child_name)); diff --git a/crates/brk_bindgen/src/generators/javascript/types.rs b/crates/brk_bindgen/src/generators/javascript/types.rs index 9b9255aee..0f0f89401 100644 --- a/crates/brk_bindgen/src/generators/javascript/types.rs +++ b/crates/brk_bindgen/src/generators/javascript/types.rs @@ -4,7 +4,7 @@ use std::fmt::Write; use serde_json::Value; -use crate::{TypeSchemas, generators::{MANUAL_GENERIC_TYPES, write_description}, ref_to_type_name, to_camel_case}; +use crate::{TypeSchemas, generators::{MANUAL_GENERIC_TYPES, write_description}, get_union_variants, ref_to_type_name, to_camel_case}; /// Generate JSDoc type definitions from OpenAPI schemas. pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { @@ -165,11 +165,7 @@ pub fn schema_to_js_type(schema: &Value, current_type: Option<&str>) -> String { } } - if let Some(variants) = schema - .get("anyOf") - .or_else(|| schema.get("oneOf")) - .and_then(|v| v.as_array()) - { + if let Some(variants) = get_union_variants(schema) { let types: Vec = variants .iter() .map(|v| schema_to_js_type(v, current_type)) diff --git a/crates/brk_bindgen/src/generators/python/types.rs b/crates/brk_bindgen/src/generators/python/types.rs index 4d340f900..de546e808 100644 --- a/crates/brk_bindgen/src/generators/python/types.rs +++ b/crates/brk_bindgen/src/generators/python/types.rs @@ -5,7 +5,7 @@ use std::fmt::Write; use serde_json::Value; -use crate::{TypeSchemas, escape_python_keyword, generators::MANUAL_GENERIC_TYPES, 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) { @@ -36,7 +36,7 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { for name in type_aliases { let schema = &schemas[&name]; let type_desc = schema.get("description").and_then(|d| d.as_str()); - let py_type = schema_to_python_type_quoting(schema, Some(&name), &typed_dict_set); + let py_type = schema_to_python_type(schema, Some(&name), Some(&typed_dict_set)); if let Some(desc) = type_desc { for line in desc.lines() { writeln!(output, "# {}", line).unwrap(); @@ -87,7 +87,7 @@ pub fn generate_type_definitions(output: &mut String, schemas: &TypeSchemas) { } for (prop_name, prop_schema) in props { - let prop_type = schema_to_python_type_ctx(prop_schema, Some(&name)); + let prop_type = schema_to_python_type(prop_schema, Some(&name), None); let safe_name = escape_python_keyword(prop_name); writeln!(output, " {}: {}", safe_name, prop_type).unwrap(); } @@ -193,13 +193,13 @@ fn json_type_to_python(ty: &str, schema: &Value, current_type: Option<&str>) -> "array" => { let item_type = schema .get("items") - .map(|s| schema_to_python_type_ctx(s, current_type)) + .map(|s| schema_to_python_type(s, current_type, None)) .unwrap_or_else(|| "Any".to_string()); format!("List[{}]", item_type) } "object" => { if let Some(add_props) = schema.get("additionalProperties") { - let value_type = schema_to_python_type_ctx(add_props, current_type); + let value_type = schema_to_python_type(add_props, current_type, None); return format!("dict[str, {}]", value_type); } "dict".to_string() @@ -208,15 +208,18 @@ fn json_type_to_python(ty: &str, schema: &Value, current_type: Option<&str>) -> } } -/// Convert JSON Schema to Python type, quoting types in the given set -fn schema_to_python_type_quoting( +/// Convert JSON Schema to Python type. +/// +/// - `current_type`: Used to detect and quote self-references for recursive types +/// - `quote_types`: Optional set of additional type names that should be quoted +pub fn schema_to_python_type( schema: &Value, current_type: Option<&str>, - quote_types: &HashSet, + quote_types: Option<&HashSet>, ) -> String { if let Some(all_of) = schema.get("allOf").and_then(|v| v.as_array()) { for item in all_of { - let resolved = schema_to_python_type_quoting(item, current_type, quote_types); + let resolved = schema_to_python_type(item, current_type, quote_types); if resolved != "Any" { return resolved; } @@ -227,67 +230,9 @@ fn schema_to_python_type_quoting( 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 - if current_type == Some(type_name) || quote_types.contains(type_name) { - return format!("\"{}\"", type_name); - } - return type_name.to_string(); - } - - // Handle enum (array of string values) - if let Some(enum_values) = schema.get("enum").and_then(|e| e.as_array()) { - let literals: Vec = enum_values - .iter() - .filter_map(|v| v.as_str()) - .map(|s| format!("\"{}\"", s)) - .collect(); - if !literals.is_empty() { - return format!("Literal[{}]", literals.join(", ")); - } - } - - if let Some(variants) = schema - .get("anyOf") - .or_else(|| schema.get("oneOf")) - .and_then(|v| v.as_array()) - { - let types: Vec = variants - .iter() - .map(|v| schema_to_python_type_quoting(v, current_type, quote_types)) - .collect(); - let filtered: Vec<_> = types.iter().filter(|t| *t != "Any").collect(); - if !filtered.is_empty() { - return format!( - "Union[{}]", - filtered - .iter() - .map(|s| s.as_str()) - .collect::>() - .join(", ") - ); - } - return format!("Union[{}]", types.join(", ")); - } - - // Fall back to regular conversion for other cases - schema_to_python_type_ctx(schema, current_type) -} - -/// Convert JSON Schema to Python type with context for detecting self-references -pub fn schema_to_python_type_ctx(schema: &Value, current_type: Option<&str>) -> String { - if let Some(all_of) = schema.get("allOf").and_then(|v| v.as_array()) { - for item in all_of { - let resolved = schema_to_python_type_ctx(item, current_type); - if resolved != "Any" { - return resolved; - } - } - } - - // Handle $ref - 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 to handle recursive types - if current_type == Some(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); } return type_name.to_string(); @@ -310,7 +255,7 @@ pub fn schema_to_python_type_ctx(schema: &Value, current_type: Option<&str>) -> let types: Vec = type_array .iter() .filter_map(|t| t.as_str()) - .filter(|t| *t != "null") // Filter out null for cleaner Optional handling + .filter(|t| *t != "null") .map(|t| json_type_to_python(t, schema, current_type)) .collect(); let has_null = type_array.iter().any(|t| t.as_str() == Some("null")); @@ -337,14 +282,10 @@ pub fn schema_to_python_type_ctx(schema: &Value, current_type: Option<&str>) -> } } - if let Some(variants) = schema - .get("anyOf") - .or_else(|| schema.get("oneOf")) - .and_then(|v| v.as_array()) - { + if let Some(variants) = get_union_variants(schema) { let types: Vec = variants .iter() - .map(|v| schema_to_python_type_ctx(v, current_type)) + .map(|v| schema_to_python_type(v, current_type, quote_types)) .collect(); let filtered: Vec<_> = types.iter().filter(|t| *t != "Any").collect(); if !filtered.is_empty() { diff --git a/crates/brk_bindgen/src/openapi.rs b/crates/brk_bindgen/src/openapi.rs index 4f10b0e13..d6b76713e 100644 --- a/crates/brk_bindgen/src/openapi.rs +++ b/crates/brk_bindgen/src/openapi.rs @@ -165,24 +165,17 @@ pub fn extract_endpoints(spec: &Spec) -> Vec { endpoints } -fn get_operations(path_item: &PathItem) -> Vec<(String, &Operation)> { - let mut ops = Vec::new(); - if let Some(op) = &path_item.get { - ops.push(("GET".to_string(), op)); - } - if let Some(op) = &path_item.post { - ops.push(("POST".to_string(), op)); - } - if let Some(op) = &path_item.put { - ops.push(("PUT".to_string(), op)); - } - if let Some(op) = &path_item.delete { - ops.push(("DELETE".to_string(), op)); - } - if let Some(op) = &path_item.patch { - ops.push(("PATCH".to_string(), op)); - } - ops +fn get_operations(path_item: &PathItem) -> Vec<(&'static str, &Operation)> { + [ + ("GET", &path_item.get), + ("POST", &path_item.post), + ("PUT", &path_item.put), + ("DELETE", &path_item.delete), + ("PATCH", &path_item.patch), + ] + .into_iter() + .filter_map(|(method, op)| op.as_ref().map(|o| (method, o))) + .collect() } fn extract_endpoint(path: &str, method: &str, operation: &Operation) -> Option { diff --git a/crates/brk_bindgen/src/types/schema.rs b/crates/brk_bindgen/src/types/schema.rs index 98ce7b6f9..64a17265f 100644 --- a/crates/brk_bindgen/src/types/schema.rs +++ b/crates/brk_bindgen/src/types/schema.rs @@ -33,3 +33,11 @@ pub fn extract_inner_type(type_str: &str) -> String { pub fn ref_to_type_name(ref_path: &str) -> Option<&str> { ref_path.rsplit('/').next() } + +/// Get union variants from anyOf or oneOf schema. +pub fn get_union_variants(schema: &Value) -> Option<&Vec> { + schema + .get("anyOf") + .or_else(|| schema.get("oneOf")) + .and_then(|v| v.as_array()) +} diff --git a/crates/brk_bindgen/tests/catalog_test.rs b/crates/brk_bindgen/tests/catalog_test.rs index 3d663ce5a..ecc9263d4 100644 --- a/crates/brk_bindgen/tests/catalog_test.rs +++ b/crates/brk_bindgen/tests/catalog_test.rs @@ -371,9 +371,11 @@ fn test_generated_rust_output() { ); println!(" Output size: {} bytes", rust_output.len()); - // Write output to actual client location - let output_path = concat!(env!("CARGO_MANIFEST_DIR"), "/../brk_client/src/lib.rs"); - std::fs::write(output_path, &rust_output).expect("Failed to write client output"); + // Write output to test directory (not actual client) + let output_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/output"); + std::fs::create_dir_all(output_dir).ok(); + let output_path = format!("{}/rust_client.rs", output_dir); + std::fs::write(&output_path, &rust_output).expect("Failed to write client output"); println!(" Wrote output to: {}", output_path); // Verify the output contains the key components @@ -468,12 +470,11 @@ fn test_generated_javascript_output() { println!(" Output size: {} bytes", js_output.len()); println!(" Output lines: {}", js_output.lines().count()); - // Write output to actual client location - let output_path = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../modules/brk-client/index.js" - ); - std::fs::write(output_path, &js_output).expect("Failed to write JS client output"); + // Write output to test directory (not actual client) + let output_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/output"); + std::fs::create_dir_all(output_dir).ok(); + let output_path = format!("{}/js_client.js", output_dir); + std::fs::write(&output_path, &js_output).expect("Failed to write JS client output"); println!(" Wrote output to: {}", output_path); // Verify the output contains key components @@ -562,12 +563,11 @@ fn test_generated_python_output() { println!(" Output size: {} bytes", py_output.len()); println!(" Output lines: {}", py_output.lines().count()); - // Write output to actual client location - let output_path = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../packages/brk_client/brk_client/__init__.py" - ); - std::fs::write(output_path, &py_output).expect("Failed to write Python client output"); + // Write output to test directory (not actual client) + let output_dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/output"); + std::fs::create_dir_all(output_dir).ok(); + let output_path = format!("{}/python_client.py", output_dir); + std::fs::write(&output_path, &py_output).expect("Failed to write Python client output"); println!(" Wrote output to: {}", output_path); // Verify the output contains key components diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index a597b79fa..49e9897c9 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -2642,56 +2642,6 @@ impl Price111dSmaPattern { } } -/// Pattern struct for repeated tree structure. -pub struct PercentilesPattern { - pub pct05: MetricPattern4, - pub pct10: MetricPattern4, - pub pct15: MetricPattern4, - pub pct20: MetricPattern4, - pub pct25: MetricPattern4, - pub pct30: MetricPattern4, - pub pct35: MetricPattern4, - pub pct40: MetricPattern4, - pub pct45: MetricPattern4, - pub pct50: MetricPattern4, - pub pct55: MetricPattern4, - pub pct60: MetricPattern4, - pub pct65: MetricPattern4, - pub pct70: MetricPattern4, - pub pct75: MetricPattern4, - pub pct80: MetricPattern4, - pub pct85: MetricPattern4, - pub pct90: MetricPattern4, - pub pct95: MetricPattern4, -} - -impl PercentilesPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - pct05: MetricPattern4::new(client.clone(), _m(&acc, "pct05")), - pct10: MetricPattern4::new(client.clone(), _m(&acc, "pct10")), - pct15: MetricPattern4::new(client.clone(), _m(&acc, "pct15")), - pct20: MetricPattern4::new(client.clone(), _m(&acc, "pct20")), - pct25: MetricPattern4::new(client.clone(), _m(&acc, "pct25")), - pct30: MetricPattern4::new(client.clone(), _m(&acc, "pct30")), - pct35: MetricPattern4::new(client.clone(), _m(&acc, "pct35")), - pct40: MetricPattern4::new(client.clone(), _m(&acc, "pct40")), - pct45: MetricPattern4::new(client.clone(), _m(&acc, "pct45")), - pct50: MetricPattern4::new(client.clone(), _m(&acc, "pct50")), - pct55: MetricPattern4::new(client.clone(), _m(&acc, "pct55")), - pct60: MetricPattern4::new(client.clone(), _m(&acc, "pct60")), - pct65: MetricPattern4::new(client.clone(), _m(&acc, "pct65")), - pct70: MetricPattern4::new(client.clone(), _m(&acc, "pct70")), - pct75: MetricPattern4::new(client.clone(), _m(&acc, "pct75")), - pct80: MetricPattern4::new(client.clone(), _m(&acc, "pct80")), - pct85: MetricPattern4::new(client.clone(), _m(&acc, "pct85")), - pct90: MetricPattern4::new(client.clone(), _m(&acc, "pct90")), - pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct ActivePriceRatioPattern { pub ratio: MetricPattern4, @@ -2742,6 +2692,56 @@ impl ActivePriceRatioPattern { } } +/// Pattern struct for repeated tree structure. +pub struct PercentilesPattern { + pub pct05: MetricPattern4, + pub pct10: MetricPattern4, + pub pct15: MetricPattern4, + pub pct20: MetricPattern4, + pub pct25: MetricPattern4, + pub pct30: MetricPattern4, + pub pct35: MetricPattern4, + pub pct40: MetricPattern4, + pub pct45: MetricPattern4, + pub pct50: MetricPattern4, + pub pct55: MetricPattern4, + pub pct60: MetricPattern4, + pub pct65: MetricPattern4, + pub pct70: MetricPattern4, + pub pct75: MetricPattern4, + pub pct80: MetricPattern4, + pub pct85: MetricPattern4, + pub pct90: MetricPattern4, + pub pct95: MetricPattern4, +} + +impl PercentilesPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + pct05: MetricPattern4::new(client.clone(), _m(&acc, "pct05")), + pct10: MetricPattern4::new(client.clone(), _m(&acc, "pct10")), + pct15: MetricPattern4::new(client.clone(), _m(&acc, "pct15")), + pct20: MetricPattern4::new(client.clone(), _m(&acc, "pct20")), + pct25: MetricPattern4::new(client.clone(), _m(&acc, "pct25")), + pct30: MetricPattern4::new(client.clone(), _m(&acc, "pct30")), + pct35: MetricPattern4::new(client.clone(), _m(&acc, "pct35")), + pct40: MetricPattern4::new(client.clone(), _m(&acc, "pct40")), + pct45: MetricPattern4::new(client.clone(), _m(&acc, "pct45")), + pct50: MetricPattern4::new(client.clone(), _m(&acc, "pct50")), + pct55: MetricPattern4::new(client.clone(), _m(&acc, "pct55")), + pct60: MetricPattern4::new(client.clone(), _m(&acc, "pct60")), + pct65: MetricPattern4::new(client.clone(), _m(&acc, "pct65")), + pct70: MetricPattern4::new(client.clone(), _m(&acc, "pct70")), + pct75: MetricPattern4::new(client.clone(), _m(&acc, "pct75")), + pct80: MetricPattern4::new(client.clone(), _m(&acc, "pct80")), + pct85: MetricPattern4::new(client.clone(), _m(&acc, "pct85")), + pct90: MetricPattern4::new(client.clone(), _m(&acc, "pct90")), + pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct RelativePattern5 { pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1, @@ -2993,17 +2993,17 @@ impl ClassAveragePricePattern { /// 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_average_price")), - _2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_average_price")), - _2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_average_price")), - _2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_average_price")), - _2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_average_price")), - _2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_average_price")), - _2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_average_price")), - _2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_average_price")), - _2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_average_price")), - _2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_average_price")), - _2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_average_price")), + _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")), } } } @@ -3168,36 +3168,6 @@ impl AddrCountPattern { } } -/// Pattern struct for repeated tree structure. -pub struct FullnessPattern { - pub average: MetricPattern2, - pub base: MetricPattern11, - pub max: MetricPattern2, - pub median: MetricPattern6, - pub min: MetricPattern2, - pub pct10: MetricPattern6, - pub pct25: MetricPattern6, - pub pct75: MetricPattern6, - pub pct90: MetricPattern6, -} - -impl FullnessPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - average: MetricPattern2::new(client.clone(), _m(&acc, "average")), - base: MetricPattern11::new(client.clone(), acc.clone()), - max: MetricPattern2::new(client.clone(), _m(&acc, "max")), - median: MetricPattern6::new(client.clone(), _m(&acc, "median")), - min: MetricPattern2::new(client.clone(), _m(&acc, "min")), - pct10: MetricPattern6::new(client.clone(), _m(&acc, "pct10")), - pct25: MetricPattern6::new(client.clone(), _m(&acc, "pct25")), - pct75: MetricPattern6::new(client.clone(), _m(&acc, "pct75")), - pct90: MetricPattern6::new(client.clone(), _m(&acc, "pct90")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct FeeRatePattern { pub average: MetricPattern1, @@ -3228,6 +3198,36 @@ impl FeeRatePattern { } } +/// Pattern struct for repeated tree structure. +pub struct FullnessPattern { + pub average: MetricPattern2, + pub base: MetricPattern11, + pub max: MetricPattern2, + pub median: MetricPattern6, + pub min: MetricPattern2, + pub pct10: MetricPattern6, + pub pct25: MetricPattern6, + pub pct75: MetricPattern6, + pub pct90: MetricPattern6, +} + +impl FullnessPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + average: MetricPattern2::new(client.clone(), _m(&acc, "average")), + base: MetricPattern11::new(client.clone(), acc.clone()), + max: MetricPattern2::new(client.clone(), _m(&acc, "max")), + median: MetricPattern6::new(client.clone(), _m(&acc, "median")), + min: MetricPattern2::new(client.clone(), _m(&acc, "min")), + pct10: MetricPattern6::new(client.clone(), _m(&acc, "pct10")), + pct25: MetricPattern6::new(client.clone(), _m(&acc, "pct25")), + pct75: MetricPattern6::new(client.clone(), _m(&acc, "pct75")), + pct90: MetricPattern6::new(client.clone(), _m(&acc, "pct90")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct _0satsPattern { pub activity: ActivityPattern2, @@ -3256,32 +3256,6 @@ 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, @@ -3308,6 +3282,58 @@ impl _100btcPattern { } } +/// Pattern struct for repeated tree structure. +pub struct _10yTo12yPattern { + pub activity: ActivityPattern2, + pub cost_basis: CostBasisPattern2, + pub outputs: OutputsPattern, + pub realized: RealizedPattern2, + pub relative: RelativePattern2, + pub supply: SupplyPattern2, + pub unrealized: UnrealizedPattern, +} + +impl _10yTo12yPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + activity: ActivityPattern2::new(client.clone(), acc.clone()), + cost_basis: CostBasisPattern2::new(client.clone(), acc.clone()), + outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")), + realized: RealizedPattern2::new(client.clone(), acc.clone()), + relative: RelativePattern2::new(client.clone(), acc.clone()), + supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")), + unrealized: UnrealizedPattern::new(client.clone(), acc.clone()), + } + } +} + +/// 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 _0satsPattern2 { pub activity: ActivityPattern2, @@ -3360,32 +3386,6 @@ impl UnrealizedPattern { } } -/// Pattern struct for repeated tree structure. -pub struct _10yTo12yPattern { - pub activity: ActivityPattern2, - pub cost_basis: CostBasisPattern2, - pub outputs: OutputsPattern, - pub realized: RealizedPattern2, - pub relative: RelativePattern2, - pub supply: SupplyPattern2, - pub unrealized: UnrealizedPattern, -} - -impl _10yTo12yPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - activity: ActivityPattern2::new(client.clone(), acc.clone()), - cost_basis: CostBasisPattern2::new(client.clone(), acc.clone()), - outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")), - realized: RealizedPattern2::new(client.clone(), acc.clone()), - relative: RelativePattern2::new(client.clone(), acc.clone()), - supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")), - unrealized: UnrealizedPattern::new(client.clone(), acc.clone()), - } - } -} - /// Pattern struct for repeated tree structure. pub struct _10yPattern { pub activity: ActivityPattern2, @@ -3454,6 +3454,96 @@ impl SplitPattern2 { } } +/// Pattern struct for repeated tree structure. +pub struct ActiveSupplyPattern { + pub bitcoin: MetricPattern1, + pub dollars: MetricPattern1, + pub sats: MetricPattern1, +} + +impl ActiveSupplyPattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")), + dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")), + sats: MetricPattern1::new(client.clone(), acc.clone()), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct CoinbasePattern { + pub bitcoin: BitcoinPattern, + pub dollars: DollarsPattern, + pub sats: DollarsPattern, +} + +impl CoinbasePattern { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + bitcoin: BitcoinPattern::new(client.clone(), _m(&acc, "btc")), + dollars: DollarsPattern::new(client.clone(), _m(&acc, "usd")), + sats: DollarsPattern::new(client.clone(), acc.clone()), + } + } +} + +/// Pattern struct for repeated tree structure. +pub struct CoinbasePattern2 { + pub bitcoin: BlockCountPattern, + pub dollars: BlockCountPattern, + pub sats: BlockCountPattern, +} + +impl CoinbasePattern2 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + bitcoin: BlockCountPattern::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 SegwitAdoptionPattern { + pub base: MetricPattern11, + pub cumulative: MetricPattern2, + pub sum: MetricPattern2, +} + +impl SegwitAdoptionPattern { + /// 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: MetricPattern1, + pub min: MetricPattern1, + pub percentiles: PercentilesPattern, +} + +impl CostBasisPattern2 { + /// Create a new pattern node with accumulated metric name. + pub fn new(client: Arc, acc: String) -> Self { + Self { + max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")), + min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")), + percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")), + } + } +} + /// Pattern struct for repeated tree structure. pub struct UnclaimedRewardsPattern { pub bitcoin: BitcoinPattern2, @@ -3490,96 +3580,6 @@ impl _2015Pattern { } } -/// Pattern struct for repeated tree structure. -pub struct CoinbasePattern { - pub bitcoin: BitcoinPattern, - pub dollars: DollarsPattern, - pub sats: DollarsPattern, -} - -impl CoinbasePattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - bitcoin: BitcoinPattern::new(client.clone(), _m(&acc, "btc")), - dollars: DollarsPattern::new(client.clone(), _m(&acc, "usd")), - sats: DollarsPattern::new(client.clone(), acc.clone()), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct CostBasisPattern2 { - pub max: MetricPattern1, - pub min: MetricPattern1, - pub percentiles: PercentilesPattern, -} - -impl CostBasisPattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")), - min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")), - percentiles: PercentilesPattern::new(client.clone(), _m(&acc, "cost_basis")), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct SegwitAdoptionPattern { - pub base: MetricPattern11, - pub cumulative: MetricPattern2, - pub sum: MetricPattern2, -} - -impl SegwitAdoptionPattern { - /// 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 ActiveSupplyPattern { - pub bitcoin: MetricPattern1, - pub dollars: MetricPattern1, - pub sats: MetricPattern1, -} - -impl ActiveSupplyPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")), - dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")), - sats: MetricPattern1::new(client.clone(), acc.clone()), - } - } -} - -/// Pattern struct for repeated tree structure. -pub struct CoinbasePattern2 { - pub bitcoin: BlockCountPattern, - pub dollars: BlockCountPattern, - pub sats: BlockCountPattern, -} - -impl CoinbasePattern2 { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - bitcoin: BlockCountPattern::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 _1dReturns1mSdPattern { pub sd: MetricPattern4, @@ -3596,6 +3596,22 @@ impl _1dReturns1mSdPattern { } } +/// 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 SupplyPattern2 { pub halved: ActiveSupplyPattern, @@ -3628,38 +3644,6 @@ impl CostBasisPattern { } } -/// 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 SatsPattern { - pub ohlc: MetricPattern1, - pub split: SplitPattern2, -} - -impl SatsPattern { - /// Create a new pattern node with accumulated metric name. - pub fn new(client: Arc, acc: String) -> Self { - Self { - ohlc: MetricPattern1::new(client.clone(), _m(&acc, "ohlc_sats")), - split: SplitPattern2::new(client.clone(), _m(&acc, "sats")), - } - } -} - /// Pattern struct for repeated tree structure. pub struct BlockCountPattern { pub cumulative: MetricPattern1, @@ -3693,15 +3677,17 @@ impl BitcoinPattern2 { } /// Pattern struct for repeated tree structure. -pub struct OutputsPattern { - pub utxo_count: MetricPattern1, +pub struct SatsPattern { + pub ohlc: MetricPattern1, + pub split: SplitPattern2, } -impl OutputsPattern { +impl SatsPattern { /// 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()), + ohlc: MetricPattern1::new(client.clone(), _m(&acc, "ohlc_sats")), + split: SplitPattern2::new(client.clone(), _m(&acc, "sats")), } } } @@ -3720,6 +3706,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. @@ -6266,6 +6266,7 @@ impl MetricsTree_Price_Cents_Split { /// Metrics tree node. pub struct MetricsTree_Price_Oracle { pub ohlc_cents: MetricPattern6, + pub ohlc_dollars: MetricPattern6, pub price_cents: MetricPattern11, pub tx_count: MetricPattern6, } @@ -6274,6 +6275,7 @@ impl MetricsTree_Price_Oracle { pub fn new(client: Arc, base_path: String) -> Self { Self { ohlc_cents: MetricPattern6::new(client.clone(), "oracle_ohlc_cents".to_string()), + ohlc_dollars: MetricPattern6::new(client.clone(), "oracle_ohlc_dollars".to_string()), price_cents: MetricPattern11::new(client.clone(), "orange_price_cents".to_string()), tx_count: MetricPattern6::new(client.clone(), "oracle_tx_count".to_string()), } diff --git a/crates/brk_computer/src/market/lookback/vecs.rs b/crates/brk_computer/src/market/lookback/vecs.rs index 61c59cb08..cb9a0b5b6 100644 --- a/crates/brk_computer/src/market/lookback/vecs.rs +++ b/crates/brk_computer/src/market/lookback/vecs.rs @@ -7,5 +7,6 @@ use crate::internal::ComputedFromDateLast; /// Price lookback metrics #[derive(Clone, Traversable)] pub struct Vecs { + #[traversable(flatten)] pub price_ago: ByLookbackPeriod>, } diff --git a/crates/brk_computer/src/price/oracle/compute.rs b/crates/brk_computer/src/price/oracle/compute.rs index 200f63542..1cb19e2c8 100644 --- a/crates/brk_computer/src/price/oracle/compute.rs +++ b/crates/brk_computer/src/price/oracle/compute.rs @@ -20,6 +20,9 @@ use super::{ }; use crate::{ComputeIndexes, indexes}; +/// Flush interval for periodic writes during oracle computation. +const FLUSH_INTERVAL: usize = 10_000; + impl Vecs { /// Compute oracle prices from on-chain data pub fn compute( @@ -275,9 +278,15 @@ impl Vecs { self.price_cents .truncate_push_at(height.to_usize(), price_cents)?; + + // Periodic flush to avoid data loss on long computations + if height.to_usize() % FLUSH_INTERVAL == 0 { + let _lock = exit.lock(); + self.price_cents.write()?; + } } - // Write height prices + // Final write { let _lock = exit.lock(); self.price_cents.write()?; diff --git a/crates/brk_computer/src/price/oracle/import.rs b/crates/brk_computer/src/price/oracle/import.rs index 532db95fd..e6c8004f6 100644 --- a/crates/brk_computer/src/price/oracle/import.rs +++ b/crates/brk_computer/src/price/oracle/import.rs @@ -1,6 +1,6 @@ use brk_error::Result; -use brk_types::Version; -use vecdb::{BytesVec, Database, ImportableVec, PcoVec}; +use brk_types::{DateIndex, OHLCCents, OHLCDollars, Version}; +use vecdb::{BytesVec, Database, ImportableVec, IterableCloneableVec, LazyVecFrom1, PcoVec}; use super::Vecs; @@ -10,9 +10,17 @@ impl Vecs { let ohlc_cents = BytesVec::forced_import(db, "oracle_ohlc_cents", version)?; let tx_count = PcoVec::forced_import(db, "oracle_tx_count", version)?; + let ohlc_dollars = LazyVecFrom1::init( + "oracle_ohlc", + version, + ohlc_cents.boxed_clone(), + |di: DateIndex, iter| iter.get(di).map(|o: OHLCCents| OHLCDollars::from(o)), + ); + Ok(Self { price_cents, ohlc_cents, + ohlc_dollars, tx_count, }) } diff --git a/crates/brk_computer/src/price/oracle/vecs.rs b/crates/brk_computer/src/price/oracle/vecs.rs index ba2a71059..0ea0be795 100644 --- a/crates/brk_computer/src/price/oracle/vecs.rs +++ b/crates/brk_computer/src/price/oracle/vecs.rs @@ -1,6 +1,6 @@ use brk_traversable::Traversable; -use brk_types::{Cents, DateIndex, Height, OHLCCents, StoredU32}; -use vecdb::{BytesVec, PcoVec}; +use brk_types::{Cents, DateIndex, Height, OHLCCents, OHLCDollars, StoredU32}; +use vecdb::{BytesVec, LazyVecFrom1, PcoVec}; /// Vectors storing UTXOracle-derived price data #[derive(Clone, Traversable)] @@ -13,6 +13,9 @@ pub struct Vecs { /// Uses BytesVec because OHLCCents is a complex type pub ohlc_cents: BytesVec, + /// Daily OHLC in dollars (lazy conversion from cents) + pub ohlc_dollars: LazyVecFrom1, + /// Number of qualifying transactions per day (for confidence) pub tx_count: PcoVec, } diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 46451c535..5b7258d89 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -2902,59 +2902,6 @@ function createPrice111dSmaPattern(client, acc) { }; } -/** - * @typedef {Object} PercentilesPattern - * @property {MetricPattern4} pct05 - * @property {MetricPattern4} pct10 - * @property {MetricPattern4} pct15 - * @property {MetricPattern4} pct20 - * @property {MetricPattern4} pct25 - * @property {MetricPattern4} pct30 - * @property {MetricPattern4} pct35 - * @property {MetricPattern4} pct40 - * @property {MetricPattern4} pct45 - * @property {MetricPattern4} pct50 - * @property {MetricPattern4} pct55 - * @property {MetricPattern4} pct60 - * @property {MetricPattern4} pct65 - * @property {MetricPattern4} pct70 - * @property {MetricPattern4} pct75 - * @property {MetricPattern4} pct80 - * @property {MetricPattern4} pct85 - * @property {MetricPattern4} pct90 - * @property {MetricPattern4} pct95 - */ - -/** - * Create a PercentilesPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {PercentilesPattern} - */ -function createPercentilesPattern(client, acc) { - return { - pct05: createMetricPattern4(client, _m(acc, "pct05")), - pct10: createMetricPattern4(client, _m(acc, "pct10")), - pct15: createMetricPattern4(client, _m(acc, "pct15")), - pct20: createMetricPattern4(client, _m(acc, "pct20")), - pct25: createMetricPattern4(client, _m(acc, "pct25")), - pct30: createMetricPattern4(client, _m(acc, "pct30")), - pct35: createMetricPattern4(client, _m(acc, "pct35")), - pct40: createMetricPattern4(client, _m(acc, "pct40")), - pct45: createMetricPattern4(client, _m(acc, "pct45")), - pct50: createMetricPattern4(client, _m(acc, "pct50")), - pct55: createMetricPattern4(client, _m(acc, "pct55")), - pct60: createMetricPattern4(client, _m(acc, "pct60")), - pct65: createMetricPattern4(client, _m(acc, "pct65")), - pct70: createMetricPattern4(client, _m(acc, "pct70")), - pct75: createMetricPattern4(client, _m(acc, "pct75")), - pct80: createMetricPattern4(client, _m(acc, "pct80")), - pct85: createMetricPattern4(client, _m(acc, "pct85")), - pct90: createMetricPattern4(client, _m(acc, "pct90")), - pct95: createMetricPattern4(client, _m(acc, "pct95")), - }; -} - /** * @typedef {Object} ActivePriceRatioPattern * @property {MetricPattern4} ratio @@ -3008,6 +2955,59 @@ function createActivePriceRatioPattern(client, acc) { }; } +/** + * @typedef {Object} PercentilesPattern + * @property {MetricPattern4} pct05 + * @property {MetricPattern4} pct10 + * @property {MetricPattern4} pct15 + * @property {MetricPattern4} pct20 + * @property {MetricPattern4} pct25 + * @property {MetricPattern4} pct30 + * @property {MetricPattern4} pct35 + * @property {MetricPattern4} pct40 + * @property {MetricPattern4} pct45 + * @property {MetricPattern4} pct50 + * @property {MetricPattern4} pct55 + * @property {MetricPattern4} pct60 + * @property {MetricPattern4} pct65 + * @property {MetricPattern4} pct70 + * @property {MetricPattern4} pct75 + * @property {MetricPattern4} pct80 + * @property {MetricPattern4} pct85 + * @property {MetricPattern4} pct90 + * @property {MetricPattern4} pct95 + */ + +/** + * Create a PercentilesPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {PercentilesPattern} + */ +function createPercentilesPattern(client, acc) { + return { + pct05: createMetricPattern4(client, _m(acc, "pct05")), + pct10: createMetricPattern4(client, _m(acc, "pct10")), + pct15: createMetricPattern4(client, _m(acc, "pct15")), + pct20: createMetricPattern4(client, _m(acc, "pct20")), + pct25: createMetricPattern4(client, _m(acc, "pct25")), + pct30: createMetricPattern4(client, _m(acc, "pct30")), + pct35: createMetricPattern4(client, _m(acc, "pct35")), + pct40: createMetricPattern4(client, _m(acc, "pct40")), + pct45: createMetricPattern4(client, _m(acc, "pct45")), + pct50: createMetricPattern4(client, _m(acc, "pct50")), + pct55: createMetricPattern4(client, _m(acc, "pct55")), + pct60: createMetricPattern4(client, _m(acc, "pct60")), + pct65: createMetricPattern4(client, _m(acc, "pct65")), + pct70: createMetricPattern4(client, _m(acc, "pct70")), + pct75: createMetricPattern4(client, _m(acc, "pct75")), + pct80: createMetricPattern4(client, _m(acc, "pct80")), + pct85: createMetricPattern4(client, _m(acc, "pct85")), + pct90: createMetricPattern4(client, _m(acc, "pct90")), + pct95: createMetricPattern4(client, _m(acc, "pct95")), + }; +} + /** * @typedef {Object} RelativePattern5 * @property {MetricPattern1} negUnrealizedLossRelToMarketCap @@ -3338,17 +3338,17 @@ function createBitcoinPattern(client, acc) { */ function createClassAveragePricePattern(client, acc) { return { - _2015: createMetricPattern4(client, _m(acc, "2015_average_price")), - _2016: createMetricPattern4(client, _m(acc, "2016_average_price")), - _2017: createMetricPattern4(client, _m(acc, "2017_average_price")), - _2018: createMetricPattern4(client, _m(acc, "2018_average_price")), - _2019: createMetricPattern4(client, _m(acc, "2019_average_price")), - _2020: createMetricPattern4(client, _m(acc, "2020_average_price")), - _2021: createMetricPattern4(client, _m(acc, "2021_average_price")), - _2022: createMetricPattern4(client, _m(acc, "2022_average_price")), - _2023: createMetricPattern4(client, _m(acc, "2023_average_price")), - _2024: createMetricPattern4(client, _m(acc, "2024_average_price")), - _2025: createMetricPattern4(client, _m(acc, "2025_average_price")), + _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")), }; } @@ -3588,41 +3588,6 @@ function createAddrCountPattern(client, acc) { }; } -/** - * @template T - * @typedef {Object} FullnessPattern - * @property {MetricPattern2} average - * @property {MetricPattern11} base - * @property {MetricPattern2} max - * @property {MetricPattern6} median - * @property {MetricPattern2} min - * @property {MetricPattern6} pct10 - * @property {MetricPattern6} pct25 - * @property {MetricPattern6} pct75 - * @property {MetricPattern6} pct90 - */ - -/** - * Create a FullnessPattern pattern node - * @template T - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {FullnessPattern} - */ -function createFullnessPattern(client, acc) { - return { - average: createMetricPattern2(client, _m(acc, "average")), - base: createMetricPattern11(client, acc), - max: createMetricPattern2(client, _m(acc, "max")), - median: createMetricPattern6(client, _m(acc, "median")), - min: createMetricPattern2(client, _m(acc, "min")), - pct10: createMetricPattern6(client, _m(acc, "pct10")), - pct25: createMetricPattern6(client, _m(acc, "pct25")), - pct75: createMetricPattern6(client, _m(acc, "pct75")), - pct90: createMetricPattern6(client, _m(acc, "pct90")), - }; -} - /** * @template T * @typedef {Object} FeeRatePattern @@ -3658,6 +3623,41 @@ function createFeeRatePattern(client, acc) { }; } +/** + * @template T + * @typedef {Object} FullnessPattern + * @property {MetricPattern2} average + * @property {MetricPattern11} base + * @property {MetricPattern2} max + * @property {MetricPattern6} median + * @property {MetricPattern2} min + * @property {MetricPattern6} pct10 + * @property {MetricPattern6} pct25 + * @property {MetricPattern6} pct75 + * @property {MetricPattern6} pct90 + */ + +/** + * Create a FullnessPattern pattern node + * @template T + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {FullnessPattern} + */ +function createFullnessPattern(client, acc) { + return { + average: createMetricPattern2(client, _m(acc, "average")), + base: createMetricPattern11(client, acc), + max: createMetricPattern2(client, _m(acc, "max")), + median: createMetricPattern6(client, _m(acc, "median")), + min: createMetricPattern2(client, _m(acc, "min")), + pct10: createMetricPattern6(client, _m(acc, "pct10")), + pct25: createMetricPattern6(client, _m(acc, "pct25")), + pct75: createMetricPattern6(client, _m(acc, "pct75")), + pct90: createMetricPattern6(client, _m(acc, "pct90")), + }; +} + /** * @typedef {Object} _0satsPattern * @property {ActivityPattern2} activity @@ -3689,35 +3689,6 @@ 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 @@ -3747,6 +3718,64 @@ function create_100btcPattern(client, acc) { }; } +/** + * @typedef {Object} _10yTo12yPattern + * @property {ActivityPattern2} activity + * @property {CostBasisPattern2} costBasis + * @property {OutputsPattern} outputs + * @property {RealizedPattern2} realized + * @property {RelativePattern2} relative + * @property {SupplyPattern2} supply + * @property {UnrealizedPattern} unrealized + */ + +/** + * Create a _10yTo12yPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {_10yTo12yPattern} + */ +function create_10yTo12yPattern(client, acc) { + return { + activity: createActivityPattern2(client, acc), + costBasis: createCostBasisPattern2(client, acc), + outputs: createOutputsPattern(client, _m(acc, "utxo_count")), + realized: createRealizedPattern2(client, acc), + relative: createRelativePattern2(client, acc), + supply: createSupplyPattern2(client, _m(acc, "supply")), + unrealized: createUnrealizedPattern(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} _0satsPattern2 * @property {ActivityPattern2} activity @@ -3820,35 +3849,6 @@ function createUnrealizedPattern(client, acc) { }; } -/** - * @typedef {Object} _10yTo12yPattern - * @property {ActivityPattern2} activity - * @property {CostBasisPattern2} costBasis - * @property {OutputsPattern} outputs - * @property {RealizedPattern2} realized - * @property {RelativePattern2} relative - * @property {SupplyPattern2} supply - * @property {UnrealizedPattern} unrealized - */ - -/** - * Create a _10yTo12yPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {_10yTo12yPattern} - */ -function create_10yTo12yPattern(client, acc) { - return { - activity: createActivityPattern2(client, acc), - costBasis: createCostBasisPattern2(client, acc), - outputs: createOutputsPattern(client, _m(acc, "utxo_count")), - realized: createRealizedPattern2(client, acc), - relative: createRelativePattern2(client, acc), - supply: createSupplyPattern2(client, _m(acc, "supply")), - unrealized: createUnrealizedPattern(client, acc), - }; -} - /** * @typedef {Object} _10yPattern * @property {ActivityPattern2} activity @@ -3940,6 +3940,111 @@ function createSplitPattern2(client, acc) { }; } +/** + * @typedef {Object} ActiveSupplyPattern + * @property {MetricPattern1} bitcoin + * @property {MetricPattern1} dollars + * @property {MetricPattern1} sats + */ + +/** + * Create a ActiveSupplyPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {ActiveSupplyPattern} + */ +function createActiveSupplyPattern(client, acc) { + return { + bitcoin: createMetricPattern1(client, _m(acc, "btc")), + dollars: createMetricPattern1(client, _m(acc, "usd")), + sats: createMetricPattern1(client, acc), + }; +} + +/** + * @typedef {Object} CoinbasePattern + * @property {BitcoinPattern} bitcoin + * @property {DollarsPattern} dollars + * @property {DollarsPattern} sats + */ + +/** + * Create a CoinbasePattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {CoinbasePattern} + */ +function createCoinbasePattern(client, acc) { + return { + bitcoin: createBitcoinPattern(client, _m(acc, "btc")), + dollars: createDollarsPattern(client, _m(acc, "usd")), + sats: createDollarsPattern(client, acc), + }; +} + +/** + * @typedef {Object} CoinbasePattern2 + * @property {BlockCountPattern} bitcoin + * @property {BlockCountPattern} dollars + * @property {BlockCountPattern} sats + */ + +/** + * Create a CoinbasePattern2 pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {CoinbasePattern2} + */ +function createCoinbasePattern2(client, acc) { + return { + bitcoin: createBlockCountPattern(client, _m(acc, "btc")), + dollars: createBlockCountPattern(client, _m(acc, "usd")), + sats: createBlockCountPattern(client, acc), + }; +} + +/** + * @typedef {Object} SegwitAdoptionPattern + * @property {MetricPattern11} base + * @property {MetricPattern2} cumulative + * @property {MetricPattern2} sum + */ + +/** + * Create a SegwitAdoptionPattern pattern node + * @param {BrkClientBase} client + * @param {string} acc - Accumulated metric name + * @returns {SegwitAdoptionPattern} + */ +function createSegwitAdoptionPattern(client, acc) { + return { + base: createMetricPattern11(client, acc), + cumulative: createMetricPattern2(client, _m(acc, "cumulative")), + sum: createMetricPattern2(client, _m(acc, "sum")), + }; +} + +/** + * @typedef {Object} CostBasisPattern2 + * @property {MetricPattern1} max + * @property {MetricPattern1} 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: createMetricPattern1(client, _m(acc, "max_cost_basis")), + min: createMetricPattern1(client, _m(acc, "min_cost_basis")), + percentiles: createPercentilesPattern(client, _m(acc, "cost_basis")), + }; +} + /** * @typedef {Object} UnclaimedRewardsPattern * @property {BitcoinPattern2} bitcoin @@ -3982,111 +4087,6 @@ function create_2015Pattern(client, acc) { }; } -/** - * @typedef {Object} CoinbasePattern - * @property {BitcoinPattern} bitcoin - * @property {DollarsPattern} dollars - * @property {DollarsPattern} sats - */ - -/** - * Create a CoinbasePattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {CoinbasePattern} - */ -function createCoinbasePattern(client, acc) { - return { - bitcoin: createBitcoinPattern(client, _m(acc, "btc")), - dollars: createDollarsPattern(client, _m(acc, "usd")), - sats: createDollarsPattern(client, acc), - }; -} - -/** - * @typedef {Object} CostBasisPattern2 - * @property {MetricPattern1} max - * @property {MetricPattern1} 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: createMetricPattern1(client, _m(acc, "max_cost_basis")), - min: createMetricPattern1(client, _m(acc, "min_cost_basis")), - percentiles: createPercentilesPattern(client, _m(acc, "cost_basis")), - }; -} - -/** - * @typedef {Object} SegwitAdoptionPattern - * @property {MetricPattern11} base - * @property {MetricPattern2} cumulative - * @property {MetricPattern2} sum - */ - -/** - * Create a SegwitAdoptionPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {SegwitAdoptionPattern} - */ -function createSegwitAdoptionPattern(client, acc) { - return { - base: createMetricPattern11(client, acc), - cumulative: createMetricPattern2(client, _m(acc, "cumulative")), - sum: createMetricPattern2(client, _m(acc, "sum")), - }; -} - -/** - * @typedef {Object} ActiveSupplyPattern - * @property {MetricPattern1} bitcoin - * @property {MetricPattern1} dollars - * @property {MetricPattern1} sats - */ - -/** - * Create a ActiveSupplyPattern pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {ActiveSupplyPattern} - */ -function createActiveSupplyPattern(client, acc) { - return { - bitcoin: createMetricPattern1(client, _m(acc, "btc")), - dollars: createMetricPattern1(client, _m(acc, "usd")), - sats: createMetricPattern1(client, acc), - }; -} - -/** - * @typedef {Object} CoinbasePattern2 - * @property {BlockCountPattern} bitcoin - * @property {BlockCountPattern} dollars - * @property {BlockCountPattern} sats - */ - -/** - * Create a CoinbasePattern2 pattern node - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {CoinbasePattern2} - */ -function createCoinbasePattern2(client, acc) { - return { - bitcoin: createBlockCountPattern(client, _m(acc, "btc")), - dollars: createBlockCountPattern(client, _m(acc, "usd")), - sats: createBlockCountPattern(client, acc), - }; -} - /** * @typedef {Object} _1dReturns1mSdPattern * @property {MetricPattern4} sd @@ -4106,6 +4106,31 @@ function create_1dReturns1mSdPattern(client, acc) { }; } +/** + * @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"), + ), + }; +} + /** * @typedef {Object} SupplyPattern2 * @property {ActiveSupplyPattern} halved @@ -4144,52 +4169,6 @@ function createCostBasisPattern(client, acc) { }; } -/** - * @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} SatsPattern - * @property {MetricPattern1} ohlc - * @property {SplitPattern2} split - */ - -/** - * Create a SatsPattern pattern node - * @template T - * @param {BrkClientBase} client - * @param {string} acc - Accumulated metric name - * @returns {SatsPattern} - */ -function createSatsPattern(client, acc) { - return { - ohlc: createMetricPattern1(client, _m(acc, "ohlc_sats")), - split: createSplitPattern2(client, _m(acc, "sats")), - }; -} - /** * @template T * @typedef {Object} BlockCountPattern @@ -4233,19 +4212,23 @@ function createBitcoinPattern2(client, acc) { } /** - * @typedef {Object} OutputsPattern - * @property {MetricPattern1} utxoCount + * @template T + * @typedef {Object} SatsPattern + * @property {MetricPattern1} ohlc + * @property {SplitPattern2} split */ /** - * Create a OutputsPattern pattern node + * Create a SatsPattern pattern node + * @template T * @param {BrkClientBase} client * @param {string} acc - Accumulated metric name - * @returns {OutputsPattern} + * @returns {SatsPattern} */ -function createOutputsPattern(client, acc) { +function createSatsPattern(client, acc) { return { - utxoCount: createMetricPattern1(client, acc), + ohlc: createMetricPattern1(client, _m(acc, "ohlc_sats")), + split: createSplitPattern2(client, _m(acc, "sats")), }; } @@ -4266,6 +4249,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 /** @@ -5408,6 +5408,7 @@ function createRealizedPriceExtraPattern(client, acc) { /** * @typedef {Object} MetricsTree_Price_Oracle * @property {MetricPattern6} ohlcCents + * @property {MetricPattern6} ohlcDollars * @property {MetricPattern11} priceCents * @property {MetricPattern6} txCount */ @@ -7617,6 +7618,7 @@ class BrkClient extends BrkClientBase { }, oracle: { ohlcCents: createMetricPattern6(this, "oracle_ohlc_cents"), + ohlcDollars: createMetricPattern6(this, "oracle_ohlc_dollars"), priceCents: createMetricPattern11(this, "orange_price_cents"), txCount: createMetricPattern6(this, "oracle_tx_count"), }, diff --git a/modules/brk-client/tests/tree.js b/modules/brk-client/tests/tree.js index 4e9212767..50138c8c0 100644 --- a/modules/brk-client/tests/tree.js +++ b/modules/brk-client/tests/tree.js @@ -4,31 +4,44 @@ import { BrkClient } from "../index.js"; +/** + * @typedef {import('../index.js').AnyMetricPattern} AnyMetricPattern + */ + +/** + * Check if an object is a metric pattern (has indexes() method and by object). + * @param {any} obj + * @returns {obj is AnyMetricPattern} + */ +function isMetricPattern(obj) { + return ( + obj && + typeof obj === "object" && + typeof obj.indexes === "function" && + obj.by && + typeof obj.by === "object" + ); +} + /** * Recursively collect all metric patterns from the tree. * @param {Record} obj * @param {string} path - * @returns {Array<{path: string, metric: Record, indexes: string[]}>} + * @returns {Array<{path: string, metric: AnyMetricPattern}>} */ function getAllMetrics(obj, path = "") { + /** @type {Array<{path: string, metric: AnyMetricPattern}>} */ const metrics = []; for (const key of Object.keys(obj)) { - if (key.startsWith("_")) continue; - const attr = obj[key]; if (!attr || typeof attr !== "object") continue; const currentPath = path ? `${path}.${key}` : key; - // Check if this is a metric pattern (has 'by' property with index getters) - if (attr.by && typeof attr.by === "object") { - const indexes = Object.keys(attr.by).filter( - (k) => !k.startsWith("_") && typeof attr.by[k] === "object", - ); - if (indexes.length > 0) { - metrics.push({ path: currentPath, metric: attr, indexes }); - } + // Check if this is a metric pattern using the indexes() method + if (isMetricPattern(attr)) { + metrics.push({ path: currentPath, metric: attr }); } // Recurse into nested tree nodes @@ -40,32 +53,37 @@ function getAllMetrics(obj, path = "") { return metrics; } -// Endpoints with sparse data (holes at the end) - skip these -const SKIP_ENDPOINTS = new Set([ - "distribution.addressesData.empty.by.emptyaddressindex", - "distribution.addressesData.loaded.by.loadedaddressindex", -]); - async function testAllEndpoints() { - const client = new BrkClient({ baseUrl: "http://localhost:3110", timeout: 15000 }); + const client = new BrkClient({ + baseUrl: "http://localhost:3110", + timeout: 15000, + }); const metrics = getAllMetrics(client.metrics); console.log(`\nFound ${metrics.length} metrics`); let success = 0; - let skipped = 0; - for (const { path, metric, indexes } of metrics) { + for (const { path, metric } of metrics) { + // Use the indexes() method to get all available indexes + const indexes = metric.indexes(); + for (const idxName of indexes) { const fullPath = `${path}.by.${idxName}`; - if (SKIP_ENDPOINTS.has(fullPath)) { - skipped++; - console.log(`SKIP: ${fullPath} -> sparse data`); - continue; - } + try { - const endpoint = metric.by[idxName]; - await endpoint.last(0); + // Verify both access methods work: .by[index] and .get(index) + const endpointByProperty = metric.by[idxName]; + const endpointByGet = metric.get(idxName); + + if (!endpointByProperty) { + throw new Error(`metric.by.${idxName} is undefined`); + } + if (!endpointByGet) { + throw new Error(`metric.get('${idxName}') returned undefined`); + } + + await endpointByProperty.last(0); success++; console.log(`OK: ${fullPath}`); } catch (e) { @@ -79,7 +97,6 @@ async function testAllEndpoints() { console.log(`\n=== Results ===`); console.log(`Success: ${success}`); - console.log(`Skipped: ${skipped}`); } testAllEndpoints(); diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index b5d0802ef..130aed2e9 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -2586,31 +2586,6 @@ class Price111dSmaPattern: self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct99_usd')) self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio')) -class PercentilesPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.pct05: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct05')) - self.pct10: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct10')) - self.pct15: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct15')) - self.pct20: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct20')) - self.pct25: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct25')) - self.pct30: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct30')) - self.pct35: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct35')) - self.pct40: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct40')) - self.pct45: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct45')) - self.pct50: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct50')) - self.pct55: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct55')) - self.pct60: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct60')) - self.pct65: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct65')) - self.pct70: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct70')) - self.pct75: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct75')) - self.pct80: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct80')) - self.pct85: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct85')) - self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90')) - self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95')) - class ActivePriceRatioPattern: """Pattern struct for repeated tree structure.""" @@ -2636,6 +2611,31 @@ class ActivePriceRatioPattern: self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd')) self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc) +class PercentilesPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.pct05: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct05')) + self.pct10: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct10')) + self.pct15: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct15')) + self.pct20: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct20')) + self.pct25: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct25')) + self.pct30: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct30')) + self.pct35: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct35')) + self.pct40: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct40')) + self.pct45: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct45')) + self.pct50: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct50')) + self.pct55: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct55')) + self.pct60: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct60')) + self.pct65: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct65')) + self.pct70: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct70')) + self.pct75: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct75')) + self.pct80: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct80')) + self.pct85: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct85')) + self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90')) + self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95')) + class RelativePattern5: """Pattern struct for repeated tree structure.""" @@ -2757,17 +2757,17 @@ class ClassAveragePricePattern(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_average_price')) - self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_average_price')) - self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_average_price')) - self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_average_price')) - self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_average_price')) - self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_average_price')) - self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_average_price')) - self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_average_price')) - self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_average_price')) - self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_average_price')) - self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_average_price')) + 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')) class DollarsPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2849,21 +2849,6 @@ class AddrCountPattern: self.p2wpkh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2wpkh', acc)) self.p2wsh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2wsh', acc)) -class FullnessPattern(Generic[T]): - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.average: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'average')) - self.base: MetricPattern11[T] = MetricPattern11(client, acc) - self.max: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'max')) - self.median: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'median')) - self.min: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'min')) - self.pct10: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct10')) - self.pct25: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct25')) - self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75')) - self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90')) - class FeeRatePattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -2879,6 +2864,21 @@ class FeeRatePattern(Generic[T]): self.pct90: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct90')) self.txindex: MetricPattern27[T] = MetricPattern27(client, acc) +class FullnessPattern(Generic[T]): + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.average: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'average')) + self.base: MetricPattern11[T] = MetricPattern11(client, acc) + self.max: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'max')) + self.median: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'median')) + self.min: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'min')) + self.pct10: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct10')) + self.pct25: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct25')) + self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75')) + self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90')) + class _0satsPattern: """Pattern struct for repeated tree structure.""" @@ -2893,6 +2893,32 @@ class _0satsPattern: self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) +class _100btcPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.activity: ActivityPattern2 = ActivityPattern2(client, acc) + self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc) + self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) + self.realized: RealizedPattern = RealizedPattern(client, acc) + self.relative: RelativePattern = RelativePattern(client, acc) + self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) + self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) + +class _10yTo12yPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.activity: ActivityPattern2 = ActivityPattern2(client, acc) + self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc) + self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) + self.realized: RealizedPattern2 = RealizedPattern2(client, acc) + self.relative: RelativePattern2 = RelativePattern2(client, acc) + self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) + self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) + class PeriodCagrPattern: """Pattern struct for repeated tree structure.""" @@ -2906,19 +2932,6 @@ class PeriodCagrPattern: 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.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.activity: ActivityPattern2 = ActivityPattern2(client, acc) - self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc) - self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) - self.realized: RealizedPattern = RealizedPattern(client, acc) - self.relative: RelativePattern = RelativePattern(client, acc) - self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) - self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) - class _0satsPattern2: """Pattern struct for repeated tree structure.""" @@ -2945,19 +2958,6 @@ class UnrealizedPattern: self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss')) self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit')) -class _10yTo12yPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.activity: ActivityPattern2 = ActivityPattern2(client, acc) - self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc) - self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) - self.realized: RealizedPattern2 = RealizedPattern2(client, acc) - self.relative: RelativePattern2 = RelativePattern2(client, acc) - self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) - self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) - class _10yPattern: """Pattern struct for repeated tree structure.""" @@ -2992,6 +2992,51 @@ class SplitPattern2(Generic[T]): self.low: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'low')) self.open: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'open')) +class ActiveSupplyPattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc')) + self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) + self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc) + +class CoinbasePattern: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.bitcoin: BitcoinPattern = BitcoinPattern(client, _m(acc, 'btc')) + self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd')) + self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc) + +class CoinbasePattern2: + """Pattern struct for repeated tree structure.""" + + def __init__(self, client: BrkClientBase, acc: str): + """Create pattern node with accumulated metric name.""" + self.bitcoin: BlockCountPattern[Bitcoin] = BlockCountPattern(client, _m(acc, 'btc')) + self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) + self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) + +class SegwitAdoptionPattern: + """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: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) + self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) + self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) + class UnclaimedRewardsPattern: """Pattern struct for repeated tree structure.""" @@ -3010,51 +3055,6 @@ class _2015Pattern: self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd')) self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc) -class CoinbasePattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.bitcoin: BitcoinPattern = BitcoinPattern(client, _m(acc, 'btc')) - self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd')) - self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc) - -class CostBasisPattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) - self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) - self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) - -class SegwitAdoptionPattern: - """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 ActiveSupplyPattern: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc')) - self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) - self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc) - -class CoinbasePattern2: - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.bitcoin: BlockCountPattern[Bitcoin] = BlockCountPattern(client, _m(acc, 'btc')) - self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) - self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) - class _1dReturns1mSdPattern: """Pattern struct for repeated tree structure.""" @@ -3063,6 +3063,14 @@ class _1dReturns1mSdPattern: 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 SupplyPattern2: """Pattern struct for repeated tree structure.""" @@ -3079,22 +3087,6 @@ class CostBasisPattern: self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) -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 SatsPattern(Generic[T]): - """Pattern struct for repeated tree structure.""" - - def __init__(self, client: BrkClientBase, acc: str): - """Create pattern node with accumulated metric name.""" - self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc_sats')) - self.split: SplitPattern2[T] = SplitPattern2(client, _m(acc, 'sats')) - class BlockCountPattern(Generic[T]): """Pattern struct for repeated tree structure.""" @@ -3111,12 +3103,13 @@ class BitcoinPattern2(Generic[T]): self.cumulative: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'cumulative')) self.sum: MetricPattern1[T] = MetricPattern1(client, acc) -class OutputsPattern: +class SatsPattern(Generic[T]): """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) + self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc_sats')) + self.split: SplitPattern2[T] = SplitPattern2(client, _m(acc, 'sats')) class RealizedPriceExtraPattern: """Pattern struct for repeated tree structure.""" @@ -3125,6 +3118,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: @@ -4329,6 +4329,7 @@ class MetricsTree_Price_Oracle: def __init__(self, client: BrkClientBase, base_path: str = ''): self.ohlc_cents: MetricPattern6[OHLCCents] = MetricPattern6(client, 'oracle_ohlc_cents') + self.ohlc_dollars: MetricPattern6[OHLCDollars] = MetricPattern6(client, 'oracle_ohlc_dollars') self.price_cents: MetricPattern11[Cents] = MetricPattern11(client, 'orange_price_cents') self.tx_count: MetricPattern6[StoredU32] = MetricPattern6(client, 'oracle_tx_count')