global: snapshot

This commit is contained in:
nym21
2026-03-26 15:57:22 +01:00
parent 6d3307c0df
commit 18bb4186a8
72 changed files with 2013 additions and 1150 deletions
Generated
+16 -22
View File
@@ -2045,9 +2045,9 @@ dependencies = [
[[package]]
name = "libredox"
version = "0.1.14"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08"
dependencies = [
"libc",
]
@@ -2201,9 +2201,9 @@ dependencies = [
[[package]]
name = "num-conv"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "num-traits"
@@ -2546,8 +2546,6 @@ dependencies = [
[[package]]
name = "rawdb"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "912a9c6f76a5f141057139d510b969b082ff74f39a72a1c27178d8e1eeec95dc"
dependencies = [
"libc",
"log",
@@ -2916,9 +2914,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
version = "1.0.4"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98"
dependencies = [
"serde_core",
]
@@ -3182,9 +3180,9 @@ dependencies = [
[[package]]
name = "toml"
version = "1.0.7+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96"
checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc"
dependencies = [
"indexmap",
"serde_core",
@@ -3197,27 +3195,27 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "1.0.1+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9"
checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.10+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420"
checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.7+spec-1.1.0"
version = "1.1.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed"
[[package]]
name = "tower"
@@ -3352,9 +3350,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
checksum = "da36089a805484bcccfffe0739803392c8298778a2d2f09febf76fac5ad9025b"
[[package]]
name = "unicode-xid"
@@ -3440,8 +3438,6 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
[[package]]
name = "vecdb"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f89be182f86511ee28832cc04039d818564b88c63b14093fcf1871c732c0dd"
dependencies = [
"itoa",
"libc",
@@ -3463,8 +3459,6 @@ dependencies = [
[[package]]
name = "vecdb_derive"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42915a5ca404d941e3e6d024f794403c481e5aeb96b1867d12d1f1e972d1433f"
dependencies = [
"quote",
"syn",
+2 -2
View File
@@ -87,8 +87,8 @@ tower-http = { version = "0.6.8", features = ["catch-panic", "compression-br", "
tower-layer = "0.3"
tracing = { version = "0.1", default-features = false, features = ["std"] }
ureq = { version = "3.3.0", features = ["json"] }
vecdb = { version = "0.7.2", features = ["derive", "serde_json", "pco", "schemars"] }
# vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
# vecdb = { version = "0.7.2", features = ["derive", "serde_json", "pco", "schemars"] }
vecdb = { path = "../anydb/crates/vecdb", features = ["derive", "serde_json", "pco", "schemars"] }
[workspace.metadata.release]
shared-version = true
+1 -1
View File
@@ -26,7 +26,7 @@ owo-colors = { workspace = true }
tracing = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
toml = "1.0.7"
toml = "1.1.0"
vecdb = { workspace = true }
[[bin]]
+289 -239
View File
@@ -1247,7 +1247,7 @@ impl GrossInvestedInvestorLossNetNuplProfitSentimentPattern2 {
pub struct BpsCentsPercentilesRatioSatsSmaStdUsdPattern {
pub bps: SeriesPattern1<BasisPoints32>,
pub cents: SeriesPattern1<Cents>,
pub percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub ratio: SeriesPattern1<StoredF32>,
pub sats: SeriesPattern1<SatsFract>,
pub sma: _1m1w1y2y4yAllPattern,
@@ -1255,6 +1255,34 @@ pub struct BpsCentsPercentilesRatioSatsSmaStdUsdPattern {
pub usd: SeriesPattern1<Dollars>,
}
/// Pattern struct for repeated tree structure.
pub struct Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern {
pub pct0_5: BpsPriceRatioPattern,
pub pct1: BpsPriceRatioPattern,
pub pct2: BpsPriceRatioPattern,
pub pct5: BpsPriceRatioPattern,
pub pct95: BpsPriceRatioPattern,
pub pct98: BpsPriceRatioPattern,
pub pct99: BpsPriceRatioPattern,
pub pct99_5: BpsPriceRatioPattern,
}
impl Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
pct0_5: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct0_5".to_string()),
pct1: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct1".to_string()),
pct2: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct2".to_string()),
pct5: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct5".to_string()),
pct95: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct95".to_string()),
pct98: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct98".to_string()),
pct99: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct99".to_string()),
pct99_5: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct99_5".to_string()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _10y2y3y4y5y6y8yPattern {
pub _10y: BpsPercentRatioPattern,
@@ -1487,7 +1515,7 @@ impl AverageBlockCumulativeInSumPattern {
pub struct BpsCentsPercentilesRatioSatsUsdPattern {
pub bps: SeriesPattern1<BasisPoints32>,
pub cents: SeriesPattern1<Cents>,
pub percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub ratio: SeriesPattern1<StoredF32>,
pub sats: SeriesPattern1<SatsFract>,
pub usd: SeriesPattern1<Dollars>,
@@ -1499,7 +1527,7 @@ impl BpsCentsPercentilesRatioSatsUsdPattern {
Self {
bps: SeriesPattern1::new(client.clone(), _m(&acc, "ratio_bps")),
cents: SeriesPattern1::new(client.clone(), _m(&acc, "cents")),
percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), acc.clone()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), acc.clone()),
ratio: SeriesPattern1::new(client.clone(), _m(&acc, "ratio")),
sats: SeriesPattern1::new(client.clone(), _m(&acc, "sats")),
usd: SeriesPattern1::new(client.clone(), acc.clone()),
@@ -1603,30 +1631,6 @@ impl DeltaHalfInToTotalPattern2 {
}
}
/// Pattern struct for repeated tree structure.
pub struct Pct1Pct2Pct5Pct95Pct98Pct99Pattern {
pub pct1: BpsPriceRatioPattern,
pub pct2: BpsPriceRatioPattern,
pub pct5: BpsPriceRatioPattern,
pub pct95: BpsPriceRatioPattern,
pub pct98: BpsPriceRatioPattern,
pub pct99: BpsPriceRatioPattern,
}
impl Pct1Pct2Pct5Pct95Pct98Pct99Pattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
pct1: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct1".to_string()),
pct2: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct2".to_string()),
pct5: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct5".to_string()),
pct95: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct95".to_string()),
pct98: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct98".to_string()),
pct99: BpsPriceRatioPattern::new(client.clone(), acc.clone(), "pct99".to_string()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _1m1w1y24hBlockPattern {
pub _1m: SeriesPattern1<StoredF32>,
@@ -3126,6 +3130,7 @@ pub struct SeriesTree {
pub constants: SeriesTree_Constants,
pub indexes: SeriesTree_Indexes,
pub indicators: SeriesTree_Indicators,
pub investing: SeriesTree_Investing,
pub market: SeriesTree_Market,
pub pools: SeriesTree_Pools,
pub prices: SeriesTree_Prices,
@@ -3148,6 +3153,7 @@ impl SeriesTree {
constants: SeriesTree_Constants::new(client.clone(), format!("{base_path}_constants")),
indexes: SeriesTree_Indexes::new(client.clone(), format!("{base_path}_indexes")),
indicators: SeriesTree_Indicators::new(client.clone(), format!("{base_path}_indicators")),
investing: SeriesTree_Investing::new(client.clone(), format!("{base_path}_investing")),
market: SeriesTree_Market::new(client.clone(), format!("{base_path}_market")),
pools: SeriesTree_Pools::new(client.clone(), format!("{base_path}_pools")),
prices: SeriesTree_Prices::new(client.clone(), format!("{base_path}_prices")),
@@ -3215,17 +3221,13 @@ impl SeriesTree_Blocks_Difficulty {
/// Series tree node.
pub struct SeriesTree_Blocks_Time {
pub timestamp: SeriesPattern1<Timestamp>,
pub date: SeriesPattern18<Date>,
pub timestamp_monotonic: SeriesPattern18<Timestamp>,
pub timestamp: SeriesPattern18<Timestamp>,
}
impl SeriesTree_Blocks_Time {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
timestamp: SeriesPattern1::new(client.clone(), "timestamp".to_string()),
date: SeriesPattern18::new(client.clone(), "date".to_string()),
timestamp_monotonic: SeriesPattern18::new(client.clone(), "timestamp_monotonic".to_string()),
timestamp: SeriesPattern18::new(client.clone(), "timestamp".to_string()),
}
}
}
@@ -4562,6 +4564,7 @@ pub struct SeriesTree_Indexes {
pub tx_index: SeriesTree_Indexes_TxIndex,
pub txin_index: SeriesTree_Indexes_TxinIndex,
pub txout_index: SeriesTree_Indexes_TxoutIndex,
pub timestamp: SeriesTree_Indexes_Timestamp,
}
impl SeriesTree_Indexes {
@@ -4587,6 +4590,7 @@ impl SeriesTree_Indexes {
tx_index: SeriesTree_Indexes_TxIndex::new(client.clone(), format!("{base_path}_tx_index")),
txin_index: SeriesTree_Indexes_TxinIndex::new(client.clone(), format!("{base_path}_txin_index")),
txout_index: SeriesTree_Indexes_TxoutIndex::new(client.clone(), format!("{base_path}_txout_index")),
timestamp: SeriesTree_Indexes_Timestamp::new(client.clone(), format!("{base_path}_timestamp")),
}
}
}
@@ -5129,6 +5133,21 @@ impl SeriesTree_Indexes_TxoutIndex {
}
}
/// Series tree node.
pub struct SeriesTree_Indexes_Timestamp {
pub monotonic: SeriesPattern18<Timestamp>,
pub resolutions: SeriesPattern2<Timestamp>,
}
impl SeriesTree_Indexes_Timestamp {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
monotonic: SeriesPattern18::new(client.clone(), "timestamp_monotonic".to_string()),
resolutions: SeriesPattern2::new(client.clone(), "timestamp".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Indicators {
pub puell_multiple: BpsRatioPattern2,
@@ -5141,6 +5160,7 @@ pub struct SeriesTree_Indicators {
pub dormancy: SeriesTree_Indicators_Dormancy,
pub stock_to_flow: SeriesPattern1<StoredF32>,
pub seller_exhaustion: SeriesPattern1<StoredF32>,
pub thermometer: SeriesTree_Indicators_Thermometer,
}
impl SeriesTree_Indicators {
@@ -5156,6 +5176,7 @@ impl SeriesTree_Indicators {
dormancy: SeriesTree_Indicators_Dormancy::new(client.clone(), format!("{base_path}_dormancy")),
stock_to_flow: SeriesPattern1::new(client.clone(), "stock_to_flow".to_string()),
seller_exhaustion: SeriesPattern1::new(client.clone(), "seller_exhaustion".to_string()),
thermometer: SeriesTree_Indicators_Thermometer::new(client.clone(), format!("{base_path}_thermometer")),
}
}
}
@@ -5175,6 +5196,234 @@ impl SeriesTree_Indicators_Dormancy {
}
}
/// Series tree node.
pub struct SeriesTree_Indicators_Thermometer {
pub pct0_5: CentsSatsUsdPattern,
pub pct1: CentsSatsUsdPattern,
pub pct2: CentsSatsUsdPattern,
pub pct5: CentsSatsUsdPattern,
pub pct95: CentsSatsUsdPattern,
pub pct98: CentsSatsUsdPattern,
pub pct99: CentsSatsUsdPattern,
pub pct99_5: CentsSatsUsdPattern,
pub zone: SeriesPattern1<StoredI8>,
pub score: SeriesPattern1<StoredI8>,
}
impl SeriesTree_Indicators_Thermometer {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
pct0_5: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct0_5".to_string()),
pct1: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct01".to_string()),
pct2: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct02".to_string()),
pct5: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct05".to_string()),
pct95: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct95".to_string()),
pct98: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct98".to_string()),
pct99: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct99".to_string()),
pct99_5: CentsSatsUsdPattern::new(client.clone(), "thermometer_pct99_5".to_string()),
zone: SeriesPattern1::new(client.clone(), "thermometer_zone".to_string()),
score: SeriesPattern1::new(client.clone(), "thermometer_score".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing {
pub sats_per_day: SeriesPattern18<Sats>,
pub period: SeriesTree_Investing_Period,
pub class: SeriesTree_Investing_Class,
}
impl SeriesTree_Investing {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
sats_per_day: SeriesPattern18::new(client.clone(), "dca_sats_per_day".to_string()),
period: SeriesTree_Investing_Period::new(client.clone(), format!("{base_path}_period")),
class: SeriesTree_Investing_Class::new(client.clone(), format!("{base_path}_class")),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing_Period {
pub stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3,
pub cost_basis: SeriesTree_Investing_Period_CostBasis,
pub return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2,
pub cagr: _10y2y3y4y5y6y8yPattern,
pub lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3,
pub lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2,
}
impl SeriesTree_Investing_Period {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3::new(client.clone(), "dca_stack".to_string()),
cost_basis: SeriesTree_Investing_Period_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")),
return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "dca_return".to_string()),
cagr: _10y2y3y4y5y6y8yPattern::new(client.clone(), "dca_cagr".to_string()),
lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3::new(client.clone(), "lump_sum_stack".to_string()),
lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "lump_sum_return".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing_Period_CostBasis {
pub _1w: CentsSatsUsdPattern,
pub _1m: CentsSatsUsdPattern,
pub _3m: CentsSatsUsdPattern,
pub _6m: CentsSatsUsdPattern,
pub _1y: CentsSatsUsdPattern,
pub _2y: CentsSatsUsdPattern,
pub _3y: CentsSatsUsdPattern,
pub _4y: CentsSatsUsdPattern,
pub _5y: CentsSatsUsdPattern,
pub _6y: CentsSatsUsdPattern,
pub _8y: CentsSatsUsdPattern,
pub _10y: CentsSatsUsdPattern,
}
impl SeriesTree_Investing_Period_CostBasis {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
_1w: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_1w".to_string()),
_1m: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_1m".to_string()),
_3m: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_3m".to_string()),
_6m: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_6m".to_string()),
_1y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_1y".to_string()),
_2y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_2y".to_string()),
_3y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_3y".to_string()),
_4y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_4y".to_string()),
_5y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_5y".to_string()),
_6y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_6y".to_string()),
_8y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_8y".to_string()),
_10y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_10y".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing_Class {
pub stack: SeriesTree_Investing_Class_Stack,
pub cost_basis: SeriesTree_Investing_Class_CostBasis,
pub return_: SeriesTree_Investing_Class_Return,
}
impl SeriesTree_Investing_Class {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
stack: SeriesTree_Investing_Class_Stack::new(client.clone(), format!("{base_path}_stack")),
cost_basis: SeriesTree_Investing_Class_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")),
return_: SeriesTree_Investing_Class_Return::new(client.clone(), format!("{base_path}_return")),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing_Class_Stack {
pub from_2015: BtcCentsSatsUsdPattern3,
pub from_2016: BtcCentsSatsUsdPattern3,
pub from_2017: BtcCentsSatsUsdPattern3,
pub from_2018: BtcCentsSatsUsdPattern3,
pub from_2019: BtcCentsSatsUsdPattern3,
pub from_2020: BtcCentsSatsUsdPattern3,
pub from_2021: BtcCentsSatsUsdPattern3,
pub from_2022: BtcCentsSatsUsdPattern3,
pub from_2023: BtcCentsSatsUsdPattern3,
pub from_2024: BtcCentsSatsUsdPattern3,
pub from_2025: BtcCentsSatsUsdPattern3,
pub from_2026: BtcCentsSatsUsdPattern3,
}
impl SeriesTree_Investing_Class_Stack {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
from_2015: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2015".to_string()),
from_2016: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2016".to_string()),
from_2017: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2017".to_string()),
from_2018: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2018".to_string()),
from_2019: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2019".to_string()),
from_2020: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2020".to_string()),
from_2021: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2021".to_string()),
from_2022: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2022".to_string()),
from_2023: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2023".to_string()),
from_2024: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2024".to_string()),
from_2025: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2025".to_string()),
from_2026: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2026".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing_Class_CostBasis {
pub from_2015: CentsSatsUsdPattern,
pub from_2016: CentsSatsUsdPattern,
pub from_2017: CentsSatsUsdPattern,
pub from_2018: CentsSatsUsdPattern,
pub from_2019: CentsSatsUsdPattern,
pub from_2020: CentsSatsUsdPattern,
pub from_2021: CentsSatsUsdPattern,
pub from_2022: CentsSatsUsdPattern,
pub from_2023: CentsSatsUsdPattern,
pub from_2024: CentsSatsUsdPattern,
pub from_2025: CentsSatsUsdPattern,
pub from_2026: CentsSatsUsdPattern,
}
impl SeriesTree_Investing_Class_CostBasis {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
from_2015: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2015".to_string()),
from_2016: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2016".to_string()),
from_2017: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2017".to_string()),
from_2018: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2018".to_string()),
from_2019: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2019".to_string()),
from_2020: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2020".to_string()),
from_2021: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2021".to_string()),
from_2022: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2022".to_string()),
from_2023: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2023".to_string()),
from_2024: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2024".to_string()),
from_2025: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2025".to_string()),
from_2026: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2026".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Investing_Class_Return {
pub from_2015: BpsPercentRatioPattern,
pub from_2016: BpsPercentRatioPattern,
pub from_2017: BpsPercentRatioPattern,
pub from_2018: BpsPercentRatioPattern,
pub from_2019: BpsPercentRatioPattern,
pub from_2020: BpsPercentRatioPattern,
pub from_2021: BpsPercentRatioPattern,
pub from_2022: BpsPercentRatioPattern,
pub from_2023: BpsPercentRatioPattern,
pub from_2024: BpsPercentRatioPattern,
pub from_2025: BpsPercentRatioPattern,
pub from_2026: BpsPercentRatioPattern,
}
impl SeriesTree_Investing_Class_Return {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
from_2015: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2015".to_string()),
from_2016: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2016".to_string()),
from_2017: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2017".to_string()),
from_2018: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2018".to_string()),
from_2019: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2019".to_string()),
from_2020: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2020".to_string()),
from_2021: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2021".to_string()),
from_2022: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2022".to_string()),
from_2023: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2023".to_string()),
from_2024: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2024".to_string()),
from_2025: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2025".to_string()),
from_2026: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2026".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market {
pub ath: SeriesTree_Market_Ath,
@@ -5183,7 +5432,6 @@ pub struct SeriesTree_Market {
pub volatility: _1m1w1y24hPattern<StoredF32>,
pub range: SeriesTree_Market_Range,
pub moving_average: SeriesTree_Market_MovingAverage,
pub dca: SeriesTree_Market_Dca,
pub technical: SeriesTree_Market_Technical,
}
@@ -5196,7 +5444,6 @@ impl SeriesTree_Market {
volatility: _1m1w1y24hPattern::new(client.clone(), "price_volatility".to_string()),
range: SeriesTree_Market_Range::new(client.clone(), format!("{base_path}_range")),
moving_average: SeriesTree_Market_MovingAverage::new(client.clone(), format!("{base_path}_moving_average")),
dca: SeriesTree_Market_Dca::new(client.clone(), format!("{base_path}_dca")),
technical: SeriesTree_Market_Technical::new(client.clone(), format!("{base_path}_technical")),
}
}
@@ -5565,203 +5812,6 @@ impl SeriesTree_Market_MovingAverage_Ema {
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca {
pub sats_per_day: SeriesPattern18<Sats>,
pub period: SeriesTree_Market_Dca_Period,
pub class: SeriesTree_Market_Dca_Class,
}
impl SeriesTree_Market_Dca {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
sats_per_day: SeriesPattern18::new(client.clone(), "dca_sats_per_day".to_string()),
period: SeriesTree_Market_Dca_Period::new(client.clone(), format!("{base_path}_period")),
class: SeriesTree_Market_Dca_Class::new(client.clone(), format!("{base_path}_class")),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca_Period {
pub stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3,
pub cost_basis: SeriesTree_Market_Dca_Period_CostBasis,
pub return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2,
pub cagr: _10y2y3y4y5y6y8yPattern,
pub lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3,
pub lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2,
}
impl SeriesTree_Market_Dca_Period {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3::new(client.clone(), "dca_stack".to_string()),
cost_basis: SeriesTree_Market_Dca_Period_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")),
return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "dca_return".to_string()),
cagr: _10y2y3y4y5y6y8yPattern::new(client.clone(), "dca_cagr".to_string()),
lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3::new(client.clone(), "lump_sum_stack".to_string()),
lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2::new(client.clone(), "lump_sum_return".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca_Period_CostBasis {
pub _1w: CentsSatsUsdPattern,
pub _1m: CentsSatsUsdPattern,
pub _3m: CentsSatsUsdPattern,
pub _6m: CentsSatsUsdPattern,
pub _1y: CentsSatsUsdPattern,
pub _2y: CentsSatsUsdPattern,
pub _3y: CentsSatsUsdPattern,
pub _4y: CentsSatsUsdPattern,
pub _5y: CentsSatsUsdPattern,
pub _6y: CentsSatsUsdPattern,
pub _8y: CentsSatsUsdPattern,
pub _10y: CentsSatsUsdPattern,
}
impl SeriesTree_Market_Dca_Period_CostBasis {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
_1w: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_1w".to_string()),
_1m: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_1m".to_string()),
_3m: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_3m".to_string()),
_6m: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_6m".to_string()),
_1y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_1y".to_string()),
_2y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_2y".to_string()),
_3y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_3y".to_string()),
_4y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_4y".to_string()),
_5y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_5y".to_string()),
_6y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_6y".to_string()),
_8y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_8y".to_string()),
_10y: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_10y".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca_Class {
pub stack: SeriesTree_Market_Dca_Class_Stack,
pub cost_basis: SeriesTree_Market_Dca_Class_CostBasis,
pub return_: SeriesTree_Market_Dca_Class_Return,
}
impl SeriesTree_Market_Dca_Class {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
stack: SeriesTree_Market_Dca_Class_Stack::new(client.clone(), format!("{base_path}_stack")),
cost_basis: SeriesTree_Market_Dca_Class_CostBasis::new(client.clone(), format!("{base_path}_cost_basis")),
return_: SeriesTree_Market_Dca_Class_Return::new(client.clone(), format!("{base_path}_return")),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca_Class_Stack {
pub from_2015: BtcCentsSatsUsdPattern3,
pub from_2016: BtcCentsSatsUsdPattern3,
pub from_2017: BtcCentsSatsUsdPattern3,
pub from_2018: BtcCentsSatsUsdPattern3,
pub from_2019: BtcCentsSatsUsdPattern3,
pub from_2020: BtcCentsSatsUsdPattern3,
pub from_2021: BtcCentsSatsUsdPattern3,
pub from_2022: BtcCentsSatsUsdPattern3,
pub from_2023: BtcCentsSatsUsdPattern3,
pub from_2024: BtcCentsSatsUsdPattern3,
pub from_2025: BtcCentsSatsUsdPattern3,
pub from_2026: BtcCentsSatsUsdPattern3,
}
impl SeriesTree_Market_Dca_Class_Stack {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
from_2015: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2015".to_string()),
from_2016: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2016".to_string()),
from_2017: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2017".to_string()),
from_2018: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2018".to_string()),
from_2019: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2019".to_string()),
from_2020: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2020".to_string()),
from_2021: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2021".to_string()),
from_2022: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2022".to_string()),
from_2023: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2023".to_string()),
from_2024: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2024".to_string()),
from_2025: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2025".to_string()),
from_2026: BtcCentsSatsUsdPattern3::new(client.clone(), "dca_stack_from_2026".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca_Class_CostBasis {
pub from_2015: CentsSatsUsdPattern,
pub from_2016: CentsSatsUsdPattern,
pub from_2017: CentsSatsUsdPattern,
pub from_2018: CentsSatsUsdPattern,
pub from_2019: CentsSatsUsdPattern,
pub from_2020: CentsSatsUsdPattern,
pub from_2021: CentsSatsUsdPattern,
pub from_2022: CentsSatsUsdPattern,
pub from_2023: CentsSatsUsdPattern,
pub from_2024: CentsSatsUsdPattern,
pub from_2025: CentsSatsUsdPattern,
pub from_2026: CentsSatsUsdPattern,
}
impl SeriesTree_Market_Dca_Class_CostBasis {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
from_2015: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2015".to_string()),
from_2016: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2016".to_string()),
from_2017: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2017".to_string()),
from_2018: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2018".to_string()),
from_2019: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2019".to_string()),
from_2020: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2020".to_string()),
from_2021: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2021".to_string()),
from_2022: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2022".to_string()),
from_2023: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2023".to_string()),
from_2024: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2024".to_string()),
from_2025: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2025".to_string()),
from_2026: CentsSatsUsdPattern::new(client.clone(), "dca_cost_basis_from_2026".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Dca_Class_Return {
pub from_2015: BpsPercentRatioPattern,
pub from_2016: BpsPercentRatioPattern,
pub from_2017: BpsPercentRatioPattern,
pub from_2018: BpsPercentRatioPattern,
pub from_2019: BpsPercentRatioPattern,
pub from_2020: BpsPercentRatioPattern,
pub from_2021: BpsPercentRatioPattern,
pub from_2022: BpsPercentRatioPattern,
pub from_2023: BpsPercentRatioPattern,
pub from_2024: BpsPercentRatioPattern,
pub from_2025: BpsPercentRatioPattern,
pub from_2026: BpsPercentRatioPattern,
}
impl SeriesTree_Market_Dca_Class_Return {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
from_2015: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2015".to_string()),
from_2016: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2016".to_string()),
from_2017: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2017".to_string()),
from_2018: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2018".to_string()),
from_2019: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2019".to_string()),
from_2020: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2020".to_string()),
from_2021: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2021".to_string()),
from_2022: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2022".to_string()),
from_2023: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2023".to_string()),
from_2024: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2024".to_string()),
from_2025: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2025".to_string()),
from_2026: BpsPercentRatioPattern::new(client.clone(), "dca_return_from_2026".to_string()),
}
}
}
/// Series tree node.
pub struct SeriesTree_Market_Technical {
pub rsi: SeriesTree_Market_Technical_Rsi,
@@ -6527,7 +6577,7 @@ pub struct SeriesTree_Cohorts_Utxo_All_Realized_Price {
pub sats: SeriesPattern1<SatsFract>,
pub bps: SeriesPattern1<BasisPoints32>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
pub std_dev: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev,
}
@@ -6540,7 +6590,7 @@ impl SeriesTree_Cohorts_Utxo_All_Realized_Price {
sats: SeriesPattern1::new(client.clone(), "realized_price_sats".to_string()),
bps: SeriesPattern1::new(client.clone(), "realized_price_ratio_bps".to_string()),
ratio: SeriesPattern1::new(client.clone(), "realized_price_ratio".to_string()),
percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "realized_price".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "realized_price_ratio_sma".to_string()),
std_dev: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")),
}
@@ -6957,7 +7007,7 @@ pub struct SeriesTree_Cohorts_Utxo_Sth_Realized_Price {
pub sats: SeriesPattern1<SatsFract>,
pub bps: SeriesPattern1<BasisPoints32>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
pub std_dev: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev,
}
@@ -6970,7 +7020,7 @@ impl SeriesTree_Cohorts_Utxo_Sth_Realized_Price {
sats: SeriesPattern1::new(client.clone(), "sth_realized_price_sats".to_string()),
bps: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio_bps".to_string()),
ratio: SeriesPattern1::new(client.clone(), "sth_realized_price_ratio".to_string()),
percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "sth_realized_price".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "sth_realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "sth_realized_price_ratio_sma".to_string()),
std_dev: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")),
}
@@ -7225,7 +7275,7 @@ pub struct SeriesTree_Cohorts_Utxo_Lth_Realized_Price {
pub sats: SeriesPattern1<SatsFract>,
pub bps: SeriesPattern1<BasisPoints32>,
pub ratio: SeriesPattern1<StoredF32>,
pub percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern,
pub sma: _1m1w1y2y4yAllPattern,
pub std_dev: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev,
}
@@ -7238,7 +7288,7 @@ impl SeriesTree_Cohorts_Utxo_Lth_Realized_Price {
sats: SeriesPattern1::new(client.clone(), "lth_realized_price_sats".to_string()),
bps: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio_bps".to_string()),
ratio: SeriesPattern1::new(client.clone(), "lth_realized_price_ratio".to_string()),
percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "lth_realized_price".to_string()),
percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern::new(client.clone(), "lth_realized_price".to_string()),
sma: _1m1w1y2y4yAllPattern::new(client.clone(), "lth_realized_price_ratio_sma".to_string()),
std_dev: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev::new(client.clone(), format!("{base_path}_std_dev")),
}
@@ -8145,7 +8195,7 @@ pub struct BrkClient {
impl BrkClient {
/// Client version.
pub const VERSION: &'static str = "v0.1.9";
pub const VERSION: &'static str = "v0.2.2";
/// Create a new client with the given base URL.
pub fn new(base_url: impl Into<String>) -> Self {
+9 -8
View File
@@ -17,12 +17,10 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
// Sequential: time → lookback (dependency chain)
self.time
.timestamp
.compute(indexer, indexes, starting_indexes, exit)?;
self.lookback
.compute(&self.time, starting_indexes, exit)?;
self.db.sync_bg_tasks()?;
// lookback depends on indexes.timestamp.monotonic
self.lookback.compute(indexes, starting_indexes, exit)?;
// Parallel: remaining sub-modules are independent of each other.
// size depends on lookback (already computed above).
@@ -52,8 +50,11 @@ impl Vecs {
Ok(())
})?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+1 -3
View File
@@ -10,7 +10,7 @@ use crate::{
};
use super::{
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, LookbackVecs, SizeVecs, TimeVecs, Vecs,
CountVecs, DifficultyVecs, HalvingVecs, IntervalVecs, LookbackVecs, SizeVecs, Vecs,
WeightVecs,
};
@@ -30,7 +30,6 @@ impl Vecs {
let interval = IntervalVecs::forced_import(&db, version, indexes, cached_starts)?;
let size = SizeVecs::forced_import(&db, version, indexes, cached_starts)?;
let weight = WeightVecs::forced_import(&db, version, indexes, cached_starts, &size)?;
let time = TimeVecs::forced_import(&db, version, indexes)?;
let difficulty = DifficultyVecs::forced_import(&db, version, indexer, indexes)?;
let halving = HalvingVecs::forced_import(&db, version, indexes)?;
@@ -41,7 +40,6 @@ impl Vecs {
interval,
size,
weight,
time,
difficulty,
halving,
};
+55 -54
View File
@@ -3,9 +3,10 @@ use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Timestamp, Version};
use vecdb::{AnyVec, CachedVec, Cursor, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex};
use crate::internal::{CachedWindowStarts, Windows, WindowStarts};
use super::time;
use crate::{
indexes,
internal::{CachedWindowStarts, Windows, WindowStarts},
};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
@@ -178,71 +179,71 @@ impl Vecs {
pub(crate) fn compute(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_rolling_start_hours(time, starting_indexes, exit, 1, |s| {
self.compute_rolling_start_hours(indexes, starting_indexes, exit, 1, |s| {
&mut s._1h
})?;
self.compute_rolling_start(time, starting_indexes, exit, 1, |s| &mut s._24h)?;
self.compute_rolling_start(time, starting_indexes, exit, 3, |s| &mut s._3d)?;
self.compute_rolling_start(time, starting_indexes, exit, 7, |s| &mut s._1w)?;
self.compute_rolling_start(time, starting_indexes, exit, 8, |s| &mut s._8d)?;
self.compute_rolling_start(time, starting_indexes, exit, 9, |s| &mut s._9d)?;
self.compute_rolling_start(time, starting_indexes, exit, 12, |s| &mut s._12d)?;
self.compute_rolling_start(time, starting_indexes, exit, 13, |s| &mut s._13d)?;
self.compute_rolling_start(time, starting_indexes, exit, 14, |s| &mut s._2w)?;
self.compute_rolling_start(time, starting_indexes, exit, 21, |s| &mut s._21d)?;
self.compute_rolling_start(time, starting_indexes, exit, 26, |s| &mut s._26d)?;
self.compute_rolling_start(time, starting_indexes, exit, 30, |s| &mut s._1m)?;
self.compute_rolling_start(time, starting_indexes, exit, 34, |s| &mut s._34d)?;
self.compute_rolling_start(time, starting_indexes, exit, 55, |s| &mut s._55d)?;
self.compute_rolling_start(time, starting_indexes, exit, 60, |s| &mut s._2m)?;
self.compute_rolling_start(time, starting_indexes, exit, 63, |s| &mut s._9w)?;
self.compute_rolling_start(time, starting_indexes, exit, 84, |s| &mut s._12w)?;
self.compute_rolling_start(time, starting_indexes, exit, 89, |s| &mut s._89d)?;
self.compute_rolling_start(time, starting_indexes, exit, 90, |s| &mut s._3m)?;
self.compute_rolling_start(time, starting_indexes, exit, 98, |s| &mut s._14w)?;
self.compute_rolling_start(time, starting_indexes, exit, 111, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 1, |s| &mut s._24h)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 3, |s| &mut s._3d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 7, |s| &mut s._1w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 8, |s| &mut s._8d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 9, |s| &mut s._9d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 12, |s| &mut s._12d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 13, |s| &mut s._13d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 14, |s| &mut s._2w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 21, |s| &mut s._21d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 26, |s| &mut s._26d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 30, |s| &mut s._1m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 34, |s| &mut s._34d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 55, |s| &mut s._55d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 60, |s| &mut s._2m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 63, |s| &mut s._9w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 84, |s| &mut s._12w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 89, |s| &mut s._89d)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 90, |s| &mut s._3m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 98, |s| &mut s._14w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 111, |s| {
&mut s._111d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 144, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 144, |s| {
&mut s._144d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 180, |s| &mut s._6m)?;
self.compute_rolling_start(time, starting_indexes, exit, 182, |s| &mut s._26w)?;
self.compute_rolling_start(time, starting_indexes, exit, 200, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 180, |s| &mut s._6m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 182, |s| &mut s._26w)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 200, |s| {
&mut s._200d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 270, |s| &mut s._9m)?;
self.compute_rolling_start(time, starting_indexes, exit, 350, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 270, |s| &mut s._9m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 350, |s| {
&mut s._350d
})?;
self.compute_rolling_start(time, starting_indexes, exit, 360, |s| &mut s._12m)?;
self.compute_rolling_start(time, starting_indexes, exit, 365, |s| &mut s._1y)?;
self.compute_rolling_start(time, starting_indexes, exit, 420, |s| &mut s._14m)?;
self.compute_rolling_start(time, starting_indexes, exit, 730, |s| &mut s._2y)?;
self.compute_rolling_start(time, starting_indexes, exit, 780, |s| &mut s._26m)?;
self.compute_rolling_start(time, starting_indexes, exit, 1095, |s| &mut s._3y)?;
self.compute_rolling_start(time, starting_indexes, exit, 1400, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 360, |s| &mut s._12m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 365, |s| &mut s._1y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 420, |s| &mut s._14m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 730, |s| &mut s._2y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 780, |s| &mut s._26m)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1095, |s| &mut s._3y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1400, |s| {
&mut s._200w
})?;
self.compute_rolling_start(time, starting_indexes, exit, 1460, |s| &mut s._4y)?;
self.compute_rolling_start(time, starting_indexes, exit, 1825, |s| &mut s._5y)?;
self.compute_rolling_start(time, starting_indexes, exit, 2190, |s| &mut s._6y)?;
self.compute_rolling_start(time, starting_indexes, exit, 2920, |s| &mut s._8y)?;
self.compute_rolling_start(time, starting_indexes, exit, 3285, |s| &mut s._9y)?;
self.compute_rolling_start(time, starting_indexes, exit, 3650, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 1460, |s| &mut s._4y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 1825, |s| &mut s._5y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 2190, |s| &mut s._6y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 2920, |s| &mut s._8y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 3285, |s| &mut s._9y)?;
self.compute_rolling_start(indexes, starting_indexes, exit, 3650, |s| {
&mut s._10y
})?;
self.compute_rolling_start(time, starting_indexes, exit, 4380, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 4380, |s| {
&mut s._12y
})?;
self.compute_rolling_start(time, starting_indexes, exit, 5110, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 5110, |s| {
&mut s._14y
})?;
self.compute_rolling_start(time, starting_indexes, exit, 9490, |s| {
self.compute_rolling_start(indexes, starting_indexes, exit, 9490, |s| {
&mut s._26y
})?;
@@ -251,7 +252,7 @@ impl Vecs {
fn compute_rolling_start<F>(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
days: usize,
@@ -260,14 +261,14 @@ impl Vecs {
where
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
{
self.compute_rolling_start_inner(time, starting_indexes, exit, get_field, |t, prev_ts| {
self.compute_rolling_start_inner(indexes, starting_indexes, exit, get_field, |t, prev_ts| {
t.difference_in_days_between(prev_ts) >= days
})
}
fn compute_rolling_start_hours<F>(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
hours: usize,
@@ -276,14 +277,14 @@ impl Vecs {
where
F: FnOnce(&mut Self) -> &mut EagerVec<PcoVec<Height, Height>>,
{
self.compute_rolling_start_inner(time, starting_indexes, exit, get_field, |t, prev_ts| {
self.compute_rolling_start_inner(indexes, starting_indexes, exit, get_field, |t, prev_ts| {
t.difference_in_hours_between(prev_ts) >= hours
})
}
fn compute_rolling_start_inner<F, D>(
&mut self,
time: &time::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
get_field: F,
@@ -300,12 +301,12 @@ impl Vecs {
} else {
Height::ZERO
};
let mut cursor = Cursor::new(&time.timestamp_monotonic);
let mut cursor = Cursor::new(&indexes.timestamp.monotonic);
cursor.advance(prev.to_usize());
let mut prev_ts = cursor.next().unwrap();
Ok(field.compute_transform(
starting_indexes.height,
&time.timestamp_monotonic,
&indexes.timestamp.monotonic,
|(h, t, ..)| {
while expired(t, prev_ts) {
prev.increment();
+1 -4
View File
@@ -4,7 +4,6 @@ pub mod halving;
pub mod interval;
pub mod lookback;
pub mod size;
pub mod time;
pub mod weight;
mod compute;
@@ -19,7 +18,6 @@ pub use halving::Vecs as HalvingVecs;
pub use interval::Vecs as IntervalVecs;
pub use lookback::Vecs as LookbackVecs;
pub use size::Vecs as SizeVecs;
pub use time::Vecs as TimeVecs;
pub use weight::Vecs as WeightVecs;
pub const DB_NAME: &str = "blocks";
@@ -37,7 +35,7 @@ pub(crate) const ONE_TERA_HASH: f64 = 1_000_000_000_000.0;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(skip)]
pub(crate) db: Database,
pub db: Database,
pub count: CountVecs<M>,
pub lookback: LookbackVecs<M>,
@@ -46,7 +44,6 @@ pub struct Vecs<M: StorageMode = Rw> {
pub size: SizeVecs<M>,
#[traversable(flatten)]
pub weight: WeightVecs<M>,
pub time: TimeVecs<M>,
pub difficulty: DifficultyVecs<M>,
pub halving: HalvingVecs<M>,
}
@@ -1,34 +0,0 @@
use brk_error::Result;
use brk_indexer::Indexer;
use vecdb::{Exit, ReadableVec};
use super::Vecs;
impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
starting_height: brk_types::Height,
exit: &Exit,
) -> Result<()> {
let mut prev_timestamp_monotonic = None;
self.timestamp_monotonic.compute_transform(
starting_height,
&indexer.vecs.blocks.timestamp,
|(h, timestamp, this)| {
if prev_timestamp_monotonic.is_none()
&& let Some(prev_h) = h.decremented()
{
prev_timestamp_monotonic.replace(this.collect_one(prev_h).unwrap());
}
let timestamp_monotonic =
prev_timestamp_monotonic.map_or(timestamp, |prev_d| prev_d.max(timestamp));
prev_timestamp_monotonic.replace(timestamp_monotonic);
(h, timestamp_monotonic)
},
exit,
)?;
Ok(())
}
}
@@ -1,66 +0,0 @@
use brk_error::Result;
use brk_types::{Date, Height, Version};
use vecdb::{Database, EagerVec, ImportableVec, LazyVecFrom1, ReadableCloneableVec};
use super::{TimestampIndexes, Vecs};
use crate::indexes;
impl Vecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let timestamp_monotonic = EagerVec::forced_import(db, "timestamp_monotonic", version)?;
Ok(Self {
date: LazyVecFrom1::init(
"date",
version,
timestamp_monotonic.read_only_boxed_clone(),
|_height: Height, timestamp| Date::from(timestamp),
),
timestamp_monotonic,
timestamp: TimestampIndexes::forced_import(db, version, indexes)?,
})
}
}
impl TimestampIndexes {
fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
macro_rules! period {
($field:ident) => {
LazyVecFrom1::init(
"timestamp",
version,
indexes.$field.first_height.read_only_boxed_clone(),
|idx, _: Height| idx.to_timestamp(),
)
};
}
macro_rules! epoch {
($field:ident) => {
ImportableVec::forced_import(db, "timestamp", version)?
};
}
Ok(Self(crate::internal::PerResolution {
minute10: period!(minute10),
minute30: period!(minute30),
hour1: period!(hour1),
hour4: period!(hour4),
hour12: period!(hour12),
day1: period!(day1),
day3: period!(day3),
week1: period!(week1),
month1: period!(month1),
month3: period!(month3),
month6: period!(month6),
year1: period!(year1),
year10: period!(year10),
halving: epoch!(halving),
epoch: epoch!(difficulty),
}))
}
}
@@ -1,5 +0,0 @@
mod compute;
mod import;
mod vecs;
pub use vecs::{TimestampIndexes, Vecs};
@@ -1,80 +0,0 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Date, Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Indexes,
Minute10, Minute30, Month1, Month3, Month6, Timestamp, Week1, Year1, Year10,
};
use derive_more::{Deref, DerefMut};
use vecdb::{EagerVec, Exit, LazyVecFrom1, PcoVec, ReadableVec, Rw, StorageMode};
use crate::{indexes, internal::PerResolution};
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
pub date: LazyVecFrom1<Height, Date, Height, Timestamp>,
pub timestamp_monotonic: M::Stored<EagerVec<PcoVec<Height, Timestamp>>>,
pub timestamp: TimestampIndexes<M>,
}
/// Per-period timestamp indexes.
///
/// Time-based periods (minute10year10) are lazy: `idx.to_timestamp()` is a pure
/// function of the index, so no storage or decompression is needed.
/// Epoch-based periods (halving, difficulty) are eager: their timestamps
/// come from block data via `compute_indirect_sequential`.
#[derive(Deref, DerefMut, Traversable)]
#[traversable(transparent)]
pub struct TimestampIndexes<M: StorageMode = Rw>(
#[allow(clippy::type_complexity)]
pub PerResolution<
LazyVecFrom1<Minute10, Timestamp, Minute10, Height>,
LazyVecFrom1<Minute30, Timestamp, Minute30, Height>,
LazyVecFrom1<Hour1, Timestamp, Hour1, Height>,
LazyVecFrom1<Hour4, Timestamp, Hour4, Height>,
LazyVecFrom1<Hour12, Timestamp, Hour12, Height>,
LazyVecFrom1<Day1, Timestamp, Day1, Height>,
LazyVecFrom1<Day3, Timestamp, Day3, Height>,
LazyVecFrom1<Week1, Timestamp, Week1, Height>,
LazyVecFrom1<Month1, Timestamp, Month1, Height>,
LazyVecFrom1<Month3, Timestamp, Month3, Height>,
LazyVecFrom1<Month6, Timestamp, Month6, Height>,
LazyVecFrom1<Year1, Timestamp, Year1, Height>,
LazyVecFrom1<Year10, Timestamp, Year10, Height>,
M::Stored<EagerVec<PcoVec<Halving, Timestamp>>>,
M::Stored<EagerVec<PcoVec<Epoch, Timestamp>>>,
>,
);
impl TimestampIndexes {
/// Compute epoch timestamps via indirect lookup from block timestamps.
/// Time-based periods are lazy (idx.to_timestamp()) and need no compute.
pub(crate) fn compute(
&mut self,
indexer: &brk_indexer::Indexer,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let prev_height = starting_indexes.height.decremented().unwrap_or_default();
self.halving.compute_indirect_sequential(
indexes
.height
.halving
.collect_one(prev_height)
.unwrap_or_default(),
&indexes.halving.first_height,
&indexer.vecs.blocks.timestamp,
exit,
)?;
self.epoch.compute_indirect_sequential(
indexes
.height
.epoch
.collect_one(prev_height)
.unwrap_or_default(),
&indexes.epoch.first_height,
&indexer.vecs.blocks.timestamp,
exit,
)?;
Ok(())
}
}
+7 -2
View File
@@ -17,6 +17,8 @@ impl Vecs {
distribution: &distribution::Vecs,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// Activity computes first (liveliness, vaultedness, etc.)
self.activity
.compute(starting_indexes, distribution, exit)?;
@@ -80,8 +82,11 @@ impl Vecs {
r3?;
r4?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+11 -6
View File
@@ -227,6 +227,8 @@ impl Vecs {
starting_indexes: &mut Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// 1. Find minimum height we have data for across stateful vecs
let current_height = Height::from(self.supply_state.len());
let min_stateful = self.min_stateful_len();
@@ -300,7 +302,7 @@ impl Vecs {
.cents
.height
.len()
.min(blocks.time.timestamp_monotonic.len());
.min(indexes.timestamp.monotonic.len());
let cache_current_len = self.caches.prices.len();
if cache_target_len < cache_current_len {
self.caches.prices.truncate(cache_target_len);
@@ -312,9 +314,9 @@ impl Vecs {
.cents
.height
.collect_range_at(cache_current_len, cache_target_len);
let new_timestamps = blocks
.time
.timestamp_monotonic
let new_timestamps = indexes
.timestamp
.monotonic
.collect_range_at(cache_current_len, cache_target_len);
self.caches.prices.extend(new_prices);
self.caches.timestamps.extend(new_timestamps);
@@ -499,8 +501,11 @@ impl Vecs {
self.addr_cohorts
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
+43 -42
View File
@@ -13,6 +13,7 @@ mod minute30;
mod month1;
mod month3;
mod month6;
pub mod timestamp;
mod tx_index;
mod txin_index;
mod txout_index;
@@ -31,13 +32,11 @@ use brk_types::{
};
use vecdb::{CachedVec, Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
blocks,
internal::db_utils::{finalize_db, open_db},
};
use crate::internal::db_utils::{finalize_db, open_db};
pub use cached_mappings::CachedMappings;
pub use addr::Vecs as AddrVecs;
pub use timestamp::Timestamps;
pub use cached_mappings::CachedMappings;
pub use day1::Vecs as Day1Vecs;
pub use day3::Vecs as Day3Vecs;
pub use epoch::Vecs as EpochVecs;
@@ -85,6 +84,7 @@ pub struct Vecs<M: StorageMode = Rw> {
pub tx_index: TxIndexVecs<M>,
pub txin_index: TxInIndexVecs,
pub txout_index: TxOutIndexVecs,
pub timestamp: Timestamps<M>,
}
impl Vecs {
@@ -136,6 +136,11 @@ impl Vecs {
epoch_identity: CachedVec::new(&epoch.identity),
};
let timestamp = Timestamps::forced_import_from_locals(
&db, version, &minute10, &minute30, &hour1, &hour4, &hour12, &day1, &day3, &week1,
&month1, &month3, &month6, &year1, &year10,
)?;
let this = Self {
cached_mappings,
addr,
@@ -158,6 +163,7 @@ impl Vecs {
tx_index,
txin_index,
txout_index,
timestamp,
db,
};
@@ -168,36 +174,24 @@ impl Vecs {
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
blocks: &mut blocks::Vecs,
starting_indexes: Indexes,
exit: &Exit,
) -> Result<Indexes> {
blocks
.time
.compute(indexer, starting_indexes.height, exit)?;
let indexes = self.compute_(indexer, &blocks.time, starting_indexes, exit)?;
let _lock = exit.lock();
self.db.compact()?;
Ok(indexes)
}
self.db.sync_bg_tasks()?;
// timestamp_monotonic must be computed first — other mappings read it
self.timestamp
.compute_monotonic(indexer, starting_indexes.height, exit)?;
fn compute_(
&mut self,
indexer: &Indexer,
blocks_time: &blocks::time::Vecs,
starting_indexes: Indexes,
exit: &Exit,
) -> Result<Indexes> {
self.compute_tx_indexes(indexer, &starting_indexes, exit)?;
self.compute_height_indexes(indexer, &starting_indexes, exit)?;
let prev_height = starting_indexes.height.decremented().unwrap_or_default();
self.compute_timestamp_mappings(blocks_time, &starting_indexes, exit)?;
self.compute_timestamp_mappings(&starting_indexes, exit)?;
let starting_day1 = self.compute_calendar_mappings(
indexer,
blocks_time,
&starting_indexes,
prev_height,
exit,
@@ -205,13 +199,26 @@ impl Vecs {
self.compute_period_vecs(
indexer,
blocks_time,
&starting_indexes,
prev_height,
starting_day1,
exit,
)?;
self.timestamp.compute_per_resolution(
indexer,
&self.height,
&self.halving,
&self.epoch,
&starting_indexes,
exit,
)?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(starting_indexes)
}
@@ -266,7 +273,6 @@ impl Vecs {
fn compute_timestamp_mappings(
&mut self,
blocks_time: &blocks::time::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
@@ -274,7 +280,7 @@ impl Vecs {
($field:ident, $period:ty) => {
self.height.$field.compute_transform(
starting_indexes.height,
&blocks_time.timestamp_monotonic,
&self.timestamp.monotonic,
|(h, ts, _)| (h, <$period>::from_timestamp(ts)),
exit,
)?;
@@ -294,7 +300,6 @@ impl Vecs {
fn compute_calendar_mappings(
&mut self,
indexer: &Indexer,
blocks_time: &blocks::time::Vecs,
starting_indexes: &Indexes,
prev_height: Height,
exit: &Exit,
@@ -307,8 +312,8 @@ impl Vecs {
self.height.day1.compute_transform(
starting_indexes.height,
&blocks_time.date,
|(h, d, ..)| (h, Day1::try_from(d).unwrap()),
&self.timestamp.monotonic,
|(h, ts, ..)| (h, Day1::try_from(Date::from(ts)).unwrap()),
exit,
)?;
@@ -318,7 +323,7 @@ impl Vecs {
starting_day1
};
self.compute_epoch(indexer, blocks_time, starting_indexes, prev_height, exit)?;
self.compute_epoch(indexer, starting_indexes, prev_height, exit)?;
self.height.week1.compute_transform(
starting_indexes.height,
@@ -363,7 +368,6 @@ impl Vecs {
fn compute_epoch(
&mut self,
indexer: &Indexer,
blocks_time: &blocks::time::Vecs,
starting_indexes: &Indexes,
prev_height: Height,
exit: &Exit,
@@ -389,14 +393,12 @@ impl Vecs {
&self.epoch.first_height,
exit,
)?;
self.epoch
.height_count
.compute_count_from_indexes(
starting_difficulty,
&self.epoch.first_height,
&blocks_time.date,
exit,
)?;
self.epoch.height_count.compute_count_from_indexes(
starting_difficulty,
&self.epoch.first_height,
&self.timestamp.monotonic,
exit,
)?;
let starting_halving = self
.height
@@ -426,7 +428,6 @@ impl Vecs {
fn compute_period_vecs(
&mut self,
indexer: &Indexer,
blocks_time: &blocks::time::Vecs,
starting_indexes: &Indexes,
prev_height: Height,
starting_day1: Day1,
@@ -478,7 +479,7 @@ impl Vecs {
exit,
)?;
let date = &blocks_time.date;
let ts = &self.timestamp.monotonic;
macro_rules! dated_period {
($period:ident) => {{
@@ -500,7 +501,7 @@ impl Vecs {
self.$period.date.compute_transform(
start,
&self.$period.first_height,
|(idx, first_h, _)| (idx, date.collect_one(first_h).unwrap()),
|(idx, first_h, _)| (idx, Date::from(ts.collect_one(first_h).unwrap())),
exit,
)?;
}};
@@ -0,0 +1,150 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{
Day1, Day3, Epoch, Halving, Height, Hour1, Hour4, Hour12, Indexes, Minute10, Minute30, Month1,
Month3, Month6, Timestamp, Week1, Year1, Year10,
};
use derive_more::{Deref, DerefMut};
use vecdb::{
Database, EagerVec, Exit, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec,
ReadableVec, Rw, StorageMode, Version,
};
use crate::internal::PerResolution;
/// Timestamps: monotonic height→timestamp + per-period timestamp lookups.
///
/// Time-based periods (minute10year10) are lazy: `idx.to_timestamp()` is a pure
/// function of the index, so no storage or decompression is needed.
/// Epoch-based periods (halving, difficulty) are eager: their timestamps
/// come from block data via `compute_indirect_sequential`.
#[derive(Deref, DerefMut, Traversable)]
pub struct Timestamps<M: StorageMode = Rw> {
pub monotonic: M::Stored<EagerVec<PcoVec<Height, Timestamp>>>,
#[deref]
#[deref_mut]
#[traversable(flatten)]
#[allow(clippy::type_complexity)]
pub resolutions: PerResolution<
LazyVecFrom1<Minute10, Timestamp, Minute10, Height>,
LazyVecFrom1<Minute30, Timestamp, Minute30, Height>,
LazyVecFrom1<Hour1, Timestamp, Hour1, Height>,
LazyVecFrom1<Hour4, Timestamp, Hour4, Height>,
LazyVecFrom1<Hour12, Timestamp, Hour12, Height>,
LazyVecFrom1<Day1, Timestamp, Day1, Height>,
LazyVecFrom1<Day3, Timestamp, Day3, Height>,
LazyVecFrom1<Week1, Timestamp, Week1, Height>,
LazyVecFrom1<Month1, Timestamp, Month1, Height>,
LazyVecFrom1<Month3, Timestamp, Month3, Height>,
LazyVecFrom1<Month6, Timestamp, Month6, Height>,
LazyVecFrom1<Year1, Timestamp, Year1, Height>,
LazyVecFrom1<Year10, Timestamp, Year10, Height>,
M::Stored<EagerVec<PcoVec<Halving, Timestamp>>>,
M::Stored<EagerVec<PcoVec<Epoch, Timestamp>>>,
>,
}
impl Timestamps {
#[allow(clippy::too_many_arguments)]
pub(crate) fn forced_import_from_locals(
db: &Database,
version: Version,
minute10: &super::Minute10Vecs,
minute30: &super::Minute30Vecs,
hour1: &super::Hour1Vecs,
hour4: &super::Hour4Vecs,
hour12: &super::Hour12Vecs,
day1: &super::Day1Vecs,
day3: &super::Day3Vecs,
week1: &super::Week1Vecs,
month1: &super::Month1Vecs,
month3: &super::Month3Vecs,
month6: &super::Month6Vecs,
year1: &super::Year1Vecs,
year10: &super::Year10Vecs,
) -> Result<Self> {
let monotonic = EagerVec::forced_import(db, "timestamp_monotonic", version)?;
macro_rules! period {
($field:ident) => {
LazyVecFrom1::init(
"timestamp",
version,
$field.first_height.read_only_boxed_clone(),
|idx, _: Height| idx.to_timestamp(),
)
};
}
Ok(Self {
monotonic,
resolutions: PerResolution {
minute10: period!(minute10),
minute30: period!(minute30),
hour1: period!(hour1),
hour4: period!(hour4),
hour12: period!(hour12),
day1: period!(day1),
day3: period!(day3),
week1: period!(week1),
month1: period!(month1),
month3: period!(month3),
month6: period!(month6),
year1: period!(year1),
year10: period!(year10),
halving: ImportableVec::forced_import(db, "timestamp", version)?,
epoch: ImportableVec::forced_import(db, "timestamp", version)?,
},
})
}
pub(crate) fn compute_monotonic(
&mut self,
indexer: &brk_indexer::Indexer,
starting_height: Height,
exit: &Exit,
) -> Result<()> {
let mut prev = None;
self.monotonic.compute_transform(
starting_height,
&indexer.vecs.blocks.timestamp,
|(h, timestamp, this)| {
if prev.is_none()
&& let Some(prev_h) = h.decremented()
{
prev.replace(this.collect_one(prev_h).unwrap());
}
let monotonic = prev.map_or(timestamp, |p| p.max(timestamp));
prev.replace(monotonic);
(h, monotonic)
},
exit,
)?;
Ok(())
}
pub(crate) fn compute_per_resolution(
&mut self,
indexer: &brk_indexer::Indexer,
height: &super::HeightVecs,
halving_vecs: &super::HalvingVecs,
epoch_vecs: &super::EpochVecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let prev_height = starting_indexes.height.decremented().unwrap_or_default();
self.resolutions.halving.compute_indirect_sequential(
height.halving.collect_one(prev_height).unwrap_or_default(),
&halving_vecs.first_height,
&indexer.vecs.blocks.timestamp,
exit,
)?;
self.resolutions.epoch.compute_indirect_sequential(
height.epoch.collect_one(prev_height).unwrap_or_default(),
&epoch_vecs.first_height,
&indexer.vecs.blocks.timestamp,
exit,
)?;
Ok(())
}
}
@@ -16,6 +16,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// Puell Multiple: daily_subsidy_usd / sma_365d_subsidy_usd
self.puell_multiple
.bps
@@ -199,8 +201,11 @@ impl Vecs {
exit,
)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+4 -1
View File
@@ -3,7 +3,7 @@ use std::path::Path;
use brk_error::Result;
use brk_types::Version;
use super::Vecs;
use super::{Vecs, thermometer::Thermometer};
use crate::{
indexes,
internal::{PerBlock, PercentPerBlock, RatioPerBlock, db_utils::{finalize_db, open_db}},
@@ -38,6 +38,8 @@ impl Vecs {
let seller_exhaustion =
PerBlock::forced_import(&db, "seller_exhaustion", v, indexes)?;
let thermometer = Thermometer::forced_import(&db, v, indexes)?;
let this = Self {
db,
puell_multiple,
@@ -50,6 +52,7 @@ impl Vecs {
dormancy,
stock_to_flow,
seller_exhaustion,
thermometer,
};
finalize_db(&this.db, &this)?;
Ok(this)
@@ -1,6 +1,7 @@
mod compute;
mod gini;
mod import;
pub mod thermometer;
mod vecs;
pub use vecs::Vecs;
@@ -0,0 +1,254 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, Height, Indexes, StoredI8, Version};
use vecdb::{AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, WritableVec};
use crate::{
cointime,
distribution,
indexes,
internal::{PerBlock, Price, RatioPerBlockPercentiles},
prices,
};
#[derive(Traversable)]
pub struct Thermometer<M: StorageMode = Rw> {
pub pct0_5: Price<PerBlock<Cents, M>>,
pub pct1: Price<PerBlock<Cents, M>>,
pub pct2: Price<PerBlock<Cents, M>>,
pub pct5: Price<PerBlock<Cents, M>>,
pub pct95: Price<PerBlock<Cents, M>>,
pub pct98: Price<PerBlock<Cents, M>>,
pub pct99: Price<PerBlock<Cents, M>>,
pub pct99_5: Price<PerBlock<Cents, M>>,
pub zone: PerBlock<StoredI8, M>,
pub score: PerBlock<StoredI8, M>,
}
const VERSION: Version = Version::new(2);
impl Thermometer {
pub(crate) fn forced_import(
db: &Database,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let v = version + VERSION;
Ok(Self {
pct0_5: Price::forced_import(db, "thermometer_pct0_5", v, indexes)?,
pct1: Price::forced_import(db, "thermometer_pct01", v, indexes)?,
pct2: Price::forced_import(db, "thermometer_pct02", v, indexes)?,
pct5: Price::forced_import(db, "thermometer_pct05", v, indexes)?,
pct95: Price::forced_import(db, "thermometer_pct95", v, indexes)?,
pct98: Price::forced_import(db, "thermometer_pct98", v, indexes)?,
pct99: Price::forced_import(db, "thermometer_pct99", v, indexes)?,
pct99_5: Price::forced_import(db, "thermometer_pct99_5", v, indexes)?,
zone: PerBlock::forced_import(db, "thermometer_zone", v, indexes)?,
score: PerBlock::forced_import(db, "thermometer_score", v, indexes)?,
})
}
pub(crate) fn compute(
&mut self,
distribution: &distribution::Vecs,
cointime: &cointime::Vecs,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let realized = &distribution.utxo_cohorts.all.metrics.realized;
let ct = &cointime.prices;
let sth_realized = &distribution.utxo_cohorts.sth.metrics.realized;
let lth_realized = &distribution.utxo_cohorts.lth.metrics.realized;
let models: [&RatioPerBlockPercentiles; 10] = [
&realized.price_ratio_percentiles,
&realized.investor.price.percentiles,
&sth_realized.price_ratio_percentiles,
&sth_realized.investor.price.percentiles,
&lth_realized.price_ratio_percentiles,
&lth_realized.investor.price.percentiles,
&ct.vaulted.percentiles,
&ct.active.percentiles,
&ct.true_market_mean.percentiles,
&ct.cointime.percentiles,
];
macro_rules! sources {
($pct:ident) => {
models.each_ref().map(|m| &m.$pct.price.cents.height)
};
}
// Lower percentiles: max across all models (tightest lower bound)
self.pct0_5.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct0_5), exit)?;
self.pct1.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct1), exit)?;
self.pct2.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct2), exit)?;
self.pct5.cents.height.compute_max_of_others(starting_indexes.height, &sources!(pct5), exit)?;
// Upper percentiles: min across all models (tightest upper bound)
self.pct95.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct95), exit)?;
self.pct98.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct98), exit)?;
self.pct99.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct99), exit)?;
self.pct99_5.cents.height.compute_min_of_others(starting_indexes.height, &sources!(pct99_5), exit)?;
let spot = &prices.spot.cents.height;
// Zone: spot vs own envelope bands (-4 to +4)
self.compute_zone(spot, starting_indexes, exit)?;
// Temperature: per-model band crossings (-40 to +40)
self.compute_score(&models, spot, starting_indexes, exit)?;
Ok(())
}
fn compute_zone(
&mut self,
spot: &EagerVec<PcoVec<Height, Cents>>,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let bands: [&_; 8] = [
&self.pct0_5.cents.height,
&self.pct1.cents.height,
&self.pct2.cents.height,
&self.pct5.cents.height,
&self.pct95.cents.height,
&self.pct98.cents.height,
&self.pct99.cents.height,
&self.pct99_5.cents.height,
];
let dep_version: Version = bands.iter().map(|b| b.version()).sum::<Version>() + spot.version();
self.zone.height.validate_computed_version_or_reset(dep_version)?;
self.zone.height.truncate_if_needed(starting_indexes.height)?;
self.zone.height.repeat_until_complete(exit, |vec| {
let skip = vec.len();
let source_end = bands.iter().map(|b| b.len()).min().unwrap().min(spot.len());
let end = vec.batch_end(source_end);
if skip >= end {
return Ok(());
}
let spot_batch = spot.collect_range_at(skip, end);
let b: [Vec<Cents>; 8] = bands.each_ref().map(|v| v.collect_range_at(skip, end));
for j in 0..(end - skip) {
let price = spot_batch[j];
let mut score: i8 = 0;
if price < b[3][j] { score -= 1; }
if price < b[2][j] { score -= 1; }
if price < b[1][j] { score -= 1; }
if price < b[0][j] { score -= 1; }
if price > b[4][j] { score += 1; }
if price > b[5][j] { score += 1; }
if price > b[6][j] { score += 1; }
if price > b[7][j] { score += 1; }
vec.push(StoredI8::new(score));
}
Ok(())
})?;
Ok(())
}
fn compute_score(
&mut self,
models: &[&RatioPerBlockPercentiles; 10],
spot: &EagerVec<PcoVec<Height, Cents>>,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
let dep_version: Version = models
.iter()
.map(|p| {
p.pct0_5.price.cents.height.version()
+ p.pct1.price.cents.height.version()
+ p.pct2.price.cents.height.version()
+ p.pct5.price.cents.height.version()
+ p.pct95.price.cents.height.version()
+ p.pct98.price.cents.height.version()
+ p.pct99.price.cents.height.version()
+ p.pct99_5.price.cents.height.version()
})
.sum::<Version>()
+ spot.version();
self.score.height.validate_computed_version_or_reset(dep_version)?;
self.score.height.truncate_if_needed(starting_indexes.height)?;
self.score.height.repeat_until_complete(exit, |vec| {
let skip = vec.len();
let source_end = models
.iter()
.flat_map(|p| {
[
p.pct0_5.price.cents.height.len(),
p.pct1.price.cents.height.len(),
p.pct2.price.cents.height.len(),
p.pct5.price.cents.height.len(),
p.pct95.price.cents.height.len(),
p.pct98.price.cents.height.len(),
p.pct99.price.cents.height.len(),
p.pct99_5.price.cents.height.len(),
]
})
.min()
.unwrap()
.min(spot.len());
let end = vec.batch_end(source_end);
if skip >= end {
return Ok(());
}
let spot_batch = spot.collect_range_at(skip, end);
let bands: Vec<[Vec<Cents>; 8]> = models
.iter()
.map(|p| {
[
p.pct0_5.price.cents.height.collect_range_at(skip, end),
p.pct1.price.cents.height.collect_range_at(skip, end),
p.pct2.price.cents.height.collect_range_at(skip, end),
p.pct5.price.cents.height.collect_range_at(skip, end),
p.pct95.price.cents.height.collect_range_at(skip, end),
p.pct98.price.cents.height.collect_range_at(skip, end),
p.pct99.price.cents.height.collect_range_at(skip, end),
p.pct99_5.price.cents.height.collect_range_at(skip, end),
]
})
.collect();
for j in 0..(end - skip) {
let price = spot_batch[j];
let mut total: i8 = 0;
for model in &bands {
if price < model[3][j] { total -= 1; }
if price < model[2][j] { total -= 1; }
if price < model[1][j] { total -= 1; }
if price < model[0][j] { total -= 1; }
if price > model[4][j] { total += 1; }
if price > model[5][j] { total += 1; }
if price > model[6][j] { total += 1; }
if price > model[7][j] { total += 1; }
}
vec.push(StoredI8::new(total));
}
Ok(())
})?;
Ok(())
}
}
@@ -2,6 +2,7 @@ use brk_traversable::Traversable;
use brk_types::{BasisPoints16, BasisPoints32, StoredF32};
use vecdb::{Database, Rw, StorageMode};
use super::thermometer::Thermometer;
use crate::internal::{PerBlock, PercentPerBlock, RatioPerBlock};
#[derive(Traversable)]
@@ -24,4 +25,5 @@ pub struct Vecs<M: StorageMode = Rw> {
pub dormancy: DormancyVecs<M>,
pub stock_to_flow: PerBlock<StoredF32, M>,
pub seller_exhaustion: PerBlock<StoredF32, M>,
pub thermometer: Thermometer<M>,
}
+7 -2
View File
@@ -15,13 +15,18 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
self.spent
.compute(indexer, starting_indexes, exit)?;
self.count
.compute(indexer, indexes, blocks, starting_indexes, exit)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
@@ -71,20 +71,20 @@ impl ExpandingPercentiles {
self.tree.add(Self::to_bucket(value), &1);
}
/// Compute 6 percentiles in one call via kth. O(6 × log N) but with
/// shared tree traversal across all 6 targets for better cache locality.
/// Compute 8 percentiles in one call via kth. O(8 × log N) but with
/// shared tree traversal across all 8 targets for better cache locality.
/// Quantiles q must be sorted ascending in (0, 1). Output is in BPS.
pub fn quantiles(&self, qs: &[f64; 6], out: &mut [u32; 6]) {
pub fn quantiles(&self, qs: &[f64; 8], out: &mut [u32; 8]) {
if self.count == 0 {
out.iter_mut().for_each(|o| *o = 0);
return;
}
let mut targets = [0u32; 6];
let mut targets = [0u32; 8];
for (i, &q) in qs.iter().enumerate() {
let k = ((q * self.count as f64).ceil() as u32).clamp(1, self.count);
targets[i] = k - 1; // 0-indexed
}
let mut buckets = [0usize; 6];
let mut buckets = [0usize; 8];
self.tree.kth(&targets, &|n: &u32| *n, &mut buckets);
for (i, bucket) in buckets.iter().enumerate() {
out[i] = *bucket as u32 * BUCKET_BPS as u32;
@@ -97,8 +97,8 @@ mod tests {
use super::*;
fn quantile(ep: &ExpandingPercentiles, q: f64) -> u32 {
let mut out = [0u32; 6];
ep.quantiles(&[q, q, q, q, q, q], &mut out);
let mut out = [0u32; 8];
ep.quantiles(&[q, q, q, q, q, q, q, q], &mut out);
out[0]
}
@@ -22,18 +22,20 @@ pub struct RatioBand<M: StorageMode = Rw> {
#[derive(Traversable)]
pub struct RatioPerBlockPercentiles<M: StorageMode = Rw> {
pub pct99_5: RatioBand<M>,
pub pct99: RatioBand<M>,
pub pct98: RatioBand<M>,
pub pct95: RatioBand<M>,
pub pct5: RatioBand<M>,
pub pct2: RatioBand<M>,
pub pct1: RatioBand<M>,
pub pct0_5: RatioBand<M>,
#[traversable(skip)]
expanding_pct: ExpandingPercentiles,
}
const VERSION: Version = Version::new(5);
const VERSION: Version = Version::new(6);
/// First height included in ratio percentile computation (first halving).
/// Earlier blocks lack meaningful market data and pollute the distribution.
@@ -70,12 +72,14 @@ impl RatioPerBlockPercentiles {
}
Ok(Self {
pct99_5: import_band!("pct99_5"),
pct99: import_band!("pct99"),
pct98: import_band!("pct98"),
pct95: import_band!("pct95"),
pct5: import_band!("pct5"),
pct2: import_band!("pct2"),
pct1: import_band!("pct1"),
pct0_5: import_band!("pct0_5"),
expanding_pct: ExpandingPercentiles::default(),
})
}
@@ -114,16 +118,18 @@ impl RatioPerBlockPercentiles {
}
let new_ratios = ratio_source.collect_range_at(start, ratio_len);
let mut pct_vecs: [&mut EagerVec<PcoVec<Height, BasisPoints32>>; 6] = [
let mut pct_vecs: [&mut EagerVec<PcoVec<Height, BasisPoints32>>; 8] = [
&mut self.pct0_5.ratio.bps.height,
&mut self.pct1.ratio.bps.height,
&mut self.pct2.ratio.bps.height,
&mut self.pct5.ratio.bps.height,
&mut self.pct95.ratio.bps.height,
&mut self.pct98.ratio.bps.height,
&mut self.pct99.ratio.bps.height,
&mut self.pct99_5.ratio.bps.height,
];
const PCTS: [f64; 6] = [0.01, 0.02, 0.05, 0.95, 0.98, 0.99];
let mut out = [0u32; 6];
const PCTS: [f64; 8] = [0.005, 0.01, 0.02, 0.05, 0.95, 0.98, 0.99, 0.995];
let mut out = [0u32; 8];
for vec in pct_vecs.iter_mut() {
vec.truncate_if_needed_at(start)?;
@@ -160,12 +166,14 @@ impl RatioPerBlockPercentiles {
};
}
compute_band!(pct99_5);
compute_band!(pct99);
compute_band!(pct98);
compute_band!(pct95);
compute_band!(pct5);
compute_band!(pct2);
compute_band!(pct1);
compute_band!(pct0_5);
Ok(())
}
@@ -174,12 +182,14 @@ impl RatioPerBlockPercentiles {
&mut self,
) -> impl Iterator<Item = &mut EagerVec<PcoVec<Height, BasisPoints32>>> {
[
&mut self.pct0_5.ratio.bps.height,
&mut self.pct1.ratio.bps.height,
&mut self.pct2.ratio.bps.height,
&mut self.pct5.ratio.bps.height,
&mut self.pct95.ratio.bps.height,
&mut self.pct98.ratio.bps.height,
&mut self.pct99.ratio.bps.height,
&mut self.pct99_5.ratio.bps.height,
]
.into_iter()
}
@@ -1,5 +1,7 @@
use brk_traversable::Traversable;
use crate::market::lookback::ByLookbackPeriod;
/// DCA period identifiers with their day counts
pub const DCA_PERIOD_DAYS: ByDcaPeriod<u32> = ByDcaPeriod {
_1w: 7,
@@ -173,6 +175,26 @@ impl<T> ByDcaPeriod<T> {
}
}
impl<T> ByDcaPeriod<&T> {
/// Get the DCA-matching subset from lookback (excludes 24h)
pub(crate) fn from_lookback(lookback: &ByLookbackPeriod<T>) -> ByDcaPeriod<&T> {
ByDcaPeriod {
_1w: &lookback._1w,
_1m: &lookback._1m,
_3m: &lookback._3m,
_6m: &lookback._6m,
_1y: &lookback._1y,
_2y: &lookback._2y,
_3y: &lookback._3y,
_4y: &lookback._4y,
_5y: &lookback._5y,
_6y: &lookback._6y,
_8y: &lookback._8y,
_10y: &lookback._10y,
}
}
}
/// Generic wrapper for DCA CAGR data (periods >= 2 years)
#[derive(Clone, Default, Traversable)]
pub struct ByDcaCagr<T> {
@@ -2,8 +2,8 @@ use brk_error::Result;
use brk_types::{BasisPointsSigned32, Bitcoin, Cents, Date, Day1, Dollars, Indexes, Sats};
use vecdb::{AnyVec, Exit, ReadableOptionVec, ReadableVec, VecIndex};
use super::Vecs;
use crate::{blocks, indexes, internal::RatioDiffCentsBps32, market::lookback, prices};
use super::{ByDcaPeriod, Vecs};
use crate::{blocks, indexes, internal::RatioDiffCentsBps32, market, prices};
const DCA_AMOUNT: Dollars = Dollars::mint(100.0);
@@ -13,10 +13,12 @@ impl Vecs {
indexes: &indexes::Vecs,
prices: &prices::Vecs,
blocks: &blocks::Vecs,
lookback: &lookback::Vecs,
lookback: &market::lookback::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
let h2d = &indexes.height.day1;
let close = &prices.split.close.usd.day1;
@@ -71,17 +73,12 @@ impl Vecs {
self.period.cost_basis.zip_mut_with_days(&self.period.stack)
{
let days = days as usize;
let start = average_price.cents.height.len().min(starting_height);
let stack_data = stack
.sats
.height
.collect_range_at(start, stack.sats.height.len());
average_price.cents.height.compute_transform(
average_price.cents.height.compute_transform2(
starting_indexes.height,
h2d,
|(h, di, _)| {
&stack.sats.height,
|(h, di, stack_sats, ..)| {
let di_usize = di.to_usize();
let stack_sats = stack_data[h.to_usize() - start];
let avg = if di_usize > first_price_di {
let num_days = days.min(di_usize + 1 - first_price_di);
Cents::from(DCA_AMOUNT * num_days / Bitcoin::from(stack_sats))
@@ -125,21 +122,16 @@ impl Vecs {
}
// Lump sum by period - stack
let lookback_dca = lookback.price_past.as_dca_period();
let lookback_dca = ByDcaPeriod::from_lookback(&lookback.price_past);
for (stack, lookback_price, days) in
self.period.lump_sum_stack.zip_mut_with_days(&lookback_dca)
{
let total_invested = DCA_AMOUNT * days as usize;
let ls_start = stack.sats.height.len().min(starting_height);
let lookback_data = lookback_price
.cents
.height
.collect_range_at(ls_start, lookback_price.cents.height.len());
stack.sats.height.compute_transform(
stack.sats.height.compute_transform2(
starting_indexes.height,
h2d,
|(h, _di, _)| {
let lp = lookback_data[h.to_usize() - ls_start];
&lookback_price.cents.height,
|(h, _di, lp, ..)| {
let sats = if lp == Cents::ZERO {
Sats::ZERO
} else {
@@ -238,20 +230,15 @@ impl Vecs {
.zip(start_days)
{
let from_usize = from.to_usize();
let cls_start = average_price.cents.height.len().min(starting_height);
let stack_data = stack
.sats
.height
.collect_range_at(cls_start, stack.sats.height.len());
average_price.cents.height.compute_transform(
average_price.cents.height.compute_transform2(
starting_indexes.height,
h2d,
|(h, di, _)| {
&stack.sats.height,
|(h, di, stack_sats, ..)| {
let di_usize = di.to_usize();
if di_usize < from_usize {
return (h, Cents::ZERO);
}
let stack_sats = stack_data[h.to_usize() - cls_start];
let num_days = di_usize + 1 - from_usize;
let avg = Cents::from(DCA_AMOUNT * num_days / Bitcoin::from(stack_sats));
(h, avg)
@@ -275,6 +262,11 @@ impl Vecs {
)?;
}
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
@@ -1,43 +1,50 @@
use std::path::Path;
use brk_error::Result;
use brk_types::Version;
use vecdb::{Database, ImportableVec};
use vecdb::ImportableVec;
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, Vecs};
use super::vecs::{ClassVecs, PeriodVecs};
use crate::{
indexes,
internal::{AmountPerBlock, PercentPerBlock, Price},
internal::{
db_utils::{finalize_db, open_db},
AmountPerBlock, PercentPerBlock, Price,
},
};
impl Vecs {
pub(crate) fn forced_import(
db: &Database,
version: Version,
parent_path: &Path,
parent_version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let db = open_db(parent_path, super::DB_NAME, 50_000)?;
let version = parent_version;
let stack = ByDcaPeriod::try_new(|name, _days| {
AmountPerBlock::forced_import(db, &format!("dca_stack_{name}"), version, indexes)
AmountPerBlock::forced_import(&db, &format!("dca_stack_{name}"), version, indexes)
})?;
let cost_basis = ByDcaPeriod::try_new(|name, _days| {
Price::forced_import(db, &format!("dca_cost_basis_{name}"), version, indexes)
Price::forced_import(&db, &format!("dca_cost_basis_{name}"), version, indexes)
})?;
let r#return = ByDcaPeriod::try_new(|name, _days| {
PercentPerBlock::forced_import(db, &format!("dca_return_{name}"), version, indexes)
PercentPerBlock::forced_import(&db, &format!("dca_return_{name}"), version, indexes)
})?;
let cagr = ByDcaCagr::try_new(|name, _days| {
PercentPerBlock::forced_import(db, &format!("dca_cagr_{name}"), version, indexes)
PercentPerBlock::forced_import(&db, &format!("dca_cagr_{name}"), version, indexes)
})?;
let lump_sum_stack = ByDcaPeriod::try_new(|name, _days| {
AmountPerBlock::forced_import(db, &format!("lump_sum_stack_{name}"), version, indexes)
AmountPerBlock::forced_import(&db, &format!("lump_sum_stack_{name}"), version, indexes)
})?;
let lump_sum_return = ByDcaPeriod::try_new(|name, _days| {
PercentPerBlock::forced_import(
db,
&db,
&format!("lump_sum_return_{name}"),
version,
indexes,
@@ -45,19 +52,19 @@ impl Vecs {
})?;
let class_stack = ByDcaClass::try_new(|name, _year, _day1| {
AmountPerBlock::forced_import(db, &format!("dca_stack_{name}"), version, indexes)
AmountPerBlock::forced_import(&db, &format!("dca_stack_{name}"), version, indexes)
})?;
let class_cost_basis = ByDcaClass::try_new(|name, _year, _day1| {
Price::forced_import(db, &format!("dca_cost_basis_{name}"), version, indexes)
Price::forced_import(&db, &format!("dca_cost_basis_{name}"), version, indexes)
})?;
let class_return = ByDcaClass::try_new(|name, _year, _day1| {
PercentPerBlock::forced_import(db, &format!("dca_return_{name}"), version, indexes)
PercentPerBlock::forced_import(&db, &format!("dca_return_{name}"), version, indexes)
})?;
Ok(Self {
sats_per_day: ImportableVec::forced_import(db, "dca_sats_per_day", version)?,
let this = Self {
sats_per_day: ImportableVec::forced_import(&db, "dca_sats_per_day", version)?,
period: PeriodVecs {
stack,
cost_basis,
@@ -71,6 +78,9 @@ impl Vecs {
cost_basis: class_cost_basis,
r#return: class_return,
},
})
db,
};
finalize_db(&this.db, &this)?;
Ok(this)
}
}
@@ -7,3 +7,5 @@ mod vecs;
pub use by_class::*;
pub use by_period::*;
pub use vecs::Vecs;
pub const DB_NAME: &str = "investing";
@@ -1,6 +1,6 @@
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Cents, Height, Sats};
use vecdb::{EagerVec, PcoVec, Rw, StorageMode};
use vecdb::{Database, EagerVec, PcoVec, Rw, StorageMode};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
use crate::internal::{AmountPerBlock, PerBlock, PercentPerBlock, Price};
@@ -24,6 +24,8 @@ pub struct ClassVecs<M: StorageMode = Rw> {
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(skip)]
pub(crate) db: Database,
pub sats_per_day: M::Stored<EagerVec<PcoVec<Height, Sats>>>,
pub period: PeriodVecs<M>,
pub class: ClassVecs<M>,
+41 -6
View File
@@ -18,6 +18,7 @@ mod indicators;
pub mod indexes;
mod inputs;
mod internal;
mod investing;
mod market;
mod mining;
mod outputs;
@@ -39,6 +40,7 @@ pub struct Computer<M: StorageMode = Rw> {
pub constants: Box<constants::Vecs>,
pub indexes: Box<indexes::Vecs<M>>,
pub indicators: Box<indicators::Vecs<M>>,
pub investing: Box<investing::Vecs<M>>,
pub market: Box<market::Vecs<M>>,
pub pools: Box<pools::Vecs<M>>,
pub prices: Box<prices::Vecs<M>>,
@@ -180,8 +182,8 @@ impl Computer {
// Market, indicators, and distribution are independent; import in parallel.
// Supply depends on distribution so it runs after.
let (distribution, market, indicators) =
timed("Imported distribution/market/indicators", || {
let (distribution, market, indicators, investing) =
timed("Imported distribution/market/indicators/investing", || {
thread::scope(|s| -> Result<_> {
let market_handle = big_thread().spawn_scoped(s, || -> Result<_> {
Ok(Box::new(market::Vecs::forced_import(
@@ -199,6 +201,14 @@ impl Computer {
)?))
})?;
let investing_handle = big_thread().spawn_scoped(s, || -> Result<_> {
Ok(Box::new(investing::Vecs::forced_import(
&computed_path,
VERSION,
&indexes,
)?))
})?;
let distribution = Box::new(distribution::Vecs::forced_import(
&computed_path,
VERSION,
@@ -208,7 +218,8 @@ impl Computer {
let market = market_handle.join().unwrap()?;
let indicators = indicators_handle.join().unwrap()?;
Ok((distribution, market, indicators))
let investing = investing_handle.join().unwrap()?;
Ok((distribution, market, indicators, investing))
})
})?;
@@ -232,6 +243,7 @@ impl Computer {
scripts,
constants,
indicators,
investing,
market,
distribution,
supply,
@@ -260,6 +272,7 @@ impl Computer {
cointime::DB_NAME,
indicators::DB_NAME,
indexes::DB_NAME,
investing::DB_NAME,
market::DB_NAME,
pools::DB_NAME,
prices::DB_NAME,
@@ -305,7 +318,7 @@ impl Computer {
let mut starting_indexes = timed("Computed indexes", || {
self.indexes
.compute(indexer, &mut self.blocks, starting_indexes, exit)
.compute(indexer, starting_indexes, exit)
})?;
thread::scope(|scope| -> Result<()> {
@@ -339,8 +352,8 @@ impl Computer {
let market = scope.spawn(|| {
timed("Computed market", || {
self.market.compute(
&self.indexes,
&self.prices,
&self.indexes,
&self.blocks,
&starting_indexes,
exit,
@@ -422,6 +435,19 @@ impl Computer {
})
});
let investing = scope.spawn(|| {
timed("Computed investing", || {
self.investing.compute(
&self.indexes,
&self.prices,
&self.blocks,
&self.market.lookback,
&starting_indexes_clone,
exit,
)
})
});
timed("Computed distribution", || {
self.distribution.compute(
indexer,
@@ -437,6 +463,7 @@ impl Computer {
})?;
pools.join().unwrap()?;
investing.join().unwrap()?;
Ok(())
})?;
@@ -485,6 +512,14 @@ impl Computer {
Ok(())
})?;
self.indicators.thermometer.compute(
&self.distribution,
&self.cointime,
&self.prices,
&starting_indexes,
exit,
)?;
info!("Total compute time: {:?}", compute_start.elapsed());
Ok(())
}
@@ -517,7 +552,7 @@ macro_rules! impl_iter_named {
}
impl_iter_named!(blocks, mining, transactions, scripts, positions, cointime,
constants, indicators, indexes, market, pools, prices, distribution, supply, inputs, outputs);
constants, indicators, indexes, investing, market, pools, prices, distribution, supply, inputs, outputs);
fn timed<T>(label: &str, f: impl FnOnce() -> T) -> T {
let start = Instant::now();
@@ -3,13 +3,13 @@ use brk_types::{Indexes, StoredF32, Timestamp};
use vecdb::{Exit, ReadableVec, VecIndex};
use super::Vecs;
use crate::{blocks, prices};
use crate::{indexes, prices};
impl Vecs {
pub(crate) fn compute(
&mut self,
prices: &prices::Vecs,
blocks: &blocks::Vecs,
indexes: &indexes::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
@@ -24,7 +24,7 @@ impl Vecs {
starting_indexes.height,
&self.high.cents.height,
&prices.spot.cents.height,
&blocks.time.timestamp_monotonic,
&indexes.timestamp.monotonic,
|(i, ath, price, ts, slf)| {
if ath_ts.is_none() {
let idx = i.to_usize();
+11 -22
View File
@@ -9,17 +9,19 @@ use super::Vecs;
impl Vecs {
pub(crate) fn compute(
&mut self,
indexes: &indexes::Vecs,
prices: &prices::Vecs,
indexes: &indexes::Vecs,
blocks: &blocks::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// Phase 1: Independent sub-modules in parallel
let (r1, r2) = rayon::join(
|| {
rayon::join(
|| self.ath.compute(prices, blocks, starting_indexes, exit),
|| self.ath.compute(prices, indexes, starting_indexes, exit),
|| self.lookback.compute(blocks, prices, starting_indexes, exit),
)
},
@@ -39,24 +41,8 @@ impl Vecs {
r2.1?;
// Phase 2: Depend on lookback
let (r3, r4) = rayon::join(
|| {
self.returns
.compute(prices, blocks, &self.lookback, starting_indexes, exit)
},
|| {
self.dca.compute(
indexes,
prices,
blocks,
&self.lookback,
starting_indexes,
exit,
)
},
);
r3?;
r4?;
self.returns
.compute(prices, blocks, &self.lookback, starting_indexes, exit)?;
// Phase 3: Depends on returns, moving_average
self.technical.compute(
@@ -68,8 +54,11 @@ impl Vecs {
exit,
)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+1 -3
View File
@@ -9,7 +9,7 @@ use crate::{
};
use super::{
AthVecs, DcaVecs, TechnicalVecs, LookbackVecs, MovingAverageVecs, RangeVecs, ReturnsVecs,
AthVecs, TechnicalVecs, LookbackVecs, MovingAverageVecs, RangeVecs, ReturnsVecs,
Vecs, VolatilityVecs,
};
@@ -28,7 +28,6 @@ impl Vecs {
let volatility = VolatilityVecs::forced_import(version, &returns)?;
let range = RangeVecs::forced_import(&db, version, indexes)?;
let moving_average = MovingAverageVecs::forced_import(&db, version, indexes)?;
let dca = DcaVecs::forced_import(&db, version, indexes)?;
let technical = TechnicalVecs::forced_import(&db, version, indexes)?;
let this = Self {
@@ -39,7 +38,6 @@ impl Vecs {
volatility,
range,
moving_average,
dca,
technical,
};
finalize_db(&this.db, &this)?;
@@ -1,7 +1,5 @@
use brk_traversable::Traversable;
use crate::market::dca::ByDcaPeriod;
/// Lookback period days (includes 24h, unlike DCA)
pub const LOOKBACK_PERIOD_DAYS: ByLookbackPeriod<u32> = ByLookbackPeriod {
_24h: 1,
@@ -117,22 +115,4 @@ impl<T> ByLookbackPeriod<T> {
]
.into_iter()
}
/// Get the DCA-matching subset (excludes 24h)
pub(crate) fn as_dca_period(&self) -> ByDcaPeriod<&T> {
ByDcaPeriod {
_1w: &self._1w,
_1m: &self._1m,
_3m: &self._3m,
_6m: &self._6m,
_1y: &self._1y,
_2y: &self._2y,
_3y: &self._3y,
_4y: &self._4y,
_5y: &self._5y,
_6y: &self._6y,
_8y: &self._8y,
_10y: &self._10y,
}
}
}
-3
View File
@@ -1,6 +1,5 @@
pub mod ath;
mod compute;
pub mod dca;
mod import;
pub mod technical;
pub mod lookback;
@@ -13,7 +12,6 @@ use brk_traversable::Traversable;
use vecdb::{Database, Rw, StorageMode};
pub use ath::Vecs as AthVecs;
pub use dca::Vecs as DcaVecs;
pub use technical::Vecs as TechnicalVecs;
pub use lookback::Vecs as LookbackVecs;
pub use moving_average::Vecs as MovingAverageVecs;
@@ -32,6 +30,5 @@ pub struct Vecs<M: StorageMode = Rw> {
pub volatility: VolatilityVecs,
pub range: RangeVecs<M>,
pub moving_average: MovingAverageVecs<M>,
pub dca: DcaVecs<M>,
pub technical: TechnicalVecs<M>,
}
@@ -3,7 +3,7 @@ use brk_types::{BasisPointsSigned32, Dollars, Indexes};
use vecdb::Exit;
use super::Vecs;
use crate::{blocks, internal::RatioDiffDollarsBps32, market::lookback, prices};
use crate::{blocks, internal::RatioDiffDollarsBps32, investing::ByDcaPeriod, market::lookback, prices};
impl Vecs {
pub(crate) fn compute(
@@ -29,7 +29,7 @@ impl Vecs {
}
// CAGR computed from returns at height level (2y+ periods only)
let price_return_dca = self.periods.as_dca_period();
let price_return_dca = ByDcaPeriod::from_lookback(&self.periods);
for (cagr, returns, days) in self.cagr.zip_mut_with_period(&price_return_dca) {
let years = days as f64 / 365.0;
cagr.bps.height.compute_transform(
@@ -7,7 +7,7 @@ use super::Vecs;
use crate::{
indexes,
internal::{StdDevPerBlock, PercentPerBlock, Windows},
market::dca::ByDcaCagr,
investing::ByDcaCagr,
};
impl Vecs {
@@ -4,7 +4,8 @@ use vecdb::{Rw, StorageMode};
use crate::{
internal::{PercentPerBlock, StdDevPerBlock, Windows},
market::{dca::ByDcaCagr, lookback::ByLookbackPeriod},
investing::ByDcaCagr,
market::lookback::ByLookbackPeriod,
};
#[derive(Traversable)]
+7 -2
View File
@@ -18,6 +18,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// Block rewards (coinbase, subsidy, fee_dominance, etc.)
self.rewards.compute(
indexer,
@@ -39,8 +41,11 @@ impl Vecs {
exit,
)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+7 -2
View File
@@ -18,6 +18,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
self.count.compute(
indexer,
indexes,
@@ -27,10 +29,13 @@ impl Vecs {
starting_indexes,
exit,
)?;
let _lock = self
let lock = self
.spent
.compute(indexer, inputs, starting_indexes, exit)?;
self.db.compact()?;
self.db.run_bg(move |db| {
let _lock = lock;
db.compact()
});
Ok(())
}
}
@@ -10,13 +10,13 @@ use crate::inputs;
const HEIGHT_BATCH: u32 = 10_000;
impl Vecs {
pub(crate) fn compute<'a>(
pub(crate) fn compute(
&mut self,
indexer: &Indexer,
inputs: &inputs::Vecs,
starting_indexes: &Indexes,
exit: &'a Exit,
) -> Result<ExitGuard<'a>> {
exit: &Exit,
) -> Result<ExitGuard> {
let target_height = indexer.vecs.blocks.blockhash.len();
if target_height == 0 {
return Ok(exit.lock());
+7 -2
View File
@@ -86,6 +86,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
self.compute_pool(indexer, indexes, starting_indexes, exit)?;
self.major.par_iter_mut().try_for_each(|(_, vecs)| {
@@ -103,8 +105,11 @@ impl Vecs {
vecs.compute(starting_indexes, &self.pool, blocks, exit)
})?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
+7 -2
View File
@@ -45,9 +45,14 @@ impl Vecs {
reader: &Reader,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
self.compute_(indexer, starting_indexes, reader, exit)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
+7 -2
View File
@@ -18,6 +18,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
self.compute_prices(indexer, starting_indexes, exit)?;
self.split.open.cents.compute_first(
starting_indexes,
@@ -47,8 +49,11 @@ impl Vecs {
exit,
)?;
let _lock = exit.lock();
self.db().compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
+1 -4
View File
@@ -25,7 +25,7 @@ pub const DB_NAME: &str = "prices";
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(skip)]
pub(crate) db: Database,
pub db: Database,
pub split: SplitByUnit<M>,
pub ohlc: OhlcByUnit<M>,
@@ -183,7 +183,4 @@ impl Vecs {
})
}
pub(crate) fn db(&self) -> &Database {
&self.db
}
}
+7 -2
View File
@@ -15,13 +15,18 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
self.count.compute(indexer, starting_indexes, exit)?;
self.value
.compute(indexer, prices, starting_indexes, exit)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+7 -2
View File
@@ -21,6 +21,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
// 1. Compute burned/unspendable supply
self.burned.compute(
scripts,
@@ -76,8 +78,11 @@ impl Vecs {
)?;
}
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
@@ -20,6 +20,8 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.db.sync_bg_tasks()?;
let (r1, (r2, r3)) = rayon::join(
|| {
self.count
@@ -57,8 +59,11 @@ impl Vecs {
exit,
)?;
let _lock = exit.lock();
self.db.compact()?;
let exit = exit.clone();
self.db.run_bg(move |db| {
let _lock = exit.lock();
db.compact()
});
Ok(())
}
}
+28 -4
View File
@@ -6,6 +6,7 @@ use brk_error::Result;
use brk_iterator::Blocks;
use brk_rpc::Client;
use brk_types::Height;
use fjall::PersistMode;
use tracing::{debug, info};
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Ro, Rw, StorageMode};
mod constants;
@@ -107,6 +108,8 @@ impl Indexer {
exit: &Exit,
check_collisions: bool,
) -> Result<Indexes> {
self.vecs.db.sync_bg_tasks()?;
debug!("Starting indexing...");
let last_blockhash = self.vecs.blocks.blockhash.collect_last();
@@ -248,11 +251,32 @@ impl Indexer {
drop(readers);
if !is_export_height(indexes.height) {
export(stores, vecs, indexes.height)?;
}
let lock = exit.lock();
let tasks = self.stores.take_all_pending_ingests(indexes.height)?;
self.vecs.stamped_write(indexes.height)?;
let fjall_db = self.stores.db.clone();
self.vecs.compact()?;
self.vecs.db.run_bg(move |db| {
let _lock = lock;
if !tasks.is_empty() {
let i = Instant::now();
for task in tasks {
task().map_err(vecdb::RawDBError::other)?;
}
info!("Stores committed in {:?}", i.elapsed());
let i = Instant::now();
fjall_db
.persist(PersistMode::SyncData)
.map_err(vecdb::RawDBError::other)?;
info!("Stores persisted in {:?}", i.elapsed());
}
db.flush()?;
db.compact()?;
Ok(())
});
Ok(starting_indexes)
}
+37 -9
View File
@@ -6,8 +6,8 @@ use brk_cohort::ByAddrType;
use brk_error::Result;
use brk_store::{AnyStore, Kind, Mode, Store};
use brk_types::{
AddrHash, AddrIndexOutPoint, AddrIndexTxIndex, BlockHashPrefix, Height, OutPoint,
OutputType, StoredString, TxIndex, TxOutIndex, TxidPrefix, TypeIndex, Unit, Version, Vout,
AddrHash, AddrIndexOutPoint, AddrIndexTxIndex, BlockHashPrefix, Height, OutPoint, OutputType,
StoredString, TxIndex, TxOutIndex, TxidPrefix, TypeIndex, Unit, Version, Vout,
};
use fjall::{Database, PersistMode};
use rayon::prelude::*;
@@ -24,8 +24,7 @@ pub struct Stores {
pub addr_type_to_addr_hash_to_addr_index: ByAddrType<Store<AddrHash, TypeIndex>>,
pub addr_type_to_addr_index_and_tx_index: ByAddrType<Store<AddrIndexTxIndex, Unit>>,
pub addr_type_to_addr_index_and_unspent_outpoint:
ByAddrType<Store<AddrIndexOutPoint, Unit>>,
pub addr_type_to_addr_index_and_unspent_outpoint: ByAddrType<Store<AddrIndexOutPoint, Unit>>,
pub blockhash_prefix_to_height: Store<BlockHashPrefix, Height>,
pub height_to_coinbase_tag: Store<Height, StoredString>,
pub txid_prefix_to_tx_index: Store<TxidPrefix, TxIndex>,
@@ -194,6 +193,39 @@ impl Stores {
Ok(())
}
/// Takes all pending puts/dels from every store and returns closures
/// that can ingest them on a background thread.
#[allow(clippy::type_complexity)]
pub fn take_all_pending_ingests(
&mut self,
height: Height,
) -> Result<Vec<Box<dyn FnOnce() -> Result<()> + Send>>> {
let h = height;
let mut tasks = Vec::new();
macro_rules! take {
($store:expr) => {
tasks.extend($store.take_pending_ingest(h)?);
};
}
take!(self.blockhash_prefix_to_height);
take!(self.height_to_coinbase_tag);
take!(self.txid_prefix_to_tx_index);
for store in self.addr_type_to_addr_hash_to_addr_index.values_mut() {
take!(store);
}
for store in self.addr_type_to_addr_index_and_tx_index.values_mut() {
take!(store);
}
for store in self.addr_type_to_addr_index_and_unspent_outpoint.values_mut() {
take!(store);
}
Ok(tasks)
}
pub fn rollback_if_needed(
&mut self,
vecs: &mut Vecs,
@@ -368,11 +400,7 @@ impl Stores {
let addr_type = output_type;
let addr_index = type_index;
addr_index_tx_index_to_remove.insert((
addr_type,
addr_index,
spending_tx_index,
));
addr_index_tx_index_to_remove.insert((addr_type, addr_index, spending_tx_index));
self.addr_type_to_addr_index_and_unspent_outpoint
.get_mut_unwrap(addr_type)
+8 -6
View File
@@ -30,7 +30,7 @@ use crate::Indexes;
#[derive(Traversable)]
pub struct Vecs<M: StorageMode = Rw> {
#[traversable(skip)]
db: Database,
pub db: Database,
pub blocks: BlocksVecs<M>,
#[traversable(wrap = "transactions", rename = "raw")]
pub transactions: TransactionsVecs<M>,
@@ -121,8 +121,7 @@ impl Vecs {
}
pub fn flush(&mut self, height: Height) -> Result<()> {
self.par_iter_mut_any_stored_vec()
.try_for_each(|vec| vec.stamped_write(Stamp::from(height)))?;
self.stamped_write(height)?;
self.db.flush()?;
Ok(())
}
@@ -137,6 +136,12 @@ impl Vecs {
.unwrap()
}
pub fn stamped_write(&mut self, height: Height) -> Result<()> {
self.par_iter_mut_any_stored_vec()
.try_for_each(|vec| vec.stamped_write(Stamp::from(height)))?;
Ok(())
}
pub fn compact(&self) -> Result<()> {
self.db.compact()?;
Ok(())
@@ -168,7 +173,4 @@ impl Vecs {
.chain(self.scripts.par_iter_mut_any())
}
pub fn db(&self) -> &Database {
&self.db
}
}
@@ -46,7 +46,7 @@ impl<'a> Day1Iter<'a> {
.to_usize()
.saturating_sub(self.start_di.to_usize())
+ 1;
let timestamps = &self.computer.blocks.time.timestamp.day1;
let timestamps = &self.computer.indexes.timestamp.day1;
let heights = &self.computer.indexes.day1.first_height;
let mut entries = Vec::with_capacity(total / self.step + 1);
@@ -45,8 +45,7 @@ impl Query {
// Get timestamps using difficulty_to_timestamp for epoch start
let epoch_start_timestamp = computer
.blocks
.time
.indexes
.timestamp
.epoch
.collect_one(current_epoch)
+1 -1
View File
@@ -22,7 +22,7 @@ pub fn iter_difficulty_epochs(
.unwrap_or_default();
let epoch_to_height = &computer.indexes.epoch.first_height;
let epoch_to_timestamp = &computer.blocks.time.timestamp.epoch;
let epoch_to_timestamp = &computer.indexes.timestamp.epoch;
let epoch_to_difficulty = &computer.blocks.difficulty.value.epoch;
let mut results = Vec::with_capacity(end_epoch.to_usize() - start_epoch.to_usize() + 1);
+1 -1
View File
@@ -56,7 +56,7 @@ impl Query {
let step = (total_days / 200).max(1); // Max ~200 data points
let hashrate_vec = &computer.mining.hashrate.rate.base.day1;
let timestamp_vec = &computer.blocks.time.timestamp.day1;
let timestamp_vec = &computer.indexes.timestamp.day1;
let mut hashrates = Vec::with_capacity(total_days / step + 1);
let mut di = start_day1.to_usize();
+1 -1
View File
@@ -373,7 +373,7 @@ impl Query {
// Slow path: rebuild from computer's precomputed monotonic timestamps
let mut map = HEIGHT_BY_MONOTONIC_TIMESTAMP.write();
if map.len() <= current_height {
*map = RangeMap::from(self.computer().blocks.time.timestamp_monotonic.collect());
*map = RangeMap::from(self.computer().indexes.timestamp.monotonic.collect());
}
map.ceil(ts).map(usize::from).unwrap_or(current_height)
}
+28
View File
@@ -206,6 +206,34 @@ where
}
}
/// Takes buffered puts/dels and returns a closure that ingests them into the keyspace.
/// The store is left with empty buffers, ready for the next batch.
#[allow(clippy::type_complexity)]
pub fn take_pending_ingest(
&mut self,
height: Height,
) -> Result<Option<Box<dyn FnOnce() -> Result<()> + Send>>>
where
K: Send + 'static,
V: Send + 'static,
for<'a> ByteView: From<&'a K> + From<&'a V>,
{
self.export_meta_if_needed(height)?;
let puts = mem::take(&mut self.puts);
let dels = mem::take(&mut self.dels);
if puts.is_empty() && dels.is_empty() {
return Ok(None);
}
let keyspace = self.keyspace.clone();
Ok(Some(Box::new(move || {
Self::ingest(&keyspace, puts.iter(), dels.iter())
})))
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (K, V)> {
self.keyspace
+237 -199
View File
@@ -1973,7 +1973,7 @@ function createGrossInvestedInvestorLossNetNuplProfitSentimentPattern2(client, a
* @typedef {Object} BpsCentsPercentilesRatioSatsSmaStdUsdPattern
* @property {SeriesPattern1<BasisPoints32>} bps
* @property {SeriesPattern1<Cents>} cents
* @property {Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<SatsFract>} sats
* @property {_1m1w1y2y4yAllPattern} sma
@@ -1981,6 +1981,37 @@ function createGrossInvestedInvestorLossNetNuplProfitSentimentPattern2(client, a
* @property {SeriesPattern1<Dollars>} usd
*/
/**
* @typedef {Object} Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern
* @property {BpsPriceRatioPattern} pct05
* @property {BpsPriceRatioPattern} pct1
* @property {BpsPriceRatioPattern} pct2
* @property {BpsPriceRatioPattern} pct5
* @property {BpsPriceRatioPattern} pct95
* @property {BpsPriceRatioPattern} pct98
* @property {BpsPriceRatioPattern} pct99
* @property {BpsPriceRatioPattern} pct995
*/
/**
* Create a Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern}
*/
function createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc) {
return {
pct05: createBpsPriceRatioPattern(client, acc, 'pct0_5'),
pct1: createBpsPriceRatioPattern(client, acc, 'pct1'),
pct2: createBpsPriceRatioPattern(client, acc, 'pct2'),
pct5: createBpsPriceRatioPattern(client, acc, 'pct5'),
pct95: createBpsPriceRatioPattern(client, acc, 'pct95'),
pct98: createBpsPriceRatioPattern(client, acc, 'pct98'),
pct99: createBpsPriceRatioPattern(client, acc, 'pct99'),
pct995: createBpsPriceRatioPattern(client, acc, 'pct99_5'),
};
}
/**
* @typedef {Object} _10y2y3y4y5y6y8yPattern
* @property {BpsPercentRatioPattern} _10y
@@ -2242,7 +2273,7 @@ function createAverageBlockCumulativeInSumPattern(client, acc) {
* @typedef {Object} BpsCentsPercentilesRatioSatsUsdPattern
* @property {SeriesPattern1<BasisPoints32>} bps
* @property {SeriesPattern1<Cents>} cents
* @property {Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {SeriesPattern1<StoredF32>} ratio
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<Dollars>} usd
@@ -2258,7 +2289,7 @@ function createBpsCentsPercentilesRatioSatsUsdPattern(client, acc) {
return {
bps: createSeriesPattern1(client, _m(acc, 'ratio_bps')),
cents: createSeriesPattern1(client, _m(acc, 'cents')),
percentiles: createPct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc),
ratio: createSeriesPattern1(client, _m(acc, 'ratio')),
sats: createSeriesPattern1(client, _m(acc, 'sats')),
usd: createSeriesPattern1(client, acc),
@@ -2373,33 +2404,6 @@ function createDeltaHalfInToTotalPattern2(client, acc) {
};
}
/**
* @typedef {Object} Pct1Pct2Pct5Pct95Pct98Pct99Pattern
* @property {BpsPriceRatioPattern} pct1
* @property {BpsPriceRatioPattern} pct2
* @property {BpsPriceRatioPattern} pct5
* @property {BpsPriceRatioPattern} pct95
* @property {BpsPriceRatioPattern} pct98
* @property {BpsPriceRatioPattern} pct99
*/
/**
* Create a Pct1Pct2Pct5Pct95Pct98Pct99Pattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {Pct1Pct2Pct5Pct95Pct98Pct99Pattern}
*/
function createPct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc) {
return {
pct1: createBpsPriceRatioPattern(client, acc, 'pct1'),
pct2: createBpsPriceRatioPattern(client, acc, 'pct2'),
pct5: createBpsPriceRatioPattern(client, acc, 'pct5'),
pct95: createBpsPriceRatioPattern(client, acc, 'pct95'),
pct98: createBpsPriceRatioPattern(client, acc, 'pct98'),
pct99: createBpsPriceRatioPattern(client, acc, 'pct99'),
};
}
/**
* @typedef {Object} _1m1w1y24hBlockPattern
* @property {SeriesPattern1<StoredF32>} _1m
@@ -4146,6 +4150,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesTree_Constants} constants
* @property {SeriesTree_Indexes} indexes
* @property {SeriesTree_Indicators} indicators
* @property {SeriesTree_Investing} investing
* @property {SeriesTree_Market} market
* @property {SeriesTree_Pools} pools
* @property {SeriesTree_Prices} prices
@@ -4180,9 +4185,7 @@ function createTransferPattern(client, acc) {
/**
* @typedef {Object} SeriesTree_Blocks_Time
* @property {SeriesPattern1<Timestamp>} timestamp
* @property {SeriesPattern18<Date>} date
* @property {SeriesPattern18<Timestamp>} timestampMonotonic
* @property {SeriesPattern18<Timestamp>} timestamp
*/
/**
@@ -4769,6 +4772,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesTree_Indexes_TxIndex} txIndex
* @property {SeriesTree_Indexes_TxinIndex} txinIndex
* @property {SeriesTree_Indexes_TxoutIndex} txoutIndex
* @property {SeriesTree_Indexes_Timestamp} timestamp
*/
/**
@@ -4992,6 +4996,12 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern21<TxOutIndex>} identity
*/
/**
* @typedef {Object} SeriesTree_Indexes_Timestamp
* @property {SeriesPattern18<Timestamp>} monotonic
* @property {SeriesPattern2<Timestamp>} resolutions
*/
/**
* @typedef {Object} SeriesTree_Indicators
* @property {BpsRatioPattern2} puellMultiple
@@ -5004,6 +5014,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesTree_Indicators_Dormancy} dormancy
* @property {SeriesPattern1<StoredF32>} stockToFlow
* @property {SeriesPattern1<StoredF32>} sellerExhaustion
* @property {SeriesTree_Indicators_Thermometer} thermometer
*/
/**
@@ -5012,6 +5023,108 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<StoredF32>} flow
*/
/**
* @typedef {Object} SeriesTree_Indicators_Thermometer
* @property {CentsSatsUsdPattern} pct05
* @property {CentsSatsUsdPattern} pct1
* @property {CentsSatsUsdPattern} pct2
* @property {CentsSatsUsdPattern} pct5
* @property {CentsSatsUsdPattern} pct95
* @property {CentsSatsUsdPattern} pct98
* @property {CentsSatsUsdPattern} pct99
* @property {CentsSatsUsdPattern} pct995
* @property {SeriesPattern1<StoredI8>} zone
* @property {SeriesPattern1<StoredI8>} score
*/
/**
* @typedef {Object} SeriesTree_Investing
* @property {SeriesPattern18<Sats>} satsPerDay
* @property {SeriesTree_Investing_Period} period
* @property {SeriesTree_Investing_Class} class
*/
/**
* @typedef {Object} SeriesTree_Investing_Period
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern3} stack
* @property {SeriesTree_Investing_Period_CostBasis} costBasis
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern2} return
* @property {_10y2y3y4y5y6y8yPattern} cagr
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern3} lumpSumStack
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern2} lumpSumReturn
*/
/**
* @typedef {Object} SeriesTree_Investing_Period_CostBasis
* @property {CentsSatsUsdPattern} _1w
* @property {CentsSatsUsdPattern} _1m
* @property {CentsSatsUsdPattern} _3m
* @property {CentsSatsUsdPattern} _6m
* @property {CentsSatsUsdPattern} _1y
* @property {CentsSatsUsdPattern} _2y
* @property {CentsSatsUsdPattern} _3y
* @property {CentsSatsUsdPattern} _4y
* @property {CentsSatsUsdPattern} _5y
* @property {CentsSatsUsdPattern} _6y
* @property {CentsSatsUsdPattern} _8y
* @property {CentsSatsUsdPattern} _10y
*/
/**
* @typedef {Object} SeriesTree_Investing_Class
* @property {SeriesTree_Investing_Class_Stack} stack
* @property {SeriesTree_Investing_Class_CostBasis} costBasis
* @property {SeriesTree_Investing_Class_Return} return
*/
/**
* @typedef {Object} SeriesTree_Investing_Class_Stack
* @property {BtcCentsSatsUsdPattern3} from2015
* @property {BtcCentsSatsUsdPattern3} from2016
* @property {BtcCentsSatsUsdPattern3} from2017
* @property {BtcCentsSatsUsdPattern3} from2018
* @property {BtcCentsSatsUsdPattern3} from2019
* @property {BtcCentsSatsUsdPattern3} from2020
* @property {BtcCentsSatsUsdPattern3} from2021
* @property {BtcCentsSatsUsdPattern3} from2022
* @property {BtcCentsSatsUsdPattern3} from2023
* @property {BtcCentsSatsUsdPattern3} from2024
* @property {BtcCentsSatsUsdPattern3} from2025
* @property {BtcCentsSatsUsdPattern3} from2026
*/
/**
* @typedef {Object} SeriesTree_Investing_Class_CostBasis
* @property {CentsSatsUsdPattern} from2015
* @property {CentsSatsUsdPattern} from2016
* @property {CentsSatsUsdPattern} from2017
* @property {CentsSatsUsdPattern} from2018
* @property {CentsSatsUsdPattern} from2019
* @property {CentsSatsUsdPattern} from2020
* @property {CentsSatsUsdPattern} from2021
* @property {CentsSatsUsdPattern} from2022
* @property {CentsSatsUsdPattern} from2023
* @property {CentsSatsUsdPattern} from2024
* @property {CentsSatsUsdPattern} from2025
* @property {CentsSatsUsdPattern} from2026
*/
/**
* @typedef {Object} SeriesTree_Investing_Class_Return
* @property {BpsPercentRatioPattern} from2015
* @property {BpsPercentRatioPattern} from2016
* @property {BpsPercentRatioPattern} from2017
* @property {BpsPercentRatioPattern} from2018
* @property {BpsPercentRatioPattern} from2019
* @property {BpsPercentRatioPattern} from2020
* @property {BpsPercentRatioPattern} from2021
* @property {BpsPercentRatioPattern} from2022
* @property {BpsPercentRatioPattern} from2023
* @property {BpsPercentRatioPattern} from2024
* @property {BpsPercentRatioPattern} from2025
* @property {BpsPercentRatioPattern} from2026
*/
/**
* @typedef {Object} SeriesTree_Market
* @property {SeriesTree_Market_Ath} ath
@@ -5020,7 +5133,6 @@ function createTransferPattern(client, acc) {
* @property {_1m1w1y24hPattern<StoredF32>} volatility
* @property {SeriesTree_Market_Range} range
* @property {SeriesTree_Market_MovingAverage} movingAverage
* @property {SeriesTree_Market_Dca} dca
* @property {SeriesTree_Market_Technical} technical
*/
@@ -5183,94 +5295,6 @@ function createTransferPattern(client, acc) {
* @property {BpsCentsRatioSatsUsdPattern} _4y
*/
/**
* @typedef {Object} SeriesTree_Market_Dca
* @property {SeriesPattern18<Sats>} satsPerDay
* @property {SeriesTree_Market_Dca_Period} period
* @property {SeriesTree_Market_Dca_Class} class
*/
/**
* @typedef {Object} SeriesTree_Market_Dca_Period
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern3} stack
* @property {SeriesTree_Market_Dca_Period_CostBasis} costBasis
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern2} return
* @property {_10y2y3y4y5y6y8yPattern} cagr
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern3} lumpSumStack
* @property {_10y1m1w1y2y3m3y4y5y6m6y8yPattern2} lumpSumReturn
*/
/**
* @typedef {Object} SeriesTree_Market_Dca_Period_CostBasis
* @property {CentsSatsUsdPattern} _1w
* @property {CentsSatsUsdPattern} _1m
* @property {CentsSatsUsdPattern} _3m
* @property {CentsSatsUsdPattern} _6m
* @property {CentsSatsUsdPattern} _1y
* @property {CentsSatsUsdPattern} _2y
* @property {CentsSatsUsdPattern} _3y
* @property {CentsSatsUsdPattern} _4y
* @property {CentsSatsUsdPattern} _5y
* @property {CentsSatsUsdPattern} _6y
* @property {CentsSatsUsdPattern} _8y
* @property {CentsSatsUsdPattern} _10y
*/
/**
* @typedef {Object} SeriesTree_Market_Dca_Class
* @property {SeriesTree_Market_Dca_Class_Stack} stack
* @property {SeriesTree_Market_Dca_Class_CostBasis} costBasis
* @property {SeriesTree_Market_Dca_Class_Return} return
*/
/**
* @typedef {Object} SeriesTree_Market_Dca_Class_Stack
* @property {BtcCentsSatsUsdPattern3} from2015
* @property {BtcCentsSatsUsdPattern3} from2016
* @property {BtcCentsSatsUsdPattern3} from2017
* @property {BtcCentsSatsUsdPattern3} from2018
* @property {BtcCentsSatsUsdPattern3} from2019
* @property {BtcCentsSatsUsdPattern3} from2020
* @property {BtcCentsSatsUsdPattern3} from2021
* @property {BtcCentsSatsUsdPattern3} from2022
* @property {BtcCentsSatsUsdPattern3} from2023
* @property {BtcCentsSatsUsdPattern3} from2024
* @property {BtcCentsSatsUsdPattern3} from2025
* @property {BtcCentsSatsUsdPattern3} from2026
*/
/**
* @typedef {Object} SeriesTree_Market_Dca_Class_CostBasis
* @property {CentsSatsUsdPattern} from2015
* @property {CentsSatsUsdPattern} from2016
* @property {CentsSatsUsdPattern} from2017
* @property {CentsSatsUsdPattern} from2018
* @property {CentsSatsUsdPattern} from2019
* @property {CentsSatsUsdPattern} from2020
* @property {CentsSatsUsdPattern} from2021
* @property {CentsSatsUsdPattern} from2022
* @property {CentsSatsUsdPattern} from2023
* @property {CentsSatsUsdPattern} from2024
* @property {CentsSatsUsdPattern} from2025
* @property {CentsSatsUsdPattern} from2026
*/
/**
* @typedef {Object} SeriesTree_Market_Dca_Class_Return
* @property {BpsPercentRatioPattern} from2015
* @property {BpsPercentRatioPattern} from2016
* @property {BpsPercentRatioPattern} from2017
* @property {BpsPercentRatioPattern} from2018
* @property {BpsPercentRatioPattern} from2019
* @property {BpsPercentRatioPattern} from2020
* @property {BpsPercentRatioPattern} from2021
* @property {BpsPercentRatioPattern} from2022
* @property {BpsPercentRatioPattern} from2023
* @property {BpsPercentRatioPattern} from2024
* @property {BpsPercentRatioPattern} from2025
* @property {BpsPercentRatioPattern} from2026
*/
/**
* @typedef {Object} SeriesTree_Market_Technical
* @property {SeriesTree_Market_Technical_Rsi} rsi
@@ -5624,7 +5648,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<BasisPoints32>} bps
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
* @property {SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev} stdDev
*/
@@ -5815,7 +5839,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<BasisPoints32>} bps
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
* @property {SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev} stdDev
*/
@@ -5937,7 +5961,7 @@ function createTransferPattern(client, acc) {
* @property {SeriesPattern1<SatsFract>} sats
* @property {SeriesPattern1<BasisPoints32>} bps
* @property {SeriesPattern1<StoredF32>} ratio
* @property {Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} percentiles
* @property {_1m1w1y2y4yAllPattern} sma
* @property {SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev} stdDev
*/
@@ -7557,9 +7581,7 @@ class BrkClient extends BrkClientBase {
daysToRetarget: createSeriesPattern1(this, 'days_to_retarget'),
},
time: {
timestamp: createSeriesPattern1(this, 'timestamp'),
date: createSeriesPattern18(this, 'date'),
timestampMonotonic: createSeriesPattern18(this, 'timestamp_monotonic'),
timestamp: createSeriesPattern18(this, 'timestamp'),
},
size: {
base: createSeriesPattern18(this, 'total_size'),
@@ -8101,6 +8123,10 @@ class BrkClient extends BrkClientBase {
txoutIndex: {
identity: createSeriesPattern21(this, 'txout_index'),
},
timestamp: {
monotonic: createSeriesPattern18(this, 'timestamp_monotonic'),
resolutions: createSeriesPattern2(this, 'timestamp'),
},
},
indicators: {
puellMultiple: createBpsRatioPattern2(this, 'puell_multiple'),
@@ -8116,6 +8142,86 @@ class BrkClient extends BrkClientBase {
},
stockToFlow: createSeriesPattern1(this, 'stock_to_flow'),
sellerExhaustion: createSeriesPattern1(this, 'seller_exhaustion'),
thermometer: {
pct05: createCentsSatsUsdPattern(this, 'thermometer_pct0_5'),
pct1: createCentsSatsUsdPattern(this, 'thermometer_pct01'),
pct2: createCentsSatsUsdPattern(this, 'thermometer_pct02'),
pct5: createCentsSatsUsdPattern(this, 'thermometer_pct05'),
pct95: createCentsSatsUsdPattern(this, 'thermometer_pct95'),
pct98: createCentsSatsUsdPattern(this, 'thermometer_pct98'),
pct99: createCentsSatsUsdPattern(this, 'thermometer_pct99'),
pct995: createCentsSatsUsdPattern(this, 'thermometer_pct99_5'),
zone: createSeriesPattern1(this, 'thermometer_zone'),
score: createSeriesPattern1(this, 'thermometer_score'),
},
},
investing: {
satsPerDay: createSeriesPattern18(this, 'dca_sats_per_day'),
period: {
stack: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(this, 'dca_stack'),
costBasis: {
_1w: createCentsSatsUsdPattern(this, 'dca_cost_basis_1w'),
_1m: createCentsSatsUsdPattern(this, 'dca_cost_basis_1m'),
_3m: createCentsSatsUsdPattern(this, 'dca_cost_basis_3m'),
_6m: createCentsSatsUsdPattern(this, 'dca_cost_basis_6m'),
_1y: createCentsSatsUsdPattern(this, 'dca_cost_basis_1y'),
_2y: createCentsSatsUsdPattern(this, 'dca_cost_basis_2y'),
_3y: createCentsSatsUsdPattern(this, 'dca_cost_basis_3y'),
_4y: createCentsSatsUsdPattern(this, 'dca_cost_basis_4y'),
_5y: createCentsSatsUsdPattern(this, 'dca_cost_basis_5y'),
_6y: createCentsSatsUsdPattern(this, 'dca_cost_basis_6y'),
_8y: createCentsSatsUsdPattern(this, 'dca_cost_basis_8y'),
_10y: createCentsSatsUsdPattern(this, 'dca_cost_basis_10y'),
},
return: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(this, 'dca_return'),
cagr: create_10y2y3y4y5y6y8yPattern(this, 'dca_cagr'),
lumpSumStack: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(this, 'lump_sum_stack'),
lumpSumReturn: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(this, 'lump_sum_return'),
},
class: {
stack: {
from2015: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2015'),
from2016: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2016'),
from2017: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2017'),
from2018: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2018'),
from2019: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2019'),
from2020: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2020'),
from2021: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2021'),
from2022: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2022'),
from2023: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2023'),
from2024: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2024'),
from2025: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2025'),
from2026: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2026'),
},
costBasis: {
from2015: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2015'),
from2016: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2016'),
from2017: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2017'),
from2018: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2018'),
from2019: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2019'),
from2020: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2020'),
from2021: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2021'),
from2022: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2022'),
from2023: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2023'),
from2024: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2024'),
from2025: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2025'),
from2026: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2026'),
},
return: {
from2015: createBpsPercentRatioPattern(this, 'dca_return_from_2015'),
from2016: createBpsPercentRatioPattern(this, 'dca_return_from_2016'),
from2017: createBpsPercentRatioPattern(this, 'dca_return_from_2017'),
from2018: createBpsPercentRatioPattern(this, 'dca_return_from_2018'),
from2019: createBpsPercentRatioPattern(this, 'dca_return_from_2019'),
from2020: createBpsPercentRatioPattern(this, 'dca_return_from_2020'),
from2021: createBpsPercentRatioPattern(this, 'dca_return_from_2021'),
from2022: createBpsPercentRatioPattern(this, 'dca_return_from_2022'),
from2023: createBpsPercentRatioPattern(this, 'dca_return_from_2023'),
from2024: createBpsPercentRatioPattern(this, 'dca_return_from_2024'),
from2025: createBpsPercentRatioPattern(this, 'dca_return_from_2025'),
from2026: createBpsPercentRatioPattern(this, 'dca_return_from_2026'),
},
},
},
market: {
ath: {
@@ -8238,74 +8344,6 @@ class BrkClient extends BrkClientBase {
_4y: createBpsCentsRatioSatsUsdPattern(this, 'price_ema_4y'),
},
},
dca: {
satsPerDay: createSeriesPattern18(this, 'dca_sats_per_day'),
period: {
stack: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(this, 'dca_stack'),
costBasis: {
_1w: createCentsSatsUsdPattern(this, 'dca_cost_basis_1w'),
_1m: createCentsSatsUsdPattern(this, 'dca_cost_basis_1m'),
_3m: createCentsSatsUsdPattern(this, 'dca_cost_basis_3m'),
_6m: createCentsSatsUsdPattern(this, 'dca_cost_basis_6m'),
_1y: createCentsSatsUsdPattern(this, 'dca_cost_basis_1y'),
_2y: createCentsSatsUsdPattern(this, 'dca_cost_basis_2y'),
_3y: createCentsSatsUsdPattern(this, 'dca_cost_basis_3y'),
_4y: createCentsSatsUsdPattern(this, 'dca_cost_basis_4y'),
_5y: createCentsSatsUsdPattern(this, 'dca_cost_basis_5y'),
_6y: createCentsSatsUsdPattern(this, 'dca_cost_basis_6y'),
_8y: createCentsSatsUsdPattern(this, 'dca_cost_basis_8y'),
_10y: createCentsSatsUsdPattern(this, 'dca_cost_basis_10y'),
},
return: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(this, 'dca_return'),
cagr: create_10y2y3y4y5y6y8yPattern(this, 'dca_cagr'),
lumpSumStack: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(this, 'lump_sum_stack'),
lumpSumReturn: create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(this, 'lump_sum_return'),
},
class: {
stack: {
from2015: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2015'),
from2016: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2016'),
from2017: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2017'),
from2018: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2018'),
from2019: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2019'),
from2020: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2020'),
from2021: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2021'),
from2022: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2022'),
from2023: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2023'),
from2024: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2024'),
from2025: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2025'),
from2026: createBtcCentsSatsUsdPattern3(this, 'dca_stack_from_2026'),
},
costBasis: {
from2015: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2015'),
from2016: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2016'),
from2017: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2017'),
from2018: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2018'),
from2019: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2019'),
from2020: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2020'),
from2021: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2021'),
from2022: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2022'),
from2023: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2023'),
from2024: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2024'),
from2025: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2025'),
from2026: createCentsSatsUsdPattern(this, 'dca_cost_basis_from_2026'),
},
return: {
from2015: createBpsPercentRatioPattern(this, 'dca_return_from_2015'),
from2016: createBpsPercentRatioPattern(this, 'dca_return_from_2016'),
from2017: createBpsPercentRatioPattern(this, 'dca_return_from_2017'),
from2018: createBpsPercentRatioPattern(this, 'dca_return_from_2018'),
from2019: createBpsPercentRatioPattern(this, 'dca_return_from_2019'),
from2020: createBpsPercentRatioPattern(this, 'dca_return_from_2020'),
from2021: createBpsPercentRatioPattern(this, 'dca_return_from_2021'),
from2022: createBpsPercentRatioPattern(this, 'dca_return_from_2022'),
from2023: createBpsPercentRatioPattern(this, 'dca_return_from_2023'),
from2024: createBpsPercentRatioPattern(this, 'dca_return_from_2024'),
from2025: createBpsPercentRatioPattern(this, 'dca_return_from_2025'),
from2026: createBpsPercentRatioPattern(this, 'dca_return_from_2026'),
},
},
},
technical: {
rsi: {
_24h: createRsiStochPattern(this, 'rsi', '24h'),
@@ -8569,7 +8607,7 @@ class BrkClient extends BrkClientBase {
sats: createSeriesPattern1(this, 'realized_price_sats'),
bps: createSeriesPattern1(this, 'realized_price_ratio_bps'),
ratio: createSeriesPattern1(this, 'realized_price_ratio'),
percentiles: createPct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'realized_price'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'realized_price_ratio_sma'),
stdDev: {
all: {
@@ -8713,7 +8751,7 @@ class BrkClient extends BrkClientBase {
sats: createSeriesPattern1(this, 'sth_realized_price_sats'),
bps: createSeriesPattern1(this, 'sth_realized_price_ratio_bps'),
ratio: createSeriesPattern1(this, 'sth_realized_price_ratio'),
percentiles: createPct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'sth_realized_price'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'sth_realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'sth_realized_price_ratio_sma'),
stdDev: {
all: {
@@ -8812,7 +8850,7 @@ class BrkClient extends BrkClientBase {
sats: createSeriesPattern1(this, 'lth_realized_price_sats'),
bps: createSeriesPattern1(this, 'lth_realized_price_ratio_bps'),
ratio: createSeriesPattern1(this, 'lth_realized_price_ratio'),
percentiles: createPct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'lth_realized_price'),
percentiles: createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(this, 'lth_realized_price'),
sma: create_1m1w1y2y4yAllPattern(this, 'lth_realized_price_ratio_sma'),
stdDev: {
all: {
@@ -238,6 +238,7 @@ export class QuickMatch {
*/
_rank(indices, minScore, qwords, sep, limit) {
const { items, _scores: scores } = this;
/** @type {[number[], number[], number[]]} */
const buckets = [[], [], []]; // ps=0, ps=1, ps=2
for (let i = 0; i < indices.length; i++) {
+139 -115
View File
@@ -2306,6 +2306,20 @@ class BpsCentsPercentilesRatioSatsSmaStdUsdPattern:
"""Pattern struct for repeated tree structure."""
pass
class Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.pct0_5: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct0_5')
self.pct1: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct1')
self.pct2: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct2')
self.pct5: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct5')
self.pct95: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct95')
self.pct98: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct98')
self.pct99: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct99')
self.pct99_5: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct99_5')
class _10y2y3y4y5y6y8yPattern:
"""Pattern struct for repeated tree structure."""
@@ -2427,7 +2441,7 @@ class BpsCentsPercentilesRatioSatsUsdPattern:
"""Create pattern node with accumulated series name."""
self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, _m(acc, 'ratio_bps'))
self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents'))
self.percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc)
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc)
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'ratio'))
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
@@ -2480,18 +2494,6 @@ class DeltaHalfInToTotalPattern2:
self.to_circulating: BpsPercentRatioPattern3 = BpsPercentRatioPattern3(client, _m(acc, 'to_circulating'))
self.total: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, acc)
class Pct1Pct2Pct5Pct95Pct98Pct99Pattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.pct1: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct1')
self.pct2: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct2')
self.pct5: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct5')
self.pct95: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct95')
self.pct98: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct98')
self.pct99: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct99')
class _1m1w1y24hBlockPattern:
"""Pattern struct for repeated tree structure."""
@@ -3251,9 +3253,7 @@ class SeriesTree_Blocks_Time:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.timestamp: SeriesPattern1[Timestamp] = SeriesPattern1(client, 'timestamp')
self.date: SeriesPattern18[Date] = SeriesPattern18(client, 'date')
self.timestamp_monotonic: SeriesPattern18[Timestamp] = SeriesPattern18(client, 'timestamp_monotonic')
self.timestamp: SeriesPattern18[Timestamp] = SeriesPattern18(client, 'timestamp')
class SeriesTree_Blocks_Size:
"""Series tree node."""
@@ -4149,6 +4149,13 @@ class SeriesTree_Indexes_TxoutIndex:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.identity: SeriesPattern21[TxOutIndex] = SeriesPattern21(client, 'txout_index')
class SeriesTree_Indexes_Timestamp:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.monotonic: SeriesPattern18[Timestamp] = SeriesPattern18(client, 'timestamp_monotonic')
self.resolutions: SeriesPattern2[Timestamp] = SeriesPattern2(client, 'timestamp')
class SeriesTree_Indexes:
"""Series tree node."""
@@ -4173,6 +4180,7 @@ class SeriesTree_Indexes:
self.tx_index: SeriesTree_Indexes_TxIndex = SeriesTree_Indexes_TxIndex(client)
self.txin_index: SeriesTree_Indexes_TxinIndex = SeriesTree_Indexes_TxinIndex(client)
self.txout_index: SeriesTree_Indexes_TxoutIndex = SeriesTree_Indexes_TxoutIndex(client)
self.timestamp: SeriesTree_Indexes_Timestamp = SeriesTree_Indexes_Timestamp(client)
class SeriesTree_Indicators_Dormancy:
"""Series tree node."""
@@ -4181,6 +4189,21 @@ class SeriesTree_Indicators_Dormancy:
self.supply_adjusted: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy_supply_adjusted')
self.flow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy_flow')
class SeriesTree_Indicators_Thermometer:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.pct0_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct0_5')
self.pct1: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct01')
self.pct2: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct02')
self.pct5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct05')
self.pct95: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct95')
self.pct98: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct98')
self.pct99: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct99')
self.pct99_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'thermometer_pct99_5')
self.zone: SeriesPattern1[StoredI8] = SeriesPattern1(client, 'thermometer_zone')
self.score: SeriesPattern1[StoredI8] = SeriesPattern1(client, 'thermometer_score')
class SeriesTree_Indicators:
"""Series tree node."""
@@ -4195,6 +4218,102 @@ class SeriesTree_Indicators:
self.dormancy: SeriesTree_Indicators_Dormancy = SeriesTree_Indicators_Dormancy(client)
self.stock_to_flow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'stock_to_flow')
self.seller_exhaustion: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'seller_exhaustion')
self.thermometer: SeriesTree_Indicators_Thermometer = SeriesTree_Indicators_Thermometer(client)
class SeriesTree_Investing_Period_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self._1w: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1w')
self._1m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1m')
self._3m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_3m')
self._6m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_6m')
self._1y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1y')
self._2y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_2y')
self._3y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_3y')
self._4y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_4y')
self._5y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_5y')
self._6y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_6y')
self._8y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_8y')
self._10y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_10y')
class SeriesTree_Investing_Period:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'dca_stack')
self.cost_basis: SeriesTree_Investing_Period_CostBasis = SeriesTree_Investing_Period_CostBasis(client)
self.return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'dca_return')
self.cagr: _10y2y3y4y5y6y8yPattern = _10y2y3y4y5y6y8yPattern(client, 'dca_cagr')
self.lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'lump_sum_stack')
self.lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'lump_sum_return')
class SeriesTree_Investing_Class_Stack:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.from_2015: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2015')
self.from_2016: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2016')
self.from_2017: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2017')
self.from_2018: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2018')
self.from_2019: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2019')
self.from_2020: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2020')
self.from_2021: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2021')
self.from_2022: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2022')
self.from_2023: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2023')
self.from_2024: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2024')
self.from_2025: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2025')
self.from_2026: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2026')
class SeriesTree_Investing_Class_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.from_2015: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2015')
self.from_2016: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2016')
self.from_2017: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2017')
self.from_2018: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2018')
self.from_2019: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2019')
self.from_2020: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2020')
self.from_2021: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2021')
self.from_2022: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2022')
self.from_2023: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2023')
self.from_2024: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2024')
self.from_2025: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2025')
self.from_2026: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2026')
class SeriesTree_Investing_Class_Return:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.from_2015: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2015')
self.from_2016: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2016')
self.from_2017: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2017')
self.from_2018: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2018')
self.from_2019: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2019')
self.from_2020: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2020')
self.from_2021: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2021')
self.from_2022: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2022')
self.from_2023: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2023')
self.from_2024: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2024')
self.from_2025: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2025')
self.from_2026: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2026')
class SeriesTree_Investing_Class:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.stack: SeriesTree_Investing_Class_Stack = SeriesTree_Investing_Class_Stack(client)
self.cost_basis: SeriesTree_Investing_Class_CostBasis = SeriesTree_Investing_Class_CostBasis(client)
self.return_: SeriesTree_Investing_Class_Return = SeriesTree_Investing_Class_Return(client)
class SeriesTree_Investing:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.sats_per_day: SeriesPattern18[Sats] = SeriesPattern18(client, 'dca_sats_per_day')
self.period: SeriesTree_Investing_Period = SeriesTree_Investing_Period(client)
self.class_: SeriesTree_Investing_Class = SeriesTree_Investing_Class(client)
class SeriesTree_Market_Ath:
"""Series tree node."""
@@ -4370,101 +4489,6 @@ class SeriesTree_Market_MovingAverage:
self.sma: SeriesTree_Market_MovingAverage_Sma = SeriesTree_Market_MovingAverage_Sma(client)
self.ema: SeriesTree_Market_MovingAverage_Ema = SeriesTree_Market_MovingAverage_Ema(client)
class SeriesTree_Market_Dca_Period_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self._1w: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1w')
self._1m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1m')
self._3m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_3m')
self._6m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_6m')
self._1y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1y')
self._2y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_2y')
self._3y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_3y')
self._4y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_4y')
self._5y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_5y')
self._6y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_6y')
self._8y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_8y')
self._10y: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_10y')
class SeriesTree_Market_Dca_Period:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'dca_stack')
self.cost_basis: SeriesTree_Market_Dca_Period_CostBasis = SeriesTree_Market_Dca_Period_CostBasis(client)
self.return_: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'dca_return')
self.cagr: _10y2y3y4y5y6y8yPattern = _10y2y3y4y5y6y8yPattern(client, 'dca_cagr')
self.lump_sum_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'lump_sum_stack')
self.lump_sum_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'lump_sum_return')
class SeriesTree_Market_Dca_Class_Stack:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.from_2015: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2015')
self.from_2016: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2016')
self.from_2017: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2017')
self.from_2018: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2018')
self.from_2019: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2019')
self.from_2020: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2020')
self.from_2021: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2021')
self.from_2022: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2022')
self.from_2023: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2023')
self.from_2024: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2024')
self.from_2025: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2025')
self.from_2026: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'dca_stack_from_2026')
class SeriesTree_Market_Dca_Class_CostBasis:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.from_2015: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2015')
self.from_2016: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2016')
self.from_2017: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2017')
self.from_2018: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2018')
self.from_2019: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2019')
self.from_2020: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2020')
self.from_2021: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2021')
self.from_2022: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2022')
self.from_2023: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2023')
self.from_2024: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2024')
self.from_2025: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2025')
self.from_2026: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2026')
class SeriesTree_Market_Dca_Class_Return:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.from_2015: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2015')
self.from_2016: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2016')
self.from_2017: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2017')
self.from_2018: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2018')
self.from_2019: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2019')
self.from_2020: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2020')
self.from_2021: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2021')
self.from_2022: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2022')
self.from_2023: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2023')
self.from_2024: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2024')
self.from_2025: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2025')
self.from_2026: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2026')
class SeriesTree_Market_Dca_Class:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.stack: SeriesTree_Market_Dca_Class_Stack = SeriesTree_Market_Dca_Class_Stack(client)
self.cost_basis: SeriesTree_Market_Dca_Class_CostBasis = SeriesTree_Market_Dca_Class_CostBasis(client)
self.return_: SeriesTree_Market_Dca_Class_Return = SeriesTree_Market_Dca_Class_Return(client)
class SeriesTree_Market_Dca:
"""Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.sats_per_day: SeriesPattern18[Sats] = SeriesPattern18(client, 'dca_sats_per_day')
self.period: SeriesTree_Market_Dca_Period = SeriesTree_Market_Dca_Period(client)
self.class_: SeriesTree_Market_Dca_Class = SeriesTree_Market_Dca_Class(client)
class SeriesTree_Market_Technical_Rsi:
"""Series tree node."""
@@ -4529,7 +4553,6 @@ class SeriesTree_Market:
self.volatility: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'price_volatility')
self.range: SeriesTree_Market_Range = SeriesTree_Market_Range(client)
self.moving_average: SeriesTree_Market_MovingAverage = SeriesTree_Market_MovingAverage(client)
self.dca: SeriesTree_Market_Dca = SeriesTree_Market_Dca(client)
self.technical: SeriesTree_Market_Technical = SeriesTree_Market_Technical(client)
class SeriesTree_Pools_Major:
@@ -4890,7 +4913,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price:
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'realized_price_sats')
self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, 'realized_price_ratio_bps')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio')
self.percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'realized_price')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'realized_price_ratio_sma')
self.std_dev: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev(client)
@@ -5097,7 +5120,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price:
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'sth_realized_price_sats')
self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, 'sth_realized_price_ratio_bps')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio')
self.percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'sth_realized_price')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'sth_realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'sth_realized_price_ratio_sma')
self.std_dev: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev(client)
@@ -5227,7 +5250,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price:
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'lth_realized_price_sats')
self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, 'lth_realized_price_ratio_bps')
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio')
self.percentiles: Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'lth_realized_price')
self.percentiles: Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern = Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, 'lth_realized_price')
self.sma: _1m1w1y2y4yAllPattern = _1m1w1y2y4yAllPattern(client, 'lth_realized_price_ratio_sma')
self.std_dev: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev(client)
@@ -5646,6 +5669,7 @@ class SeriesTree:
self.constants: SeriesTree_Constants = SeriesTree_Constants(client)
self.indexes: SeriesTree_Indexes = SeriesTree_Indexes(client)
self.indicators: SeriesTree_Indicators = SeriesTree_Indicators(client)
self.investing: SeriesTree_Investing = SeriesTree_Investing(client)
self.market: SeriesTree_Market = SeriesTree_Market(client)
self.pools: SeriesTree_Pools = SeriesTree_Pools(client)
self.prices: SeriesTree_Prices = SeriesTree_Prices(client)
+163 -16
View File
@@ -9,7 +9,7 @@ import {
import { createLegend } from "./legend.js";
import { capture } from "./capture.js";
import { colors } from "../utils/colors.js";
import { createRadios, createSelect } from "../utils/dom.js";
import { createRadios, createSelect, getElementById } from "../utils/dom.js";
import { createPersistedValue } from "../utils/persisted.js";
import { onChange as onThemeChange } from "../utils/theme.js";
import { throttle, debounce } from "../utils/timing.js";
@@ -74,6 +74,71 @@ const lineWidth = /** @type {1} */ (/** @type {unknown} */ (1.5));
const MAX_SIZE = 10_000;
/** @typedef {{ label: string, index: IndexLabel, from: number }} RangePreset */
/** @returns {RangePreset[]} */
function getRangePresets() {
const nowSec = Math.floor(Date.now() / 1000);
const now = new Date();
const y = now.getUTCFullYear();
const m = now.getUTCMonth();
const d = now.getUTCDate();
/** @param {number} n */
const monthsAgo = (n) => Math.floor(Date.UTC(y, m - n, d) / 1000);
/** @type {RangePreset[]} */
const presets = [
{
label: "1w",
index: /** @type {IndexLabel} */ ("30mn"),
from: nowSec - 7 * 86_400,
},
{
label: "1m",
index: /** @type {IndexLabel} */ ("1h"),
from: monthsAgo(1),
},
{
label: "3m",
index: /** @type {IndexLabel} */ ("4h"),
from: monthsAgo(3),
},
{
label: "6m",
index: /** @type {IndexLabel} */ ("12h"),
from: monthsAgo(6),
},
{
label: "1y",
index: /** @type {IndexLabel} */ ("1d"),
from: monthsAgo(12),
},
{
label: "4y",
index: /** @type {IndexLabel} */ ("3d"),
from: monthsAgo(48),
},
];
// Insert ytd at the right position
const ytdFrom = Math.floor(Date.UTC(y, 0, 1) / 1000);
const ri = presets.findIndex((e) => e.from <= ytdFrom);
const insertAt = ri === -1 ? presets.length : ri;
presets.splice(insertAt, 0, {
label: "ytd",
index: presets[ri === -1 ? presets.length - 1 : ri].index,
from: ytdFrom,
});
presets.push({
label: "all",
index: /** @type {IndexLabel} */ ("1w"),
from: -Infinity,
});
return presets;
}
/**
* @param {Object} args
* @param {HTMLElement} args.parent
@@ -89,8 +154,8 @@ export function createChart({ parent, brk, fitContent }) {
/** @param {ChartableIndex} idx */
const getTimeEndpoint = (idx) =>
idx === "height"
? brk.series.blocks.time.timestampMonotonic.by[idx]
: brk.series.blocks.time.timestamp.by[idx];
? brk.series.indexes.timestamp.monotonic.by[idx]
: brk.series.indexes.timestamp.resolutions.by[idx];
const index = {
/** @type {Set<(index: ChartableIndex) => void>} */
@@ -204,10 +269,6 @@ export function createChart({ parent, brk, fitContent }) {
enableResize: false,
},
},
grid: {
vertLines: { visible: false },
horzLines: { visible: false },
},
rightPriceScale: {
borderVisible: false,
},
@@ -290,6 +351,8 @@ export function createChart({ parent, brk, fitContent }) {
const defaultColor = colors.default();
const offColor = colors.gray();
const borderColor = colors.border();
const offBorderColor = colors.offBorder();
console.log(borderColor);
ichart.applyOptions({
layout: {
textColor: offColor,
@@ -307,6 +370,14 @@ export function createChart({ parent, brk, fitContent }) {
labelBackgroundColor: defaultColor,
},
},
grid: {
horzLines: {
color: offBorderColor,
},
vertLines: {
color: offBorderColor,
},
},
});
}
applyColors();
@@ -402,8 +473,9 @@ export function createChart({ parent, brk, fitContent }) {
const pane = ichart.panes().at(paneIndex);
if (!pane) return;
if (this.isAllHidden(paneIndex)) {
const collapsedHeight = paneIndex === 0 ? 32 : 64;
const chartHeight = ichart.chartElement().clientHeight;
pane.setStretchFactor(chartHeight > 0 ? 48 / (chartHeight - 48) : 0);
pane.setStretchFactor(chartHeight > 0 ? collapsedHeight / (chartHeight - collapsedHeight) : 0);
} else {
pane.setStretchFactor(1);
}
@@ -885,6 +957,7 @@ export function createChart({ parent, brk, fitContent }) {
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {Color | [Color, Color]} [args.color] - Single color or [positive, negative] colors
* @param {(value: number) => Color} [args.colorFn]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
* @param {HistogramSeriesPartialOptions} [args.options]
@@ -894,6 +967,7 @@ export function createChart({ parent, brk, fitContent }) {
name,
key,
color = colors.bi.p1,
colorFn,
order,
unit,
paneIndex = 0,
@@ -930,7 +1004,17 @@ export function createChart({ parent, brk, fitContent }) {
});
},
setData: (data) => {
if (isDualColor) {
if (colorFn) {
iseries.setData(
data.map((d) => ({
...d,
color:
"value" in d
? (colorFn(d.value) ?? (() => "transparent"))()
: "transparent",
})),
);
} else if (isDualColor) {
iseries.setData(
data.map((d) => ({
...d,
@@ -957,6 +1041,7 @@ export function createChart({ parent, brk, fitContent }) {
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {Color} args.color
* @param {(value: number) => Color} [args.colorFn]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
* @param {LineSeriesPartialOptions} [args.options]
@@ -967,6 +1052,7 @@ export function createChart({ parent, brk, fitContent }) {
key,
order,
color,
colorFn,
unit,
paneIndex = 0,
defaultActive,
@@ -999,7 +1085,18 @@ export function createChart({ parent, brk, fitContent }) {
color: color.highlight(highlighted),
});
},
setData: (data) => iseries.setData(data),
setData: (data) => {
if (colorFn) {
iseries.setData(
data.map((d) => ({
...d,
color: "value" in d ? (colorFn(d.value) ?? color)() : color(),
})),
);
} else {
iseries.setData(data);
}
},
update: (data) => iseries.update(data),
getData: () => iseries.data(),
onRemove: () => ichart.removeSeries(iseries),
@@ -1373,7 +1470,11 @@ export function createChart({ parent, brk, fitContent }) {
break;
case "Histogram":
pane.series.push(
serieses.addHistogram({ ...common, color: blueprint.color }),
serieses.addHistogram({
...common,
color: blueprint.color,
colorFn: blueprint.colorFn,
}),
);
break;
case "Candlestick":
@@ -1414,6 +1515,7 @@ export function createChart({ parent, brk, fitContent }) {
serieses.addLine({
...common,
color: blueprint.color ?? defaultColor,
colorFn: blueprint.colorFn,
}),
);
}
@@ -1443,9 +1545,35 @@ export function createChart({ parent, brk, fitContent }) {
/** @type {HTMLElement | null} */
let indexField = null;
const lastTd = ichart
.chartElement()
.querySelector("table > tr:last-child > td:last-child");
/** @param {RangePreset} preset */
function applyPreset(preset) {
preferredIndex = preset.index;
/** @type {HTMLSelectElement} */ (getElementById("index")).value =
preset.index;
index.name.set(preset.index);
const targetGen = generation;
const waitAndApply = () => {
if (generation !== targetGen) return;
if (!initialLoadComplete) {
requestAnimationFrame(waitAndApply);
return;
}
const data = blueprints.panes[0].series[0]?.getData();
if (!data?.length) return;
const from = isFinite(preset.from)
? (data.findIndex(
(d) => /** @type {number} */ (d.time) >= preset.from,
) ?? 0)
: 0;
const padding = Math.round((data.length - from) * 0.025);
ichart.timeScale().setVisibleLogicalRange({
from: from - padding,
to: data.length + padding,
});
};
requestAnimationFrame(waitAndApply);
}
const chart = {
get panes() {
@@ -1464,7 +1592,13 @@ export function createChart({ parent, brk, fitContent }) {
index.name.set(currentValue);
}
indexField = createSelect({
indexField = window.document.createElement("div");
indexField.classList.add("index-bar");
const scroller = window.document.createElement("div");
indexField.append(scroller);
const selectField = createSelect({
initialValue: currentValue,
onChange: (v) => {
preferredIndex = v;
@@ -1474,7 +1608,20 @@ export function createChart({ parent, brk, fitContent }) {
groups,
id: "index",
});
if (lastTd) lastTd.append(indexField);
scroller.append(selectField);
const sep = window.document.createElement("span");
sep.textContent = "|";
scroller.append(sep);
for (const preset of getRangePresets()) {
const btn = window.document.createElement("button");
btn.textContent = preset.label;
btn.addEventListener("click", () => applyPreset(preset));
scroller.append(btn);
}
chartEl.append(indexField);
},
/**
+37 -37
View File
@@ -69,19 +69,19 @@ const ALL_YEARS = /** @type {const} */ ([...YEARS_2020S, ...YEARS_2010S]);
/**
* Build DCA class entry from year
* @param {MarketDca} dca
* @param {Investing} investing
* @param {DcaYear} year
* @param {number} i
* @returns {BaseEntryItem}
*/
function buildYearEntry(dca, year, i) {
function buildYearEntry(investing, year, i) {
const key = /** @type {DcaYearKey} */ (`from${year}`);
return {
name: `${year}`,
color: colors.at(i, ALL_YEARS.length),
costBasis: dca.class.costBasis[key],
returns: dca.class.return[key],
stack: dca.class.stack[key],
costBasis: investing.class.costBasis[key],
returns: investing.class.return[key],
stack: investing.class.stack[key],
};
}
@@ -90,16 +90,16 @@ function buildYearEntry(dca, year, i) {
* @returns {PartialOptionsGroup}
*/
export function createInvestingSection() {
const { market } = brk.series;
const { dca, lookback, returns } = market;
const { market, investing } = brk.series;
const { lookback, returns } = market;
return {
name: "Investing",
tree: [
createDcaVsLumpSumSection({ dca, lookback, returns }),
createDcaByPeriodSection({ dca, returns }),
createLumpSumByPeriodSection({ dca, lookback, returns }),
createDcaByStartYearSection({ dca }),
createDcaVsLumpSumSection({ investing, lookback, returns }),
createDcaByPeriodSection({ investing, returns }),
createLumpSumByPeriodSection({ investing, lookback, returns }),
createDcaByStartYearSection({ investing }),
],
};
}
@@ -270,15 +270,15 @@ function createLongSingleEntry(item) {
/**
* Create DCA vs Lump Sum section
* @param {Object} args
* @param {Market["dca"]} args.dca
* @param {Investing} args.investing
* @param {Market["lookback"]} args.lookback
* @param {Market["returns"]} args.returns
*/
export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
export function createDcaVsLumpSumSection({ investing, lookback, returns }) {
/** @param {AllPeriodKey} key */
const topPane = (key) => [
price({
series: dca.period.costBasis[key],
series: investing.period.costBasis[key],
name: "DCA",
color: colors.profit,
}),
@@ -299,11 +299,11 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
top: topPane(key),
bottom: [
...percentRatioBaseline({
pattern: dca.period.return[key],
pattern: investing.period.return[key],
name: "DCA",
}),
...percentRatioBaseline({
pattern: dca.period.lumpSumReturn[key],
pattern: investing.period.lumpSumReturn[key],
name: "Lump Sum",
color: colors.bi.p2,
}),
@@ -317,7 +317,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
top: topPane(key),
bottom: [
...percentRatioBaseline({
pattern: dca.period.cagr[key],
pattern: investing.period.cagr[key],
name: "DCA",
}),
...percentRatioBaseline({
@@ -335,12 +335,12 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
top: topPane(key),
bottom: [
...satsBtcUsd({
pattern: dca.period.stack[key],
pattern: investing.period.stack[key],
name: "DCA",
color: colors.profit,
}),
...satsBtcUsd({
pattern: dca.period.lumpSumStack[key],
pattern: investing.period.lumpSumStack[key],
name: "Lump Sum",
color: colors.bitcoin,
}),
@@ -395,11 +395,11 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
/**
* Create period-based section (DCA or Lump Sum)
* @param {Object} args
* @param {Market["dca"]} args.dca
* @param {Investing} args.investing
* @param {Market["lookback"]} [args.lookback]
* @param {Market["returns"]} args.returns
*/
function createPeriodSection({ dca, lookback, returns }) {
function createPeriodSection({ investing, lookback, returns }) {
const isLumpSum = !!lookback;
const suffix = isLumpSum ? "Lump Sum" : "DCA";
@@ -409,20 +409,20 @@ function createPeriodSection({ dca, lookback, returns }) {
const buildBaseEntry = (key, i) => ({
name: periodName(key),
color: colors.at(i, allPeriods.length),
costBasis: isLumpSum ? lookback[key] : dca.period.costBasis[key],
costBasis: isLumpSum ? lookback[key] : investing.period.costBasis[key],
returns: isLumpSum
? dca.period.lumpSumReturn[key]
: dca.period.return[key],
? investing.period.lumpSumReturn[key]
: investing.period.return[key],
stack: isLumpSum
? dca.period.lumpSumStack[key]
: dca.period.stack[key],
? investing.period.lumpSumStack[key]
: investing.period.stack[key],
});
/** @param {LongPeriodKey} key @param {number} i @returns {LongEntryItem} */
const buildLongEntry = (key, i) =>
withCagr(
buildBaseEntry(key, i),
isLumpSum ? returns.cagr[key] : dca.period.cagr[key],
isLumpSum ? returns.cagr[key] : investing.period.cagr[key],
);
/** @param {BaseEntryItem} entry */
@@ -471,30 +471,30 @@ function createPeriodSection({ dca, lookback, returns }) {
/**
* Create DCA by Period section
* @param {Object} args
* @param {Market["dca"]} args.dca
* @param {Investing} args.investing
* @param {Market["returns"]} args.returns
*/
export function createDcaByPeriodSection({ dca, returns }) {
return createPeriodSection({ dca, returns });
export function createDcaByPeriodSection({ investing, returns }) {
return createPeriodSection({ investing, returns });
}
/**
* Create Lump Sum by Period section
* @param {Object} args
* @param {Market["dca"]} args.dca
* @param {Investing} args.investing
* @param {Market["lookback"]} args.lookback
* @param {Market["returns"]} args.returns
*/
export function createLumpSumByPeriodSection({ dca, lookback, returns }) {
return createPeriodSection({ dca, lookback, returns });
export function createLumpSumByPeriodSection({ investing, lookback, returns }) {
return createPeriodSection({ investing, lookback, returns });
}
/**
* Create DCA by Start Year section
* @param {Object} args
* @param {Market["dca"]} args.dca
* @param {Investing} args.investing
*/
export function createDcaByStartYearSection({ dca }) {
export function createDcaByStartYearSection({ investing }) {
/** @param {string} name @param {string} title @param {BaseEntryItem[]} entries */
const createDecadeGroup = (name, title, entries) => ({
name,
@@ -511,10 +511,10 @@ export function createDcaByStartYearSection({ dca }) {
});
const entries2020s = YEARS_2020S.map((year, i) =>
buildYearEntry(dca, year, i),
buildYearEntry(investing, year, i),
);
const entries2010s = YEARS_2010S.map((year, i) =>
buildYearEntry(dca, year, YEARS_2020S.length + i),
buildYearEntry(investing, year, YEARS_2020S.length + i),
);
return {
+45 -2
View File
@@ -16,7 +16,7 @@ import {
ROLLING_WINDOWS,
ROLLING_WINDOWS_TO_1M,
} from "./series.js";
import { simplePriceRatioTree } from "./shared.js";
import { simplePriceRatioTree, percentileBands, priceBands } from "./shared.js";
import { periodIdToName } from "./utils.js";
/**
@@ -165,7 +165,10 @@ function returnsSubSectionWithCagr(name, periods) {
...periods.map((p) => ({
name: periodIdToName(p.id, true),
title: `${periodIdToName(p.id, true)} Total Price Returns`,
bottom: percentRatioBaseline({ pattern: p.returns, name: "Return" }),
bottom: percentRatioBaseline({
pattern: p.returns,
name: "Return",
}),
})),
],
},
@@ -1084,6 +1087,46 @@ export function createMarketSection() {
color: colors.loss,
}),
},
{
name: "Thermometer",
tree: [
{
name: "Bands",
title: "Thermometer",
top: priceBands(percentileBands(indicators.thermometer), { defaultActive: true }),
},
{
name: "Score",
title: "Thermometer",
top: priceBands(percentileBands(indicators.thermometer)),
bottom: [
histogram({
series: indicators.thermometer.zone,
name: "Zone",
unit: Unit.count,
colorFn: (v) => /** @type {const} */ ([
colors.ratioPct._0_5,
colors.ratioPct._1,
colors.ratioPct._2,
colors.ratioPct._5,
colors.transparent,
colors.ratioPct._95,
colors.ratioPct._98,
colors.ratioPct._99,
colors.ratioPct._99_5,
])[v + 4],
}),
baseline({
series: indicators.thermometer.score,
name: "Score",
unit: Unit.count,
color: [colors.ratioPct._99, colors.ratioPct._1],
defaultActive: false,
}),
],
},
],
},
],
},
],
+6
View File
@@ -147,6 +147,7 @@ function percentileSeries({ pattern, unit, title = "" }) {
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {LineStyle} [args.style]
* @param {Color} [args.color]
* @param {(value: number) => Color} [args.colorFn]
* @param {boolean} [args.defaultActive]
* @param {LineSeriesPartialOptions} [args.options]
* @returns {FetchedLineSeriesBlueprint}
@@ -157,6 +158,7 @@ export function line({
key,
style,
color,
colorFn,
defaultActive,
unit,
options,
@@ -166,6 +168,7 @@ export function line({
title: name,
key,
color,
colorFn,
unit,
defaultActive,
options: {
@@ -370,6 +373,7 @@ export function dotsBaseline({
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {Color | [Color, Color]} [args.color]
* @param {(value: number) => Color} [args.colorFn]
* @param {boolean} [args.defaultActive]
* @param {HistogramSeriesPartialOptions} [args.options]
* @returns {FetchedHistogramSeriesBlueprint}
@@ -379,6 +383,7 @@ export function histogram({
name,
key,
color,
colorFn,
defaultActive,
unit,
options,
@@ -389,6 +394,7 @@ export function histogram({
title: name,
key,
color,
colorFn,
unit,
defaultActive,
options,
+110 -22
View File
@@ -1,7 +1,13 @@
/** Shared helpers for options */
import { Unit } from "../utils/units.js";
import { ROLLING_WINDOWS, line, baseline, price, sumsAndAveragesCumulativeWith } from "./series.js";
import {
ROLLING_WINDOWS,
line,
baseline,
price,
sumsAndAveragesCumulativeWith,
} from "./series.js";
import { priceLine, priceLines } from "./constants.js";
import { colors } from "../utils/colors.js";
@@ -270,11 +276,19 @@ export function simplePriceRatioTree({ pattern, title, legend, color }) {
}
/**
* @template T
* @param {InvestorPercentilesPattern} p
* @param {(entry: InvestorPercentileEntry) => T} extract
* @param {{ pct95: AnyPricePattern, pct5: AnyPricePattern, pct98: AnyPricePattern, pct2: AnyPricePattern, pct99: AnyPricePattern, pct1: AnyPricePattern, pct995: AnyPricePattern, pct05: AnyPricePattern }} p
*/
function percentileBands(p, extract) {
export function percentileBands(p) {
return percentileBandsWith(p, (e) => e);
}
/**
* @template E
* @template T
* @param {{ pct95: E, pct5: E, pct98: E, pct2: E, pct99: E, pct1: E, pct995: E, pct05: E }} p
* @param {(entry: E) => T} extract
*/
export function percentileBandsWith(p, extract) {
return [
{ name: "P95", prop: extract(p.pct95), color: colors.ratioPct._95 },
{ name: "P5", prop: extract(p.pct5), color: colors.ratioPct._5 },
@@ -282,20 +296,38 @@ function percentileBands(p, extract) {
{ name: "P2", prop: extract(p.pct2), color: colors.ratioPct._2 },
{ name: "P99", prop: extract(p.pct99), color: colors.ratioPct._99 },
{ name: "P1", prop: extract(p.pct1), color: colors.ratioPct._1 },
{ name: "P99.5", prop: extract(p.pct995), color: colors.ratioPct._99_5 },
{ name: "P0.5", prop: extract(p.pct05), color: colors.ratioPct._0_5 },
];
}
/** @param {{ name: string, prop: AnyPricePattern, color: Color }[]} bands */
function priceBands(bands) {
/**
* @param {{ name: string, prop: AnyPricePattern, color: Color }[]} bands
* @param {{ defaultActive?: boolean }} [opts]
*/
export function priceBands(bands, opts) {
return bands.map(({ name, prop, color }) =>
price({ series: prop, name, color, defaultActive: false, options: { lineStyle: 1 } }),
price({
series: prop,
name,
color,
defaultActive: opts?.defaultActive ?? false,
options: { lineStyle: 1 },
}),
);
}
/** @param {{ name: string, prop: AnySeriesPattern, color: Color }[]} bands */
function ratioBands(bands) {
return bands.map(({ name, prop, color }) =>
line({ series: prop, name, color, defaultActive: false, unit: Unit.ratio, options: { lineStyle: 1 } }),
line({
series: prop,
name,
color,
defaultActive: false,
unit: Unit.ratio,
options: { lineStyle: 1 },
}),
);
}
@@ -319,8 +351,8 @@ export function priceRatioPercentilesTree({
priceReferences,
}) {
const p = pattern.percentiles;
const pctUsd = percentileBands(p, (e) => e.price);
const pctRatio = percentileBands(p, (e) => e.ratio);
const pctUsd = percentileBandsWith(p, (e) => e.price);
const pctRatio = percentileBandsWith(p, (e) => e.ratio);
return [
{
name: "Price",
@@ -500,7 +532,11 @@ export function ratioSmas(ratio) {
{ name: "1y SMA", series: ratio.sma._1y.ratio },
{ name: "2y SMA", series: ratio.sma._2y.ratio },
{ name: "4y SMA", series: ratio.sma._4y.ratio },
{ name: "All Time SMA", series: ratio.sma.all.ratio, color: colors.time.all },
{
name: "All Time SMA",
series: ratio.sma.all.ratio,
color: colors.time.all,
},
].map((s, i, arr) => ({ color: colors.at(i, arr.length), ...s }));
}
@@ -543,7 +579,14 @@ export function ratioBottomSeries(ratio) {
* @param {string} [args.legend]
* @returns {PartialChartOption}
*/
export function createRatioChart({ title, pricePattern, ratio, color, name, legend }) {
export function createRatioChart({
title,
pricePattern,
ratio,
color,
name,
legend,
}) {
return {
name: name ?? "Ratio",
title: title(name ?? "Ratio"),
@@ -727,7 +770,7 @@ export function createPriceRatioCharts({
priceReferences,
}) {
const titleFn = formatCohortTitle(context);
const pctUsd = percentileBands(ratio.percentiles, (e) => e.price);
const pctUsd = percentileBandsWith(ratio.percentiles, (e) => e.price);
return [
{
name: "Price",
@@ -775,20 +818,39 @@ export function createPriceRatioCharts({
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
export function groupedWindowsCumulative({ list, all, title, metricTitle, getWindowSeries, getCumulativeSeries, seriesFn, unit }) {
export function groupedWindowsCumulative({
list,
all,
title,
metricTitle,
getWindowSeries,
getCumulativeSeries,
seriesFn,
unit,
}) {
return [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${metricTitle}`),
bottom: mapCohortsWithAll(list, all, (c) =>
seriesFn({ series: getWindowSeries(c, w.key), name: c.name, color: c.color, unit }),
seriesFn({
series: getWindowSeries(c, w.key),
name: c.name,
color: c.color,
unit,
}),
),
})),
{
name: "Cumulative",
title: title(`Cumulative ${metricTitle}`),
bottom: mapCohortsWithAll(list, all, (c) =>
seriesFn({ series: getCumulativeSeries(c), name: c.name, color: c.color, unit }),
seriesFn({
series: getCumulativeSeries(c),
name: c.name,
color: c.color,
unit,
}),
),
},
];
@@ -807,9 +869,21 @@ export function groupedWindowsCumulative({ list, all, title, metricTitle, getWin
* @param {(args: { series: AnySeriesPattern, name: string, color: Color, unit: Unit }) => AnyFetchedSeriesBlueprint} [args.seriesFn]
* @returns {PartialOptionsTree}
*/
export function groupedWindowsCumulativeUsd({ list, all, title, metricTitle, getMetric, seriesFn = line }) {
export function groupedWindowsCumulativeUsd({
list,
all,
title,
metricTitle,
getMetric,
seriesFn = line,
}) {
return groupedWindowsCumulative({
list, all, title, metricTitle, seriesFn, unit: Unit.usd,
list,
all,
title,
metricTitle,
seriesFn,
unit: Unit.usd,
getWindowSeries: (c, key) => getMetric(c).sum[key].usd,
getCumulativeSeries: (c) => getMetric(c).cumulative.usd,
});
@@ -827,20 +901,34 @@ export function groupedWindowsCumulativeUsd({ list, all, title, metricTitle, get
* @param {(c: T | A) => { sum: Record<string, AnyValuePattern>, cumulative: AnyValuePattern }} args.getMetric
* @returns {PartialOptionsTree}
*/
export function groupedWindowsCumulativeSatsBtcUsd({ list, all, title, metricTitle, getMetric }) {
export function groupedWindowsCumulativeSatsBtcUsd({
list,
all,
title,
metricTitle,
getMetric,
}) {
return [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${metricTitle}`),
bottom: flatMapCohortsWithAll(list, all, (c) =>
satsBtcUsd({ pattern: getMetric(c).sum[w.key], name: c.name, color: c.color }),
satsBtcUsd({
pattern: getMetric(c).sum[w.key],
name: c.name,
color: c.color,
}),
),
})),
{
name: "Cumulative",
title: title(`Cumulative ${metricTitle}`),
bottom: flatMapCohortsWithAll(list, all, (c) =>
satsBtcUsd({ pattern: getMetric(c).cumulative, name: c.name, color: c.color }),
satsBtcUsd({
pattern: getMetric(c).cumulative,
name: c.name,
color: c.color,
}),
),
},
];
+2
View File
@@ -20,12 +20,14 @@
* @typedef {Object} LineSeriesBlueprintSpecific
* @property {"Line"} [type]
* @property {Color} [color]
* @property {(value: number) => Color} [colorFn]
* @property {LineSeriesPartialOptions} [options]
* @typedef {BaseSeriesBlueprint & LineSeriesBlueprintSpecific} LineSeriesBlueprint
*
* @typedef {Object} HistogramSeriesBlueprintSpecific
* @property {"Histogram"} type
* @property {Color | [Color, Color]} [color] - Single color or [positive, negative] colors (defaults to green/red)
* @property {(value: number) => Color} [colorFn]
* @property {HistogramSeriesPartialOptions} [options]
* @typedef {BaseSeriesBlueprint & HistogramSeriesBlueprintSpecific} HistogramSeriesBlueprint
*
+1 -1
View File
@@ -3,7 +3,7 @@ import {
searchLabelElement,
searchResultsElement,
} from "../utils/elements.js";
import { QuickMatch } from "../modules/quickmatch-js/0.4.0/src/index.js";
import { QuickMatch } from "../modules/quickmatch-js/0.4.1/src/index.js";
/**
* @param {Options} options
+2 -2
View File
@@ -180,7 +180,7 @@
* Tree branch types
* @typedef {Brk.SeriesTree_Market} Market
* @typedef {Brk.SeriesTree_Market_MovingAverage} MarketMovingAverage
* @typedef {Brk.SeriesTree_Market_Dca} MarketDca
* @typedef {Brk.SeriesTree_Investing} Investing
* @typedef {Brk._10y2y3y4y5y6y8yPattern} PeriodCagrPattern
* @typedef {FullStatsPattern} AnyFullStatsPattern
*
@@ -243,7 +243,7 @@
* @typedef {Brk.AbsoluteRatePattern2} FiatDeltaPattern
*
* Investor price percentiles (pct1/2/5/95/98/99)
* @typedef {Brk.Pct1Pct2Pct5Pct95Pct98Pct99Pattern} InvestorPercentilesPattern
* @typedef {Brk.Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} InvestorPercentilesPattern
* @typedef {Brk.BpsPriceRatioPattern} InvestorPercentileEntry
*
* Generic tree node type for walking
+9 -5
View File
@@ -124,9 +124,11 @@ function seq(keys) {
}
export const colors = {
transparent: createColor(() => "transparent"),
default: createColor(() => getLightDarkValue("--color")),
gray: createColor(() => getColor("gray")),
border: createColor(() => getLightDarkValue("--border-color")),
offBorder: createColor(() => getLightDarkValue("--off-border-color")),
// Directional
profit: palette.green,
@@ -214,12 +216,14 @@ export const colors = {
// Ratio percentile bands (extreme values)
ratioPct: {
_99: palette.rose,
_98: palette.pink,
_95: palette.fuchsia,
_5: palette.teal,
_99_5: palette.red,
_99: palette.orange,
_98: palette.amber,
_95: palette.yellow,
_5: palette.cyan,
_2: palette.sky,
_1: palette.indigo,
_1: palette.blue,
_0_5: palette.indigo,
},
// Standard deviation bands (warm = positive, cool = negative)
+72 -6
View File
@@ -7,8 +7,8 @@
label,
select {
margin: -0.5rem;
padding: 0.5rem;
margin: -0.375rem;
padding: 0.375rem;
}
select {
@@ -67,7 +67,7 @@
overflow-x: auto;
scrollbar-width: thin;
padding: 0 var(--main-padding);
padding-top: 0.278rem;
padding-top: 0.375rem;
padding-bottom: 0.75rem;
> * {
pointer-events: auto;
@@ -210,6 +210,7 @@
display: inline-flex;
font-size: var(--font-size-xs);
align-items: center;
text-transform: uppercase;
}
tr:not(:last-child) > td:last-child > .field {
@@ -217,7 +218,6 @@
right: 0;
gap: 0.375rem;
background-color: var(--background-color);
text-transform: uppercase;
padding-left: 0.625rem;
padding-top: 0.35rem;
padding-bottom: 0.125rem;
@@ -237,8 +237,74 @@
}
}
tr:last-child > td:last-child > .field {
bottom: 2.125rem;
.index-bar {
position: absolute;
bottom: 1.8rem;
left: calc(var(--main-padding) * -1);
right: 50px;
z-index: 20;
font-size: var(--font-size-xs);
line-height: var(--line-height-xs);
text-transform: uppercase;
pointer-events: none;
&::before,
&::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
width: var(--main-padding);
z-index: 1;
pointer-events: none;
}
&::before {
left: 0;
background-image: linear-gradient(
to left,
transparent,
var(--background-color)
);
}
&::after {
right: 0;
background-image: linear-gradient(
to right,
transparent,
var(--background-color)
);
}
> div {
display: flex;
align-items: center;
overflow-x: auto;
scrollbar-width: thin;
padding: 0 var(--main-padding);
padding-top: 0.375rem;
padding-bottom: 0.375rem;
> * {
pointer-events: auto;
flex-shrink: 0;
}
> span {
padding: 0 0.75rem;
}
button {
color: var(--off-color);
padding: 0.375rem;
margin: -0.375rem 0rem;
&:hover {
color: var(--color);
}
}
}
}
button.capture {
+8 -7
View File
@@ -1,13 +1,13 @@
:root {
color-scheme: light dark;
/*--white: oklch(90% 0 0);*/
--white: oklch(93.3% 0.006 75);
/*oklch(0.9333 0.0059 59.65)*/
--light-gray: oklch(85% 0.01 75);
--gray: oklch(60% 0.01 44);
--dark-gray: oklch(25% 0.006 90);
--black: oklch(16.5% 0.006 90);
--white: oklch(95% 0 0);
--dark-white: oklch(92.5% 0 0);
--light-gray: oklch(90% 0 0);
--gray: oklch(55% 0 0);
--dark-gray: oklch(20% 0 0);
--light-black: oklch(17.5% 0 0);
--black: oklch(15% 0 0);
/*oklch(0.2038 0.0076 196.57)*/
--red: oklch(0.607 0.241 26.328);
--orange: oklch(67.64% 0.191 44.41);
@@ -31,6 +31,7 @@
--color: light-dark(var(--black), var(--white));
--off-color: var(--gray);
--border-color: light-dark(var(--light-gray), var(--dark-gray));
--off-border-color: light-dark(var(--dark-white), var(--light-black));
--font-size-xs: 0.75rem;
--line-height-xs: calc(1 / 0.75);