global: reorg fixes + clients improved

This commit is contained in:
nym21
2026-01-28 23:35:51 +01:00
parent fecaf0f400
commit 6709ded66c
55 changed files with 4312 additions and 83 deletions
Generated
+4 -10
View File
@@ -2433,8 +2433,6 @@ dependencies = [
[[package]]
name = "rawdb"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f53bd783ecee89e4b43e82634a355a28c74743e89596772287dbe06bb95942"
dependencies = [
"libc",
"log",
@@ -3253,8 +3251,6 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
[[package]]
name = "vecdb"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "867dfa2136db3ff905f3668eb0ed76daf8de14237338c1d498dbd7ce925bf90e"
dependencies = [
"ctrlc",
"log",
@@ -3274,8 +3270,6 @@ dependencies = [
[[package]]
name = "vecdb_derive"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1819035219d794f6dd96feab3a700aa582e193ddd4ac730ef771c0d31ea03260"
dependencies = [
"quote",
"syn",
@@ -3694,18 +3688,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.34"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d"
checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.34"
version = "0.8.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d"
checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22"
dependencies = [
"proc-macro2",
"quote",
+2 -2
View File
@@ -82,8 +82,8 @@ tokio = { version = "1.49.0", features = ["rt-multi-thread"] }
tracing = { version = "0.1", default-features = false, features = ["std"] }
tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "compression-gzip", "compression-zstd", "cors", "normalize-path", "timeout", "trace"] }
tower-layer = "0.3"
vecdb = { version = "0.6.2", features = ["derive", "serde_json", "pco", "schemars"] }
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
# vecdb = { version = "0.6.2", features = ["derive", "serde_json", "pco", "schemars"] }
vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
[workspace.metadata.release]
shared-version = true
@@ -48,15 +48,120 @@ class BrkError extends Error {{
}}
}}
// Date conversion constants and helpers
const _GENESIS = new Date(2009, 0, 3); // dateindex 0, weekindex 0
const _DAY_ONE = new Date(2009, 0, 9); // dateindex 1 (6 day gap after genesis)
const _MS_PER_DAY = 24 * 60 * 60 * 1000;
const _MS_PER_WEEK = 7 * _MS_PER_DAY;
const _DATE_INDEXES = new Set(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']);
/** @param {{number}} months @returns {{globalThis.Date}} */
const _addMonths = (months) => new Date(2009, months, 1);
/**
* Convert an index value to a Date for date-based indexes.
* @param {{Index}} index - The index type
* @param {{number}} i - The index value
* @returns {{globalThis.Date}}
*/
function indexToDate(index, i) {{
switch (index) {{
case 'dateindex': return i === 0 ? _GENESIS : new Date(_DAY_ONE.getTime() + (i - 1) * _MS_PER_DAY);
case 'weekindex': return new Date(_GENESIS.getTime() + i * _MS_PER_WEEK);
case 'monthindex': return _addMonths(i);
case 'yearindex': return new Date(2009 + i, 0, 1);
case 'quarterindex': return _addMonths(i * 3);
case 'semesterindex': return _addMonths(i * 6);
case 'decadeindex': return new Date(2009 + i * 10, 0, 1);
default: throw new Error(`${{index}} is not a date-based index`);
}}
}}
/**
* Check if an index type is date-based.
* @param {{Index}} index
* @returns {{boolean}}
*/
function isDateIndex(index) {{
return _DATE_INDEXES.has(index);
}}
/**
* Wrap raw metric data with helper methods.
* @template T
* @param {{MetricData<T>}} raw - Raw JSON response
* @returns {{MetricData<T>}}
*/
function _wrapMetricData(raw) {{
const {{ index, start, end, data }} = raw;
return /** @type {{MetricData<T>}} */ ({{
...raw,
dates() {{
/** @type {{globalThis.Date[]}} */
const result = [];
for (let i = start; i < end; i++) result.push(indexToDate(index, i));
return result;
}},
indexes() {{
/** @type {{number[]}} */
const result = [];
for (let i = start; i < end; i++) result.push(i);
return result;
}},
toDateMap() {{
/** @type {{Map<globalThis.Date, T>}} */
const map = new Map();
for (let i = 0; i < data.length; i++) map.set(indexToDate(index, start + i), data[i]);
return map;
}},
toIndexMap() {{
/** @type {{Map<number, T>}} */
const map = new Map();
for (let i = 0; i < data.length; i++) map.set(start + i, data[i]);
return map;
}},
dateEntries() {{
/** @type {{Array<[globalThis.Date, T]>}} */
const result = [];
for (let i = 0; i < data.length; i++) result.push([indexToDate(index, start + i), data[i]]);
return result;
}},
indexEntries() {{
/** @type {{Array<[number, T]>}} */
const result = [];
for (let i = 0; i < data.length; i++) result.push([start + i, data[i]]);
return result;
}},
*iter() {{
for (let i = 0; i < data.length; i++) yield [start + i, data[i]];
}},
*iterDates() {{
for (let i = 0; i < data.length; i++) yield [indexToDate(index, start + i), data[i]];
}},
[Symbol.iterator]() {{
return this.iter();
}},
}});
}}
/**
* @template T
* @typedef {{Object}} MetricData
* @property {{number}} version - Version of the metric data
* @property {{Index}} index - The index type used for this query
* @property {{number}} total - Total number of data points
* @property {{number}} start - Start index (inclusive)
* @property {{number}} end - End index (exclusive)
* @property {{string}} stamp - ISO 8601 timestamp of when the response was generated
* @property {{T[]}} data - The metric data
* @property {{() => globalThis.Date[]}} dates - Convert index range to dates (date-based indexes only)
* @property {{() => number[]}} indexes - Get index range as array
* @property {{() => Map<globalThis.Date, T>}} toDateMap - Return data as Map keyed by date (date-based only)
* @property {{() => Map<number, T>}} toIndexMap - Return data as Map keyed by index
* @property {{() => Array<[globalThis.Date, T]>}} dateEntries - Return data as [date, value] pairs (date-based only)
* @property {{() => Array<[number, T]>}} indexEntries - Return data as [index, value] pairs
* @property {{() => IterableIterator<[number, T]>}} iter - Iterate over [index, value] pairs
* @property {{() => IterableIterator<[globalThis.Date, T]>}} iterDates - Iterate over [date, value] pairs (date-based only)
*/
/** @typedef {{MetricData<any>}} AnyMetricData */
@@ -150,18 +255,18 @@ function _endpoint(client, name, index) {{
* @returns {{RangeBuilder<T>}}
*/
const rangeBuilder = (start, end) => ({{
fetch(onUpdate) {{ return client.getJson(buildPath(start, end), onUpdate); }},
fetch(onUpdate) {{ return client._fetchMetricData(buildPath(start, end), onUpdate); }},
fetchCsv() {{ return client.getText(buildPath(start, end, 'csv')); }},
then(resolve, reject) {{ return this.fetch().then(resolve, reject); }},
}});
/**
* @param {{number}} index
* @param {{number}} idx
* @returns {{SingleItemBuilder<T>}}
*/
const singleItemBuilder = (index) => ({{
fetch(onUpdate) {{ return client.getJson(buildPath(index, index + 1), onUpdate); }},
fetchCsv() {{ return client.getText(buildPath(index, index + 1, 'csv')); }},
const singleItemBuilder = (idx) => ({{
fetch(onUpdate) {{ return client._fetchMetricData(buildPath(idx, idx + 1), onUpdate); }},
fetchCsv() {{ return client.getText(buildPath(idx, idx + 1, 'csv')); }},
then(resolve, reject) {{ return this.fetch().then(resolve, reject); }},
}});
@@ -171,19 +276,19 @@ function _endpoint(client, name, index) {{
*/
const skippedBuilder = (start) => ({{
take(n) {{ return rangeBuilder(start, start + n); }},
fetch(onUpdate) {{ return client.getJson(buildPath(start, undefined), onUpdate); }},
fetch(onUpdate) {{ return client._fetchMetricData(buildPath(start, undefined), onUpdate); }},
fetchCsv() {{ return client.getText(buildPath(start, undefined, 'csv')); }},
then(resolve, reject) {{ return this.fetch().then(resolve, reject); }},
}});
/** @type {{MetricEndpointBuilder<T>}} */
const endpoint = {{
get(index) {{ return singleItemBuilder(index); }},
get(idx) {{ return singleItemBuilder(idx); }},
slice(start, end) {{ return rangeBuilder(start, end); }},
first(n) {{ return rangeBuilder(undefined, n); }},
last(n) {{ return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); }},
skip(n) {{ return skippedBuilder(n); }},
fetch(onUpdate) {{ return client.getJson(buildPath(), onUpdate); }},
fetch(onUpdate) {{ return client._fetchMetricData(buildPath(), onUpdate); }},
fetchCsv() {{ return client.getText(buildPath(undefined, undefined, 'csv')); }},
then(resolve, reject) {{ return this.fetch().then(resolve, reject); }},
get path() {{ return p; }},
@@ -263,6 +368,19 @@ class BrkClientBase {{
const res = await this.get(path);
return res.text();
}}
/**
* Fetch metric data and wrap with helper methods (internal)
* @template T
* @param {{string}} path
* @param {{(value: MetricData<T>) => void}} [onUpdate]
* @returns {{Promise<MetricData<T>>}}
*/
async _fetchMetricData(path, onUpdate) {{
const wrappedOnUpdate = onUpdate ? (/** @type {{MetricData<T>}} */ raw) => onUpdate(_wrapMetricData(raw)) : undefined;
const raw = await this.getJson(path, wrappedOnUpdate);
return _wrapMetricData(raw);
}}
}}
/**
@@ -299,6 +417,31 @@ pub fn generate_static_constants(output: &mut String) {
for (name, value) in CohortConstants::all() {
write_static_const(output, name, &format_json(&camel_case_keys(value)));
}
// Helper methods
writeln!(
output,
r#" /**
* Convert an index value to a Date for date-based indexes.
* @param {{Index}} index - The index type
* @param {{number}} i - The index value
* @returns {{globalThis.Date}}
*/
indexToDate(index, i) {{
return indexToDate(index, i);
}}
/**
* Check if an index type is date-based.
* @param {{Index}} index
* @returns {{boolean}}
*/
isDateIndex(index) {{
return isDateIndex(index);
}}
"#
)
.unwrap();
}
fn indent_json_const(json: &str) -> String {
@@ -39,6 +39,16 @@ pub fn generate_main_client(output: &mut String, endpoints: &[Endpoint]) {
writeln!(output, " return MetricEndpointBuilder(self, metric, index)").unwrap();
writeln!(output).unwrap();
// Generate helper methods
writeln!(output, " def index_to_date(self, index: Index, i: int) -> date:").unwrap();
writeln!(output, " \"\"\"Convert an index value to a date for date-based indexes.\"\"\"").unwrap();
writeln!(output, " return index_to_date(index, i)").unwrap();
writeln!(output).unwrap();
writeln!(output, " def is_date_index(self, index: Index) -> bool:").unwrap();
writeln!(output, " \"\"\"Check if an index type is date-based.\"\"\"").unwrap();
writeln!(output, " return is_date_index(index)").unwrap();
writeln!(output).unwrap();
// Generate API methods
generate_api_methods(output, endpoints);
}
@@ -131,16 +131,114 @@ def _p(prefix: str, acc: str) -> str:
pub fn generate_endpoint_class(output: &mut String) {
writeln!(
output,
r#"@dataclass
r#"# Date conversion constants
_GENESIS = date(2009, 1, 3) # dateindex 0, weekindex 0
_DAY_ONE = date(2009, 1, 9) # dateindex 1 (6 day gap after genesis)
_DATE_INDEXES = frozenset(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex'])
def is_date_index(index: str) -> bool:
"""Check if an index type is date-based."""
return index in _DATE_INDEXES
def index_to_date(index: str, i: int) -> date:
"""Convert an index value to a date for date-based indexes."""
if index == 'dateindex':
return _GENESIS if i == 0 else _DAY_ONE + timedelta(days=i - 1)
elif index == 'weekindex':
return _GENESIS + timedelta(weeks=i)
elif index == 'monthindex':
return date(2009 + i // 12, i % 12 + 1, 1)
elif index == 'yearindex':
return date(2009 + i, 1, 1)
elif index == 'quarterindex':
m = i * 3
return date(2009 + m // 12, m % 12 + 1, 1)
elif index == 'semesterindex':
m = i * 6
return date(2009 + m // 12, m % 12 + 1, 1)
elif index == 'decadeindex':
return date(2009 + i * 10, 1, 1)
else:
raise ValueError(f"{{index}} is not a date-based index")
@dataclass
class MetricData(Generic[T]):
"""Metric data with range information."""
version: int
index: Index
total: int
start: int
end: int
stamp: str
data: List[T]
def dates(self) -> List[date]:
"""Convert index range to dates. Only works for date-based indexes."""
return [index_to_date(self.index, i) for i in range(self.start, self.end)]
def indexes(self) -> List[int]:
"""Get index range as list."""
return list(range(self.start, self.end))
def to_date_dict(self) -> dict[date, T]:
"""Return data as {{date: value}} dict. Only works for date-based indexes."""
return dict(zip(self.dates(), self.data))
def to_index_dict(self) -> dict[int, T]:
"""Return data as {{index: value}} dict."""
return dict(zip(range(self.start, self.end), self.data))
def date_items(self) -> List[Tuple[date, T]]:
"""Return data as [(date, value), ...] pairs. Only works for date-based indexes."""
return list(zip(self.dates(), self.data))
def index_items(self) -> List[Tuple[int, T]]:
"""Return data as [(index, value), ...] pairs."""
return list(zip(range(self.start, self.end), self.data))
def iter(self) -> Iterator[Tuple[int, T]]:
"""Iterate over (index, value) pairs."""
return iter(zip(range(self.start, self.end), self.data))
def iter_dates(self) -> Iterator[Tuple[date, T]]:
"""Iterate over (date, value) pairs. Date-based indexes only."""
return iter(zip(self.dates(), self.data))
def __iter__(self) -> Iterator[Tuple[int, T]]:
"""Default iteration over (index, value) pairs."""
return self.iter()
def to_polars(self, with_dates: bool = True) -> pl.DataFrame:
"""Convert to Polars DataFrame. Requires polars to be installed.
Returns a DataFrame with columns:
- 'date' (date) and 'value' (T) if with_dates=True and index is date-based
- 'index' (int) and 'value' (T) otherwise
"""
try:
import polars as pl # type: ignore[import-not-found]
except ImportError:
raise ImportError("polars is required: pip install polars")
if with_dates and self.index in _DATE_INDEXES:
return pl.DataFrame({{"date": self.dates(), "value": self.data}})
return pl.DataFrame({{"index": list(range(self.start, self.end)), "value": self.data}})
def to_pandas(self, with_dates: bool = True) -> pd.DataFrame:
"""Convert to Pandas DataFrame. Requires pandas to be installed.
Returns a DataFrame with columns:
- 'date' (date) and 'value' (T) if with_dates=True and index is date-based
- 'index' (int) and 'value' (T) otherwise
"""
try:
import pandas as pd # type: ignore[import-not-found]
except ImportError:
raise ImportError("pandas is required: pip install pandas")
if with_dates and self.index in _DATE_INDEXES:
return pd.DataFrame({{"date": self.dates(), "value": self.data}})
return pd.DataFrame({{"index": list(range(self.start, self.end)), "value": self.data}})
# Type alias for non-generic usage
AnyMetricData = MetricData[Any]
@@ -177,9 +275,8 @@ class _EndpointConfig:
p = self.path()
return f"{{p}}?{{query}}" if query else p
def get_json(self) -> MetricData:
data = self.client.get_json(self._build_path())
return MetricData(**data)
def get_metric(self) -> MetricData:
return MetricData(**self.client.get_json(self._build_path()))
def get_csv(self) -> str:
return self.client.get_text(self._build_path(format='csv'))
@@ -193,7 +290,7 @@ class RangeBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch the range as parsed JSON."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch the range as CSV string."""
@@ -208,7 +305,7 @@ class SingleItemBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch the single item."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch as CSV."""
@@ -231,7 +328,7 @@ class SkippedBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch from skipped position to end."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch as CSV."""
@@ -315,7 +412,7 @@ class MetricEndpointBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch all data as parsed JSON."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch all data as CSV string."""
@@ -29,7 +29,7 @@ pub fn generate_python_client(
writeln!(output, "from dataclasses import dataclass").unwrap();
writeln!(
output,
"from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload"
"from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING"
)
.unwrap();
writeln!(
@@ -38,7 +38,11 @@ pub fn generate_python_client(
)
.unwrap();
writeln!(output, "from urllib.parse import urlparse").unwrap();
writeln!(output, "from datetime import date, timedelta").unwrap();
writeln!(output, "import json\n").unwrap();
writeln!(output, "if TYPE_CHECKING:").unwrap();
writeln!(output, " import pandas as pd # type: ignore[import-not-found]").unwrap();
writeln!(output, " import polars as pl # type: ignore[import-not-found]\n").unwrap();
writeln!(output, "T = TypeVar('T')\n").unwrap();
types::generate_type_definitions(&mut output, schemas);
+6 -1
View File
@@ -518,13 +518,18 @@ fn test_generated_python_output() {
let mut py_output = String::new();
writeln!(py_output, "# Auto-generated BRK Python client").unwrap();
writeln!(py_output, "# Do not edit manually\n").unwrap();
writeln!(py_output, "from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload").unwrap();
writeln!(py_output, "from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING").unwrap();
writeln!(py_output, "\nif TYPE_CHECKING:").unwrap();
writeln!(py_output, " import pandas as pd # type: ignore[import-not-found]").unwrap();
writeln!(py_output, " import polars as pl # type: ignore[import-not-found]").unwrap();
writeln!(
py_output,
"from http.client import HTTPSConnection, HTTPConnection"
)
.unwrap();
writeln!(py_output, "from urllib.parse import urlparse").unwrap();
writeln!(py_output, "from datetime import date, timedelta").unwrap();
writeln!(py_output, "from dataclasses import dataclass").unwrap();
writeln!(py_output, "import json\n").unwrap();
writeln!(py_output, "T = TypeVar('T')\n").unwrap();
+1 -1
View File
@@ -56,7 +56,7 @@ fn run() -> Result<()> {
// Pre-run indexer if too far behind, then drop and reimport to reduce memory
let chain_height = client.get_last_height()?;
let indexed_height = indexer.vecs.starting_height();
if u32::from(chain_height) - u32::from(indexed_height) > 1000 {
if u32::from(chain_height).saturating_sub(u32::from(indexed_height)) > 1000 {
indexer.checked_index(&blocks, &client, &exit)?;
drop(indexer);
Mimalloc::collect();
@@ -58,9 +58,10 @@ macro_rules! define_any_address_indexes_vecs {
}
/// Get address index for a given type and typeindex.
/// Uses get_any_or_read_at_unwrap to check updated layer (needed after rollback).
pub fn get(&self, address_type: OutputType, typeindex: TypeIndex, reader: &Reader) -> AnyAddressIndex {
match address_type {
$(OutputType::$variant => self.$field.get_pushed_or_read_at_unwrap(typeindex.into(), reader),)*
$(OutputType::$variant => self.$field.get_any_or_read_at_unwrap(typeindex.into(), reader),)*
_ => unreachable!("Invalid address type: {:?}", address_type),
}
}
@@ -112,16 +112,18 @@ pub fn load_uncached_address_data(
Some(match anyaddressindex.to_enum() {
AnyAddressDataIndexEnum::Loaded(loaded_index) => {
let reader = &vr.anyaddressindex_to_anyaddressdata.loaded;
// Use get_any_or_read_unwrap to check updated layer (needed after rollback)
let loaded_data = addresses_data
.loaded
.get_pushed_or_read_unwrap(loaded_index, reader);
.get_any_or_read_unwrap(loaded_index, reader);
WithAddressDataSource::FromLoaded(loaded_index, loaded_data)
}
AnyAddressDataIndexEnum::Empty(empty_index) => {
let reader = &vr.anyaddressindex_to_anyaddressdata.empty;
// Use get_any_or_read_unwrap to check updated layer (needed after rollback)
let empty_data = addresses_data
.empty
.get_pushed_or_read_unwrap(empty_index, reader);
.get_any_or_read_unwrap(empty_index, reader);
WithAddressDataSource::FromEmpty(empty_index, empty_data.into())
}
})
@@ -2,6 +2,7 @@ use std::{cmp::Ordering, collections::BTreeSet};
use brk_error::Result;
use brk_types::Height;
use tracing::{debug, warn};
use vecdb::Stamp;
use super::super::{
@@ -44,27 +45,49 @@ pub fn recover_state(
// If rollbacks are inconsistent, start fresh
if consistent_height.is_zero() {
warn!("Rollback consistency check failed: inconsistent heights");
return Ok(RecoveredState {
starting_height: Height::ZERO,
});
}
// Rollback can land at an earlier height (multi-block change file), which is fine.
// But if it lands AHEAD of target, that means rollback failed (missing change files).
if consistent_height > height {
warn!(
"Rollback failed: still at {} but target was {}, falling back to fresh start",
consistent_height, height
);
return Ok(RecoveredState {
starting_height: Height::ZERO,
});
}
if consistent_height != height {
debug!(
"Rollback landed at {} instead of {}, will resume from there",
consistent_height, height
);
}
// Import UTXO cohort states - all must succeed
if !utxo_cohorts.import_separate_states(height) {
if !utxo_cohorts.import_separate_states(consistent_height) {
warn!("UTXO cohort state import failed at height {}", consistent_height);
return Ok(RecoveredState {
starting_height: Height::ZERO,
});
}
// Import address cohort states - all must succeed
if !address_cohorts.import_separate_states(height) {
if !address_cohorts.import_separate_states(consistent_height) {
warn!("Address cohort state import failed at height {}", consistent_height);
return Ok(RecoveredState {
starting_height: Height::ZERO,
});
}
Ok(RecoveredState {
starting_height: height,
starting_height: consistent_height,
})
}
@@ -132,28 +155,38 @@ fn rollback_states(
// All rollbacks must succeed - any error means fresh start
let Ok(s) = chain_state_rollback else {
warn!("chain_state rollback failed: {:?}", chain_state_rollback);
return Height::ZERO;
};
heights.insert(Height::from(s).incremented());
let chain_height = Height::from(s).incremented();
debug!("chain_state rolled back to stamp {:?}, height {}", s, chain_height);
heights.insert(chain_height);
let Ok(stamps) = address_indexes_rollbacks else {
warn!("address_indexes rollback failed: {:?}", address_indexes_rollbacks);
return Height::ZERO;
};
for s in stamps {
heights.insert(Height::from(s).incremented());
for (i, s) in stamps.iter().enumerate() {
let h = Height::from(*s).incremented();
debug!("address_indexes[{}] rolled back to stamp {:?}, height {}", i, s, h);
heights.insert(h);
}
let Ok(stamps) = address_data_rollbacks else {
warn!("address_data rollback failed: {:?}", address_data_rollbacks);
return Height::ZERO;
};
for s in stamps {
heights.insert(Height::from(s).incremented());
for (i, s) in stamps.iter().enumerate() {
let h = Height::from(*s).incremented();
debug!("address_data[{}] rolled back to stamp {:?}, height {}", i, s, h);
heights.insert(h);
}
// All must agree on the same height
if heights.len() == 1 {
heights.pop_first().unwrap()
} else {
warn!("Rollback heights inconsistent: {:?}", heights);
Height::ZERO
}
}
@@ -1,6 +1,6 @@
use std::ops::Bound;
use brk_types::{Dollars, Sats};
use brk_types::{CentsUnsigned, Dollars, Sats};
use vecdb::CheckedSub;
use super::price_to_amount::PriceToAmount;
@@ -11,6 +11,10 @@ pub struct UnrealizedState {
pub supply_in_loss: Sats,
pub unrealized_profit: Dollars,
pub unrealized_loss: Dollars,
/// Invested capital in profit: Σ(sats × price) where price <= spot
pub invested_capital_in_profit: Dollars,
/// Invested capital in loss: Σ(sats × price) where price > spot
pub invested_capital_in_loss: Dollars,
}
impl UnrealizedState {
@@ -19,6 +23,8 @@ impl UnrealizedState {
supply_in_loss: Sats::ZERO,
unrealized_profit: Dollars::NAN,
unrealized_loss: Dollars::NAN,
invested_capital_in_profit: Dollars::NAN,
invested_capital_in_loss: Dollars::NAN,
};
pub const ZERO: Self = Self {
@@ -26,6 +32,8 @@ impl UnrealizedState {
supply_in_loss: Sats::ZERO,
unrealized_profit: Dollars::ZERO,
unrealized_loss: Dollars::ZERO,
invested_capital_in_profit: Dollars::ZERO,
invested_capital_in_loss: Dollars::ZERO,
};
}
@@ -62,14 +70,17 @@ impl CachedUnrealizedState {
/// Update cached state when a receive happens.
/// Determines profit/loss classification relative to cached price.
pub fn on_receive(&mut self, purchase_price: Dollars, sats: Sats) {
let invested_capital = purchase_price * sats;
if purchase_price <= self.at_price {
self.state.supply_in_profit += sats;
self.state.invested_capital_in_profit += invested_capital;
if purchase_price < self.at_price {
let diff = self.at_price.checked_sub(purchase_price).unwrap();
self.state.unrealized_profit += diff * sats;
}
} else {
self.state.supply_in_loss += sats;
self.state.invested_capital_in_loss += invested_capital;
let diff = purchase_price.checked_sub(self.at_price).unwrap();
self.state.unrealized_loss += diff * sats;
}
@@ -77,9 +88,15 @@ impl CachedUnrealizedState {
/// Update cached state when a send happens from historical price.
pub fn on_send(&mut self, historical_price: Dollars, sats: Sats) {
let invested_capital = historical_price * sats;
if historical_price <= self.at_price {
// Was in profit
self.state.supply_in_profit -= sats;
self.state.invested_capital_in_profit = self
.state
.invested_capital_in_profit
.checked_sub(invested_capital)
.unwrap();
if historical_price < self.at_price {
let diff = self.at_price.checked_sub(historical_price).unwrap();
let profit_removed = diff * sats;
@@ -92,6 +109,11 @@ impl CachedUnrealizedState {
} else {
// Was in loss
self.state.supply_in_loss -= sats;
self.state.invested_capital_in_loss = self
.state
.invested_capital_in_loss
.checked_sub(invested_capital)
.unwrap();
let diff = historical_price.checked_sub(self.at_price).unwrap();
let loss_removed = diff * sats;
self.state.unrealized_loss = self
@@ -210,14 +232,17 @@ impl CachedUnrealizedState {
let mut state = UnrealizedState::ZERO;
for (price, &sats) in price_to_amount.iter() {
let invested_capital = price * sats;
if price <= current_price {
state.supply_in_profit += sats;
state.invested_capital_in_profit += invested_capital;
if price < current_price {
let diff = current_price.checked_sub(price).unwrap();
state.unrealized_profit += diff * sats;
}
} else {
state.supply_in_loss += sats;
state.invested_capital_in_loss += invested_capital;
let diff = price.checked_sub(current_price).unwrap();
state.unrealized_loss += diff * sats;
}
+1 -2
View File
@@ -1,12 +1,11 @@
use std::str::FromStr;
use brk_types::{Height, TxIndex, Txid, TxidPrefix, Version};
use brk_types::{TxIndex, Txid, TxidPrefix, Version};
// One version for all data sources
// Increment on **change _OR_ addition**
pub const VERSION: Version = Version::new(24);
pub const SNAPSHOT_BLOCK_RANGE: usize = 1_000;
pub const COLLISIONS_CHECKED_UP_TO: Height = Height::new(0);
/// Known duplicate Bitcoin transactions (BIP30)
/// https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki
+10 -4
View File
@@ -102,6 +102,15 @@ impl Indexer {
let (starting_indexes, prev_hash) = if let Some(hash) = last_blockhash {
let (height, hash) = client.get_closest_valid_height(hash)?;
// TEST: force rollback 5 blocks (only if we have enough blocks)
let (height, hash) = if *height > 10 {
let height = Height::from(height.checked_sub(1).unwrap());
let hash = self.vecs.blocks.blockhash.iter()?.get(height).unwrap();
(height, hash)
} else {
(height, hash)
};
// END TEST
match Indexes::from_vecs_and_stores(height.incremented(), &mut self.vecs, &self.stores)
{
Some(starting_indexes) => {
@@ -177,13 +186,10 @@ impl Indexer {
indexes.height = height;
// Used to check rapidhash collisions
let block_check_collisions = check_collisions && height > COLLISIONS_CHECKED_UP_TO;
let mut processor = BlockProcessor {
block: &block,
height,
check_collisions: block_check_collisions,
check_collisions,
indexes: &mut indexes,
vecs,
stores,
+10
View File
@@ -78,6 +78,8 @@ impl<'a> BlockProcessor<'a> {
pub fn store_transaction_metadata(&mut self, txs: Vec<ComputedTx>) -> Result<()> {
let height = self.height;
let mut inserted = 0usize;
let mut skipped = 0usize;
for ct in txs {
if ct.prev_txindex_opt.is_none() {
@@ -86,6 +88,9 @@ impl<'a> BlockProcessor<'a> {
ct.txindex,
height,
);
inserted += 1;
} else {
skipped += 1;
}
self.vecs
@@ -118,6 +123,11 @@ impl<'a> BlockProcessor<'a> {
.checked_push(ct.txindex, StoredBool::from(ct.tx.is_explicitly_rbf()))?;
}
tracing::debug!(
"store_transaction_metadata: height={}, inserted={}, skipped={}",
height, inserted, skipped
);
Ok(())
}
}
+5
View File
@@ -81,6 +81,11 @@ impl<'a> BlockProcessor<'a> {
{
txindex
} else {
let store_result = self.stores.txidprefix_to_txindex.get(&txid_prefix)?;
tracing::error!(
"UnknownTxid: txid={}, prefix={:?}, store_result={:?}, current_txindex={:?}",
txid, txid_prefix, store_result, self.indexes.txindex
);
return Err(Error::UnknownTxid);
};
+11
View File
@@ -252,6 +252,14 @@ impl Stores {
}
if starting_indexes.txindex != TxIndex::ZERO {
let txid_vec_len = vecs.transactions.txid.len();
let skip_count = starting_indexes.txindex.to_usize();
let remove_count = txid_vec_len.saturating_sub(skip_count);
tracing::debug!(
"Rollback TXIDs: vec_len={}, skip={}, removing={}",
txid_vec_len, skip_count, remove_count
);
vecs.transactions
.txid
.iter()?
@@ -272,6 +280,9 @@ impl Stores {
self.txidprefix_to_txindex.remove(txidprefix);
}
});
// Clear caches to prevent stale reads after rollback
self.txidprefix_to_txindex.clear_caches();
} else {
unreachable!();
}
+4 -2
View File
@@ -156,6 +156,7 @@ impl Query {
Ok(ResolvedQuery {
vecs,
format: params.format(),
index: params.index,
version,
total,
start,
@@ -170,6 +171,7 @@ impl Query {
let ResolvedQuery {
vecs,
format,
index,
version,
total,
start,
@@ -182,7 +184,7 @@ impl Query {
Format::JSON => {
if vecs.len() == 1 {
let mut buf = Vec::new();
MetricData::serialize(vecs[0], start, end, &mut buf)?;
MetricData::serialize(vecs[0], index, start, end, &mut buf)?;
Output::Json(buf)
} else {
let mut buf = Vec::new();
@@ -191,7 +193,7 @@ impl Query {
if i > 0 {
buf.push(b',');
}
MetricData::serialize(*vec, start, end, &mut buf)?;
MetricData::serialize(*vec, index, start, end, &mut buf)?;
}
buf.push(b']');
Output::Json(buf)
+2 -1
View File
@@ -1,4 +1,4 @@
use brk_types::{Etag, Format};
use brk_types::{Etag, Format, Index};
use vecdb::AnyExportableVec;
/// A resolved metric query ready for formatting.
@@ -6,6 +6,7 @@ use vecdb::AnyExportableVec;
pub struct ResolvedQuery {
pub(crate) vecs: Vec<&'static dyn AnyExportableVec>,
pub(crate) format: Format,
pub(crate) index: Index,
pub(crate) version: u64,
pub(crate) total: usize,
pub(crate) start: usize,
+4 -2
View File
@@ -23,14 +23,16 @@ mod legacy;
/// Maximum allowed request weight in bytes (650KB)
const MAX_WEIGHT: usize = 65 * 10_000;
/// Maximum allowed request weight for localhost (50MB)
const MAX_WEIGHT_LOCALHOST: usize = 50 * 1_000_000;
/// Cache control header for metric data responses
const CACHE_CONTROL: &str = "public, max-age=1, must-revalidate";
/// Returns the max weight for a request based on the client address.
/// Localhost requests have no weight limit.
/// Localhost requests get a generous limit, external requests get a stricter one.
fn max_weight(addr: &SocketAddr) -> usize {
if addr.ip().is_loopback() {
usize::MAX
MAX_WEIGHT_LOCALHOST
} else {
MAX_WEIGHT
}
+8
View File
@@ -212,6 +212,14 @@ where
}
}
/// Clear all caches. Call after bulk removals (e.g., rollback) to prevent stale reads.
#[inline]
pub fn clear_caches(&mut self) {
for cache in &mut self.caches {
cache.clear();
}
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (K, V)> {
self.keyspace
+236
View File
@@ -0,0 +1,236 @@
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use vecdb::{Formattable, Pco};
use super::Dollars;
/// Signed cents (i64) - for values that can be negative.
/// Used for profit/loss calculations, deltas, etc.
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct CentsSigned(i64);
impl CentsSigned {
pub const ZERO: Self = Self(0);
#[inline]
pub const fn new(value: i64) -> Self {
Self(value)
}
#[inline]
pub const fn inner(self) -> i64 {
self.0
}
#[inline]
pub fn is_negative(self) -> bool {
self.0 < 0
}
#[inline]
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
#[inline]
pub fn checked_add(self, rhs: Self) -> Option<Self> {
self.0.checked_add(rhs.0).map(Self)
}
pub fn to_dollars(self) -> Dollars {
Dollars::from(self.0 as f64 / 100.0)
}
pub fn round_to(self, digits: i32) -> Self {
let v = self.0;
let ilog10 = v.unsigned_abs().checked_ilog10().unwrap_or(0) as i32;
Self::from(if ilog10 >= digits {
let log_diff = ilog10 - digits + 1;
let pow = 10.0_f64.powi(log_diff);
((v as f64 / pow).round() * pow) as i64
} else {
v
})
}
}
impl From<Dollars> for CentsSigned {
#[inline]
fn from(value: Dollars) -> Self {
Self((*value * 100.0).round() as i64)
}
}
impl From<CentsSigned> for Dollars {
#[inline]
fn from(value: CentsSigned) -> Self {
value.to_dollars()
}
}
impl From<CentsSigned> for f64 {
#[inline]
fn from(value: CentsSigned) -> Self {
value.0 as f64
}
}
impl From<i64> for CentsSigned {
#[inline]
fn from(value: i64) -> Self {
Self(value)
}
}
impl From<u64> for CentsSigned {
#[inline]
fn from(value: u64) -> Self {
Self(value as i64)
}
}
impl From<CentsSigned> for usize {
#[inline]
fn from(value: CentsSigned) -> Self {
debug_assert!(value.0 >= 0, "Cannot convert negative CentsSigned to usize");
value.0 as usize
}
}
impl From<usize> for CentsSigned {
#[inline]
fn from(value: usize) -> Self {
Self(value as i64)
}
}
impl From<CentsSigned> for i64 {
#[inline]
fn from(value: CentsSigned) -> Self {
value.0
}
}
impl From<CentsSigned> for u64 {
#[inline]
fn from(value: CentsSigned) -> Self {
debug_assert!(value.0 >= 0, "Cannot convert negative CentsSigned to u64");
value.0 as u64
}
}
impl Add for CentsSigned {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl AddAssign for CentsSigned {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl Sub for CentsSigned {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl SubAssign for CentsSigned {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl Div<CentsSigned> for CentsSigned {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl Div<usize> for CentsSigned {
type Output = Self;
#[inline]
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as i64)
}
}
impl From<u128> for CentsSigned {
#[inline]
fn from(value: u128) -> Self {
debug_assert!(value <= i64::MAX as u128, "u128 overflow to CentsSigned");
Self(value as i64)
}
}
impl From<CentsSigned> for u128 {
#[inline]
fn from(value: CentsSigned) -> Self {
debug_assert!(value.0 >= 0, "Cannot convert negative CentsSigned to u128");
value.0 as u128
}
}
impl Mul<CentsSigned> for CentsSigned {
type Output = CentsSigned;
#[inline]
fn mul(self, rhs: CentsSigned) -> Self::Output {
Self(self.0.checked_mul(rhs.0).expect("CentsSigned overflow"))
}
}
impl Mul<i64> for CentsSigned {
type Output = CentsSigned;
#[inline]
fn mul(self, rhs: i64) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Mul<usize> for CentsSigned {
type Output = CentsSigned;
#[inline]
fn mul(self, rhs: usize) -> Self::Output {
Self(self.0 * rhs as i64)
}
}
impl std::fmt::Display for CentsSigned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = itoa::Buffer::new();
let str = buf.format(self.0);
f.write_str(str)
}
}
impl Formattable for CentsSigned {
#[inline(always)]
fn may_need_escaping() -> bool {
false
}
}
@@ -0,0 +1,91 @@
use serde::{Deserialize, Serialize};
use super::Dollars;
/// Compact signed cents (i32) - memory-efficient for map keys.
/// Supports prices from -$21,474,836.47 to $21,474,836.47 (i32 range / 100).
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct CentsSignedCompact(i32);
impl CentsSignedCompact {
pub const ZERO: Self = Self(0);
#[inline]
pub const fn new(value: i32) -> Self {
Self(value)
}
#[inline]
pub const fn inner(self) -> i32 {
self.0
}
#[inline]
pub fn is_negative(self) -> bool {
self.0 < 0
}
#[inline]
pub fn to_dollars(self) -> Dollars {
Dollars::from(self.0 as f64 / 100.0)
}
#[inline]
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl From<Dollars> for CentsSignedCompact {
#[inline]
fn from(value: Dollars) -> Self {
let f = f64::from(value);
if f.is_nan() {
Self::ZERO
} else {
let cents = (f * 100.0).round();
debug_assert!(
cents >= i32::MIN as f64 && cents <= i32::MAX as f64,
"Price ${} exceeds CentsSignedCompact range (~$21.5M)",
f
);
Self(cents as i32)
}
}
}
impl From<CentsSignedCompact> for Dollars {
#[inline]
fn from(value: CentsSignedCompact) -> Self {
value.to_dollars()
}
}
impl From<i32> for CentsSignedCompact {
#[inline]
fn from(value: i32) -> Self {
Self(value)
}
}
impl From<CentsSignedCompact> for i32 {
#[inline]
fn from(value: CentsSignedCompact) -> Self {
value.0
}
}
impl From<CentsSignedCompact> for f64 {
#[inline]
fn from(value: CentsSignedCompact) -> Self {
value.0 as f64
}
}
impl std::fmt::Display for CentsSignedCompact {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = itoa::Buffer::new();
let str = buf.format(self.0);
f.write_str(str)
}
}
+207
View File
@@ -0,0 +1,207 @@
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use vecdb::{Formattable, Pco};
use super::Dollars;
/// Unsigned cents (u64) - for values that should never be negative.
/// Used for invested capital, realized cap, etc.
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Serialize,
Deserialize,
Pco,
JsonSchema,
)]
pub struct CentsUnsigned(u64);
impl CentsUnsigned {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u64::MAX);
#[inline]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[inline]
pub const fn inner(self) -> u64 {
self.0
}
#[inline]
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
#[inline]
pub fn saturating_sub(self, rhs: Self) -> Self {
Self(self.0.saturating_sub(rhs.0))
}
#[inline]
pub fn checked_add(self, rhs: Self) -> Option<Self> {
self.0.checked_add(rhs.0).map(Self)
}
pub fn to_dollars(self) -> Dollars {
Dollars::from(self.0 as f64 / 100.0)
}
}
impl From<Dollars> for CentsUnsigned {
#[inline]
fn from(value: Dollars) -> Self {
let f = f64::from(value);
if f.is_nan() || f < 0.0 {
Self::ZERO
} else {
Self((f * 100.0).round() as u64)
}
}
}
impl From<CentsUnsigned> for Dollars {
#[inline]
fn from(value: CentsUnsigned) -> Self {
value.to_dollars()
}
}
impl From<u64> for CentsUnsigned {
#[inline]
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<CentsUnsigned> for u64 {
#[inline]
fn from(value: CentsUnsigned) -> Self {
value.0
}
}
impl From<u128> for CentsUnsigned {
#[inline]
fn from(value: u128) -> Self {
debug_assert!(value <= u64::MAX as u128, "u128 overflow to CentsUnsigned");
Self(value as u64)
}
}
impl From<CentsUnsigned> for u128 {
#[inline]
fn from(value: CentsUnsigned) -> Self {
value.0 as u128
}
}
impl From<CentsUnsigned> for f64 {
#[inline]
fn from(value: CentsUnsigned) -> Self {
value.0 as f64
}
}
impl Add for CentsUnsigned {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl AddAssign for CentsUnsigned {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
impl Sub for CentsUnsigned {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl SubAssign for CentsUnsigned {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 -= rhs.0;
}
}
impl Mul for CentsUnsigned {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0.checked_mul(rhs.0).expect("CentsUnsigned overflow"))
}
}
impl Mul<u64> for CentsUnsigned {
type Output = Self;
#[inline]
fn mul(self, rhs: u64) -> Self::Output {
Self(self.0 * rhs)
}
}
impl Mul<usize> for CentsUnsigned {
type Output = Self;
#[inline]
fn mul(self, rhs: usize) -> Self::Output {
Self(self.0 * rhs as u64)
}
}
impl Div<CentsUnsigned> for CentsUnsigned {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl Div<u64> for CentsUnsigned {
type Output = Self;
#[inline]
fn div(self, rhs: u64) -> Self::Output {
Self(self.0 / rhs)
}
}
impl Div<usize> for CentsUnsigned {
type Output = Self;
#[inline]
fn div(self, rhs: usize) -> Self::Output {
Self(self.0 / rhs as u64)
}
}
impl std::fmt::Display for CentsUnsigned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = itoa::Buffer::new();
let str = buf.format(self.0);
f.write_str(str)
}
}
impl Formattable for CentsUnsigned {
#[inline(always)]
fn may_need_escaping() -> bool {
false
}
}
@@ -0,0 +1,92 @@
use serde::{Deserialize, Serialize};
use super::Dollars;
/// Compact unsigned cents (u32) - memory-efficient for map keys.
/// Supports values from $0.00 to $42,949,672.95 (u32::MAX / 100).
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct CentsUnsignedCompact(u32);
impl CentsUnsignedCompact {
pub const ZERO: Self = Self(0);
pub const MAX: Self = Self(u32::MAX);
#[inline]
pub const fn new(value: u32) -> Self {
Self(value)
}
#[inline]
pub const fn inner(self) -> u32 {
self.0
}
#[inline]
pub fn to_dollars(self) -> Dollars {
Dollars::from(self.0 as f64 / 100.0)
}
#[inline]
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
#[inline]
pub fn saturating_sub(self, rhs: Self) -> Self {
Self(self.0.saturating_sub(rhs.0))
}
}
impl From<Dollars> for CentsUnsignedCompact {
#[inline]
fn from(value: Dollars) -> Self {
let f = f64::from(value);
if f.is_nan() || f < 0.0 {
Self::ZERO
} else {
let cents = (f * 100.0).round();
debug_assert!(
cents <= u32::MAX as f64,
"Price ${} exceeds CentsUnsignedCompact max (~$42.9M)",
f
);
Self(cents as u32)
}
}
}
impl From<CentsUnsignedCompact> for Dollars {
#[inline]
fn from(value: CentsUnsignedCompact) -> Self {
value.to_dollars()
}
}
impl From<u32> for CentsUnsignedCompact {
#[inline]
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<CentsUnsignedCompact> for u32 {
#[inline]
fn from(value: CentsUnsignedCompact) -> Self {
value.0
}
}
impl From<CentsUnsignedCompact> for f64 {
#[inline]
fn from(value: CentsUnsignedCompact) -> Self {
value.0 as f64
}
}
impl std::fmt::Display for CentsUnsignedCompact {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf = itoa::Buffer::new();
let str = buf.format(self.0);
f.write_str(str)
}
}
+236 -1
View File
@@ -7,7 +7,7 @@ use vecdb::{Formattable, Pco};
use crate::ONE_DAY_IN_SEC_F64;
use super::{DateIndex, Timestamp};
use super::{DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Timestamp, WeekIndex, YearIndex};
/// Date in YYYYMMDD format stored as u32
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Pco, JsonSchema)]
@@ -111,6 +111,72 @@ impl From<DateIndex> for Date {
}
}
impl From<WeekIndex> for Date {
#[inline]
fn from(value: WeekIndex) -> Self {
// Week 0 starts at genesis (2009-01-03), add i weeks
Self::from(
Self::INDEX_ZERO_
.checked_add(Span::new().weeks(i64::from(u16::from(value))))
.unwrap(),
)
}
}
impl From<MonthIndex> for Date {
#[inline]
fn from(value: MonthIndex) -> Self {
// Month 0 is January 2009, add i months
Self::from(
Date_::constant(2009, 1, 1)
.checked_add(Span::new().months(i64::from(u16::from(value))))
.unwrap(),
)
}
}
impl From<YearIndex> for Date {
#[inline]
fn from(value: YearIndex) -> Self {
// Year 0 is 2009
let year = 2009i16 + usize::from(value) as i16;
Self::from(Date_::constant(year, 1, 1))
}
}
impl From<QuarterIndex> for Date {
#[inline]
fn from(value: QuarterIndex) -> Self {
// Quarter 0 is Q1 2009, add i*3 months
Self::from(
Date_::constant(2009, 1, 1)
.checked_add(Span::new().months(usize::from(value) as i64 * 3))
.unwrap(),
)
}
}
impl From<SemesterIndex> for Date {
#[inline]
fn from(value: SemesterIndex) -> Self {
// Semester 0 is H1 2009, add i*6 months
Self::from(
Date_::constant(2009, 1, 1)
.checked_add(Span::new().months(usize::from(value) as i64 * 6))
.unwrap(),
)
}
}
impl From<DecadeIndex> for Date {
#[inline]
fn from(value: DecadeIndex) -> Self {
// Decade 0 is 2009, add i*10 years
let year = 2009i16 + usize::from(value) as i16 * 10;
Self::from(Date_::constant(year, 1, 1))
}
}
impl Serialize for Date {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -190,3 +256,172 @@ impl Formattable for Date {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_date_from_dateindex_zero() {
// DateIndex 0 is genesis: Jan 3, 2009
let date = Date::from(DateIndex::from(0_usize));
assert_eq!(date, Date::INDEX_ZERO);
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 3);
}
#[test]
fn test_date_from_dateindex_one() {
// DateIndex 1 is Jan 9, 2009 (6 day gap after genesis)
let date = Date::from(DateIndex::from(1_usize));
assert_eq!(date, Date::INDEX_ONE);
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 9);
}
#[test]
fn test_date_from_dateindex_two() {
// DateIndex 2 is Jan 10, 2009
let date = Date::from(DateIndex::from(2_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 10);
}
#[test]
fn test_date_from_weekindex_zero() {
// WeekIndex 0 starts at genesis: Jan 3, 2009
let date = Date::from(WeekIndex::from(0_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 3);
}
#[test]
fn test_date_from_weekindex_one() {
// WeekIndex 1 is Jan 10, 2009 (one week after genesis)
let date = Date::from(WeekIndex::from(1_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 10);
}
#[test]
fn test_date_from_monthindex_zero() {
// MonthIndex 0 is Jan 1, 2009
let date = Date::from(MonthIndex::from(0_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_monthindex_one() {
// MonthIndex 1 is Feb 1, 2009
let date = Date::from(MonthIndex::from(1_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 2);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_monthindex_twelve() {
// MonthIndex 12 is Jan 1, 2010
let date = Date::from(MonthIndex::from(12_usize));
assert_eq!(date.year(), 2010);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_yearindex_zero() {
// YearIndex 0 is Jan 1, 2009
let date = Date::from(YearIndex::from(0_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_yearindex_one() {
// YearIndex 1 is Jan 1, 2010
let date = Date::from(YearIndex::from(1_usize));
assert_eq!(date.year(), 2010);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_quarterindex_zero() {
// QuarterIndex 0 is Q1 2009: Jan 1, 2009
let date = Date::from(QuarterIndex::from(0_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_quarterindex_one() {
// QuarterIndex 1 is Q2 2009: Apr 1, 2009
let date = Date::from(QuarterIndex::from(1_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 4);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_quarterindex_four() {
// QuarterIndex 4 is Q1 2010: Jan 1, 2010
let date = Date::from(QuarterIndex::from(4_usize));
assert_eq!(date.year(), 2010);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_semesterindex_zero() {
// SemesterIndex 0 is H1 2009: Jan 1, 2009
let date = Date::from(SemesterIndex::from(0_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_semesterindex_one() {
// SemesterIndex 1 is H2 2009: Jul 1, 2009
let date = Date::from(SemesterIndex::from(1_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 7);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_semesterindex_two() {
// SemesterIndex 2 is H1 2010: Jan 1, 2010
let date = Date::from(SemesterIndex::from(2_usize));
assert_eq!(date.year(), 2010);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_decadeindex_zero() {
// DecadeIndex 0 is 2009: Jan 1, 2009
let date = Date::from(DecadeIndex::from(0_usize));
assert_eq!(date.year(), 2009);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
#[test]
fn test_date_from_decadeindex_one() {
// DecadeIndex 1 is 2019: Jan 1, 2019
let date = Date::from(DecadeIndex::from(1_usize));
assert_eq!(date.year(), 2019);
assert_eq!(date.month(), 1);
assert_eq!(date.day(), 1);
}
}
+138 -1
View File
@@ -8,7 +8,7 @@ use vecdb::PrintableIndex;
use crate::PairOutputIndex;
use super::{
DateIndex, DecadeIndex, DifficultyEpoch, EmptyAddressIndex, EmptyOutputIndex, HalvingEpoch,
Date, DateIndex, DecadeIndex, DifficultyEpoch, EmptyAddressIndex, EmptyOutputIndex, HalvingEpoch,
Height, LoadedAddressIndex, MonthIndex, OpReturnIndex, P2AAddressIndex, P2MSOutputIndex,
P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2TRAddressIndex,
P2WPKHAddressIndex, P2WSHAddressIndex, QuarterIndex, SemesterIndex, TxInIndex, TxIndex,
@@ -144,6 +144,35 @@ impl Index {
_ => 1,
}
}
/// Returns true if this index type is date-based.
pub const fn is_date_based(&self) -> bool {
matches!(
self,
Self::DateIndex
| Self::WeekIndex
| Self::MonthIndex
| Self::YearIndex
| Self::QuarterIndex
| Self::SemesterIndex
| Self::DecadeIndex
)
}
/// Convert an index value to a date for date-based indexes.
/// Returns None for non-date-based indexes.
pub fn index_to_date(&self, i: usize) -> Option<Date> {
match self {
Self::DateIndex => Some(Date::from(DateIndex::from(i))),
Self::WeekIndex => Some(Date::from(WeekIndex::from(i))),
Self::MonthIndex => Some(Date::from(MonthIndex::from(i))),
Self::YearIndex => Some(Date::from(YearIndex::from(i))),
Self::QuarterIndex => Some(Date::from(QuarterIndex::from(i))),
Self::SemesterIndex => Some(Date::from(SemesterIndex::from(i))),
Self::DecadeIndex => Some(Date::from(DecadeIndex::from(i))),
_ => None,
}
}
}
impl TryFrom<&str> for Index {
@@ -219,3 +248,111 @@ impl<'de> Deserialize<'de> for Index {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_date_based_dateindex() {
assert!(Index::DateIndex.is_date_based());
}
#[test]
fn test_is_date_based_weekindex() {
assert!(Index::WeekIndex.is_date_based());
}
#[test]
fn test_is_date_based_monthindex() {
assert!(Index::MonthIndex.is_date_based());
}
#[test]
fn test_is_date_based_yearindex() {
assert!(Index::YearIndex.is_date_based());
}
#[test]
fn test_is_date_based_quarterindex() {
assert!(Index::QuarterIndex.is_date_based());
}
#[test]
fn test_is_date_based_semesterindex() {
assert!(Index::SemesterIndex.is_date_based());
}
#[test]
fn test_is_date_based_decadeindex() {
assert!(Index::DecadeIndex.is_date_based());
}
#[test]
fn test_is_not_date_based_height() {
assert!(!Index::Height.is_date_based());
}
#[test]
fn test_is_not_date_based_txindex() {
assert!(!Index::TxIndex.is_date_based());
}
#[test]
fn test_index_to_date_dateindex_zero() {
let date = Index::DateIndex.index_to_date(0).unwrap();
assert_eq!(date, Date::from(DateIndex::from(0_usize)));
}
#[test]
fn test_index_to_date_dateindex_one() {
let date = Index::DateIndex.index_to_date(1).unwrap();
assert_eq!(date, Date::from(DateIndex::from(1_usize)));
}
#[test]
fn test_index_to_date_weekindex() {
let date = Index::WeekIndex.index_to_date(1).unwrap();
assert_eq!(date, Date::from(WeekIndex::from(1_usize)));
}
#[test]
fn test_index_to_date_monthindex() {
let date = Index::MonthIndex.index_to_date(12).unwrap();
assert_eq!(date, Date::from(MonthIndex::from(12_usize)));
}
#[test]
fn test_index_to_date_yearindex() {
let date = Index::YearIndex.index_to_date(5).unwrap();
assert_eq!(date, Date::from(YearIndex::from(5_usize)));
}
#[test]
fn test_index_to_date_quarterindex() {
let date = Index::QuarterIndex.index_to_date(4).unwrap();
assert_eq!(date, Date::from(QuarterIndex::from(4_usize)));
}
#[test]
fn test_index_to_date_semesterindex() {
let date = Index::SemesterIndex.index_to_date(2).unwrap();
assert_eq!(date, Date::from(SemesterIndex::from(2_usize)));
}
#[test]
fn test_index_to_date_decadeindex() {
let date = Index::DecadeIndex.index_to_date(1).unwrap();
assert_eq!(date, Date::from(DecadeIndex::from(1_usize)));
}
#[test]
fn test_index_to_date_height_returns_none() {
assert!(Index::Height.index_to_date(100).is_none());
}
#[test]
fn test_index_to_date_txindex_returns_none() {
assert!(Index::TxIndex.index_to_date(100).is_none());
}
}
+8
View File
@@ -37,6 +37,10 @@ mod blockweightentry;
mod bytes;
mod cents;
mod centscompact;
mod cents_signed;
mod cents_signed_compact;
mod cents_unsigned;
mod cents_unsigned_compact;
mod datarange;
mod datarangeformat;
mod date;
@@ -209,6 +213,10 @@ pub use blockweightentry::*;
pub use bytes::*;
pub use cents::*;
pub use centscompact::*;
pub use cents_signed::*;
pub use cents_signed_compact::*;
pub use cents_unsigned::*;
pub use cents_unsigned_compact::*;
pub use datarange::*;
pub use datarangeformat::*;
pub use date::*;
+159 -3
View File
@@ -5,7 +5,7 @@ use serde::Deserialize;
use serde_json::Value;
use vecdb::AnySerializableVec;
use super::Timestamp;
use super::{Index, Timestamp};
/// Metric data with range information.
///
@@ -15,6 +15,8 @@ use super::Timestamp;
pub struct MetricData<T = Value> {
/// Version of the metric data
pub version: u64,
/// The index type used for this query
pub index: Index,
/// Total number of data points in the metric
pub total: usize,
/// Start index (inclusive) of the returned range
@@ -28,14 +30,16 @@ pub struct MetricData<T = Value> {
}
impl MetricData {
/// Write metric data as JSON to buffer: `{"version":N,"total":N,"start":N,"end":N,"stamp":"...","data":[...]}`
/// Write metric data as JSON to buffer: `{"version":N,"index":"...","total":N,"start":N,"end":N,"stamp":"...","data":[...]}`
pub fn serialize(
vec: &dyn AnySerializableVec,
index: Index,
start: usize,
end: usize,
buf: &mut Vec<u8>,
) -> vecdb::Result<()> {
let version = u64::from(vec.version());
let index_str = index.serialize_long();
let total = vec.len();
let end = end.min(total);
let start = start.min(end);
@@ -43,10 +47,162 @@ impl MetricData {
write!(
buf,
r#"{{"version":{version},"total":{total},"start":{start},"end":{end},"stamp":"{stamp}","data":"#,
r#"{{"version":{version},"index":"{index_str}","total":{total},"start":{start},"end":{end},"stamp":"{stamp}","data":"#,
)?;
vec.write_json(Some(start), Some(end), buf)?;
buf.push(b'}');
Ok(())
}
}
impl<T> MetricData<T> {
/// Returns an iterator over the index range.
pub fn indexes(&self) -> std::ops::Range<usize> {
self.start..self.end
}
/// Returns true if this metric uses a date-based index.
pub fn is_date_based(&self) -> bool {
self.index.is_date_based()
}
/// Returns an iterator over dates for the index range.
/// Panics if the index is not date-based.
pub fn dates(&self) -> impl Iterator<Item = super::Date> + '_ {
let index = self.index;
self.indexes().map(move |i| {
index
.index_to_date(i)
.expect("dates() called on non-date-based index")
})
}
/// Iterate over (index, &value) pairs.
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
self.indexes().zip(self.data.iter())
}
/// Iterate over (date, &value) pairs.
/// Panics if the index is not date-based.
pub fn iter_dates(&self) -> impl Iterator<Item = (super::Date, &T)> + '_ {
self.dates().zip(self.data.iter())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn date_based_metric() -> MetricData<i32> {
MetricData {
version: 1,
index: Index::DateIndex,
total: 100,
start: 0,
end: 5,
stamp: "2024-01-01T00:00:00Z".to_string(),
data: vec![100, 200, 300, 400, 500],
}
}
fn height_based_metric() -> MetricData<f64> {
MetricData {
version: 1,
index: Index::Height,
total: 1000,
start: 800000,
end: 800005,
stamp: "2024-01-01T00:00:00Z".to_string(),
data: vec![1.5, 2.5, 3.5, 4.5, 5.5],
}
}
#[test]
fn test_indexes_returns_range() {
let metric = date_based_metric();
let indexes: Vec<_> = metric.indexes().collect();
assert_eq!(indexes, vec![0, 1, 2, 3, 4]);
}
#[test]
fn test_indexes_with_offset() {
let metric = height_based_metric();
let indexes: Vec<_> = metric.indexes().collect();
assert_eq!(indexes, vec![800000, 800001, 800002, 800003, 800004]);
}
#[test]
fn test_is_date_based_true() {
let metric = date_based_metric();
assert!(metric.is_date_based());
}
#[test]
fn test_is_date_based_false() {
let metric = height_based_metric();
assert!(!metric.is_date_based());
}
#[test]
fn test_dates_for_dateindex() {
let metric = date_based_metric();
let dates: Vec<_> = metric.dates().collect();
assert_eq!(dates.len(), 5);
// DateIndex 0 = Jan 3, 2009 (genesis)
assert_eq!(dates[0].year(), 2009);
assert_eq!(dates[0].month(), 1);
assert_eq!(dates[0].day(), 3);
// DateIndex 1 = Jan 9, 2009 (day one)
assert_eq!(dates[1].year(), 2009);
assert_eq!(dates[1].month(), 1);
assert_eq!(dates[1].day(), 9);
}
#[test]
fn test_iter() {
let metric = date_based_metric();
let pairs: Vec<_> = metric.iter().collect();
assert_eq!(pairs.len(), 5);
assert_eq!(pairs[0], (0, &100));
assert_eq!(pairs[1], (1, &200));
assert_eq!(pairs[4], (4, &500));
}
#[test]
fn test_iter_with_offset() {
let metric = height_based_metric();
let pairs: Vec<_> = metric.iter().collect();
assert_eq!(pairs.len(), 5);
assert_eq!(pairs[0], (800000, &1.5));
assert_eq!(pairs[4], (800004, &5.5));
}
#[test]
fn test_iter_dates() {
let metric = date_based_metric();
let pairs: Vec<_> = metric.iter_dates().collect();
assert_eq!(pairs.len(), 5);
// First pair: (Jan 3 2009, 100)
assert_eq!(pairs[0].0.year(), 2009);
assert_eq!(pairs[0].0.month(), 1);
assert_eq!(pairs[0].0.day(), 3);
assert_eq!(pairs[0].1, &100);
// Second pair: (Jan 9 2009, 200)
assert_eq!(pairs[1].0.day(), 9);
assert_eq!(pairs[1].1, &200);
}
#[test]
#[should_panic(expected = "dates() called on non-date-based index")]
fn test_dates_panics_for_non_date_index() {
let metric = height_based_metric();
let _: Vec<_> = metric.dates().collect();
}
#[test]
#[should_panic(expected = "dates() called on non-date-based index")]
fn test_iter_dates_panics_for_non_date_index() {
let metric = height_based_metric();
let _: Vec<_> = metric.iter_dates().collect();
}
}
-1
View File
@@ -12,4 +12,3 @@ worker.*
*.mts
*.cts
*.rs
*.md
@@ -0,0 +1,25 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / \_0sdUsdPattern
# Interface: \_0sdUsdPattern
Defined in: [Developer/brk/modules/brk-client/index.js:2763](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2763)
## Properties
### dollars
> **dollars**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2764](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2764)
***
### sats
> **sats**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2765](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2765)
@@ -0,0 +1,25 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / ActivePricePattern
# Interface: ActivePricePattern
Defined in: [Developer/brk/modules/brk-client/index.js:2706](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2706)
## Properties
### dollars
> **dollars**: [`MetricPattern1`](../type-aliases/MetricPattern1.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2707](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2707)
***
### sats
> **sats**: [`MetricPattern1`](../type-aliases/MetricPattern1.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2708](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2708)
@@ -0,0 +1,57 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / AllPattern
# Interface: AllPattern
Defined in: [Developer/brk/modules/brk-client/index.js:2463](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2463)
## Properties
### balanceDecreased
> **balanceDecreased**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2464](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2464)
***
### balanceIncreased
> **balanceIncreased**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2465](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2465)
***
### both
> **both**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2466](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2466)
***
### reactivated
> **reactivated**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2467](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2467)
***
### receiving
> **receiving**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2468](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2468)
***
### sending
> **sending**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:2469](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L2469)
@@ -0,0 +1,111 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / ClassDaysInLossPattern
# Interface: ClassDaysInLossPattern\<T\>
Defined in: [Developer/brk/modules/brk-client/index.js:1932](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1932)
## Type Parameters
### T
`T`
## Properties
### \_2015
> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1933](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1933)
***
### \_2016
> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1934](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1934)
***
### \_2017
> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1935](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1935)
***
### \_2018
> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1936](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1936)
***
### \_2019
> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1937](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1937)
***
### \_2020
> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1938](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1938)
***
### \_2021
> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1939](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1939)
***
### \_2022
> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1940](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1940)
***
### \_2023
> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1941](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1941)
***
### \_2024
> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1942](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1942)
***
### \_2025
> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1943](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1943)
***
### \_2026
> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1944](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1944)
@@ -0,0 +1,33 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Cointime\_ReserveRisk
# Interface: MetricsTree\_Cointime\_ReserveRisk
Defined in: [Developer/brk/modules/brk-client/index.js:3093](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3093)
## Properties
### hodlBank
> **hodlBank**: [`MetricPattern6`](../type-aliases/MetricPattern6.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3094](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3094)
***
### reserveRisk
> **reserveRisk**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3095](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3095)
***
### vocdd365dSma
> **vocdd365dSma**: [`MetricPattern6`](../type-aliases/MetricPattern6.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3096](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3096)
@@ -0,0 +1,81 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Distribution\_AddressActivity
# Interface: MetricsTree\_Distribution\_AddressActivity
Defined in: [Developer/brk/modules/brk-client/index.js:3153](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3153)
## Properties
### all
> **all**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3154](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3154)
***
### p2a
> **p2a**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3155](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3155)
***
### p2pk33
> **p2pk33**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3156](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3156)
***
### p2pk65
> **p2pk65**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3157](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3157)
***
### p2pkh
> **p2pkh**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3158](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3158)
***
### p2sh
> **p2sh**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3159](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3159)
***
### p2tr
> **p2tr**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3160](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3160)
***
### p2wpkh
> **p2wpkh**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3161](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3161)
***
### p2wsh
> **p2wsh**: [`AllPattern`](AllPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3162](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3162)
@@ -0,0 +1,81 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Distribution\_GrowthRate
# Interface: MetricsTree\_Distribution\_GrowthRate
Defined in: [Developer/brk/modules/brk-client/index.js:3244](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3244)
## Properties
### all
> **all**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3245](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3245)
***
### p2a
> **p2a**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3246](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3246)
***
### p2pk33
> **p2pk33**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3247](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3247)
***
### p2pk65
> **p2pk65**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3248](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3248)
***
### p2pkh
> **p2pkh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3249](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3249)
***
### p2sh
> **p2sh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3250](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3250)
***
### p2tr
> **p2tr**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3251](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3251)
***
### p2wpkh
> **p2wpkh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3252](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3252)
***
### p2wsh
> **p2wsh**: [`FullnessPattern`](FullnessPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3253](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3253)
@@ -0,0 +1,81 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Distribution\_NewAddrCount
# Interface: MetricsTree\_Distribution\_NewAddrCount
Defined in: [Developer/brk/modules/brk-client/index.js:3257](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3257)
## Properties
### all
> **all**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3258](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3258)
***
### p2a
> **p2a**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3259](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3259)
***
### p2pk33
> **p2pk33**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3260](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3260)
***
### p2pk65
> **p2pk65**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3261](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3261)
***
### p2pkh
> **p2pkh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3262](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3262)
***
### p2sh
> **p2sh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3263](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3263)
***
### p2tr
> **p2tr**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3264](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3264)
***
### p2wpkh
> **p2wpkh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3265](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3265)
***
### p2wsh
> **p2wsh**: [`DollarsPattern`](DollarsPattern.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3266](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3266)
@@ -0,0 +1,105 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassAveragePrice
# Interface: MetricsTree\_Market\_Dca\_ClassAveragePrice
Defined in: [Developer/brk/modules/brk-client/index.js:3768](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3768)
## Properties
### \_2015
> **\_2015**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3769](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3769)
***
### \_2016
> **\_2016**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3770](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3770)
***
### \_2017
> **\_2017**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3771](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3771)
***
### \_2018
> **\_2018**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3772](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3772)
***
### \_2019
> **\_2019**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3773](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3773)
***
### \_2020
> **\_2020**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3774](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3774)
***
### \_2021
> **\_2021**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3775](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3775)
***
### \_2022
> **\_2022**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3776](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3776)
***
### \_2023
> **\_2023**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3777](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3777)
***
### \_2024
> **\_2024**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3778](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3778)
***
### \_2025
> **\_2025**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3779](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3779)
***
### \_2026
> **\_2026**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3780](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3780)
@@ -0,0 +1,105 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassDaysInLoss
# Interface: MetricsTree\_Market\_Dca\_ClassDaysInLoss
Defined in: [Developer/brk/modules/brk-client/index.js:3784](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3784)
## Properties
### \_2015
> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3785](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3785)
***
### \_2016
> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3786](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3786)
***
### \_2017
> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3787](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3787)
***
### \_2018
> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3788](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3788)
***
### \_2019
> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3789](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3789)
***
### \_2020
> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3790](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3790)
***
### \_2021
> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3791](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3791)
***
### \_2022
> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3792](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3792)
***
### \_2023
> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3793](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3793)
***
### \_2024
> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3794](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3794)
***
### \_2025
> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3795](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3795)
***
### \_2026
> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3796](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3796)
@@ -0,0 +1,105 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassMaxDrawdown
# Interface: MetricsTree\_Market\_Dca\_ClassMaxDrawdown
Defined in: [Developer/brk/modules/brk-client/index.js:3800](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3800)
## Properties
### \_2015
> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3801](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3801)
***
### \_2016
> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3802](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3802)
***
### \_2017
> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3803](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3803)
***
### \_2018
> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3804](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3804)
***
### \_2019
> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3805](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3805)
***
### \_2020
> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3806](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3806)
***
### \_2021
> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3807](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3807)
***
### \_2022
> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3808](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3808)
***
### \_2023
> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3809](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3809)
***
### \_2024
> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3810](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3810)
***
### \_2025
> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3811](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3811)
***
### \_2026
> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3812](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3812)
@@ -0,0 +1,105 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassMaxReturn
# Interface: MetricsTree\_Market\_Dca\_ClassMaxReturn
Defined in: [Developer/brk/modules/brk-client/index.js:3816](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3816)
## Properties
### \_2015
> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3817](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3817)
***
### \_2016
> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3818](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3818)
***
### \_2017
> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3819](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3819)
***
### \_2018
> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3820](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3820)
***
### \_2019
> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3821](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3821)
***
### \_2020
> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3822](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3822)
***
### \_2021
> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3823](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3823)
***
### \_2022
> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3824](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3824)
***
### \_2023
> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3825](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3825)
***
### \_2024
> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3826](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3826)
***
### \_2025
> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3827](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3827)
***
### \_2026
> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3828](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3828)
@@ -0,0 +1,105 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_ClassReturns
# Interface: MetricsTree\_Market\_Dca\_ClassReturns
Defined in: [Developer/brk/modules/brk-client/index.js:3832](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3832)
## Properties
### \_2015
> **\_2015**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3833](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3833)
***
### \_2016
> **\_2016**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3834](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3834)
***
### \_2017
> **\_2017**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3835](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3835)
***
### \_2018
> **\_2018**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3836](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3836)
***
### \_2019
> **\_2019**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3837](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3837)
***
### \_2020
> **\_2020**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3838](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3838)
***
### \_2021
> **\_2021**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3839](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3839)
***
### \_2022
> **\_2022**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3840](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3840)
***
### \_2023
> **\_2023**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3841](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3841)
***
### \_2024
> **\_2024**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3842](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3842)
***
### \_2025
> **\_2025**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3843](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3843)
***
### \_2026
> **\_2026**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:3844](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3844)
@@ -0,0 +1,105 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Dca\_PeriodAveragePrice
# Interface: MetricsTree\_Market\_Dca\_PeriodAveragePrice
Defined in: [Developer/brk/modules/brk-client/index.js:3864](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3864)
## Properties
### \_10y
> **\_10y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3865](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3865)
***
### \_1m
> **\_1m**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3866](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3866)
***
### \_1w
> **\_1w**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3867](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3867)
***
### \_1y
> **\_1y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3868](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3868)
***
### \_2y
> **\_2y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3869](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3869)
***
### \_3m
> **\_3m**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3870](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3870)
***
### \_3y
> **\_3y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3871](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3871)
***
### \_4y
> **\_4y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3872](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3872)
***
### \_5y
> **\_5y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3873](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3873)
***
### \_6m
> **\_6m**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3874](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3874)
***
### \_6y
> **\_6y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3875](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3875)
***
### \_8y
> **\_8y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3876](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3876)
@@ -0,0 +1,113 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Market\_Lookback
# Interface: MetricsTree\_Market\_Lookback
Defined in: [Developer/brk/modules/brk-client/index.js:3903](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3903)
## Properties
### \_10y
> **\_10y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3904](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3904)
***
### \_1d
> **\_1d**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3905](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3905)
***
### \_1m
> **\_1m**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3906](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3906)
***
### \_1w
> **\_1w**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3907](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3907)
***
### \_1y
> **\_1y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3908](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3908)
***
### \_2y
> **\_2y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3909](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3909)
***
### \_3m
> **\_3m**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3910](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3910)
***
### \_3y
> **\_3y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3911](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3911)
***
### \_4y
> **\_4y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3912](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3912)
***
### \_5y
> **\_5y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3913](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3913)
***
### \_6m
> **\_6m**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3914](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3914)
***
### \_6y
> **\_6y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3915](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3915)
***
### \_8y
> **\_8y**: [`_0sdUsdPattern`](0sdUsdPattern.md)
Defined in: [Developer/brk/modules/brk-client/index.js:3916](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L3916)
@@ -0,0 +1,25 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / MetricsTree\_Price\_Usd
# Interface: MetricsTree\_Price\_Usd
Defined in: [Developer/brk/modules/brk-client/index.js:4234](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L4234)
## Properties
### ohlc
> **ohlc**: [`MetricPattern1`](../type-aliases/MetricPattern1.md)\<[`OHLCDollars`](OHLCDollars.md)\>
Defined in: [Developer/brk/modules/brk-client/index.js:4235](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L4235)
***
### split
> **split**: [`SplitPattern2`](SplitPattern2.md)\<`number`\>
Defined in: [Developer/brk/modules/brk-client/index.js:4236](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L4236)
@@ -0,0 +1,111 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / PeriodDaysInLossPattern
# Interface: PeriodDaysInLossPattern\<T\>
Defined in: [Developer/brk/modules/brk-client/index.js:1891](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1891)
## Type Parameters
### T
`T`
## Properties
### \_10y
> **\_10y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1892](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1892)
***
### \_1m
> **\_1m**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1893](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1893)
***
### \_1w
> **\_1w**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1894](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1894)
***
### \_1y
> **\_1y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1895](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1895)
***
### \_2y
> **\_2y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1896](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1896)
***
### \_3m
> **\_3m**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1897](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1897)
***
### \_3y
> **\_3y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1898](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1898)
***
### \_4y
> **\_4y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1899](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1899)
***
### \_5y
> **\_5y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1900](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1900)
***
### \_6m
> **\_6m**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1901](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1901)
***
### \_6y
> **\_6y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1902](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1902)
***
### \_8y
> **\_8y**: [`MetricPattern4`](../type-aliases/MetricPattern4.md)\<`T`\>
Defined in: [Developer/brk/modules/brk-client/index.js:1903](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L1903)
@@ -0,0 +1,13 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / SatsFract
# Type Alias: SatsFract
> **SatsFract**\<\> = `number`
Defined in: [Developer/brk/modules/brk-client/index.js:607](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L607)
## Type Parameters
@@ -0,0 +1,13 @@
[**brk-client**](../README.md)
***
[brk-client](../globals.md) / StoredI8
# Type Alias: StoredI8
> **StoredI8**\<\> = `number`
Defined in: [Developer/brk/modules/brk-client/index.js:625](https://github.com/bitcoinresearchkit/brk/blob/3d3652470714070e3e6df47b3af6f06512541475/modules/brk-client/index.js#L625)
## Type Parameters
+145 -8
View File
@@ -838,15 +838,120 @@ class BrkError extends Error {
}
}
// Date conversion constants and helpers
const _GENESIS = new Date(2009, 0, 3); // dateindex 0, weekindex 0
const _DAY_ONE = new Date(2009, 0, 9); // dateindex 1 (6 day gap after genesis)
const _MS_PER_DAY = 24 * 60 * 60 * 1000;
const _MS_PER_WEEK = 7 * _MS_PER_DAY;
const _DATE_INDEXES = new Set(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex']);
/** @param {number} months @returns {globalThis.Date} */
const _addMonths = (months) => new Date(2009, months, 1);
/**
* Convert an index value to a Date for date-based indexes.
* @param {Index} index - The index type
* @param {number} i - The index value
* @returns {globalThis.Date}
*/
function indexToDate(index, i) {
switch (index) {
case 'dateindex': return i === 0 ? _GENESIS : new Date(_DAY_ONE.getTime() + (i - 1) * _MS_PER_DAY);
case 'weekindex': return new Date(_GENESIS.getTime() + i * _MS_PER_WEEK);
case 'monthindex': return _addMonths(i);
case 'yearindex': return new Date(2009 + i, 0, 1);
case 'quarterindex': return _addMonths(i * 3);
case 'semesterindex': return _addMonths(i * 6);
case 'decadeindex': return new Date(2009 + i * 10, 0, 1);
default: throw new Error(`${index} is not a date-based index`);
}
}
/**
* Check if an index type is date-based.
* @param {Index} index
* @returns {boolean}
*/
function isDateIndex(index) {
return _DATE_INDEXES.has(index);
}
/**
* Wrap raw metric data with helper methods.
* @template T
* @param {MetricData<T>} raw - Raw JSON response
* @returns {MetricData<T>}
*/
function _wrapMetricData(raw) {
const { index, start, end, data } = raw;
return /** @type {MetricData<T>} */ ({
...raw,
dates() {
/** @type {globalThis.Date[]} */
const result = [];
for (let i = start; i < end; i++) result.push(indexToDate(index, i));
return result;
},
indexes() {
/** @type {number[]} */
const result = [];
for (let i = start; i < end; i++) result.push(i);
return result;
},
toDateMap() {
/** @type {Map<globalThis.Date, T>} */
const map = new Map();
for (let i = 0; i < data.length; i++) map.set(indexToDate(index, start + i), data[i]);
return map;
},
toIndexMap() {
/** @type {Map<number, T>} */
const map = new Map();
for (let i = 0; i < data.length; i++) map.set(start + i, data[i]);
return map;
},
dateEntries() {
/** @type {Array<[globalThis.Date, T]>} */
const result = [];
for (let i = 0; i < data.length; i++) result.push([indexToDate(index, start + i), data[i]]);
return result;
},
indexEntries() {
/** @type {Array<[number, T]>} */
const result = [];
for (let i = 0; i < data.length; i++) result.push([start + i, data[i]]);
return result;
},
*iter() {
for (let i = 0; i < data.length; i++) yield [start + i, data[i]];
},
*iterDates() {
for (let i = 0; i < data.length; i++) yield [indexToDate(index, start + i), data[i]];
},
[Symbol.iterator]() {
return this.iter();
},
});
}
/**
* @template T
* @typedef {Object} MetricData
* @property {number} version - Version of the metric data
* @property {Index} index - The index type used for this query
* @property {number} total - Total number of data points
* @property {number} start - Start index (inclusive)
* @property {number} end - End index (exclusive)
* @property {string} stamp - ISO 8601 timestamp of when the response was generated
* @property {T[]} data - The metric data
* @property {() => globalThis.Date[]} dates - Convert index range to dates (date-based indexes only)
* @property {() => number[]} indexes - Get index range as array
* @property {() => Map<globalThis.Date, T>} toDateMap - Return data as Map keyed by date (date-based only)
* @property {() => Map<number, T>} toIndexMap - Return data as Map keyed by index
* @property {() => Array<[globalThis.Date, T]>} dateEntries - Return data as [date, value] pairs (date-based only)
* @property {() => Array<[number, T]>} indexEntries - Return data as [index, value] pairs
* @property {() => IterableIterator<[number, T]>} iter - Iterate over [index, value] pairs
* @property {() => IterableIterator<[globalThis.Date, T]>} iterDates - Iterate over [date, value] pairs (date-based only)
*/
/** @typedef {MetricData<any>} AnyMetricData */
@@ -940,18 +1045,18 @@ function _endpoint(client, name, index) {
* @returns {RangeBuilder<T>}
*/
const rangeBuilder = (start, end) => ({
fetch(onUpdate) { return client.getJson(buildPath(start, end), onUpdate); },
fetch(onUpdate) { return client._fetchMetricData(buildPath(start, end), onUpdate); },
fetchCsv() { return client.getText(buildPath(start, end, 'csv')); },
then(resolve, reject) { return this.fetch().then(resolve, reject); },
});
/**
* @param {number} index
* @param {number} idx
* @returns {SingleItemBuilder<T>}
*/
const singleItemBuilder = (index) => ({
fetch(onUpdate) { return client.getJson(buildPath(index, index + 1), onUpdate); },
fetchCsv() { return client.getText(buildPath(index, index + 1, 'csv')); },
const singleItemBuilder = (idx) => ({
fetch(onUpdate) { return client._fetchMetricData(buildPath(idx, idx + 1), onUpdate); },
fetchCsv() { return client.getText(buildPath(idx, idx + 1, 'csv')); },
then(resolve, reject) { return this.fetch().then(resolve, reject); },
});
@@ -961,19 +1066,19 @@ function _endpoint(client, name, index) {
*/
const skippedBuilder = (start) => ({
take(n) { return rangeBuilder(start, start + n); },
fetch(onUpdate) { return client.getJson(buildPath(start, undefined), onUpdate); },
fetch(onUpdate) { return client._fetchMetricData(buildPath(start, undefined), onUpdate); },
fetchCsv() { return client.getText(buildPath(start, undefined, 'csv')); },
then(resolve, reject) { return this.fetch().then(resolve, reject); },
});
/** @type {MetricEndpointBuilder<T>} */
const endpoint = {
get(index) { return singleItemBuilder(index); },
get(idx) { return singleItemBuilder(idx); },
slice(start, end) { return rangeBuilder(start, end); },
first(n) { return rangeBuilder(undefined, n); },
last(n) { return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); },
skip(n) { return skippedBuilder(n); },
fetch(onUpdate) { return client.getJson(buildPath(), onUpdate); },
fetch(onUpdate) { return client._fetchMetricData(buildPath(), onUpdate); },
fetchCsv() { return client.getText(buildPath(undefined, undefined, 'csv')); },
then(resolve, reject) { return this.fetch().then(resolve, reject); },
get path() { return p; },
@@ -1053,6 +1158,19 @@ class BrkClientBase {
const res = await this.get(path);
return res.text();
}
/**
* Fetch metric data and wrap with helper methods (internal)
* @template T
* @param {string} path
* @param {(value: MetricData<T>) => void} [onUpdate]
* @returns {Promise<MetricData<T>>}
*/
async _fetchMetricData(path, onUpdate) {
const wrappedOnUpdate = onUpdate ? (/** @type {MetricData<T>} */ raw) => onUpdate(_wrapMetricData(raw)) : undefined;
const raw = await this.getJson(path, wrappedOnUpdate);
return _wrapMetricData(raw);
}
}
/**
@@ -5265,6 +5383,25 @@ class BrkClient extends BrkClientBase {
}
});
/**
* Convert an index value to a Date for date-based indexes.
* @param {Index} index - The index type
* @param {number} i - The index value
* @returns {globalThis.Date}
*/
indexToDate(index, i) {
return indexToDate(index, i);
}
/**
* Check if an index type is date-based.
* @param {Index} index
* @returns {boolean}
*/
isDateIndex(index) {
return isDateIndex(index);
}
/**
* @param {BrkClientOptions|string} options
*/
+221
View File
@@ -0,0 +1,221 @@
/**
* Tests for MetricData helper methods and date conversion functions.
* Run: node tests/metric_data.js
*/
import { BrkClient } from "../index.js";
const client = new BrkClient("http://localhost:3110");
console.log("Testing MetricData helpers...\n");
// Fetch a date-based metric
console.log("1. Fetching price data (dateindex):");
const price = await client.metrics.price.usd.split.close.by.dateindex.first(5);
console.log(
` Total: ${price.total}, Start: ${price.start}, End: ${price.end}`,
);
// Test indexes()
console.log("\n2. indexes():");
const indexes = price.indexes();
console.log(` ${JSON.stringify(indexes)}`);
if (indexes.length !== 5) throw new Error("Expected 5 indexes");
if (indexes[0] !== price.start)
throw new Error("First index should equal start");
// Test dates()
console.log("\n3. dates():");
const dates = price.dates();
console.log(
` First: ${dates[0].toISOString()}, Last: ${dates[dates.length - 1].toISOString()}`,
);
if (dates.length !== 5) throw new Error("Expected 5 dates");
// DateIndex 0 = Jan 3, 2009 (genesis)
if (
dates[0].getFullYear() !== 2009 ||
dates[0].getMonth() !== 0 ||
dates[0].getDate() !== 3
) {
throw new Error(
`Expected genesis date (2009-01-03), got ${dates[0].toISOString()}`,
);
}
// Test toIndexMap()
console.log("\n4. toIndexMap():");
const indexMap = price.toIndexMap();
console.log(
` Size: ${indexMap.size}, First value: ${indexMap.get(price.start)}`,
);
if (indexMap.size !== 5) throw new Error("Expected map size 5");
if (indexMap.get(price.start) !== price.data[0])
throw new Error("First value mismatch");
// Test toDateMap()
console.log("\n5. toDateMap():");
const dateMap = price.toDateMap();
console.log(` Size: ${dateMap.size}`);
if (dateMap.size !== 5) throw new Error("Expected map size 5");
// Test indexEntries()
console.log("\n6. indexEntries():");
const indexEntries = price.indexEntries();
console.log(` First: [${indexEntries[0][0]}, ${indexEntries[0][1]}]`);
if (indexEntries[0][0] !== price.start)
throw new Error("First entry index mismatch");
if (indexEntries[0][1] !== price.data[0])
throw new Error("First entry value mismatch");
// Test dateEntries()
console.log("\n7. dateEntries():");
const dateEntries = price.dateEntries();
console.log(
` First: [${dateEntries[0][0].toISOString()}, ${dateEntries[0][1]}]`,
);
if (dateEntries[0][1] !== price.data[0])
throw new Error("First entry value mismatch");
// Test iter()
console.log("\n8. iter():");
const iterResult = [...price.iter()];
console.log(
` Length: ${iterResult.length}, First: [${iterResult[0][0]}, ${iterResult[0][1]}]`,
);
if (iterResult.length !== 5) throw new Error("Expected 5 items");
// Test iterDates()
console.log("\n9. iterDates():");
const iterDatesResult = [...price.iterDates()];
console.log(
` Length: ${iterDatesResult.length}, First date: ${iterDatesResult[0][0].toISOString()}`,
);
if (iterDatesResult.length !== 5) throw new Error("Expected 5 items");
// Test Symbol.iterator (for...of)
console.log("\n10. for...of iteration:");
let count = 0;
for (const [idx, val] of price.iter()) {
count++;
}
console.log(` Iterated ${count} items`);
if (count !== 5) throw new Error("Expected 5 iterations");
// Test with non-date-based index (height)
console.log("\n11. Testing height-based metric:");
const heightMetric =
await client.metrics.price.usd.split.close.by.height.last(3);
console.log(
` Total: ${heightMetric.total}, Start: ${heightMetric.start}, End: ${heightMetric.end}`,
);
const heightIndexes = heightMetric.indexes();
console.log(` Indexes: ${JSON.stringify(heightIndexes)}`);
if (heightIndexes[0] !== heightMetric.start)
throw new Error("Height index mismatch");
// Test different date indexes
console.log("\n12. Testing monthindex:");
const monthMetric =
await client.metrics.price.usd.split.close.by.monthindex.first(3);
const monthDates = monthMetric.dates();
console.log(` First month: ${monthDates[0].toISOString()}`);
// MonthIndex 0 = Jan 1, 2009
if (
monthDates[0].getFullYear() !== 2009 ||
monthDates[0].getMonth() !== 0 ||
monthDates[0].getDate() !== 1
) {
throw new Error(`Expected 2009-01-01, got ${monthDates[0].toISOString()}`);
}
// Test indexToDate directly
console.log("\n13. Testing indexToDate():");
const genesis = client.indexToDate("dateindex", 0);
if (
genesis.getFullYear() !== 2009 ||
genesis.getMonth() !== 0 ||
genesis.getDate() !== 3
) {
throw new Error(`Expected genesis 2009-01-03, got ${genesis.toISOString()}`);
}
const dayOne = client.indexToDate("dateindex", 1);
if (
dayOne.getFullYear() !== 2009 ||
dayOne.getMonth() !== 0 ||
dayOne.getDate() !== 9
) {
throw new Error(`Expected day one 2009-01-09, got ${dayOne.toISOString()}`);
}
console.log(` dateindex 0: ${genesis.toISOString()}`);
console.log(` dateindex 1: ${dayOne.toISOString()}`);
// Test weekindex
const week0 = client.indexToDate("weekindex", 0);
const week1 = client.indexToDate("weekindex", 1);
if (week0.getTime() !== genesis.getTime())
throw new Error("weekindex 0 should equal genesis");
console.log(` weekindex 0: ${week0.toISOString()}`);
console.log(` weekindex 1: ${week1.toISOString()}`);
// Test yearindex
const year0 = client.indexToDate("yearindex", 0);
const year1 = client.indexToDate("yearindex", 1);
if (
year0.getFullYear() !== 2009 ||
year0.getMonth() !== 0 ||
year0.getDate() !== 1
) {
throw new Error(`Expected 2009-01-01, got ${year0.toISOString()}`);
}
if (year1.getFullYear() !== 2010) throw new Error("yearindex 1 should be 2010");
console.log(` yearindex 0: ${year0.toISOString()}`);
console.log(` yearindex 1: ${year1.toISOString()}`);
// Test quarterindex
const q0 = client.indexToDate("quarterindex", 0);
const q1 = client.indexToDate("quarterindex", 1);
if (q0.getMonth() !== 0) throw new Error("Q0 should be January");
if (q1.getMonth() !== 3) throw new Error("Q1 should be April");
console.log(` quarterindex 0: ${q0.toISOString()}`);
console.log(` quarterindex 1: ${q1.toISOString()}`);
// Test semesterindex
const s0 = client.indexToDate("semesterindex", 0);
const s1 = client.indexToDate("semesterindex", 1);
if (s0.getMonth() !== 0) throw new Error("S0 should be January");
if (s1.getMonth() !== 6) throw new Error("S1 should be July");
console.log(` semesterindex 0: ${s0.toISOString()}`);
console.log(` semesterindex 1: ${s1.toISOString()}`);
// Test decadeindex
const d0 = client.indexToDate("decadeindex", 0);
const d1 = client.indexToDate("decadeindex", 1);
if (d0.getFullYear() !== 2009) throw new Error("decadeindex 0 should be 2009");
if (d1.getFullYear() !== 2019) throw new Error("decadeindex 1 should be 2019");
console.log(` decadeindex 0: ${d0.toISOString()}`);
console.log(` decadeindex 1: ${d1.toISOString()}`);
// Test isDateIndex
console.log("\n14. Testing isDateIndex():");
const dateIndexes = /** @type {const} */ ([
"dateindex",
"weekindex",
"monthindex",
"yearindex",
"quarterindex",
"semesterindex",
"decadeindex",
]);
const nonDateIndexes = /** @type {const} */ (["height", "txindex"]);
for (const idx of dateIndexes) {
if (!client.isDateIndex(idx))
throw new Error(`${idx} should be a date index`);
}
for (const idx of nonDateIndexes) {
if (client.isDateIndex(idx))
throw new Error(`${idx} should not be a date index`);
}
console.log(` Date indexes: ${dateIndexes.join(", ")}`);
console.log(` Non-date indexes: ${nonDateIndexes.join(", ")}`);
console.log("\nAll MetricData tests passed!");
+118 -8
View File
@@ -3,11 +3,16 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload
from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING
from http.client import HTTPSConnection, HTTPConnection
from urllib.parse import urlparse
from datetime import date, timedelta
import json
if TYPE_CHECKING:
import pandas as pd # type: ignore[import-not-found]
import polars as pl # type: ignore[import-not-found]
T = TypeVar('T')
# Type definitions
@@ -1043,16 +1048,114 @@ def _p(prefix: str, acc: str) -> str:
return f"{prefix}_{acc}" if acc else prefix
# Date conversion constants
_GENESIS = date(2009, 1, 3) # dateindex 0, weekindex 0
_DAY_ONE = date(2009, 1, 9) # dateindex 1 (6 day gap after genesis)
_DATE_INDEXES = frozenset(['dateindex', 'weekindex', 'monthindex', 'yearindex', 'quarterindex', 'semesterindex', 'decadeindex'])
def is_date_index(index: str) -> bool:
"""Check if an index type is date-based."""
return index in _DATE_INDEXES
def index_to_date(index: str, i: int) -> date:
"""Convert an index value to a date for date-based indexes."""
if index == 'dateindex':
return _GENESIS if i == 0 else _DAY_ONE + timedelta(days=i - 1)
elif index == 'weekindex':
return _GENESIS + timedelta(weeks=i)
elif index == 'monthindex':
return date(2009 + i // 12, i % 12 + 1, 1)
elif index == 'yearindex':
return date(2009 + i, 1, 1)
elif index == 'quarterindex':
m = i * 3
return date(2009 + m // 12, m % 12 + 1, 1)
elif index == 'semesterindex':
m = i * 6
return date(2009 + m // 12, m % 12 + 1, 1)
elif index == 'decadeindex':
return date(2009 + i * 10, 1, 1)
else:
raise ValueError(f"{index} is not a date-based index")
@dataclass
class MetricData(Generic[T]):
"""Metric data with range information."""
version: int
index: Index
total: int
start: int
end: int
stamp: str
data: List[T]
def dates(self) -> List[date]:
"""Convert index range to dates. Only works for date-based indexes."""
return [index_to_date(self.index, i) for i in range(self.start, self.end)]
def indexes(self) -> List[int]:
"""Get index range as list."""
return list(range(self.start, self.end))
def to_date_dict(self) -> dict[date, T]:
"""Return data as {date: value} dict. Only works for date-based indexes."""
return dict(zip(self.dates(), self.data))
def to_index_dict(self) -> dict[int, T]:
"""Return data as {index: value} dict."""
return dict(zip(range(self.start, self.end), self.data))
def date_items(self) -> List[Tuple[date, T]]:
"""Return data as [(date, value), ...] pairs. Only works for date-based indexes."""
return list(zip(self.dates(), self.data))
def index_items(self) -> List[Tuple[int, T]]:
"""Return data as [(index, value), ...] pairs."""
return list(zip(range(self.start, self.end), self.data))
def iter(self) -> Iterator[Tuple[int, T]]:
"""Iterate over (index, value) pairs."""
return iter(zip(range(self.start, self.end), self.data))
def iter_dates(self) -> Iterator[Tuple[date, T]]:
"""Iterate over (date, value) pairs. Date-based indexes only."""
return iter(zip(self.dates(), self.data))
def __iter__(self) -> Iterator[Tuple[int, T]]:
"""Default iteration over (index, value) pairs."""
return self.iter()
def to_polars(self, with_dates: bool = True) -> pl.DataFrame:
"""Convert to Polars DataFrame. Requires polars to be installed.
Returns a DataFrame with columns:
- 'date' (date) and 'value' (T) if with_dates=True and index is date-based
- 'index' (int) and 'value' (T) otherwise
"""
try:
import polars as pl # type: ignore[import-not-found]
except ImportError:
raise ImportError("polars is required: pip install polars")
if with_dates and self.index in _DATE_INDEXES:
return pl.DataFrame({"date": self.dates(), "value": self.data})
return pl.DataFrame({"index": list(range(self.start, self.end)), "value": self.data})
def to_pandas(self, with_dates: bool = True) -> pd.DataFrame:
"""Convert to Pandas DataFrame. Requires pandas to be installed.
Returns a DataFrame with columns:
- 'date' (date) and 'value' (T) if with_dates=True and index is date-based
- 'index' (int) and 'value' (T) otherwise
"""
try:
import pandas as pd # type: ignore[import-not-found]
except ImportError:
raise ImportError("pandas is required: pip install pandas")
if with_dates and self.index in _DATE_INDEXES:
return pd.DataFrame({"date": self.dates(), "value": self.data})
return pd.DataFrame({"index": list(range(self.start, self.end)), "value": self.data})
# Type alias for non-generic usage
AnyMetricData = MetricData[Any]
@@ -1089,9 +1192,8 @@ class _EndpointConfig:
p = self.path()
return f"{p}?{query}" if query else p
def get_json(self) -> MetricData:
data = self.client.get_json(self._build_path())
return MetricData(**data)
def get_metric(self) -> MetricData:
return MetricData(**self.client.get_json(self._build_path()))
def get_csv(self) -> str:
return self.client.get_text(self._build_path(format='csv'))
@@ -1105,7 +1207,7 @@ class RangeBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch the range as parsed JSON."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch the range as CSV string."""
@@ -1120,7 +1222,7 @@ class SingleItemBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch the single item."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch as CSV."""
@@ -1143,7 +1245,7 @@ class SkippedBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch from skipped position to end."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch as CSV."""
@@ -1227,7 +1329,7 @@ class MetricEndpointBuilder(Generic[T]):
def fetch(self) -> MetricData[T]:
"""Fetch all data as parsed JSON."""
return self._config.get_json()
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch all data as CSV string."""
@@ -4958,6 +5060,14 @@ class BrkClient(BrkClientBase):
"""
return MetricEndpointBuilder(self, metric, index)
def index_to_date(self, index: Index, i: int) -> date:
"""Convert an index value to a date for date-based indexes."""
return index_to_date(index, i)
def is_date_index(self, index: Index) -> bool:
"""Check if an index type is date-based."""
return is_date_index(index)
def get_api(self) -> Any:
"""Compact OpenAPI specification.
+2
View File
@@ -25,6 +25,8 @@ Repository = "https://github.com/bitcoinresearchkit/brk"
[dependency-groups]
dev = [
"pandas>=2.3.3",
"polars>=1.36.1",
"pydoc-markdown>=4.8.2",
"pytest",
]
@@ -0,0 +1,340 @@
# Tests for MetricData helper methods including polars/pandas conversion
# Run: uv run pytest tests/test_metric_data.py -v
from datetime import date
import pytest
from brk_client import MetricData, index_to_date, is_date_index
# Test data fixtures
@pytest.fixture
def date_based_metric():
"""MetricData with dateindex (date-based)."""
return MetricData(
version=1,
index="dateindex",
total=100,
start=0,
end=5,
stamp="2024-01-01T00:00:00Z",
data=[100, 200, 300, 400, 500],
)
@pytest.fixture
def height_based_metric():
"""MetricData with height (non-date-based)."""
return MetricData(
version=1,
index="height",
total=1000,
start=800000,
end=800005,
stamp="2024-01-01T00:00:00Z",
data=[1.5, 2.5, 3.5, 4.5, 5.5],
)
@pytest.fixture
def month_based_metric():
"""MetricData with monthindex."""
return MetricData(
version=1,
index="monthindex",
total=200,
start=0,
end=3,
stamp="2024-01-01T00:00:00Z",
data=[1000, 2000, 3000],
)
# ============ Date conversion tests ============
class TestIndexToDate:
"""Test the index_to_date function."""
def test_dateindex_zero(self):
"""DateIndex 0 is genesis: Jan 3, 2009."""
assert index_to_date("dateindex", 0) == date(2009, 1, 3)
def test_dateindex_one(self):
"""DateIndex 1 is Jan 9, 2009 (6 day gap after genesis)."""
assert index_to_date("dateindex", 1) == date(2009, 1, 9)
def test_dateindex_two(self):
"""DateIndex 2 is Jan 10, 2009."""
assert index_to_date("dateindex", 2) == date(2009, 1, 10)
def test_weekindex_zero(self):
"""WeekIndex 0 is genesis: Jan 3, 2009."""
assert index_to_date("weekindex", 0) == date(2009, 1, 3)
def test_weekindex_one(self):
"""WeekIndex 1 is Jan 10, 2009 (one week after genesis)."""
assert index_to_date("weekindex", 1) == date(2009, 1, 10)
def test_monthindex_zero(self):
"""MonthIndex 0 is Jan 1, 2009."""
assert index_to_date("monthindex", 0) == date(2009, 1, 1)
def test_monthindex_one(self):
"""MonthIndex 1 is Feb 1, 2009."""
assert index_to_date("monthindex", 1) == date(2009, 2, 1)
def test_monthindex_twelve(self):
"""MonthIndex 12 is Jan 1, 2010."""
assert index_to_date("monthindex", 12) == date(2010, 1, 1)
def test_yearindex_zero(self):
"""YearIndex 0 is Jan 1, 2009."""
assert index_to_date("yearindex", 0) == date(2009, 1, 1)
def test_yearindex_one(self):
"""YearIndex 1 is Jan 1, 2010."""
assert index_to_date("yearindex", 1) == date(2010, 1, 1)
def test_quarterindex_zero(self):
"""QuarterIndex 0 is Q1 2009: Jan 1, 2009."""
assert index_to_date("quarterindex", 0) == date(2009, 1, 1)
def test_quarterindex_one(self):
"""QuarterIndex 1 is Q2 2009: Apr 1, 2009."""
assert index_to_date("quarterindex", 1) == date(2009, 4, 1)
def test_quarterindex_four(self):
"""QuarterIndex 4 is Q1 2010: Jan 1, 2010."""
assert index_to_date("quarterindex", 4) == date(2010, 1, 1)
def test_semesterindex_zero(self):
"""SemesterIndex 0 is H1 2009: Jan 1, 2009."""
assert index_to_date("semesterindex", 0) == date(2009, 1, 1)
def test_semesterindex_one(self):
"""SemesterIndex 1 is H2 2009: Jul 1, 2009."""
assert index_to_date("semesterindex", 1) == date(2009, 7, 1)
def test_semesterindex_two(self):
"""SemesterIndex 2 is H1 2010: Jan 1, 2010."""
assert index_to_date("semesterindex", 2) == date(2010, 1, 1)
def test_decadeindex_zero(self):
"""DecadeIndex 0 is 2009: Jan 1, 2009."""
assert index_to_date("decadeindex", 0) == date(2009, 1, 1)
def test_decadeindex_one(self):
"""DecadeIndex 1 is 2019: Jan 1, 2019."""
assert index_to_date("decadeindex", 1) == date(2019, 1, 1)
def test_invalid_index_raises(self):
"""Non-date-based index raises ValueError."""
with pytest.raises(ValueError):
index_to_date("height", 0)
class TestIsDateIndex:
"""Test the is_date_index function."""
def test_dateindex_is_date_based(self):
assert is_date_index("dateindex") is True
def test_weekindex_is_date_based(self):
assert is_date_index("weekindex") is True
def test_monthindex_is_date_based(self):
assert is_date_index("monthindex") is True
def test_yearindex_is_date_based(self):
assert is_date_index("yearindex") is True
def test_quarterindex_is_date_based(self):
assert is_date_index("quarterindex") is True
def test_semesterindex_is_date_based(self):
assert is_date_index("semesterindex") is True
def test_decadeindex_is_date_based(self):
assert is_date_index("decadeindex") is True
def test_height_is_not_date_based(self):
assert is_date_index("height") is False
def test_txindex_is_not_date_based(self):
assert is_date_index("txindex") is False
# ============ MetricData helper method tests ============
class TestMetricDataHelpers:
"""Test MetricData helper methods."""
def test_indexes_returns_range(self, date_based_metric):
"""indexes() returns list of index values."""
assert date_based_metric.indexes() == [0, 1, 2, 3, 4]
def test_indexes_with_offset(self, height_based_metric):
"""indexes() respects start/end offsets."""
assert height_based_metric.indexes() == [800000, 800001, 800002, 800003, 800004]
def test_dates_for_dateindex(self, date_based_metric):
"""dates() returns correct dates for dateindex."""
dates = date_based_metric.dates()
assert dates[0] == date(2009, 1, 3) # dateindex 0 = genesis
assert dates[1] == date(2009, 1, 9) # dateindex 1 = day one
assert dates[2] == date(2009, 1, 10) # dateindex 2
def test_dates_for_monthindex(self, month_based_metric):
"""dates() returns correct dates for monthindex."""
dates = month_based_metric.dates()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
def test_to_index_dict(self, date_based_metric):
"""to_index_dict() returns {index: value} mapping."""
d = date_based_metric.to_index_dict()
assert d[0] == 100
assert d[1] == 200
assert d[4] == 500
def test_to_date_dict(self, date_based_metric):
"""to_date_dict() returns {date: value} mapping."""
d = date_based_metric.to_date_dict()
assert d[date(2009, 1, 3)] == 100 # genesis
assert d[date(2009, 1, 9)] == 200 # day one
def test_index_items(self, date_based_metric):
"""index_items() returns [(index, value), ...] pairs."""
items = date_based_metric.index_items()
assert items[0] == (0, 100)
assert items[4] == (4, 500)
def test_date_items(self, date_based_metric):
"""date_items() returns [(date, value), ...] pairs."""
items = date_based_metric.date_items()
assert items[0] == (date(2009, 1, 3), 100)
assert items[1] == (date(2009, 1, 9), 200)
def test_iter(self, date_based_metric):
"""iter() yields (index, value) pairs."""
result = list(date_based_metric.iter())
assert result == [(0, 100), (1, 200), (2, 300), (3, 400), (4, 500)]
def test_iter_dates(self, date_based_metric):
"""iter_dates() yields (date, value) pairs."""
result = list(date_based_metric.iter_dates())
assert result[0] == (date(2009, 1, 3), 100)
assert result[1] == (date(2009, 1, 9), 200)
def test_default_iteration(self, date_based_metric):
"""Default iteration uses iter()."""
result = list(date_based_metric)
assert result == [(0, 100), (1, 200), (2, 300), (3, 400), (4, 500)]
# ============ Polars tests ============
class TestPolarsConversion:
"""Test MetricData.to_polars() conversion."""
@pytest.fixture(autouse=True)
def check_polars(self):
"""Skip if polars not installed."""
pytest.importorskip("polars")
def test_to_polars_with_dates(self, date_based_metric):
"""to_polars() includes date column for date-based index."""
import polars as pl
df = date_based_metric.to_polars()
assert isinstance(df, pl.DataFrame)
assert "date" in df.columns
assert "value" in df.columns
assert len(df) == 5
assert df["value"].to_list() == [100, 200, 300, 400, 500]
def test_to_polars_without_dates(self, date_based_metric):
"""to_polars(with_dates=False) uses index column."""
import polars as pl
df = date_based_metric.to_polars(with_dates=False)
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].to_list() == [0, 1, 2, 3, 4]
def test_to_polars_non_date_index(self, height_based_metric):
"""to_polars() uses index column for non-date-based index."""
import polars as pl
df = height_based_metric.to_polars()
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].to_list() == [800000, 800001, 800002, 800003, 800004]
assert df["value"].to_list() == [1.5, 2.5, 3.5, 4.5, 5.5]
def test_to_polars_monthindex(self, month_based_metric):
"""to_polars() works with monthindex."""
import polars as pl
df = month_based_metric.to_polars()
assert "date" in df.columns
assert len(df) == 3
# Polars stores dates as date type
dates = df["date"].to_list()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
# ============ Pandas tests ============
class TestPandasConversion:
"""Test MetricData.to_pandas() conversion."""
@pytest.fixture(autouse=True)
def check_pandas(self):
"""Skip if pandas not installed."""
pytest.importorskip("pandas")
def test_to_pandas_with_dates(self, date_based_metric):
"""to_pandas() includes date column for date-based index."""
import pandas as pd
df = date_based_metric.to_pandas()
assert isinstance(df, pd.DataFrame)
assert "date" in df.columns
assert "value" in df.columns
assert len(df) == 5
assert df["value"].tolist() == [100, 200, 300, 400, 500]
def test_to_pandas_without_dates(self, date_based_metric):
"""to_pandas(with_dates=False) uses index column."""
import pandas as pd
df = date_based_metric.to_pandas(with_dates=False)
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].tolist() == [0, 1, 2, 3, 4]
def test_to_pandas_non_date_index(self, height_based_metric):
"""to_pandas() uses index column for non-date-based index."""
import pandas as pd
df = height_based_metric.to_pandas()
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].tolist() == [800000, 800001, 800002, 800003, 800004]
assert df["value"].tolist() == [1.5, 2.5, 3.5, 4.5, 5.5]
def test_to_pandas_monthindex(self, month_based_metric):
"""to_pandas() works with monthindex."""
import pandas as pd
df = month_based_metric.to_pandas()
assert "date" in df.columns
assert len(df) == 3
dates = df["date"].tolist()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
+508 -7
View File
@@ -2,7 +2,13 @@ version = 1
revision = 3
requires-python = ">=3.9"
resolution-markers = [
"python_full_version >= '3.10'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
"python_full_version < '3.10'",
]
@@ -44,11 +50,15 @@ wheels = [
[[package]]
name = "brk-client"
version = "0.1.0b1"
version = "0.1.1"
source = { editable = "." }
[package.dev-dependencies]
dev = [
{ name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "pandas", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "polars", version = "1.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "polars", version = "1.37.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
{ name = "pydoc-markdown" },
{ name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
@@ -58,6 +68,8 @@ dev = [
[package.metadata.requires-dev]
dev = [
{ name = "pandas", specifier = ">=2.3.3" },
{ name = "polars", specifier = ">=1.36.1" },
{ name = "pydoc-markdown", specifier = ">=4.8.2" },
{ name = "pytest" },
]
@@ -196,7 +208,13 @@ name = "click"
version = "8.3.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.10'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
]
dependencies = [
{ name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
@@ -307,7 +325,7 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
@@ -340,7 +358,13 @@ name = "iniconfig"
version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.10'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
wheels = [
@@ -495,6 +519,213 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/58/eab08df9dbd69d9e21fc5e7be6f67454f386336ec71e6b64e378a2dddea4/nr.util-0.8.12-py3-none-any.whl", hash = "sha256:91da02ac9795eb8e015372275c1efe54bac9051231ee9b0e7e6f96b0b4e7d2bb", size = 90319, upload-time = "2022-06-20T13:29:27.312Z" },
]
[[package]]
name = "numpy"
version = "2.0.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.10'",
]
sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" },
{ url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" },
{ url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" },
{ url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" },
{ url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" },
{ url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" },
{ url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" },
{ url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" },
{ url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" },
{ url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" },
{ url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" },
{ url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" },
{ url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" },
{ url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" },
{ url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" },
{ url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" },
{ url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" },
{ url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" },
{ url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" },
{ url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" },
{ url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" },
{ url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" },
{ url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" },
{ url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" },
{ url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" },
{ url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" },
{ url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" },
{ url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" },
{ url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" },
{ url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" },
{ url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" },
{ url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" },
{ url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" },
{ url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" },
{ url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" },
{ url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" },
{ url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" },
{ url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" },
{ url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" },
{ url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" },
{ url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" },
{ url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" },
{ url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" },
{ url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" },
]
[[package]]
name = "numpy"
version = "2.2.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.10.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
{ url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
{ url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
{ url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
{ url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
{ url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
{ url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
{ url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
{ url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
{ url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
{ url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
{ url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
{ url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
{ url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
{ url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
{ url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
{ url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
{ url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
{ url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
{ url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
{ url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
{ url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
{ url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
{ url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
{ url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
{ url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
{ url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
{ url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
{ url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
{ url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
{ url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
{ url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
{ url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
{ url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
{ url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
{ url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
{ url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
{ url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
{ url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
{ url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
{ url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
{ url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
{ url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
{ url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
{ url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
{ url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
{ url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
{ url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
{ url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
{ url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
{ url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
]
[[package]]
name = "numpy"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
]
sdist = { url = "https://files.pythonhosted.org/packages/24/62/ae72ff66c0f1fd959925b4c11f8c2dea61f47f6acaea75a08512cdfe3fed/numpy-2.4.1.tar.gz", hash = "sha256:a1ceafc5042451a858231588a104093474c6a5c57dcc724841f5c888d237d690", size = 20721320, upload-time = "2026-01-10T06:44:59.619Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a5/34/2b1bc18424f3ad9af577f6ce23600319968a70575bd7db31ce66731bbef9/numpy-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0cce2a669e3c8ba02ee563c7835f92c153cf02edff1ae05e1823f1dde21b16a5", size = 16944563, upload-time = "2026-01-10T06:42:14.615Z" },
{ url = "https://files.pythonhosted.org/packages/2c/57/26e5f97d075aef3794045a6ca9eada6a4ed70eb9a40e7a4a93f9ac80d704/numpy-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:899d2c18024984814ac7e83f8f49d8e8180e2fbe1b2e252f2e7f1d06bea92425", size = 12645658, upload-time = "2026-01-10T06:42:17.298Z" },
{ url = "https://files.pythonhosted.org/packages/8e/ba/80fc0b1e3cb2fd5c6143f00f42eb67762aa043eaa05ca924ecc3222a7849/numpy-2.4.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:09aa8a87e45b55a1c2c205d42e2808849ece5c484b2aab11fecabec3841cafba", size = 5474132, upload-time = "2026-01-10T06:42:19.637Z" },
{ url = "https://files.pythonhosted.org/packages/40/ae/0a5b9a397f0e865ec171187c78d9b57e5588afc439a04ba9cab1ebb2c945/numpy-2.4.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:edee228f76ee2dab4579fad6f51f6a305de09d444280109e0f75df247ff21501", size = 6804159, upload-time = "2026-01-10T06:42:21.44Z" },
{ url = "https://files.pythonhosted.org/packages/86/9c/841c15e691c7085caa6fd162f063eff494099c8327aeccd509d1ab1e36ab/numpy-2.4.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a92f227dbcdc9e4c3e193add1a189a9909947d4f8504c576f4a732fd0b54240a", size = 14708058, upload-time = "2026-01-10T06:42:23.546Z" },
{ url = "https://files.pythonhosted.org/packages/5d/9d/7862db06743f489e6a502a3b93136d73aea27d97b2cf91504f70a27501d6/numpy-2.4.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:538bf4ec353709c765ff75ae616c34d3c3dca1a68312727e8f2676ea644f8509", size = 16651501, upload-time = "2026-01-10T06:42:25.909Z" },
{ url = "https://files.pythonhosted.org/packages/a6/9c/6fc34ebcbd4015c6e5f0c0ce38264010ce8a546cb6beacb457b84a75dfc8/numpy-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ac08c63cb7779b85e9d5318e6c3518b424bc1f364ac4cb2c6136f12e5ff2dccc", size = 16492627, upload-time = "2026-01-10T06:42:28.938Z" },
{ url = "https://files.pythonhosted.org/packages/aa/63/2494a8597502dacda439f61b3c0db4da59928150e62be0e99395c3ad23c5/numpy-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f9c360ecef085e5841c539a9a12b883dff005fbd7ce46722f5e9cef52634d82", size = 18585052, upload-time = "2026-01-10T06:42:31.312Z" },
{ url = "https://files.pythonhosted.org/packages/6a/93/098e1162ae7522fc9b618d6272b77404c4656c72432ecee3abc029aa3de0/numpy-2.4.1-cp311-cp311-win32.whl", hash = "sha256:0f118ce6b972080ba0758c6087c3617b5ba243d806268623dc34216d69099ba0", size = 6236575, upload-time = "2026-01-10T06:42:33.872Z" },
{ url = "https://files.pythonhosted.org/packages/8c/de/f5e79650d23d9e12f38a7bc6b03ea0835b9575494f8ec94c11c6e773b1b1/numpy-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:18e14c4d09d55eef39a6ab5b08406e84bc6869c1e34eef45564804f90b7e0574", size = 12604479, upload-time = "2026-01-10T06:42:35.778Z" },
{ url = "https://files.pythonhosted.org/packages/dd/65/e1097a7047cff12ce3369bd003811516b20ba1078dbdec135e1cd7c16c56/numpy-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:6461de5113088b399d655d45c3897fa188766415d0f568f175ab071c8873bd73", size = 10578325, upload-time = "2026-01-10T06:42:38.518Z" },
{ url = "https://files.pythonhosted.org/packages/78/7f/ec53e32bf10c813604edf07a3682616bd931d026fcde7b6d13195dfb684a/numpy-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d3703409aac693fa82c0aee023a1ae06a6e9d065dba10f5e8e80f642f1e9d0a2", size = 16656888, upload-time = "2026-01-10T06:42:40.913Z" },
{ url = "https://files.pythonhosted.org/packages/b8/e0/1f9585d7dae8f14864e948fd7fa86c6cb72dee2676ca2748e63b1c5acfe0/numpy-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7211b95ca365519d3596a1d8688a95874cc94219d417504d9ecb2df99fa7bfa8", size = 12373956, upload-time = "2026-01-10T06:42:43.091Z" },
{ url = "https://files.pythonhosted.org/packages/8e/43/9762e88909ff2326f5e7536fa8cb3c49fb03a7d92705f23e6e7f553d9cb3/numpy-2.4.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5adf01965456a664fc727ed69cc71848f28d063217c63e1a0e200a118d5eec9a", size = 5202567, upload-time = "2026-01-10T06:42:45.107Z" },
{ url = "https://files.pythonhosted.org/packages/4b/ee/34b7930eb61e79feb4478800a4b95b46566969d837546aa7c034c742ef98/numpy-2.4.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:26f0bcd9c79a00e339565b303badc74d3ea2bd6d52191eeca5f95936cad107d0", size = 6549459, upload-time = "2026-01-10T06:42:48.152Z" },
{ url = "https://files.pythonhosted.org/packages/79/e3/5f115fae982565771be994867c89bcd8d7208dbfe9469185497d70de5ddf/numpy-2.4.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0093e85df2960d7e4049664b26afc58b03236e967fb942354deef3208857a04c", size = 14404859, upload-time = "2026-01-10T06:42:49.947Z" },
{ url = "https://files.pythonhosted.org/packages/d9/7d/9c8a781c88933725445a859cac5d01b5871588a15969ee6aeb618ba99eee/numpy-2.4.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad270f438cbdd402c364980317fb6b117d9ec5e226fff5b4148dd9aa9fc6e02", size = 16371419, upload-time = "2026-01-10T06:42:52.409Z" },
{ url = "https://files.pythonhosted.org/packages/a6/d2/8aa084818554543f17cf4162c42f162acbd3bb42688aefdba6628a859f77/numpy-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:297c72b1b98100c2e8f873d5d35fb551fce7040ade83d67dd51d38c8d42a2162", size = 16182131, upload-time = "2026-01-10T06:42:54.694Z" },
{ url = "https://files.pythonhosted.org/packages/60/db/0425216684297c58a8df35f3284ef56ec4a043e6d283f8a59c53562caf1b/numpy-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf6470d91d34bf669f61d515499859fa7a4c2f7c36434afb70e82df7217933f9", size = 18295342, upload-time = "2026-01-10T06:42:56.991Z" },
{ url = "https://files.pythonhosted.org/packages/31/4c/14cb9d86240bd8c386c881bafbe43f001284b7cce3bc01623ac9475da163/numpy-2.4.1-cp312-cp312-win32.whl", hash = "sha256:b6bcf39112e956594b3331316d90c90c90fb961e39696bda97b89462f5f3943f", size = 5959015, upload-time = "2026-01-10T06:42:59.631Z" },
{ url = "https://files.pythonhosted.org/packages/51/cf/52a703dbeb0c65807540d29699fef5fda073434ff61846a564d5c296420f/numpy-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:e1a27bb1b2dee45a2a53f5ca6ff2d1a7f135287883a1689e930d44d1ff296c87", size = 12310730, upload-time = "2026-01-10T06:43:01.627Z" },
{ url = "https://files.pythonhosted.org/packages/69/80/a828b2d0ade5e74a9fe0f4e0a17c30fdc26232ad2bc8c9f8b3197cf7cf18/numpy-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:0e6e8f9d9ecf95399982019c01223dc130542960a12edfa8edd1122dfa66a8a8", size = 10312166, upload-time = "2026-01-10T06:43:03.673Z" },
{ url = "https://files.pythonhosted.org/packages/04/68/732d4b7811c00775f3bd522a21e8dd5a23f77eb11acdeb663e4a4ebf0ef4/numpy-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d797454e37570cfd61143b73b8debd623c3c0952959adb817dd310a483d58a1b", size = 16652495, upload-time = "2026-01-10T06:43:06.283Z" },
{ url = "https://files.pythonhosted.org/packages/20/ca/857722353421a27f1465652b2c66813eeeccea9d76d5f7b74b99f298e60e/numpy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c55962006156aeef1629b953fd359064aa47e4d82cfc8e67f0918f7da3344f", size = 12368657, upload-time = "2026-01-10T06:43:09.094Z" },
{ url = "https://files.pythonhosted.org/packages/81/0d/2377c917513449cc6240031a79d30eb9a163d32a91e79e0da47c43f2c0c8/numpy-2.4.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:71abbea030f2cfc3092a0ff9f8c8fdefdc5e0bf7d9d9c99663538bb0ecdac0b9", size = 5197256, upload-time = "2026-01-10T06:43:13.634Z" },
{ url = "https://files.pythonhosted.org/packages/17/39/569452228de3f5de9064ac75137082c6214be1f5c532016549a7923ab4b5/numpy-2.4.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b55aa56165b17aaf15520beb9cbd33c9039810e0d9643dd4379e44294c7303e", size = 6545212, upload-time = "2026-01-10T06:43:15.661Z" },
{ url = "https://files.pythonhosted.org/packages/8c/a4/77333f4d1e4dac4395385482557aeecf4826e6ff517e32ca48e1dafbe42a/numpy-2.4.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0faba4a331195bfa96f93dd9dfaa10b2c7aa8cda3a02b7fd635e588fe821bf5", size = 14402871, upload-time = "2026-01-10T06:43:17.324Z" },
{ url = "https://files.pythonhosted.org/packages/ba/87/d341e519956273b39d8d47969dd1eaa1af740615394fe67d06f1efa68773/numpy-2.4.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e3087f53e2b4428766b54932644d148613c5a595150533ae7f00dab2f319a8", size = 16359305, upload-time = "2026-01-10T06:43:19.376Z" },
{ url = "https://files.pythonhosted.org/packages/32/91/789132c6666288eaa20ae8066bb99eba1939362e8f1a534949a215246e97/numpy-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:49e792ec351315e16da54b543db06ca8a86985ab682602d90c60ef4ff4db2a9c", size = 16181909, upload-time = "2026-01-10T06:43:21.808Z" },
{ url = "https://files.pythonhosted.org/packages/cf/b8/090b8bd27b82a844bb22ff8fdf7935cb1980b48d6e439ae116f53cdc2143/numpy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79e9e06c4c2379db47f3f6fc7a8652e7498251789bf8ff5bd43bf478ef314ca2", size = 18284380, upload-time = "2026-01-10T06:43:23.957Z" },
{ url = "https://files.pythonhosted.org/packages/67/78/722b62bd31842ff029412271556a1a27a98f45359dea78b1548a3a9996aa/numpy-2.4.1-cp313-cp313-win32.whl", hash = "sha256:3d1a100e48cb266090a031397863ff8a30050ceefd798f686ff92c67a486753d", size = 5957089, upload-time = "2026-01-10T06:43:27.535Z" },
{ url = "https://files.pythonhosted.org/packages/da/a6/cf32198b0b6e18d4fbfa9a21a992a7fca535b9bb2b0cdd217d4a3445b5ca/numpy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:92a0e65272fd60bfa0d9278e0484c2f52fe03b97aedc02b357f33fe752c52ffb", size = 12307230, upload-time = "2026-01-10T06:43:29.298Z" },
{ url = "https://files.pythonhosted.org/packages/44/6c/534d692bfb7d0afe30611320c5fb713659dcb5104d7cc182aff2aea092f5/numpy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:20d4649c773f66cc2fc36f663e091f57c3b7655f936a4c681b4250855d1da8f5", size = 10313125, upload-time = "2026-01-10T06:43:31.782Z" },
{ url = "https://files.pythonhosted.org/packages/da/a1/354583ac5c4caa566de6ddfbc42744409b515039e085fab6e0ff942e0df5/numpy-2.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f93bc6892fe7b0663e5ffa83b61aab510aacffd58c16e012bb9352d489d90cb7", size = 12496156, upload-time = "2026-01-10T06:43:34.237Z" },
{ url = "https://files.pythonhosted.org/packages/51/b0/42807c6e8cce58c00127b1dc24d365305189991f2a7917aa694a109c8d7d/numpy-2.4.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:178de8f87948163d98a4c9ab5bee4ce6519ca918926ec8df195af582de28544d", size = 5324663, upload-time = "2026-01-10T06:43:36.211Z" },
{ url = "https://files.pythonhosted.org/packages/fe/55/7a621694010d92375ed82f312b2f28017694ed784775269115323e37f5e2/numpy-2.4.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:98b35775e03ab7f868908b524fc0a84d38932d8daf7b7e1c3c3a1b6c7a2c9f15", size = 6645224, upload-time = "2026-01-10T06:43:37.884Z" },
{ url = "https://files.pythonhosted.org/packages/50/96/9fa8635ed9d7c847d87e30c834f7109fac5e88549d79ef3324ab5c20919f/numpy-2.4.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941c2a93313d030f219f3a71fd3d91a728b82979a5e8034eb2e60d394a2b83f9", size = 14462352, upload-time = "2026-01-10T06:43:39.479Z" },
{ url = "https://files.pythonhosted.org/packages/03/d1/8cf62d8bb2062da4fb82dd5d49e47c923f9c0738032f054e0a75342faba7/numpy-2.4.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:529050522e983e00a6c1c6b67411083630de8b57f65e853d7b03d9281b8694d2", size = 16407279, upload-time = "2026-01-10T06:43:41.93Z" },
{ url = "https://files.pythonhosted.org/packages/86/1c/95c86e17c6b0b31ce6ef219da00f71113b220bcb14938c8d9a05cee0ff53/numpy-2.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2302dc0224c1cbc49bb94f7064f3f923a971bfae45c33870dcbff63a2a550505", size = 16248316, upload-time = "2026-01-10T06:43:44.121Z" },
{ url = "https://files.pythonhosted.org/packages/30/b4/e7f5ff8697274c9d0fa82398b6a372a27e5cef069b37df6355ccb1f1db1a/numpy-2.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9171a42fcad32dcf3fa86f0a4faa5e9f8facefdb276f54b8b390d90447cff4e2", size = 18329884, upload-time = "2026-01-10T06:43:46.613Z" },
{ url = "https://files.pythonhosted.org/packages/37/a4/b073f3e9d77f9aec8debe8ca7f9f6a09e888ad1ba7488f0c3b36a94c03ac/numpy-2.4.1-cp313-cp313t-win32.whl", hash = "sha256:382ad67d99ef49024f11d1ce5dcb5ad8432446e4246a4b014418ba3a1175a1f4", size = 6081138, upload-time = "2026-01-10T06:43:48.854Z" },
{ url = "https://files.pythonhosted.org/packages/16/16/af42337b53844e67752a092481ab869c0523bc95c4e5c98e4dac4e9581ac/numpy-2.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:62fea415f83ad8fdb6c20840578e5fbaf5ddd65e0ec6c3c47eda0f69da172510", size = 12447478, upload-time = "2026-01-10T06:43:50.476Z" },
{ url = "https://files.pythonhosted.org/packages/6c/f8/fa85b2eac68ec631d0b631abc448552cb17d39afd17ec53dcbcc3537681a/numpy-2.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a7870e8c5fc11aef57d6fea4b4085e537a3a60ad2cdd14322ed531fdca68d261", size = 10382981, upload-time = "2026-01-10T06:43:52.575Z" },
{ url = "https://files.pythonhosted.org/packages/1b/a7/ef08d25698e0e4b4efbad8d55251d20fe2a15f6d9aa7c9b30cd03c165e6f/numpy-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3869ea1ee1a1edc16c29bbe3a2f2a4e515cc3a44d43903ad41e0cacdbaf733dc", size = 16652046, upload-time = "2026-01-10T06:43:54.797Z" },
{ url = "https://files.pythonhosted.org/packages/8f/39/e378b3e3ca13477e5ac70293ec027c438d1927f18637e396fe90b1addd72/numpy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e867df947d427cdd7a60e3e271729090b0f0df80f5f10ab7dd436f40811699c3", size = 12378858, upload-time = "2026-01-10T06:43:57.099Z" },
{ url = "https://files.pythonhosted.org/packages/c3/74/7ec6154f0006910ed1fdbb7591cf4432307033102b8a22041599935f8969/numpy-2.4.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:e3bd2cb07841166420d2fa7146c96ce00cb3410664cbc1a6be028e456c4ee220", size = 5207417, upload-time = "2026-01-10T06:43:59.037Z" },
{ url = "https://files.pythonhosted.org/packages/f7/b7/053ac11820d84e42f8feea5cb81cc4fcd1091499b45b1ed8c7415b1bf831/numpy-2.4.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:f0a90aba7d521e6954670550e561a4cb925713bd944445dbe9e729b71f6cabee", size = 6542643, upload-time = "2026-01-10T06:44:01.852Z" },
{ url = "https://files.pythonhosted.org/packages/c0/c4/2e7908915c0e32ca636b92e4e4a3bdec4cb1e7eb0f8aedf1ed3c68a0d8cd/numpy-2.4.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d558123217a83b2d1ba316b986e9248a1ed1971ad495963d555ccd75dcb1556", size = 14418963, upload-time = "2026-01-10T06:44:04.047Z" },
{ url = "https://files.pythonhosted.org/packages/eb/c0/3ed5083d94e7ffd7c404e54619c088e11f2e1939a9544f5397f4adb1b8ba/numpy-2.4.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f44de05659b67d20499cbc96d49f2650769afcb398b79b324bb6e297bfe3844", size = 16363811, upload-time = "2026-01-10T06:44:06.207Z" },
{ url = "https://files.pythonhosted.org/packages/0e/68/42b66f1852bf525050a67315a4fb94586ab7e9eaa541b1bef530fab0c5dd/numpy-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:69e7419c9012c4aaf695109564e3387f1259f001b4326dfa55907b098af082d3", size = 16197643, upload-time = "2026-01-10T06:44:08.33Z" },
{ url = "https://files.pythonhosted.org/packages/d2/40/e8714fc933d85f82c6bfc7b998a0649ad9769a32f3494ba86598aaf18a48/numpy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffd257026eb1b34352e749d7cc1678b5eeec3e329ad8c9965a797e08ccba205", size = 18289601, upload-time = "2026-01-10T06:44:10.841Z" },
{ url = "https://files.pythonhosted.org/packages/80/9a/0d44b468cad50315127e884802351723daca7cf1c98d102929468c81d439/numpy-2.4.1-cp314-cp314-win32.whl", hash = "sha256:727c6c3275ddefa0dc078524a85e064c057b4f4e71ca5ca29a19163c607be745", size = 6005722, upload-time = "2026-01-10T06:44:13.332Z" },
{ url = "https://files.pythonhosted.org/packages/7e/bb/c6513edcce5a831810e2dddc0d3452ce84d208af92405a0c2e58fd8e7881/numpy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:7d5d7999df434a038d75a748275cd6c0094b0ecdb0837342b332a82defc4dc4d", size = 12438590, upload-time = "2026-01-10T06:44:15.006Z" },
{ url = "https://files.pythonhosted.org/packages/e9/da/a598d5cb260780cf4d255102deba35c1d072dc028c4547832f45dd3323a8/numpy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:ce9ce141a505053b3c7bce3216071f3bf5c182b8b28930f14cd24d43932cd2df", size = 10596180, upload-time = "2026-01-10T06:44:17.386Z" },
{ url = "https://files.pythonhosted.org/packages/de/bc/ea3f2c96fcb382311827231f911723aeff596364eb6e1b6d1d91128aa29b/numpy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4e53170557d37ae404bf8d542ca5b7c629d6efa1117dac6a83e394142ea0a43f", size = 12498774, upload-time = "2026-01-10T06:44:19.467Z" },
{ url = "https://files.pythonhosted.org/packages/aa/ab/ef9d939fe4a812648c7a712610b2ca6140b0853c5efea361301006c02ae5/numpy-2.4.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:a73044b752f5d34d4232f25f18160a1cc418ea4507f5f11e299d8ac36875f8a0", size = 5327274, upload-time = "2026-01-10T06:44:23.189Z" },
{ url = "https://files.pythonhosted.org/packages/bd/31/d381368e2a95c3b08b8cf7faac6004849e960f4a042d920337f71cef0cae/numpy-2.4.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:fb1461c99de4d040666ca0444057b06541e5642f800b71c56e6ea92d6a853a0c", size = 6648306, upload-time = "2026-01-10T06:44:25.012Z" },
{ url = "https://files.pythonhosted.org/packages/c8/e5/0989b44ade47430be6323d05c23207636d67d7362a1796ccbccac6773dd2/numpy-2.4.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423797bdab2eeefbe608d7c1ec7b2b4fd3c58d51460f1ee26c7500a1d9c9ee93", size = 14464653, upload-time = "2026-01-10T06:44:26.706Z" },
{ url = "https://files.pythonhosted.org/packages/10/a7/cfbe475c35371cae1358e61f20c5f075badc18c4797ab4354140e1d283cf/numpy-2.4.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:52b5f61bdb323b566b528899cc7db2ba5d1015bda7ea811a8bcf3c89c331fa42", size = 16405144, upload-time = "2026-01-10T06:44:29.378Z" },
{ url = "https://files.pythonhosted.org/packages/f8/a3/0c63fe66b534888fa5177cc7cef061541064dbe2b4b60dcc60ffaf0d2157/numpy-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42d7dd5fa36d16d52a84f821eb96031836fd405ee6955dd732f2023724d0aa01", size = 16247425, upload-time = "2026-01-10T06:44:31.721Z" },
{ url = "https://files.pythonhosted.org/packages/6b/2b/55d980cfa2c93bd40ff4c290bf824d792bd41d2fe3487b07707559071760/numpy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7b6b5e28bbd47b7532698e5db2fe1db693d84b58c254e4389d99a27bb9b8f6b", size = 18330053, upload-time = "2026-01-10T06:44:34.617Z" },
{ url = "https://files.pythonhosted.org/packages/23/12/8b5fc6b9c487a09a7957188e0943c9ff08432c65e34567cabc1623b03a51/numpy-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:5de60946f14ebe15e713a6f22850c2372fa72f4ff9a432ab44aa90edcadaa65a", size = 6152482, upload-time = "2026-01-10T06:44:36.798Z" },
{ url = "https://files.pythonhosted.org/packages/00/a5/9f8ca5856b8940492fc24fbe13c1bc34d65ddf4079097cf9e53164d094e1/numpy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f085da926c0d491ffff3096f91078cc97ea67e7e6b65e490bc8dcda65663be2", size = 12627117, upload-time = "2026-01-10T06:44:38.828Z" },
{ url = "https://files.pythonhosted.org/packages/ad/0d/eca3d962f9eef265f01a8e0d20085c6dd1f443cbffc11b6dede81fd82356/numpy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:6436cffb4f2bf26c974344439439c95e152c9a527013f26b3577be6c2ca64295", size = 10667121, upload-time = "2026-01-10T06:44:41.644Z" },
{ url = "https://files.pythonhosted.org/packages/1e/48/d86f97919e79314a1cdee4c832178763e6e98e623e123d0bada19e92c15a/numpy-2.4.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8ad35f20be147a204e28b6a0575fbf3540c5e5f802634d4258d55b1ff5facce1", size = 16822202, upload-time = "2026-01-10T06:44:43.738Z" },
{ url = "https://files.pythonhosted.org/packages/51/e9/1e62a7f77e0f37dcfb0ad6a9744e65df00242b6ea37dfafb55debcbf5b55/numpy-2.4.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8097529164c0f3e32bb89412a0905d9100bf434d9692d9fc275e18dcf53c9344", size = 12569985, upload-time = "2026-01-10T06:44:45.945Z" },
{ url = "https://files.pythonhosted.org/packages/c7/7e/914d54f0c801342306fdcdce3e994a56476f1b818c46c47fc21ae968088c/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ea66d2b41ca4a1630aae5507ee0a71647d3124d1741980138aa8f28f44dac36e", size = 5398484, upload-time = "2026-01-10T06:44:48.012Z" },
{ url = "https://files.pythonhosted.org/packages/1c/d8/9570b68584e293a33474e7b5a77ca404f1dcc655e40050a600dee81d27fb/numpy-2.4.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d3f8f0df9f4b8be57b3bf74a1d087fec68f927a2fab68231fdb442bf2c12e426", size = 6713216, upload-time = "2026-01-10T06:44:49.725Z" },
{ url = "https://files.pythonhosted.org/packages/33/9b/9dd6e2db8d49eb24f86acaaa5258e5f4c8ed38209a4ee9de2d1a0ca25045/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2023ef86243690c2791fd6353e5b4848eedaa88ca8a2d129f462049f6d484696", size = 14538937, upload-time = "2026-01-10T06:44:51.498Z" },
{ url = "https://files.pythonhosted.org/packages/53/87/d5bd995b0f798a37105b876350d346eea5838bd8f77ea3d7a48392f3812b/numpy-2.4.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8361ea4220d763e54cff2fbe7d8c93526b744f7cd9ddab47afeff7e14e8503be", size = 16479830, upload-time = "2026-01-10T06:44:53.931Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c7/b801bf98514b6ae6475e941ac05c58e6411dd863ea92916bfd6d510b08c1/numpy-2.4.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4f1b68ff47680c2925f8063402a693ede215f0257f02596b1318ecdfb1d79e33", size = 12492579, upload-time = "2026-01-10T06:44:57.094Z" },
]
[[package]]
name = "packaging"
version = "25.0"
@@ -504,6 +735,147 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "pandas"
version = "2.3.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.10.*'",
"python_full_version < '3.10'",
]
dependencies = [
{ name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
{ name = "python-dateutil", marker = "python_full_version < '3.11'" },
{ name = "pytz", marker = "python_full_version < '3.11'" },
{ name = "tzdata", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" },
{ url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" },
{ url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" },
{ url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" },
{ url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" },
{ url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" },
{ url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" },
{ url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" },
{ url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" },
{ url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" },
{ url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" },
{ url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" },
{ url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" },
{ url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" },
{ url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" },
{ url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" },
{ url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" },
{ url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" },
{ url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" },
{ url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" },
{ url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" },
{ url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" },
{ url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" },
{ url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" },
{ url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" },
{ url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" },
{ url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" },
{ url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" },
{ url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" },
{ url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" },
{ url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" },
{ url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" },
{ url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" },
{ url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" },
{ url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" },
{ url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" },
{ url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" },
{ url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" },
{ url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" },
{ url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" },
{ url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" },
{ url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" },
{ url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" },
{ url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" },
{ url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" },
{ url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" },
{ url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" },
{ url = "https://files.pythonhosted.org/packages/56/b4/52eeb530a99e2a4c55ffcd352772b599ed4473a0f892d127f4147cf0f88e/pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2", size = 11567720, upload-time = "2025-09-29T23:33:06.209Z" },
{ url = "https://files.pythonhosted.org/packages/48/4a/2d8b67632a021bced649ba940455ed441ca854e57d6e7658a6024587b083/pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8", size = 10810302, upload-time = "2025-09-29T23:33:35.846Z" },
{ url = "https://files.pythonhosted.org/packages/13/e6/d2465010ee0569a245c975dc6967b801887068bc893e908239b1f4b6c1ac/pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff", size = 12154874, upload-time = "2025-09-29T23:33:49.939Z" },
{ url = "https://files.pythonhosted.org/packages/1f/18/aae8c0aa69a386a3255940e9317f793808ea79d0a525a97a903366bb2569/pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29", size = 12790141, upload-time = "2025-09-29T23:34:05.655Z" },
{ url = "https://files.pythonhosted.org/packages/f7/26/617f98de789de00c2a444fbe6301bb19e66556ac78cff933d2c98f62f2b4/pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73", size = 13208697, upload-time = "2025-09-29T23:34:21.835Z" },
{ url = "https://files.pythonhosted.org/packages/b9/fb/25709afa4552042bd0e15717c75e9b4a2294c3dc4f7e6ea50f03c5136600/pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9", size = 13879233, upload-time = "2025-09-29T23:34:35.079Z" },
{ url = "https://files.pythonhosted.org/packages/98/af/7be05277859a7bc399da8ba68b88c96b27b48740b6cf49688899c6eb4176/pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa", size = 11359119, upload-time = "2025-09-29T23:34:46.339Z" },
]
[[package]]
name = "pandas"
version = "3.0.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
]
dependencies = [
{ name = "numpy", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "python-dateutil", marker = "python_full_version >= '3.11'" },
{ name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/da/b1dc0481ab8d55d0f46e343cfe67d4551a0e14fcee52bd38ca1bd73258d8/pandas-3.0.0.tar.gz", hash = "sha256:0facf7e87d38f721f0af46fe70d97373a37701b1c09f7ed7aeeb292ade5c050f", size = 4633005, upload-time = "2026-01-21T15:52:04.726Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/46/1e/b184654a856e75e975a6ee95d6577b51c271cd92cb2b020c9378f53e0032/pandas-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d64ce01eb9cdca96a15266aa679ae50212ec52757c79204dbc7701a222401850", size = 10313247, upload-time = "2026-01-21T15:50:15.775Z" },
{ url = "https://files.pythonhosted.org/packages/dd/5e/e04a547ad0f0183bf151fd7c7a477468e3b85ff2ad231c566389e6cc9587/pandas-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:613e13426069793aa1ec53bdcc3b86e8d32071daea138bbcf4fa959c9cdaa2e2", size = 9913131, upload-time = "2026-01-21T15:50:18.611Z" },
{ url = "https://files.pythonhosted.org/packages/a2/93/bb77bfa9fc2aba9f7204db807d5d3fb69832ed2854c60ba91b4c65ba9219/pandas-3.0.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0192fee1f1a8e743b464a6607858ee4b071deb0b118eb143d71c2a1d170996d5", size = 10741925, upload-time = "2026-01-21T15:50:21.058Z" },
{ url = "https://files.pythonhosted.org/packages/62/fb/89319812eb1d714bfc04b7f177895caeba8ab4a37ef6712db75ed786e2e0/pandas-3.0.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0b853319dec8d5e0c8b875374c078ef17f2269986a78168d9bd57e49bf650ae", size = 11245979, upload-time = "2026-01-21T15:50:23.413Z" },
{ url = "https://files.pythonhosted.org/packages/a9/63/684120486f541fc88da3862ed31165b3b3e12b6a1c7b93be4597bc84e26c/pandas-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:707a9a877a876c326ae2cb640fbdc4ef63b0a7b9e2ef55c6df9942dcee8e2af9", size = 11756337, upload-time = "2026-01-21T15:50:25.932Z" },
{ url = "https://files.pythonhosted.org/packages/39/92/7eb0ad232312b59aec61550c3c81ad0743898d10af5df7f80bc5e5065416/pandas-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:afd0aa3d0b5cda6e0b8ffc10dbcca3b09ef3cbcd3fe2b27364f85fdc04e1989d", size = 12325517, upload-time = "2026-01-21T15:50:27.952Z" },
{ url = "https://files.pythonhosted.org/packages/51/27/bf9436dd0a4fc3130acec0828951c7ef96a0631969613a9a35744baf27f6/pandas-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:113b4cca2614ff7e5b9fee9b6f066618fe73c5a83e99d721ffc41217b2bf57dd", size = 9881576, upload-time = "2026-01-21T15:50:30.149Z" },
{ url = "https://files.pythonhosted.org/packages/e7/2b/c618b871fce0159fd107516336e82891b404e3f340821853c2fc28c7830f/pandas-3.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c14837eba8e99a8da1527c0280bba29b0eb842f64aa94982c5e21227966e164b", size = 9140807, upload-time = "2026-01-21T15:50:32.308Z" },
{ url = "https://files.pythonhosted.org/packages/0b/38/db33686f4b5fa64d7af40d96361f6a4615b8c6c8f1b3d334eee46ae6160e/pandas-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9803b31f5039b3c3b10cc858c5e40054adb4b29b4d81cb2fd789f4121c8efbcd", size = 10334013, upload-time = "2026-01-21T15:50:34.771Z" },
{ url = "https://files.pythonhosted.org/packages/a5/7b/9254310594e9774906bacdd4e732415e1f86ab7dbb4b377ef9ede58cd8ec/pandas-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14c2a4099cd38a1d18ff108168ea417909b2dea3bd1ebff2ccf28ddb6a74d740", size = 9874154, upload-time = "2026-01-21T15:50:36.67Z" },
{ url = "https://files.pythonhosted.org/packages/63/d4/726c5a67a13bc66643e66d2e9ff115cead482a44fc56991d0c4014f15aaf/pandas-3.0.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d257699b9a9960e6125686098d5714ac59d05222bef7a5e6af7a7fd87c650801", size = 10384433, upload-time = "2026-01-21T15:50:39.132Z" },
{ url = "https://files.pythonhosted.org/packages/bf/2e/9211f09bedb04f9832122942de8b051804b31a39cfbad199a819bb88d9f3/pandas-3.0.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69780c98f286076dcafca38d8b8eee1676adf220199c0a39f0ecbf976b68151a", size = 10864519, upload-time = "2026-01-21T15:50:41.043Z" },
{ url = "https://files.pythonhosted.org/packages/00/8d/50858522cdc46ac88b9afdc3015e298959a70a08cd21e008a44e9520180c/pandas-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4a66384f017240f3858a4c8a7cf21b0591c3ac885cddb7758a589f0f71e87ebb", size = 11394124, upload-time = "2026-01-21T15:50:43.377Z" },
{ url = "https://files.pythonhosted.org/packages/86/3f/83b2577db02503cd93d8e95b0f794ad9d4be0ba7cb6c8bcdcac964a34a42/pandas-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be8c515c9bc33989d97b89db66ea0cececb0f6e3c2a87fcc8b69443a6923e95f", size = 11920444, upload-time = "2026-01-21T15:50:45.932Z" },
{ url = "https://files.pythonhosted.org/packages/64/2d/4f8a2f192ed12c90a0aab47f5557ece0e56b0370c49de9454a09de7381b2/pandas-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a453aad8c4f4e9f166436994a33884442ea62aa8b27d007311e87521b97246e1", size = 9730970, upload-time = "2026-01-21T15:50:47.962Z" },
{ url = "https://files.pythonhosted.org/packages/d4/64/ff571be435cf1e643ca98d0945d76732c0b4e9c37191a89c8550b105eed1/pandas-3.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:da768007b5a33057f6d9053563d6b74dd6d029c337d93c6d0d22a763a5c2ecc0", size = 9041950, upload-time = "2026-01-21T15:50:50.422Z" },
{ url = "https://files.pythonhosted.org/packages/6f/fa/7f0ac4ca8877c57537aaff2a842f8760e630d8e824b730eb2e859ffe96ca/pandas-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b78d646249b9a2bc191040988c7bb524c92fa8534fb0898a0741d7e6f2ffafa6", size = 10307129, upload-time = "2026-01-21T15:50:52.877Z" },
{ url = "https://files.pythonhosted.org/packages/6f/11/28a221815dcea4c0c9414dfc845e34a84a6a7dabc6da3194498ed5ba4361/pandas-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bc9cba7b355cb4162442a88ce495e01cb605f17ac1e27d6596ac963504e0305f", size = 9850201, upload-time = "2026-01-21T15:50:54.807Z" },
{ url = "https://files.pythonhosted.org/packages/ba/da/53bbc8c5363b7e5bd10f9ae59ab250fc7a382ea6ba08e4d06d8694370354/pandas-3.0.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c9a1a149aed3b6c9bf246033ff91e1b02d529546c5d6fb6b74a28fea0cf4c70", size = 10354031, upload-time = "2026-01-21T15:50:57.463Z" },
{ url = "https://files.pythonhosted.org/packages/f7/a3/51e02ebc2a14974170d51e2410dfdab58870ea9bcd37cda15bd553d24dc4/pandas-3.0.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95683af6175d884ee89471842acfca29172a85031fccdabc35e50c0984470a0e", size = 10861165, upload-time = "2026-01-21T15:50:59.32Z" },
{ url = "https://files.pythonhosted.org/packages/a5/fe/05a51e3cac11d161472b8297bd41723ea98013384dd6d76d115ce3482f9b/pandas-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1fbbb5a7288719e36b76b4f18d46ede46e7f916b6c8d9915b756b0a6c3f792b3", size = 11359359, upload-time = "2026-01-21T15:51:02.014Z" },
{ url = "https://files.pythonhosted.org/packages/ee/56/ba620583225f9b85a4d3e69c01df3e3870659cc525f67929b60e9f21dcd1/pandas-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e8b9808590fa364416b49b2a35c1f4cf2785a6c156935879e57f826df22038e", size = 11912907, upload-time = "2026-01-21T15:51:05.175Z" },
{ url = "https://files.pythonhosted.org/packages/c9/8c/c6638d9f67e45e07656b3826405c5cc5f57f6fd07c8b2572ade328c86e22/pandas-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:98212a38a709feb90ae658cb6227ea3657c22ba8157d4b8f913cd4c950de5e7e", size = 9732138, upload-time = "2026-01-21T15:51:07.569Z" },
{ url = "https://files.pythonhosted.org/packages/7b/bf/bd1335c3bf1770b6d8fed2799993b11c4971af93bb1b729b9ebbc02ca2ec/pandas-3.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:177d9df10b3f43b70307a149d7ec49a1229a653f907aa60a48f1877d0e6be3be", size = 9033568, upload-time = "2026-01-21T15:51:09.484Z" },
{ url = "https://files.pythonhosted.org/packages/8e/c6/f5e2171914d5e29b9171d495344097d54e3ffe41d2d85d8115baba4dc483/pandas-3.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2713810ad3806767b89ad3b7b69ba153e1c6ff6d9c20f9c2140379b2a98b6c98", size = 10741936, upload-time = "2026-01-21T15:51:11.693Z" },
{ url = "https://files.pythonhosted.org/packages/51/88/9a0164f99510a1acb9f548691f022c756c2314aad0d8330a24616c14c462/pandas-3.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:15d59f885ee5011daf8335dff47dcb8a912a27b4ad7826dc6cbe809fd145d327", size = 10393884, upload-time = "2026-01-21T15:51:14.197Z" },
{ url = "https://files.pythonhosted.org/packages/e0/53/b34d78084d88d8ae2b848591229da8826d1e65aacf00b3abe34023467648/pandas-3.0.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24e6547fb64d2c92665dd2adbfa4e85fa4fd70a9c070e7cfb03b629a0bbab5eb", size = 10310740, upload-time = "2026-01-21T15:51:16.093Z" },
{ url = "https://files.pythonhosted.org/packages/5b/d3/bee792e7c3d6930b74468d990604325701412e55d7aaf47460a22311d1a5/pandas-3.0.0-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48ee04b90e2505c693d3f8e8f524dab8cb8aaf7ddcab52c92afa535e717c4812", size = 10700014, upload-time = "2026-01-21T15:51:18.818Z" },
{ url = "https://files.pythonhosted.org/packages/55/db/2570bc40fb13aaed1cbc3fbd725c3a60ee162477982123c3adc8971e7ac1/pandas-3.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66f72fb172959af42a459e27a8d8d2c7e311ff4c1f7db6deb3b643dbc382ae08", size = 11323737, upload-time = "2026-01-21T15:51:20.784Z" },
{ url = "https://files.pythonhosted.org/packages/bc/2e/297ac7f21c8181b62a4cccebad0a70caf679adf3ae5e83cb676194c8acc3/pandas-3.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4a4a400ca18230976724a5066f20878af785f36c6756e498e94c2a5e5d57779c", size = 11771558, upload-time = "2026-01-21T15:51:22.977Z" },
{ url = "https://files.pythonhosted.org/packages/0a/46/e1c6876d71c14332be70239acce9ad435975a80541086e5ffba2f249bcf6/pandas-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:940eebffe55528074341a5a36515f3e4c5e25e958ebbc764c9502cfc35ba3faa", size = 10473771, upload-time = "2026-01-21T15:51:25.285Z" },
{ url = "https://files.pythonhosted.org/packages/c0/db/0270ad9d13c344b7a36fa77f5f8344a46501abf413803e885d22864d10bf/pandas-3.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:597c08fb9fef0edf1e4fa2f9828dd27f3d78f9b8c9b4a748d435ffc55732310b", size = 10312075, upload-time = "2026-01-21T15:51:28.5Z" },
{ url = "https://files.pythonhosted.org/packages/09/9f/c176f5e9717f7c91becfe0f55a52ae445d3f7326b4a2cf355978c51b7913/pandas-3.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:447b2d68ac5edcbf94655fe909113a6dba6ef09ad7f9f60c80477825b6c489fe", size = 9900213, upload-time = "2026-01-21T15:51:30.955Z" },
{ url = "https://files.pythonhosted.org/packages/d9/e7/63ad4cc10b257b143e0a5ebb04304ad806b4e1a61c5da25f55896d2ca0f4/pandas-3.0.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:debb95c77ff3ed3ba0d9aa20c3a2f19165cc7956362f9873fce1ba0a53819d70", size = 10428768, upload-time = "2026-01-21T15:51:33.018Z" },
{ url = "https://files.pythonhosted.org/packages/9e/0e/4e4c2d8210f20149fd2248ef3fff26623604922bd564d915f935a06dd63d/pandas-3.0.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fedabf175e7cd82b69b74c30adbaa616de301291a5231138d7242596fc296a8d", size = 10882954, upload-time = "2026-01-21T15:51:35.287Z" },
{ url = "https://files.pythonhosted.org/packages/c6/60/c9de8ac906ba1f4d2250f8a951abe5135b404227a55858a75ad26f84db47/pandas-3.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:412d1a89aab46889f3033a386912efcdfa0f1131c5705ff5b668dda88305e986", size = 11430293, upload-time = "2026-01-21T15:51:37.57Z" },
{ url = "https://files.pythonhosted.org/packages/a1/69/806e6637c70920e5787a6d6896fd707f8134c2c55cd761e7249a97b7dc5a/pandas-3.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e979d22316f9350c516479dd3a92252be2937a9531ed3a26ec324198a99cdd49", size = 11952452, upload-time = "2026-01-21T15:51:39.618Z" },
{ url = "https://files.pythonhosted.org/packages/cb/de/918621e46af55164c400ab0ef389c9d969ab85a43d59ad1207d4ddbe30a5/pandas-3.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:083b11415b9970b6e7888800c43c82e81a06cd6b06755d84804444f0007d6bb7", size = 9851081, upload-time = "2026-01-21T15:51:41.758Z" },
{ url = "https://files.pythonhosted.org/packages/91/a1/3562a18dd0bd8c73344bfa26ff90c53c72f827df119d6d6b1dacc84d13e3/pandas-3.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:5db1e62cb99e739fa78a28047e861b256d17f88463c76b8dafc7c1338086dca8", size = 9174610, upload-time = "2026-01-21T15:51:44.312Z" },
{ url = "https://files.pythonhosted.org/packages/ce/26/430d91257eaf366f1737d7a1c158677caaf6267f338ec74e3a1ec444111c/pandas-3.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:697b8f7d346c68274b1b93a170a70974cdc7d7354429894d5927c1effdcccd73", size = 10761999, upload-time = "2026-01-21T15:51:46.899Z" },
{ url = "https://files.pythonhosted.org/packages/ec/1a/954eb47736c2b7f7fe6a9d56b0cb6987773c00faa3c6451a43db4beb3254/pandas-3.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8cb3120f0d9467ed95e77f67a75e030b67545bcfa08964e349252d674171def2", size = 10410279, upload-time = "2026-01-21T15:51:48.89Z" },
{ url = "https://files.pythonhosted.org/packages/20/fc/b96f3a5a28b250cd1b366eb0108df2501c0f38314a00847242abab71bb3a/pandas-3.0.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33fd3e6baa72899746b820c31e4b9688c8e1b7864d7aec2de7ab5035c285277a", size = 10330198, upload-time = "2026-01-21T15:51:51.015Z" },
{ url = "https://files.pythonhosted.org/packages/90/b3/d0e2952f103b4fbef1ef22d0c2e314e74fc9064b51cee30890b5e3286ee6/pandas-3.0.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8942e333dc67ceda1095227ad0febb05a3b36535e520154085db632c40ad084", size = 10728513, upload-time = "2026-01-21T15:51:53.387Z" },
{ url = "https://files.pythonhosted.org/packages/76/81/832894f286df828993dc5fd61c63b231b0fb73377e99f6c6c369174cf97e/pandas-3.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:783ac35c4d0fe0effdb0d67161859078618b1b6587a1af15928137525217a721", size = 11345550, upload-time = "2026-01-21T15:51:55.329Z" },
{ url = "https://files.pythonhosted.org/packages/34/a0/ed160a00fb4f37d806406bc0a79a8b62fe67f29d00950f8d16203ff3409b/pandas-3.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:125eb901e233f155b268bbef9abd9afb5819db74f0e677e89a61b246228c71ac", size = 11799386, upload-time = "2026-01-21T15:51:57.457Z" },
{ url = "https://files.pythonhosted.org/packages/36/c8/2ac00d7255252c5e3cf61b35ca92ca25704b0188f7454ca4aec08a33cece/pandas-3.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b86d113b6c109df3ce0ad5abbc259fe86a1bd4adfd4a31a89da42f84f65509bb", size = 10873041, upload-time = "2026-01-21T15:52:00.034Z" },
{ url = "https://files.pythonhosted.org/packages/e6/3f/a80ac00acbc6b35166b42850e98a4f466e2c0d9c64054161ba9620f95680/pandas-3.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:1c39eab3ad38f2d7a249095f0a3d8f8c22cc0f847e98ccf5bbe732b272e2d9fa", size = 9441003, upload-time = "2026-01-21T15:52:02.281Z" },
]
[[package]]
name = "pathspec"
version = "1.0.3"
@@ -530,7 +902,13 @@ name = "platformdirs"
version = "4.5.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.10'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
wheels = [
@@ -546,6 +924,84 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
]
[[package]]
name = "polars"
version = "1.36.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.10'",
]
dependencies = [
{ name = "polars-runtime-32", version = "1.36.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/dc/56f2a90c79a2cb13f9e956eab6385effe54216ae7a2068b3a6406bae4345/polars-1.36.1.tar.gz", hash = "sha256:12c7616a2305559144711ab73eaa18814f7aa898c522e7645014b68f1432d54c", size = 711993, upload-time = "2025-12-10T01:14:53.033Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f6/c6/36a1b874036b49893ecae0ac44a2f63d1a76e6212631a5b2f50a86e0e8af/polars-1.36.1-py3-none-any.whl", hash = "sha256:853c1bbb237add6a5f6d133c15094a9b727d66dd6a4eb91dbb07cdb056b2b8ef", size = 802429, upload-time = "2025-12-10T01:13:53.838Z" },
]
[[package]]
name = "polars"
version = "1.37.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
]
dependencies = [
{ name = "polars-runtime-32", version = "1.37.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/84/ae/dfebf31b9988c20998140b54d5b521f64ce08879f2c13d9b4d44d7c87e32/polars-1.37.1.tar.gz", hash = "sha256:0309e2a4633e712513401964b4d95452f124ceabf7aec6db50affb9ced4a274e", size = 715572, upload-time = "2026-01-12T23:27:03.267Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/75/ec73e38812bca7c2240aff481b9ddff20d1ad2f10dee4b3353f5eeaacdab/polars-1.37.1-py3-none-any.whl", hash = "sha256:377fed8939a2f1223c1563cfabdc7b4a3d6ff846efa1f2ddeb8644fafd9b1aff", size = 805749, upload-time = "2026-01-12T23:25:48.595Z" },
]
[[package]]
name = "polars-runtime-32"
version = "1.36.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.10'",
]
sdist = { url = "https://files.pythonhosted.org/packages/31/df/597c0ef5eb8d761a16d72327846599b57c5d40d7f9e74306fc154aba8c37/polars_runtime_32-1.36.1.tar.gz", hash = "sha256:201c2cfd80ceb5d5cd7b63085b5fd08d6ae6554f922bcb941035e39638528a09", size = 2788751, upload-time = "2025-12-10T01:14:54.172Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/ea/871129a2d296966c0925b078a9a93c6c5e7facb1c5eebfcd3d5811aeddc1/polars_runtime_32-1.36.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:327b621ca82594f277751f7e23d4b939ebd1be18d54b4cdf7a2f8406cecc18b2", size = 43494311, upload-time = "2025-12-10T01:13:56.096Z" },
{ url = "https://files.pythonhosted.org/packages/d8/76/0038210ad1e526ce5bb2933b13760d6b986b3045eccc1338e661bd656f77/polars_runtime_32-1.36.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ab0d1f23084afee2b97de8c37aa3e02ec3569749ae39571bd89e7a8b11ae9e83", size = 39300602, upload-time = "2025-12-10T01:13:59.366Z" },
{ url = "https://files.pythonhosted.org/packages/54/1e/2707bee75a780a953a77a2c59829ee90ef55708f02fc4add761c579bf76e/polars_runtime_32-1.36.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:899b9ad2e47ceb31eb157f27a09dbc2047efbf4969a923a6b1ba7f0412c3e64c", size = 44511780, upload-time = "2025-12-10T01:14:02.285Z" },
{ url = "https://files.pythonhosted.org/packages/11/b2/3fede95feee441be64b4bcb32444679a8fbb7a453a10251583053f6efe52/polars_runtime_32-1.36.1-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:d9d077bb9df711bc635a86540df48242bb91975b353e53ef261c6fae6cb0948f", size = 40688448, upload-time = "2025-12-10T01:14:05.131Z" },
{ url = "https://files.pythonhosted.org/packages/05/0f/e629713a72999939b7b4bfdbf030a32794db588b04fdf3dc977dd8ea6c53/polars_runtime_32-1.36.1-cp39-abi3-win_amd64.whl", hash = "sha256:cc17101f28c9a169ff8b5b8d4977a3683cd403621841623825525f440b564cf0", size = 44464898, upload-time = "2025-12-10T01:14:08.296Z" },
{ url = "https://files.pythonhosted.org/packages/d1/d8/a12e6aa14f63784cead437083319ec7cece0d5bb9a5bfe7678cc6578b52a/polars_runtime_32-1.36.1-cp39-abi3-win_arm64.whl", hash = "sha256:809e73857be71250141225ddd5d2b30c97e6340aeaa0d445f930e01bef6888dc", size = 39798896, upload-time = "2025-12-10T01:14:11.568Z" },
]
[[package]]
name = "polars-runtime-32"
version = "1.37.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/40/0b/addabe5e8d28a5a4c9887a08907be7ddc3fce892dc38f37d14b055438a57/polars_runtime_32-1.37.1.tar.gz", hash = "sha256:68779d4a691da20a5eb767d74165a8f80a2bdfbde4b54acf59af43f7fa028d8f", size = 2818945, upload-time = "2026-01-12T23:27:04.653Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/a2/e828ea9f845796de02d923edb790e408ca0b560cd68dbd74bb99a1b3c461/polars_runtime_32-1.37.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0b8d4d73ea9977d3731927740e59d814647c5198bdbe359bcf6a8bfce2e79771", size = 43499912, upload-time = "2026-01-12T23:25:51.182Z" },
{ url = "https://files.pythonhosted.org/packages/7e/46/81b71b7aa9e3703ee6e4ef1f69a87e40f58ea7c99212bf49a95071e99c8c/polars_runtime_32-1.37.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:c682bf83f5f352e5e02f5c16c652c48ca40442f07b236f30662b22217320ce76", size = 39695707, upload-time = "2026-01-12T23:25:54.289Z" },
{ url = "https://files.pythonhosted.org/packages/81/2e/20009d1fde7ee919e24040f5c87cb9d0e4f8e3f109b74ba06bc10c02459c/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc82b5bbe70ca1a4b764eed1419f6336752d6ba9fc1245388d7f8b12438afa2c", size = 41467034, upload-time = "2026-01-12T23:25:56.925Z" },
{ url = "https://files.pythonhosted.org/packages/eb/21/9b55bea940524324625b1e8fd96233290303eb1bf2c23b54573487bbbc25/polars_runtime_32-1.37.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8362d11ac5193b994c7e9048ffe22ccfb976699cfbf6e128ce0302e06728894", size = 45142711, upload-time = "2026-01-12T23:26:00.817Z" },
{ url = "https://files.pythonhosted.org/packages/8c/25/c5f64461aeccdac6834a89f826d051ccd3b4ce204075e562c87a06ed2619/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04f5d5a2f013dca7391b7d8e7672fa6d37573a87f1d45d3dd5f0d9b5565a4b0f", size = 41638564, upload-time = "2026-01-12T23:26:04.186Z" },
{ url = "https://files.pythonhosted.org/packages/35/af/509d3cf6c45e764ccf856beaae26fc34352f16f10f94a7839b1042920a73/polars_runtime_32-1.37.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fbfde7c0ca8209eeaed546e4a32cca1319189aa61c5f0f9a2b4494262bd0c689", size = 44721136, upload-time = "2026-01-12T23:26:07.088Z" },
{ url = "https://files.pythonhosted.org/packages/af/d1/5c0a83a625f72beef59394bebc57d12637997632a4f9d3ab2ffc2cc62bbf/polars_runtime_32-1.37.1-cp310-abi3-win_amd64.whl", hash = "sha256:da3d3642ae944e18dd17109d2a3036cb94ce50e5495c5023c77b1599d4c861bc", size = 44948288, upload-time = "2026-01-12T23:26:10.214Z" },
{ url = "https://files.pythonhosted.org/packages/10/f3/061bb702465904b6502f7c9081daee34b09ccbaa4f8c94cf43a2a3b6dd6f/polars_runtime_32-1.37.1-cp310-abi3-win_arm64.whl", hash = "sha256:55f2c4847a8d2e267612f564de7b753a4bde3902eaabe7b436a0a4abf75949a0", size = 41001914, upload-time = "2026-01-12T23:26:12.997Z" },
]
[[package]]
name = "pydoc-markdown"
version = "4.8.2"
@@ -607,7 +1063,13 @@ name = "pytest"
version = "9.0.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.10'",
"python_full_version >= '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'",
"python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'",
"python_full_version == '3.10.*'",
]
dependencies = [
{ name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" },
@@ -623,6 +1085,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
]
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
]
[[package]]
name = "pytz"
version = "2025.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
@@ -720,6 +1203,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/76/f963c61683a39084aa575f98089253e1e852a4417cb8a3a8a422923a5246/setuptools-80.10.1-py3-none-any.whl", hash = "sha256:fc30c51cbcb8199a219c12cc9c281b5925a4978d212f84229c909636d9f6984e", size = 1099859, upload-time = "2026-01-21T09:42:00.688Z" },
]
[[package]]
name = "six"
version = "1.17.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
]
[[package]]
name = "tomli"
version = "2.4.0"
@@ -804,6 +1296,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "tzdata"
version = "2025.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
]
[[package]]
name = "urllib3"
version = "2.6.3"