diff --git a/Cargo.lock b/Cargo.lock index d339ec12e..84fd85adb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -916,6 +916,35 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1090,6 +1119,15 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1154,6 +1192,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "dtype_dispatch" version = "0.2.1" @@ -2010,6 +2057,12 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -2145,6 +2198,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + [[package]] name = "num-traits" version = "0.2.19" @@ -2359,6 +2418,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -3031,6 +3096,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -3274,11 +3370,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc" dependencies = [ "base64 0.22.1", + "cookie_store", "flate2", "log", "percent-encoding", "rustls", "rustls-pki-types", + "serde", + "serde_json", "ureq-proto", "utf-8", "webpki-roots", diff --git a/Cargo.toml b/Cargo.toml index fd027d1b0..4d1eac46a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ tokio = { version = "1.50.0", features = ["rt-multi-thread"] } tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "compression-gzip", "compression-zstd", "cors", "normalize-path", "timeout", "trace"] } tower-layer = "0.3" tracing = { version = "0.1", default-features = false, features = ["std"] } -ureq = "3.2.0" +ureq = { version = "3.2.0", features = ["json"] } # vecdb = { version = "0.6.8", features = ["derive", "serde_json", "pco", "schemars"] } vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] } diff --git a/crates/brk_bindgen/src/backends/javascript.rs b/crates/brk_bindgen/src/backends/javascript.rs index 97b2dbddb..92eeeb3ef 100644 --- a/crates/brk_bindgen/src/backends/javascript.rs +++ b/crates/brk_bindgen/src/backends/javascript.rs @@ -92,8 +92,7 @@ impl LanguageSyntax for JavaScriptSyntax { // "ratio_{disc}_bps" → split on {disc} → _m(_m(acc, 'ratio'), disc) then _bps // But this is complex. For embedded disc, use string interpolation. // For suffix disc (ends with {disc}), use _m composition. - if template.ends_with("{disc}") { - let static_part = &template[..template.len() - "{disc}".len()]; + if let Some(static_part) = template.strip_suffix("{disc}") { if static_part.is_empty() { format!("_m({}, disc)", var_name) } else { diff --git a/crates/brk_bindgen/src/backends/rust.rs b/crates/brk_bindgen/src/backends/rust.rs index 15eaebcf7..187ed421e 100644 --- a/crates/brk_bindgen/src/backends/rust.rs +++ b/crates/brk_bindgen/src/backends/rust.rs @@ -1,6 +1,6 @@ //! Rust language syntax implementation. -use crate::{GenericSyntax, LanguageSyntax, to_snake_case}; +use crate::{GenericSyntax, LanguageSyntax, escape_rust_keyword, to_snake_case}; /// Rust-specific code generation syntax. pub struct RustSyntax; @@ -15,7 +15,7 @@ fn escape_rust_format(template: &str) -> String { impl LanguageSyntax for RustSyntax { fn field_name(&self, name: &str) -> String { - to_snake_case(name) + escape_rust_keyword(&to_snake_case(name)) } fn path_expr(&self, base_var: &str, suffix: &str) -> String { diff --git a/crates/brk_bindgen/src/generators/javascript/client.rs b/crates/brk_bindgen/src/generators/javascript/client.rs index 534b5a3b3..60f248b87 100644 --- a/crates/brk_bindgen/src/generators/javascript/client.rs +++ b/crates/brk_bindgen/src/generators/javascript/client.rs @@ -183,6 +183,7 @@ function _wrapMetricData(raw) {{ * @typedef {{Object}} MetricDataBase * @property {{number}} version - Version of the metric data * @property {{Index}} index - The index type used for this query + * @property {{string}} type - Value type (e.g. "f32", "u64", "Sats") * @property {{number}} total - Total number of data points * @property {{number}} start - Start index (inclusive) * @property {{number}} end - End index (exclusive) diff --git a/crates/brk_bindgen/src/generators/javascript/tree.rs b/crates/brk_bindgen/src/generators/javascript/tree.rs index 11f39eb5f..1a3a23c6c 100644 --- a/crates/brk_bindgen/src/generators/javascript/tree.rs +++ b/crates/brk_bindgen/src/generators/javascript/tree.rs @@ -24,7 +24,7 @@ pub fn generate_tree_typedefs(output: &mut String, catalog: &TreeNode, metadata: "MetricsTree", "", catalog, - &pattern_lookup, + pattern_lookup, metadata, &mut generated, ); @@ -125,7 +125,7 @@ pub fn generate_main_client( "MetricsTree", "", 3, - &pattern_lookup, + pattern_lookup, metadata, &mut generated, ); diff --git a/crates/brk_bindgen/src/generators/python/client.rs b/crates/brk_bindgen/src/generators/python/client.rs index f509e8f28..eb4add8bf 100644 --- a/crates/brk_bindgen/src/generators/python/client.rs +++ b/crates/brk_bindgen/src/generators/python/client.rs @@ -220,6 +220,7 @@ class MetricData(Generic[T]): """Metric data with range information. Always int-indexed.""" version: int index: Index + type: str total: int start: int end: int diff --git a/crates/brk_bindgen/src/generators/python/tree.rs b/crates/brk_bindgen/src/generators/python/tree.rs index 5abb7b7e5..b10620642 100644 --- a/crates/brk_bindgen/src/generators/python/tree.rs +++ b/crates/brk_bindgen/src/generators/python/tree.rs @@ -21,7 +21,7 @@ pub fn generate_tree_classes(output: &mut String, catalog: &TreeNode, metadata: "MetricsTree", "", catalog, - &pattern_lookup, + pattern_lookup, metadata, &mut generated, ); diff --git a/crates/brk_bindgen/src/generators/rust/client.rs b/crates/brk_bindgen/src/generators/rust/client.rs index e6eef3296..b3a7f2396 100644 --- a/crates/brk_bindgen/src/generators/rust/client.rs +++ b/crates/brk_bindgen/src/generators/rust/client.rs @@ -4,15 +4,14 @@ use std::fmt::Write; use crate::{ ClientMetadata, GenericSyntax, IndexSetPattern, RustSyntax, StructuralPattern, - generate_parameterized_field, index_to_field_name, to_snake_case, + escape_rust_keyword, generate_parameterized_field, index_to_field_name, to_snake_case, }; /// Generate import statements. pub fn generate_imports(output: &mut String) { writeln!( output, - r#"use std::io::Read as _; -use std::sync::Arc; + r#"use std::sync::Arc; use std::ops::{{Bound, RangeBounds}}; use serde::de::DeserializeOwned; pub use brk_cohort::*; @@ -81,40 +80,27 @@ impl BrkClientBase {{ .into(); Self {{ agent, - base_url: options.base_url, + base_url: options.base_url.trim_end_matches('/').to_string(), }} }} - fn get(&self, path: &str) -> Result> {{ - let base = self.base_url.trim_end_matches('/'); - let url = format!("{{}}{{}}", base, path); - let mut response = self.agent.get(&url) - .call() - .map_err(|e| BrkError {{ message: e.to_string() }})?; - - if response.status().as_u16() >= 400 {{ - return Err(BrkError {{ - message: format!("HTTP {{}}", response.status().as_u16()), - }}); - }} - - let mut bytes = Vec::new(); - response.body_mut().as_reader().read_to_end(&mut bytes) - .map_err(|e| BrkError {{ message: e.to_string() }})?; - Ok(bytes) + fn url(&self, path: &str) -> String {{ + format!("{{}}{{}}", self.base_url, path) }} /// Make a GET request and deserialize JSON response. pub fn get_json(&self, path: &str) -> Result {{ - let bytes = self.get(path)?; - serde_json::from_slice(&bytes) + self.agent.get(&self.url(path)) + .call() + .and_then(|mut r| r.body_mut().read_json()) .map_err(|e| BrkError {{ message: e.to_string() }}) }} /// Make a GET request and return raw text response. pub fn get_text(&self, path: &str) -> Result {{ - let bytes = self.get(path)?; - String::from_utf8(bytes) + self.agent.get(&self.url(path)) + .call() + .and_then(|mut r| r.body_mut().read_to_string()) .map_err(|e| BrkError {{ message: e.to_string() }}) }} }} @@ -525,7 +511,7 @@ pub fn generate_pattern_structs( writeln!(output, "pub struct {}{} {{", pattern.name, generic_params).unwrap(); for field in &pattern.fields { - let field_name = to_snake_case(&field.name); + let field_name = escape_rust_keyword(&to_snake_case(&field.name)); let type_annotation = metadata.field_type_annotation( field, pattern.is_generic, diff --git a/crates/brk_bindgen/src/generators/rust/tree.rs b/crates/brk_bindgen/src/generators/rust/tree.rs index aed04285d..61637f22a 100644 --- a/crates/brk_bindgen/src/generators/rust/tree.rs +++ b/crates/brk_bindgen/src/generators/rust/tree.rs @@ -7,7 +7,8 @@ use brk_types::TreeNode; use crate::{ ClientMetadata, GenericSyntax, LanguageSyntax, PatternField, RustSyntax, build_child_path, - generate_leaf_field, generate_tree_node_field, prepare_tree_node, to_snake_case, + escape_rust_keyword, generate_leaf_field, generate_tree_node_field, prepare_tree_node, + to_snake_case, }; /// Generate tree structs. @@ -21,7 +22,7 @@ pub fn generate_tree(output: &mut String, catalog: &TreeNode, metadata: &ClientM "MetricsTree", "", catalog, - &pattern_lookup, + pattern_lookup, metadata, &mut generated, ); @@ -45,7 +46,7 @@ fn generate_tree_node( writeln!(output, "pub struct {} {{", name).unwrap(); for child in &ctx.children { - let field_name = to_snake_case(child.name); + let field_name = escape_rust_keyword(&to_snake_case(child.name)); let type_annotation = if child.should_inline { child.inline_type_name.clone() } else { @@ -67,7 +68,7 @@ fn generate_tree_node( let syntax = RustSyntax; for child in &ctx.children { - let field_name = to_snake_case(child.name); + let field_name = escape_rust_keyword(&to_snake_case(child.name)); if child.is_leaf { if let TreeNode::Leaf(leaf) = child.node { diff --git a/crates/brk_bindgen/src/types/case.rs b/crates/brk_bindgen/src/types/case.rs index 767b505a8..22dbefe0d 100644 --- a/crates/brk_bindgen/src/types/case.rs +++ b/crates/brk_bindgen/src/types/case.rs @@ -14,25 +14,25 @@ pub fn to_pascal_case(s: &str) -> String { .collect() } -/// Convert a string to snake_case, handling Rust keywords. +/// Convert a string to snake_case (no keyword escaping — backends handle that). pub fn to_snake_case(s: &str) -> String { - // Convert to lowercase and replace dashes with underscores let sanitized = s.to_lowercase().replace('-', "_"); - // Prefix with _ if starts with digit - let sanitized = if sanitized.chars().next().is_some_and(|c| c.is_ascii_digit()) { + if sanitized.chars().next().is_some_and(|c| c.is_ascii_digit()) { format!("_{}", sanitized) } else { sanitized - }; + } +} - // Handle Rust keywords - match sanitized.as_str() { +/// Escape Rust reserved keywords with `_` suffix (consistent with Python). +pub fn escape_rust_keyword(name: &str) -> String { + match name { "type" | "const" | "static" | "match" | "if" | "else" | "loop" | "while" | "for" | "break" | "continue" | "return" | "fn" | "let" | "mut" | "ref" | "self" | "super" | "mod" | "use" | "pub" | "crate" | "extern" | "impl" | "trait" | "struct" | "enum" - | "where" | "async" | "await" | "dyn" | "move" => format!("r#{}", sanitized), - _ => sanitized, + | "where" | "async" | "await" | "dyn" | "move" => format!("{}_", name), + _ => name.to_string(), } } diff --git a/crates/brk_bindgen/src/types/structs.rs b/crates/brk_bindgen/src/types/structs.rs index 5ca160e9c..0cb7933c8 100644 --- a/crates/brk_bindgen/src/types/structs.rs +++ b/crates/brk_bindgen/src/types/structs.rs @@ -74,10 +74,10 @@ impl StructuralPattern { // Find a template with {disc} and extract the disc from the instance value. // Strip leading underscore since _m() handles separators. for (field_name, template) in templates { - if let Some(value) = instance_field_parts.get(field_name) { - if let Some(disc) = extract_disc(template, value) { - return Some(disc.trim_start_matches('_').to_string()); - } + if let Some(value) = instance_field_parts.get(field_name) + && let Some(disc) = extract_disc(template, value) + { + return Some(disc.trim_start_matches('_').to_string()); } } // If no template matched (all empty templates), disc is empty diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index de6053894..b349c8270 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -7,7 +7,6 @@ #![allow(clippy::useless_format)] #![allow(clippy::unnecessary_to_owned)] -use std::io::Read as _; use std::sync::Arc; use std::ops::{Bound, RangeBounds}; use serde::de::DeserializeOwned; @@ -69,40 +68,27 @@ impl BrkClientBase { .into(); Self { agent, - base_url: options.base_url, + base_url: options.base_url.trim_end_matches('/').to_string(), } } - fn get(&self, path: &str) -> Result> { - let base = self.base_url.trim_end_matches('/'); - let url = format!("{}{}", base, path); - let mut response = self.agent.get(&url) - .call() - .map_err(|e| BrkError { message: e.to_string() })?; - - if response.status().as_u16() >= 400 { - return Err(BrkError { - message: format!("HTTP {}", response.status().as_u16()), - }); - } - - let mut bytes = Vec::new(); - response.body_mut().as_reader().read_to_end(&mut bytes) - .map_err(|e| BrkError { message: e.to_string() })?; - Ok(bytes) + fn url(&self, path: &str) -> String { + format!("{}{}", self.base_url, path) } /// Make a GET request and deserialize JSON response. pub fn get_json(&self, path: &str) -> Result { - let bytes = self.get(path)?; - serde_json::from_slice(&bytes) + self.agent.get(&self.url(path)) + .call() + .and_then(|mut r| r.body_mut().read_json()) .map_err(|e| BrkError { message: e.to_string() }) } /// Make a GET request and return raw text response. pub fn get_text(&self, path: &str) -> Result { - let bytes = self.get(path)?; - String::from_utf8(bytes) + self.agent.get(&self.url(path)) + .call() + .and_then(|mut r| r.body_mut().read_to_string()) .map_err(|e| BrkError { message: e.to_string() }) } } @@ -5397,7 +5383,7 @@ impl MetricsTree_Market_Dca { pub struct MetricsTree_Market_Dca_Period { pub stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3, pub cost_basis: MetricsTree_Market_Dca_Period_CostBasis, - pub r#return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2, + pub return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2, pub cagr: _10y2y3y4y5y6y8yPattern, pub lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3, pub lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2, @@ -5408,7 +5394,7 @@ impl MetricsTree_Market_Dca_Period { Self { stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3::new(client.clone(), "dca_stack".to_string()), cost_basis: MetricsTree_Market_Dca_Period_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")), - r#return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "dca_return".to_string()), + return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "dca_return".to_string()), cagr: _10y2y3y4y5y6y8yPattern::new(client.clone(), "dca_cagr".to_string()), lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3::new(client.clone(), "lump_sum_stack".to_string()), lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "lump_sum_return".to_string()), @@ -5455,7 +5441,7 @@ impl MetricsTree_Market_Dca_Period_CostBasis { pub struct MetricsTree_Market_Dca_Class { pub stack: MetricsTree_Market_Dca_Class_Stack, pub cost_basis: MetricsTree_Market_Dca_Class_CostBasis, - pub r#return: MetricsTree_Market_Dca_Class_Return, + pub return_: MetricsTree_Market_Dca_Class_Return, } impl MetricsTree_Market_Dca_Class { @@ -5463,7 +5449,7 @@ impl MetricsTree_Market_Dca_Class { Self { stack: MetricsTree_Market_Dca_Class_Stack::new(client.clone(), format!("{base_path}_stack")), cost_basis: MetricsTree_Market_Dca_Class_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")), - r#return: MetricsTree_Market_Dca_Class_Return::new(client.clone(), format!("{base_path}_return")), + return_: MetricsTree_Market_Dca_Class_Return::new(client.clone(), format!("{base_path}_return")), } } } @@ -6232,7 +6218,7 @@ pub struct MetricsTree_Cohorts_Utxo { pub over_amount: MetricsTree_Cohorts_Utxo_OverAmount, pub amount_range: MetricsTree_Cohorts_Utxo_AmountRange, pub under_amount: MetricsTree_Cohorts_Utxo_UnderAmount, - pub r#type: MetricsTree_Cohorts_Utxo_Type, + pub type_: MetricsTree_Cohorts_Utxo_Type, pub profitability: MetricsTree_Cohorts_Utxo_Profitability, pub matured: MetricsTree_Cohorts_Utxo_Matured, } @@ -6251,7 +6237,7 @@ impl MetricsTree_Cohorts_Utxo { over_amount: MetricsTree_Cohorts_Utxo_OverAmount::new(client.clone(), format!("{base_path}_over_amount")), amount_range: MetricsTree_Cohorts_Utxo_AmountRange::new(client.clone(), format!("{base_path}_amount_range")), under_amount: MetricsTree_Cohorts_Utxo_UnderAmount::new(client.clone(), format!("{base_path}_under_amount")), - r#type: MetricsTree_Cohorts_Utxo_Type::new(client.clone(), format!("{base_path}_type")), + type_: MetricsTree_Cohorts_Utxo_Type::new(client.clone(), format!("{base_path}_type")), profitability: MetricsTree_Cohorts_Utxo_Profitability::new(client.clone(), format!("{base_path}_profitability")), matured: MetricsTree_Cohorts_Utxo_Matured::new(client.clone(), format!("{base_path}_matured")), } diff --git a/crates/brk_fetcher/examples/fetch_kraken.rs b/crates/brk_fetcher/examples/fetch_kraken.rs index c22464dcf..e706aff94 100644 --- a/crates/brk_fetcher/examples/fetch_kraken.rs +++ b/crates/brk_fetcher/examples/fetch_kraken.rs @@ -3,7 +3,8 @@ use brk_fetcher::Kraken; fn main() -> Result<()> { brk_logger::init(None)?; - let _ = dbg!(Kraken::fetch_1d()); - let _ = dbg!(Kraken::fetch_1mn()); + let kraken = Kraken::new(); + let _ = dbg!(kraken.fetch_1d()); + let _ = dbg!(kraken.fetch_1mn()); Ok(()) } diff --git a/crates/brk_fetcher/examples/fetcher.rs b/crates/brk_fetcher/examples/fetcher.rs index a1ecbb8af..e58c4a49c 100644 --- a/crates/brk_fetcher/examples/fetcher.rs +++ b/crates/brk_fetcher/examples/fetcher.rs @@ -5,22 +5,25 @@ use brk_types::{Date, Height}; fn main() -> Result<()> { brk_logger::init(None)?; - let mut brk = BRK::default(); + let mut brk = BRK::new(); dbg!(brk.get_from_height(Height::new(900_000))?); dbg!(brk.get_from_date(Date::new(2025, 6, 7))?); let mut fetcher = Fetcher::new(None)?; - let _ = Binance::fetch_1d().map(|b| { + let binance = Binance::new(None); + let kraken = Kraken::new(); + + let _ = binance.fetch_1d().map(|b| { dbg!(b.last_key_value()); }); - let _ = Kraken::fetch_1d().map(|b| { + let _ = kraken.fetch_1d().map(|b| { dbg!(b.last_key_value()); }); - let _ = Binance::fetch_1mn().map(|b| { + let _ = binance.fetch_1mn().map(|b| { dbg!(b.last_key_value()); }); - let _ = Kraken::fetch_1mn().map(|b| { + let _ = kraken.fetch_1mn().map(|b| { dbg!(b.last_key_value()); }); diff --git a/crates/brk_fetcher/src/binance.rs b/crates/brk_fetcher/src/binance.rs index ffffcbcd0..2e284cfca 100644 --- a/crates/brk_fetcher/src/binance.rs +++ b/crates/brk_fetcher/src/binance.rs @@ -26,7 +26,11 @@ pub struct Binance { } impl Binance { - pub fn new(path: Option<&Path>, agent: Agent) -> Self { + pub fn new(path: Option<&Path>) -> Self { + Self::new_with_agent(path, crate::new_agent(30)) + } + + pub fn new_with_agent(path: Option<&Path>, agent: Agent) -> Self { Self { agent, path: path.map(|p| p.to_owned()), diff --git a/crates/brk_fetcher/src/brk.rs b/crates/brk_fetcher/src/brk.rs index e15cc58ce..cef4aaf65 100644 --- a/crates/brk_fetcher/src/brk.rs +++ b/crates/brk_fetcher/src/brk.rs @@ -19,7 +19,11 @@ pub struct BRK { } impl BRK { - pub fn new(agent: Agent) -> Self { + pub fn new() -> Self { + Self::new_with_agent(crate::new_agent(30)) + } + + pub fn new_with_agent(agent: Agent) -> Self { Self { agent, height_to_ohlc: BTreeMap::new(), diff --git a/crates/brk_fetcher/src/kraken.rs b/crates/brk_fetcher/src/kraken.rs index 8edd5938a..ae287d4c4 100644 --- a/crates/brk_fetcher/src/kraken.rs +++ b/crates/brk_fetcher/src/kraken.rs @@ -19,7 +19,11 @@ pub struct Kraken { } impl Kraken { - pub fn new(agent: Agent) -> Self { + pub fn new() -> Self { + Self::new_with_agent(crate::new_agent(30)) + } + + pub fn new_with_agent(agent: Agent) -> Self { Self { agent, _1mn: None, diff --git a/crates/brk_fetcher/src/lib.rs b/crates/brk_fetcher/src/lib.rs index ea8f230d8..792b5f943 100644 --- a/crates/brk_fetcher/src/lib.rs +++ b/crates/brk_fetcher/src/lib.rs @@ -25,9 +25,11 @@ pub use source::{PriceSource, TrackedSource}; const MAX_RETRIES: usize = 12 * 60; // 12 hours of retrying /// Create a shared HTTP agent with connection pooling and default timeout. +/// Status codes are not treated as errors — callers use `checked_get` for status handling. pub fn new_agent(timeout_secs: u64) -> Agent { Agent::config_builder() .timeout_global(Some(Duration::from_secs(timeout_secs))) + .http_status_as_error(false) .build() .into() } @@ -63,9 +65,9 @@ impl Fetcher { pub fn new(hars_path: Option<&Path>) -> Result { let agent = new_agent(30); Ok(Self { - binance: TrackedSource::new(Binance::new(hars_path, agent.clone())), - kraken: TrackedSource::new(Kraken::new(agent.clone())), - brk: TrackedSource::new(BRK::new(agent.clone())), + binance: TrackedSource::new(Binance::new_with_agent(hars_path, agent.clone())), + kraken: TrackedSource::new(Kraken::new_with_agent(agent.clone())), + brk: TrackedSource::new(BRK::new_with_agent(agent.clone())), agent, }) } diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 07fececbc..cee512f95 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -1116,6 +1116,7 @@ function _wrapMetricData(raw) { * @typedef {Object} MetricDataBase * @property {number} version - Version of the metric data * @property {Index} index - The index type used for this query + * @property {string} type - Value type (e.g. "f32", "u64", "Sats") * @property {number} total - Total number of data points * @property {number} start - Start index (inclusive) * @property {number} end - End index (exclusive) diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index fe06e9e98..4632882a5 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -1270,6 +1270,7 @@ class MetricData(Generic[T]): """Metric data with range information. Always int-indexed.""" version: int index: Index + type: str total: int start: int end: int @@ -4253,7 +4254,7 @@ class MetricsTree_Market_Dca_Period: def __init__(self, client: BrkClientBase, base_path: str = ''): self.stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'dca_stack') self.cost_basis: MetricsTree_Market_Dca_Period_CostBasis = MetricsTree_Market_Dca_Period_CostBasis(client) - self.r#return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'dca_return') + self.return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'dca_return') self.cagr: _10y2y3y4y5y6y8yPattern = _10y2y3y4y5y6y8yPattern(client, 'dca_cagr') self.lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'lump_sum_stack') self.lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'lump_sum_return') @@ -4315,7 +4316,7 @@ class MetricsTree_Market_Dca_Class: def __init__(self, client: BrkClientBase, base_path: str = ''): self.stack: MetricsTree_Market_Dca_Class_Stack = MetricsTree_Market_Dca_Class_Stack(client) self.cost_basis: MetricsTree_Market_Dca_Class_CostBasis = MetricsTree_Market_Dca_Class_CostBasis(client) - self.r#return: MetricsTree_Market_Dca_Class_Return = MetricsTree_Market_Dca_Class_Return(client) + self.return_: MetricsTree_Market_Dca_Class_Return = MetricsTree_Market_Dca_Class_Return(client) class MetricsTree_Market_Dca: """Metrics tree node.""" @@ -5621,7 +5622,7 @@ class MetricsTree_Cohorts_Utxo: self.over_amount: MetricsTree_Cohorts_Utxo_OverAmount = MetricsTree_Cohorts_Utxo_OverAmount(client) self.amount_range: MetricsTree_Cohorts_Utxo_AmountRange = MetricsTree_Cohorts_Utxo_AmountRange(client) self.under_amount: MetricsTree_Cohorts_Utxo_UnderAmount = MetricsTree_Cohorts_Utxo_UnderAmount(client) - self.r#type: MetricsTree_Cohorts_Utxo_Type = MetricsTree_Cohorts_Utxo_Type(client) + self.type: MetricsTree_Cohorts_Utxo_Type = MetricsTree_Cohorts_Utxo_Type(client) self.profitability: MetricsTree_Cohorts_Utxo_Profitability = MetricsTree_Cohorts_Utxo_Profitability(client) self.matured: MetricsTree_Cohorts_Utxo_Matured = MetricsTree_Cohorts_Utxo_Matured(client)