Compare commits

..

8 Commits

Author SHA1 Message Date
nym21 f50374f983 release: v0.0.93 2025-08-26 23:34:57 +02:00
nym21 82ceb7f021 cargo: update 2025-08-26 23:34:38 +02:00
nym21 0aba3bc1d8 release: v0.0.92 2025-08-26 22:27:16 +02:00
nym21 f6c984ff3c website: add screenshot feature 2025-08-26 22:26:55 +02:00
nym21 4091ab6b6c release: v0.0.91 2025-08-26 08:31:30 +02:00
nym21 fb9fd5b51a global: add datasets and charts + fixes 2025-08-26 08:31:08 +02:00
nym21 9389700a01 release: v0.0.90 2025-08-24 17:05:51 +02:00
nym21 016c1b2233 changelog: update 2025-08-24 17:05:35 +02:00
32 changed files with 2694 additions and 886 deletions
+21 -32
View File
@@ -4,46 +4,35 @@
All notable changes to the Bitcoin Research Kit (BRK) project will be documented in this file.
## Unreleased
## [v0.0.89](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.89) - 2025-08-24
### Documentation
- **Enhanced**: Comprehensive rewrite of all crate README files for improved clarity and developer experience
- **Updated**: Main project README with better structure and documentation
- **Standardized**: README format across all workspace crates with consistent styling
- **Improved**: Documentation structure and organization across the entire project
- **Added**: More detailed descriptions for each crate's purpose and functionality
- **Enhanced**: Comprehensive rewrite of all crate README files for improved clarity and developer experience across all workspace crates: [brk](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk/README.md), [brk_bundler](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_bundler/README.md), [brk_computer](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_computer/README.md), [brk_cli](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_cli/README.md), [brk_error](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_error/README.md)
- **Updated**: Main project [README](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/README.md) with better structure and comprehensive architecture documentation
- **Standardized**: README format across all workspace crates with consistent styling and improved developer experience
- **Added**: More detailed descriptions for each crate's purpose and functionality with examples
### Computer Module
- **Refactored**: Converted ComputedFrom pattern to LazyFrom pattern for improved performance
- **Added**: New LazyVecBuilder implementation for on-demand computation
- **Restructured**: Computer module stateful operations with improved organization
- **Cleaned**: Removed hardcoded format specifications, now using defaults
- **Optimized**: Vector storage operations with lazy computation patterns
- **Refactored**: Address type handling in stateful computations
- **Reorganized**: Stateful module structure for better maintainability
- **Enhanced**: Builder patterns for grouped vector operations
- **Improved**: Memory efficiency through lazy computation strategies
### Computer Module - Major Refactoring
- **Refactored**: Converted ComputedFrom pattern to [LazyFrom pattern](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_computer/src/grouped/builder_lazy.rs) for improved performance and memory efficiency
- **Added**: New [LazyVecBuilder implementation](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_computer/src/grouped/builder_lazy.rs) for on-demand computation with support for first, average, sum, max, min, last, and cumulative operations
- **Restructured**: Computer module [stateful operations](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_computer/src/stateful/mod.rs) with improved organization and rollback functionality
- **Enhanced**: [Address type organization](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_computer/src/stateful/addresstype/) into dedicated module structure
- **Improved**: Stateful rollback functionality for better error recovery and blockchain reorganization handling
### Vector Storage
- **Enhanced**: Vector storage engine development workflow
- **Optimized**: Lazy vector computation patterns for better performance
- **Improved**: Vector builder architecture with lazy evaluation
### Data Structures
- **Added**: New [StoredI16](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_structs/src/structs/stored_i16.rs) data type for efficient 16-bit signed integer storage with compression support
- **Enhanced**: StoredF32 with additional [utility methods](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/crates/brk_structs/src/structs/stored_f32.rs) for mathematical operations
### Dependencies
- **Updated**: Rayon from 1.10.0 to 1.11.0 for improved parallel processing
- **Migrated**: vecdb from external version 0.1.0 to local development path for better development workflow
- **Reorganized**: Workspace dependency structure for consistency
### Website Frontend
- **Updated**: Upgraded solid-signals from v0.3.2 to [solidjs-signals v0.4.1](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/websites/default/packages/solidjs-signals/) for improved reactivity and performance
- **Enhanced**: Frontend package management and dependency organization
- **Improved**: Chart rendering performance with updated signal library
### Build System
- **Added**: Rust toolchain version specification (1.89) to workspace configuration
- **Enhanced**: Development environment consistency with toolchain pinning
- **Updated**: All crate versions from 0.0.88 to 0.0.89 across the workspace
- **Enhanced**: [Cargo.toml](https://github.com/bitcoinresearchkit/brk/blob/v0.0.89/Cargo.toml) dependency management and version consistency
### Server Module
- **Improved**: API interface implementations
- **Enhanced**: File serving functionality
### Indexer Module
- **Optimized**: Integration with updated computer module patterns
[View changes](https://github.com/bitcoinresearchkit/brk/compare/v0.0.88...v0.0.89)
## [v0.0.88](https://github.com/bitcoinresearchkit/brk/releases/tag/v0.0.88) - 2025-08-10
Generated
+35 -32
View File
@@ -487,7 +487,7 @@ dependencies = [
[[package]]
name = "brk"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"brk_bundler",
"brk_cli",
@@ -506,7 +506,7 @@ dependencies = [
[[package]]
name = "brk_bundler"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"brk_rolldown",
"log",
@@ -517,7 +517,7 @@ dependencies = [
[[package]]
name = "brk_cli"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"bitcoincore-rpc",
"brk_bundler",
@@ -542,7 +542,7 @@ dependencies = [
[[package]]
name = "brk_computer"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -564,7 +564,7 @@ dependencies = [
[[package]]
name = "brk_error"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"bitcoincore-rpc",
"fjall",
@@ -577,7 +577,7 @@ dependencies = [
[[package]]
name = "brk_fetcher"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"brk_error",
"brk_logger",
@@ -589,7 +589,7 @@ dependencies = [
[[package]]
name = "brk_indexer"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -606,7 +606,7 @@ dependencies = [
[[package]]
name = "brk_interface"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"brk_computer",
"brk_error",
@@ -623,7 +623,7 @@ dependencies = [
[[package]]
name = "brk_logger"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"env_logger",
"jiff",
@@ -633,7 +633,7 @@ dependencies = [
[[package]]
name = "brk_mcp"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"axum",
"brk_interface",
@@ -643,7 +643,7 @@ dependencies = [
[[package]]
name = "brk_parser"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -1021,7 +1021,7 @@ dependencies = [
[[package]]
name = "brk_server"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"axum",
"bitcoincore-rpc",
@@ -1045,7 +1045,7 @@ dependencies = [
[[package]]
name = "brk_store"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"brk_error",
"brk_structs",
@@ -1068,7 +1068,7 @@ dependencies = [
[[package]]
name = "brk_structs"
version = "0.0.89"
version = "0.0.93"
dependencies = [
"bitcoin",
"bitcoincore-rpc",
@@ -1198,18 +1198,18 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.45"
version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.44"
version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
dependencies = [
"anstream",
"anstyle",
@@ -1735,9 +1735,9 @@ dependencies = [
[[package]]
name = "fancy-regex"
version = "0.14.0"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
checksum = "bf04c5ec15464ace8355a7b440a33aece288993475556d461154d7a62ad9947c"
dependencies = [
"bit-set",
"regex-automata",
@@ -3054,9 +3054,9 @@ dependencies = [
[[package]]
name = "oxc_resolver"
version = "11.6.2"
version = "11.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d84cdcd778d15db5b21cc61baf79ac55cee97e7feb725b2664453979ba3cd76"
checksum = "0784392356fa78dd4c9047fce7c9046c141f8990736792d00310bf40935b1870"
dependencies = [
"cfg-if",
"indexmap 2.11.0",
@@ -3407,9 +3407,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "pnp"
version = "0.12.1"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab3167cbab15e437e9c7db8a4cf613eb4a77583d4327a8964d50fedd6cf364bd"
checksum = "e001eb06e9523653f2a88adb94e286ef5d7acfe64158b21bf57a6f6bc3ba65ca"
dependencies = [
"byteorder",
"clean-path",
@@ -3602,9 +3602,12 @@ dependencies = [
[[package]]
name = "rapidhash"
version = "3.1.0"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efee4b7317469c6c6e7fdeee3d094313af846a97678d6ed971d83a852d730083"
checksum = "9d126f24bc587b080d7823a831d0fe832090a606fd3fd244ecbe23c561104ab8"
dependencies = [
"rustversion",
]
[[package]]
name = "rayon"
@@ -3895,9 +3898,9 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
[[package]]
name = "seqdb"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567d567faf0a305d66eb0419fc50ba8faec58a3baa8624d7a1fd1c798395044c"
checksum = "c54ab988c96efa9d275ca2b12bf2d3c6adec993b8e82ea31a88c984abdaa14fa"
dependencies = [
"libc",
"log",
@@ -4723,9 +4726,9 @@ checksum = "8f54a172d0620933a27a4360d3db3e2ae0dd6cceae9730751a036bbf182c4b23"
[[package]]
name = "vecdb"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "236493fb96b34fe7900a3bf62cbb1bd1c5991bc9db563dbbe7ce5cc8c3037cb1"
checksum = "3e5c4ec34c376be3a41435eeb7672d0ea0e9c1d60c5d1d90218912588f91abea"
dependencies = [
"ctrlc",
"log",
@@ -4743,9 +4746,9 @@ dependencies = [
[[package]]
name = "vecdb_derive"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d117eb4f82c996a7de2dd5bca1f53da20fa39ae97646405af29eccde67be041"
checksum = "778c4874c05822465e28cae6a7dead593a73124ec80afb85b85adae5ac883368"
dependencies = [
"quote",
"syn 2.0.106",
+16 -15
View File
@@ -4,7 +4,7 @@ members = ["crates/*"]
package.description = "The Bitcoin Research Kit is a suite of tools designed to extract, compute and display data stored on a Bitcoin Core node"
package.license = "MIT"
package.edition = "2024"
package.version = "0.0.89"
package.version = "0.0.93"
package.homepage = "https://bitcoinresearchkit.org"
package.repository = "https://github.com/bitcoinresearchkit/brk"
package.readme = "README.md"
@@ -26,19 +26,19 @@ inherits = "release"
axum = "0.8.4"
bitcoin = { version = "0.32.7", features = ["serde"] }
bitcoincore-rpc = "0.19.0"
brk_bundler = { version = "0.0.89", path = "crates/brk_bundler" }
brk_cli = { version = "0.0.89", path = "crates/brk_cli" }
brk_computer = { version = "0.0.89", path = "crates/brk_computer" }
brk_error = { version = "0.0.89", path = "crates/brk_error" }
brk_fetcher = { version = "0.0.89", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.0.89", path = "crates/brk_indexer" }
brk_interface = { version = "0.0.89", path = "crates/brk_interface" }
brk_logger = { version = "0.0.89", path = "crates/brk_logger" }
brk_mcp = { version = "0.0.89", path = "crates/brk_mcp" }
brk_parser = { version = "0.0.89", path = "crates/brk_parser" }
brk_server = { version = "0.0.89", path = "crates/brk_server" }
brk_store = { version = "0.0.89", path = "crates/brk_store" }
brk_structs = { version = "0.0.89", path = "crates/brk_structs" }
brk_bundler = { version = "0.0.93", path = "crates/brk_bundler" }
brk_cli = { version = "0.0.93", path = "crates/brk_cli" }
brk_computer = { version = "0.0.93", path = "crates/brk_computer" }
brk_error = { version = "0.0.93", path = "crates/brk_error" }
brk_fetcher = { version = "0.0.93", path = "crates/brk_fetcher" }
brk_indexer = { version = "0.0.93", path = "crates/brk_indexer" }
brk_interface = { version = "0.0.93", path = "crates/brk_interface" }
brk_logger = { version = "0.0.93", path = "crates/brk_logger" }
brk_mcp = { version = "0.0.93", path = "crates/brk_mcp" }
brk_parser = { version = "0.0.93", path = "crates/brk_parser" }
brk_server = { version = "0.0.93", path = "crates/brk_server" }
brk_store = { version = "0.0.93", path = "crates/brk_store" }
brk_structs = { version = "0.0.93", path = "crates/brk_structs" }
byteview = "=0.6.1"
derive_deref = "1.1.1"
fjall = "2.11.2"
@@ -53,7 +53,7 @@ serde_derive = "1.0.219"
serde_json = { version = "1.0.143", features = ["float_roundtrip"] }
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
# vecdb = { path = "../seqdb/crates/vecdb", features = ["derive"]}
vecdb = { version = "0.2.4", features = ["derive"]}
vecdb = { version = "0.2.5", features = ["derive"]}
zerocopy = "0.8.26"
zerocopy-derive = "0.8.26"
@@ -66,6 +66,7 @@ tag-message = "release: v{{version}}"
[workspace.metadata.dist]
cargo-dist-version = "0.29.0"
ci = "github"
allow-dirty = ["ci"]
installers = []
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"]
rust-toolchain-version = "1.89"
+1 -12
View File
@@ -10,30 +10,19 @@
- pull latest version and notify is out of date
- _computer_
- **add rollback of states (in stateful)**
- remove configurable format (raw/compressed) and chose sane ones instead
- linear reads: compressed (height/date/... + txindex_to_height + txindex_to_version + ...)
- random reads: raw (outputindex_to_value + ...)
- add prices paid by percentile (percentile cost basis) back
- add costs basis by percentile (percentile cost basis) back
- add support for per index computation
- fix min feerate which is always ZERO due to coinbase transaction
- before computing multiple sources check their length, panic if not equal
- add oracle price dataset (https://utxo.live/oracle/UTXOracle.py)
- add address counts relative to all datasets
- make decade, quarter, year datasets `computed` instead of `eager`
- add 6 months (semester) interval datasets to builder
- some datasets in `indexes` can probably be removed
- add revived/sent supply datasets
- add `in-sats` version of all price datasets (average and co)
- add `p2pk` group (sum of `p2pk33` and `p2pk65`)
- add chopiness datasets
- add utxo count, address count, supply data for by reused addresses in groups by address type
- add more date ranges (3-6 months and more)
- add puell multiple dataset
- add pi cycle dataset
- add emas of price
- add 7d and 30d ema to sell side risk ratio and sopr
- don't compute everything for all cohorts as some datasets combinations are irrelevant
- addresses/utxos by amount don't need mvrvz for example
- add all possible charts from:
- https://mainnet.observer
- https://glassnode.com
+1 -1
View File
@@ -20,7 +20,7 @@ brk_logger = { workspace = true }
brk_parser = { workspace = true }
brk_server = { workspace = true }
vecdb = { workspace = true }
clap = { version = "4.5.45", features = ["string"] }
clap = { version = "4.5.46", features = ["string"] }
clap_derive = "4.5.45"
color-eyre = "0.6.5"
log = { workspace = true }
+75 -185
View File
@@ -26,6 +26,8 @@ pub struct Vecs {
pub constant_4: ComputedVecsFromHeight<StoredU16>,
pub constant_50: ComputedVecsFromHeight<StoredU16>,
pub constant_100: ComputedVecsFromHeight<StoredU16>,
pub constant_144: ComputedVecsFromHeight<StoredU16>,
pub constant_600: ComputedVecsFromHeight<StoredU16>,
pub constant_minus_1: ComputedVecsFromHeight<StoredI16>,
pub constant_minus_2: ComputedVecsFromHeight<StoredI16>,
pub constant_minus_3: ComputedVecsFromHeight<StoredI16>,
@@ -93,6 +95,22 @@ impl Vecs {
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_144: ComputedVecsFromHeight::forced_import(
&db,
"constant_144",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_600: ComputedVecsFromHeight::forced_import(
&db,
"constant_600",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
VecBuilderOptions::default().add_last(),
)?,
constant_minus_1: ComputedVecsFromHeight::forced_import(
&db,
"constant_minus_1",
@@ -149,192 +167,62 @@ impl Vecs {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.constant_0.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(0)),
exit,
)?;
Ok(())
},
)?;
[
(&mut self.constant_0, 0),
(&mut self.constant_1, 1),
(&mut self.constant_2, 2),
(&mut self.constant_3, 3),
(&mut self.constant_4, 4),
(&mut self.constant_50, 50),
(&mut self.constant_100, 100),
(&mut self.constant_144, 144),
(&mut self.constant_600, 600),
]
.into_iter()
.try_for_each(|(vec, value)| {
vec.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(value)),
exit,
)?;
Ok(())
},
)
})?;
self.constant_1.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(1)),
exit,
)?;
Ok(())
},
)?;
self.constant_2.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(2)),
exit,
)?;
Ok(())
},
)?;
self.constant_3.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(3)),
exit,
)?;
Ok(())
},
)?;
self.constant_4.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(4)),
exit,
)?;
Ok(())
},
)?;
self.constant_50.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(50)),
exit,
)?;
Ok(())
},
)?;
self.constant_100.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredU16::new(100)),
exit,
)?;
Ok(())
},
)?;
self.constant_minus_1.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredI16::new(-1)),
exit,
)?;
Ok(())
},
)?;
self.constant_minus_2.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredI16::new(-2)),
exit,
)?;
Ok(())
},
)?;
self.constant_minus_3.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredI16::new(-3)),
exit,
)?;
Ok(())
},
)?;
self.constant_minus_4.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredI16::new(-4)),
exit,
)?;
Ok(())
},
)?;
[
(&mut self.constant_minus_1, -1),
(&mut self.constant_minus_2, -2),
(&mut self.constant_minus_3, 3),
(&mut self.constant_minus_4, 4),
]
.into_iter()
.try_for_each(|(vec, value)| {
vec.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|vec, _, indexes, starting_indexes, exit| {
vec.compute_to(
starting_indexes.height,
indexes.height_to_date.len(),
indexes.height_to_date.version(),
|i| (i, StoredI16::new(value)),
exit,
)?;
Ok(())
},
)
})?;
Ok(())
}
@@ -348,6 +236,8 @@ impl Vecs {
self.constant_4.vecs(),
self.constant_50.vecs(),
self.constant_100.vecs(),
self.constant_144.vecs(),
self.constant_600.vecs(),
self.constant_minus_1.vecs(),
self.constant_minus_2.vecs(),
self.constant_minus_3.vecs(),
@@ -320,26 +320,25 @@ impl ComputedRatioVecsFromDateIndex {
price: &price::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
date_to_price_opt: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
price_opt: Option<&impl AnyIterableVec<DateIndex, Dollars>>,
) -> Result<()> {
let date_to_price = date_to_price_opt.unwrap_or_else(|| unsafe {
let closes = price.timeindexes_to_close.dateindex.as_ref().unwrap();
let price = price_opt.unwrap_or_else(|| unsafe {
std::mem::transmute(&self.price.as_ref().unwrap().dateindex)
});
let closes = price.timeindexes_to_close.dateindex.as_ref().unwrap();
self.ratio.compute_all(
indexer,
indexes,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
let mut price = date_to_price.iter();
v.compute_transform(
v.compute_transform2(
starting_indexes.dateindex,
closes,
|(i, close, ..)| {
let price = price.unwrap_get_inner(i);
price,
|(i, close, price, ..)| {
if price == Dollars::ZERO {
(i, StoredF32::from(1.0))
} else {
@@ -553,7 +552,7 @@ impl ComputedRatioVecsFromDateIndex {
None as Option<&EagerVec<_, _>>,
)?;
let date_to_price = date_to_price_opt.unwrap_or_else(|| unsafe {
let date_to_price = price_opt.unwrap_or_else(|| unsafe {
std::mem::transmute(&self.price.as_ref().unwrap().dateindex)
});
+2
View File
@@ -52,6 +52,8 @@ impl Computer {
indexer: &Indexer,
fetcher: Option<Fetcher>,
) -> Result<Self> {
info!("Importing computer...");
let computed_path = outputs_path.join("computed");
let indexes =
+220 -17
View File
@@ -48,6 +48,21 @@ pub struct Vecs {
pub indexes_to_200w_sma: ComputedRatioVecsFromDateIndex,
pub indexes_to_4y_sma: ComputedRatioVecsFromDateIndex,
pub indexes_to_1w_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_8d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_13d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_21d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_1m_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_34d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_55d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_89d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_144d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_200d_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_1y_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_2y_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_200w_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_4y_ema: ComputedRatioVecsFromDateIndex,
pub indexes_to_200d_sma_x2_4: ComputedVecsFromDateIndex<Dollars>,
pub indexes_to_200d_sma_x0_8: ComputedVecsFromDateIndex<Dollars>,
@@ -350,6 +365,119 @@ impl Vecs {
true,
)?,
indexes_to_1w_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"1w_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_8d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"8d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_13d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"13d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_21d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"21d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_1m_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"1m_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_34d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"34d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_55d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"55d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_89d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"89d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_144d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"144d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_200d_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"200d_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_1y_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"1y_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_2y_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"2y_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_200w_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"200w_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
indexes_to_4y_ema: ComputedRatioVecsFromDateIndex::forced_import(
&db,
"4y_ema",
Source::Compute,
version + VERSION + Version::ZERO,
indexes,
true,
)?,
_1d_returns: ComputedVecsFromDateIndex::forced_import(
&db,
"1d_returns",
@@ -1827,25 +1955,69 @@ impl Vecs {
thread::scope(|s| -> Result<()> {
[
(&mut self.indexes_to_1w_sma, 7),
(&mut self.indexes_to_8d_sma, 8),
(&mut self.indexes_to_13d_sma, 13),
(&mut self.indexes_to_21d_sma, 21),
(&mut self.indexes_to_1m_sma, 30),
(&mut self.indexes_to_34d_sma, 34),
(&mut self.indexes_to_55d_sma, 55),
(&mut self.indexes_to_89d_sma, 89),
(&mut self.indexes_to_144d_sma, 144),
(&mut self.indexes_to_200d_sma, 200),
(&mut self.indexes_to_1y_sma, 365),
(&mut self.indexes_to_2y_sma, 2 * 365),
(&mut self.indexes_to_200w_sma, 200 * 7),
(&mut self.indexes_to_4y_sma, 4 * 365),
(&mut self.indexes_to_1w_sma, &mut self.indexes_to_1w_ema, 7),
(&mut self.indexes_to_8d_sma, &mut self.indexes_to_8d_ema, 8),
(
&mut self.indexes_to_13d_sma,
&mut self.indexes_to_13d_ema,
13,
),
(
&mut self.indexes_to_21d_sma,
&mut self.indexes_to_21d_ema,
21,
),
(&mut self.indexes_to_1m_sma, &mut self.indexes_to_1m_ema, 30),
(
&mut self.indexes_to_34d_sma,
&mut self.indexes_to_34d_ema,
34,
),
(
&mut self.indexes_to_55d_sma,
&mut self.indexes_to_55d_ema,
55,
),
(
&mut self.indexes_to_89d_sma,
&mut self.indexes_to_89d_ema,
89,
),
(
&mut self.indexes_to_144d_sma,
&mut self.indexes_to_144d_ema,
144,
),
(
&mut self.indexes_to_200d_sma,
&mut self.indexes_to_200d_ema,
200,
),
(
&mut self.indexes_to_1y_sma,
&mut self.indexes_to_1y_ema,
365,
),
(
&mut self.indexes_to_2y_sma,
&mut self.indexes_to_2y_ema,
2 * 365,
),
(
&mut self.indexes_to_200w_sma,
&mut self.indexes_to_200w_ema,
200 * 7,
),
(
&mut self.indexes_to_4y_sma,
&mut self.indexes_to_4y_ema,
4 * 365,
),
]
.into_iter()
.for_each(|(vecs, sma)| {
.for_each(|(sma, ema, days)| {
s.spawn(move || -> Result<()> {
vecs.compute_all(
sma.compute_all(
indexer,
indexes,
price,
@@ -1855,7 +2027,24 @@ impl Vecs {
v.compute_sma(
starting_indexes.dateindex,
price.timeindexes_to_close.dateindex.as_ref().unwrap(),
sma,
days,
exit,
)?;
Ok(())
},
)?;
ema.compute_all(
indexer,
indexes,
price,
starting_indexes,
exit,
|v, _, _, starting_indexes, exit| {
v.compute_ema(
starting_indexes.dateindex,
price.timeindexes_to_close.dateindex.as_ref().unwrap(),
days,
exit,
)?;
Ok(())
@@ -1935,6 +2124,20 @@ impl Vecs {
self.indexes_to_2y_sma.vecs(),
self.indexes_to_200w_sma.vecs(),
self.indexes_to_4y_sma.vecs(),
self.indexes_to_1w_ema.vecs(),
self.indexes_to_8d_ema.vecs(),
self.indexes_to_13d_ema.vecs(),
self.indexes_to_21d_ema.vecs(),
self.indexes_to_1m_ema.vecs(),
self.indexes_to_34d_ema.vecs(),
self.indexes_to_55d_ema.vecs(),
self.indexes_to_89d_ema.vecs(),
self.indexes_to_144d_ema.vecs(),
self.indexes_to_200d_ema.vecs(),
self.indexes_to_1y_ema.vecs(),
self.indexes_to_2y_ema.vecs(),
self.indexes_to_200w_ema.vecs(),
self.indexes_to_4y_ema.vecs(),
self.indexes_to_200d_sma_x0_8.vecs(),
self.indexes_to_200d_sma_x2_4.vecs(),
self.price_1d_ago.vecs(),
+75 -1
View File
@@ -50,10 +50,14 @@ pub struct Vecs {
pub indexes_to_coinblocks_destroyed: ComputedVecsFromHeight<StoredF64>,
pub indexes_to_coindays_destroyed: ComputedVecsFromHeight<StoredF64>,
pub dateindex_to_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_spent_output_profit_ratio_7d_ema: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_adjusted_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_adjusted_spent_output_profit_ratio_7d_ema:
Option<EagerVec<DateIndex, StoredF32>>,
pub indexes_to_realized_cap_30d_change: Option<ComputedVecsFromDateIndex<Dollars>>,
pub dateindex_to_sell_side_risk_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_spent_output_profit_ratio: Option<EagerVec<DateIndex, StoredF32>>,
pub dateindex_to_sell_side_risk_ratio_7d_ema: Option<EagerVec<DateIndex, StoredF32>>,
pub indexes_to_adjusted_value_created: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_adjusted_value_destroyed: Option<ComputedVecsFromHeight<Dollars>>,
pub indexes_to_negative_realized_loss: Option<ComputedVecsFromHeight<Dollars>>,
@@ -599,6 +603,15 @@ impl Vecs {
)
.unwrap()
}),
dateindex_to_sell_side_risk_ratio_7d_ema: compute_dollars.then(|| {
EagerVec::forced_import(
db,
&suffix("sell_side_risk_ratio_7d_ema"),
version + VERSION + Version::ONE,
format,
)
.unwrap()
}),
dateindex_to_spent_output_profit_ratio: compute_dollars.then(|| {
EagerVec::forced_import(
db,
@@ -608,6 +621,15 @@ impl Vecs {
)
.unwrap()
}),
dateindex_to_spent_output_profit_ratio_7d_ema: compute_dollars.then(|| {
EagerVec::forced_import(
db,
&suffix("spent_output_profit_ratio_7d_ema"),
version + VERSION + Version::ZERO,
format,
)
.unwrap()
}),
dateindex_to_adjusted_spent_output_profit_ratio: (compute_dollars && compute_adjusted).then(|| {
EagerVec::forced_import(
db,
@@ -617,6 +639,15 @@ impl Vecs {
)
.unwrap()
}),
dateindex_to_adjusted_spent_output_profit_ratio_7d_ema: (compute_dollars && compute_adjusted).then(|| {
EagerVec::forced_import(
db,
&suffix("adjusted_spent_output_profit_ratio_7d_ema"),
version + VERSION + Version::ZERO,
format,
)
.unwrap()
}),
height_to_halved_supply_value: ComputedHeightValueVecs::forced_import(
db,
&suffix("halved_supply"),
@@ -2177,6 +2208,18 @@ impl Vecs {
exit,
)?;
self.dateindex_to_spent_output_profit_ratio_7d_ema
.as_mut()
.unwrap()
.compute_ema(
starting_indexes.dateindex,
self.dateindex_to_spent_output_profit_ratio
.as_ref()
.unwrap(),
7,
exit,
)?;
self.dateindex_to_sell_side_risk_ratio
.as_mut()
.unwrap()
@@ -2195,6 +2238,16 @@ impl Vecs {
exit,
)?;
self.dateindex_to_sell_side_risk_ratio_7d_ema
.as_mut()
.unwrap()
.compute_ema(
starting_indexes.dateindex,
self.dateindex_to_sell_side_risk_ratio.as_ref().unwrap(),
7,
exit,
)?;
self.indexes_to_supply_in_profit
.as_mut()
.unwrap()
@@ -2757,6 +2810,18 @@ impl Vecs {
.unwrap_sum(),
exit,
)?;
self.dateindex_to_adjusted_spent_output_profit_ratio_7d_ema
.as_mut()
.unwrap()
.compute_ema(
starting_indexes.dateindex,
self.dateindex_to_adjusted_spent_output_profit_ratio
.as_ref()
.unwrap(),
7,
exit,
)?;
}
}
@@ -2823,9 +2888,15 @@ impl Vecs {
self.dateindex_to_spent_output_profit_ratio
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dateindex_to_spent_output_profit_ratio_7d_ema
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dateindex_to_adjusted_spent_output_profit_ratio
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.dateindex_to_adjusted_spent_output_profit_ratio_7d_ema
.as_ref()
.map_or(vec![], |v| vec![v as &dyn AnyCollectableVec]),
self.indexes_to_value_destroyed
.as_ref()
.map_or(vec![], |v| v.vecs()),
@@ -2844,6 +2915,9 @@ impl Vecs {
self.dateindex_to_sell_side_risk_ratio
.as_ref()
.map_or(vec![], |v| vec![v]),
self.dateindex_to_sell_side_risk_ratio_7d_ema
.as_ref()
.map_or(vec![], |v| vec![v]),
self.height_to_supply_in_profit
.as_ref()
.map_or(vec![], |v| vec![v]),
@@ -38,7 +38,7 @@ impl Vecs {
db,
None,
format,
version + VERSION + Version::ZERO,
version + VERSION + Version::ONE,
indexes,
price,
None,
@@ -1644,7 +1644,13 @@ impl Vecs {
let by_size_range = self.0.amount_range.as_vec();
[
vec![(&mut self.0.all.1, self.0.epoch.vecs().to_vec())],
vec![(
&mut self.0.all.1,
by_date_range
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>(),
)],
self.0
.min_age
.as_mut_vec()
+9 -9
View File
@@ -36,20 +36,20 @@ pub struct Indexer {
impl Indexer {
pub fn forced_import(outputs_dir: &Path) -> Result<Self> {
info!("Importing indexer...");
let db = Database::open(&outputs_dir.join("indexed/vecs"))?;
db.set_min_len(PAGE_SIZE * 50_000_000)?;
info!("Opened database");
let vecs = Vecs::forced_import(&db, VERSION + Version::ZERO)?;
info!("Imported vecs");
db.set_min_len(PAGE_SIZE * 50_000_000)?;
let stores =
Stores::forced_import(&outputs_dir.join("indexed/stores"), VERSION + Version::ZERO)?;
info!("Imported stores");
Ok(Self {
vecs,
stores: Stores::forced_import(
&outputs_dir.join("indexed/stores"),
VERSION + Version::ZERO,
)?,
db,
})
Ok(Self { vecs, stores, db })
}
pub fn index(
+1 -1
View File
@@ -17,7 +17,7 @@ vecdb = {workspace = true}
byteview = { workspace = true }
derive_deref = { workspace = true }
jiff = { workspace = true }
rapidhash = "3.1.0"
rapidhash = "4.0.0"
serde = { workspace = true }
serde_bytes = { workspace = true }
zerocopy = { workspace = true }
+10 -1
View File
@@ -1,4 +1,7 @@
use std::ops::{Add, AddAssign, Div};
use std::{
iter::Sum,
ops::{Add, AddAssign, Div},
};
use derive_deref::{Deref, DerefMut};
use serde::{Serialize, Serializer, ser::SerializeTuple};
@@ -566,6 +569,12 @@ where
}
}
impl Sum for Close<Dollars> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
Self(Dollars::from(iter.map(|v| f64::from(v.0)).sum::<f64>()))
}
}
// impl<T> Mul<usize> for Close<T>
// where
// T: Mul<usize, Output = T>,
+1 -1
View File
@@ -141,7 +141,7 @@ impl Mul<StoredF64> for Sats {
impl Sum for Sats {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let sats: u64 = iter.map(|sats| sats.0).sum();
Sats::from(sats)
Self::from(sats)
}
}
@@ -2,6 +2,7 @@ use core::panic;
use std::{
cmp::Ordering,
f32,
iter::Sum,
ops::{Add, AddAssign, Div, Mul, Sub},
};
@@ -178,3 +179,9 @@ impl Printable for StoredF32 {
&["f32"]
}
}
impl Sum for StoredF32 {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
Self(iter.map(|v| v.0).sum::<f32>())
}
}
+5 -7
View File
@@ -35,13 +35,13 @@
chartOptions,
);
const baseURL = "https://bitview.space/api";
const minDate = "2022-11-09";
// const minDate = "2023-11-09";
let dates = await (
await fetch(
"https://next.bitray.xyz/api/vecs/dateindex-to-date?from=-10000",
)
await fetch(`${baseURL}/vecs/dateindex-to-date?from=-10000`)
).json();
let i = 0;
console.log(
@@ -54,9 +54,7 @@
const from = dates.length;
const ohlcs = await (
await fetch(
`https://next.bitray.xyz/api/vecs/dateindex-to-ohlc?from=-${from}`,
)
await fetch(`${baseURL}/vecs/dateindex-to-ohlc?from=-${from}`)
).json();
chart.addSeries(lc.CandlestickSeries, {}, 0).setData(
@@ -74,7 +72,7 @@
const sopr = (
await (
await fetch(
`https://next.bitray.xyz/api/vecs/dateindex-to-utxos-up-to-${cohort}-old-spent-output-profit-ratio?from=-${from}`,
`${baseURL}/vecs/dateindex-to-utxos-up-to-${cohort}-old-spent-output-profit-ratio?from=-${from}`,
)
).json()
).map((v) => v - 1);
+64 -6
View File
@@ -215,7 +215,7 @@
--light-gray: oklch(90% 0.01 44);
--gray: oklch(60% 0.01 44);
--dark-gray: oklch(30% 0.01 44);
--black: oklch(17.5% 0.005 44);
--black: oklch(15% 0.005 44);
--red: oklch(0.607 0.241 26.328);
--orange: oklch(67.64% 0.191 44.41);
--amber: oklch(0.7175 0.1835 64.199);
@@ -285,6 +285,15 @@
font-style: normal;
}
/*@font-face {
font-family: "Lilex";
src: url("./assets/fonts/Lilex-Italic[wght]-2.601.woff2")
format("woff2");
font-weight: 100 900;
font-display: block;
font-style: normal;
}*/
/*
* ---
*/
@@ -438,9 +447,9 @@
h1,
h2 {
text-transform: uppercase;
font-size: var(--font-size-lg);
line-height: var(--line-height-lg);
font-weight: 375;
font-size: var(--font-size-xl);
line-height: var(--line-height-xl);
font-weight: 325;
}
h3 {
@@ -547,6 +556,10 @@
&:has(input:checked) {
color: var(--color);
}
[data-screenshot="true"] &:has(input:not(:checked)) {
display: none;
}
}
main {
@@ -962,7 +975,6 @@
flex-shrink: 0;
display: flex;
align-items: center;
gap: 1.5rem;
margin: -0.5rem var(--negative-main-padding);
padding: 0.75rem var(--main-padding);
overflow-x: auto;
@@ -980,13 +992,33 @@
margin-top: 2rem;
margin-bottom: 1rem;
button#screenshot {
width: 100%;
text-align: left;
padding-left: 1rem;
height: 100%;
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
[data-screenshot="true"] & {
display: none;
}
}
> legend {
gap: 1.5rem;
&:empty {
display: none;
}
> div {
[data-screenshot="true"] &:has(input:not(:checked)) {
display: none;
}
flex: 0;
height: 100%;
display: flex;
align-items: center;
@@ -1024,6 +1056,11 @@
padding-right: 0.375rem;
margin-left: -0.375rem;
margin-right: -0.375rem;
[data-screenshot="true"] & {
width: 0;
opacity: 0;
}
}
}
}
@@ -1109,6 +1146,16 @@
width: 100%;
min-height: 0;
padding: var(--main-padding);
background-color: var(--background-color);
p#domain {
position: absolute;
left: 2rem;
top: 0.75rem;
color: var(--gray);
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
}
header {
flex-shrink: 0;
@@ -1117,7 +1164,7 @@
white-space: nowrap;
overflow-x: auto;
padding-bottom: 1rem;
margin-bottom: -2rem;
margin-bottom: -1.5rem;
padding-left: var(--main-padding);
margin-left: var(--negative-main-padding);
padding-right: var(--main-padding);
@@ -1132,6 +1179,17 @@
flex: 1;
}
#interval {
> span {
display: none;
[data-screenshot="true"] & {
color: var(--gray);
display: inline-block;
}
}
}
> .chart > legend,
> fieldset {
z-index: 20;
+5 -1
View File
@@ -1,7 +1,7 @@
LICENSE
*.json
*webcomponent*
README.md
README*.md
cli*
extras/
*.cjs
@@ -9,3 +9,7 @@ dev.js
*.development*
*.iife.*
nano.*
worker.*
*.ts
*.mts
*.cts
@@ -1,215 +0,0 @@
declare class uFuzzy {
constructor(opts?: uFuzzy.Options);
/** search API composed of filter/info/sort, with a info/ranking threshold (1e3) and fast outOfOrder impl */
search(
haystack: string[],
needle: string,
/** limit how many terms will be permuted, default = 0; 5 will result in up to 5! (120) search iterations. be careful with this! */
outOfOrder?: number,
/** default = 1e3 */
infoThresh?: number,
preFiltered?: uFuzzy.HaystackIdxs | null,
): uFuzzy.SearchResult;
/** initial haystack filter, can accept idxs from previous prefix/typeahead match as optimization */
filter(
haystack: string[],
needle: string,
idxs?: uFuzzy.HaystackIdxs,
): uFuzzy.HaystackIdxs | null;
/** collects stats about pre-filtered matches, does additional filtering based on term boundary settings, finds highlight ranges */
info(
idxs: uFuzzy.HaystackIdxs,
haystack: string[],
needle: string,
): uFuzzy.Info;
/** performs final result sorting via Array.sort(), relying on Info */
sort(
info: uFuzzy.Info,
haystack: string[],
needle: string,
): uFuzzy.InfoIdxOrder;
/** utility for splitting needle into terms following defined interSplit/intraSplit opts. useful for out-of-order permutes */
split(needle: string, keepCase?: boolean): uFuzzy.Terms;
/** util for creating out-of-order permutations of a needle terms array */
static permute(arr: unknown[]): unknown[][];
/** util for replacing common diacritics/accents */
static latinize<T extends string[] | string>(strings: T): T;
/** util for highlighting matched substr parts of a result */
static highlight<TAccum = string, TMarkedPart = string>(
match: string,
ranges: number[],
mark?: (part: string, matched: boolean) => TMarkedPart,
accum?: TAccum,
append?: (accum: TAccum, part: TMarkedPart) => TAccum | undefined,
): TAccum;
}
export = uFuzzy;
declare namespace uFuzzy {
/** needle's terms */
export type Terms = string[];
/** subset of idxs of a haystack array */
export type HaystackIdxs = number[];
/** sorted order in which info facets should be iterated */
export type InfoIdxOrder = number[];
export type AbortedResult = [null, null, null];
export type FilteredResult = [uFuzzy.HaystackIdxs, null, null];
export type RankedResult = [
uFuzzy.HaystackIdxs,
uFuzzy.Info,
uFuzzy.InfoIdxOrder,
];
export type SearchResult = FilteredResult | RankedResult | AbortedResult;
/** partial RegExp */
type PartialRegExp = string;
/** what should be considered acceptable term bounds */
export const enum BoundMode {
/** will match 'man' substr anywhere. e.g. tasmania */
Any = 0,
/** will match 'man' at whitespace, punct, case-change, and alpha-num boundaries. e.g. mantis, SuperMan, fooManBar, 0007man */
Loose = 1,
/** will match 'man' at whitespace, punct boundaries only. e.g. mega man, walk_man, man-made, foo.man.bar */
Strict = 2,
}
export const enum IntraMode {
/** allows any number of extra char insertions within a term, but all term chars must be present for a match */
MultiInsert = 0,
/** allows for a single-char substitution, transposition, insertion, or deletion within terms (excluding first and last chars) */
SingleError = 1,
}
export type IntraSliceIdxs = [from: number, to: number];
type CompareFn = (a: string, b: string) => number;
export interface Options {
// whether regexps use a /u unicode flag
unicode?: boolean; // false
/** @deprecated renamed to opts.alpha */
letters?: PartialRegExp | null; // a-z
// regexp character class [] of chars which should be treated as letters (case insensitive)
alpha?: PartialRegExp | null; // a-z
/** term segmentation & punct/whitespace merging */
interSplit?: PartialRegExp; // '[^A-Za-z\\d']+'
intraSplit?: PartialRegExp | null; // '[a-z][A-Z]'
/** inter bounds that will be used to increase lft2/rgt2 info counters */
interBound?: PartialRegExp | null; // '[^A-Za-z\\d]'
/** intra bounds that will be used to increase lft1/rgt1 info counters */
intraBound?: PartialRegExp | null; // '[A-Za-z][0-9]|[0-9][A-Za-z]|[a-z][A-Z]'
/** inter-term modes, during .info() can discard matches when bounds conditions are not met */
interLft?: BoundMode; // 0
interRgt?: BoundMode; // 0
/** allowance between terms */
interChars?: PartialRegExp; // '.'
interIns?: number; // Infinity
/** allowance between chars within terms */
intraChars?: PartialRegExp; // '[a-z\\d]'
intraIns?: number; // 0
/** contractions detection */
intraContr?: PartialRegExp; // "'[a-z]{1,2}\\b"
/** error tolerance mode within terms. will clamp intraIns to 1 when set to SingleError */
intraMode?: IntraMode; // 0
/** which part of each term should tolerate errors (when intraMode: 1) */
intraSlice?: IntraSliceIdxs; // [1, Infinity]
/** max substitutions (when intraMode: 1) */
intraSub?: 0 | 1; // 0
/** max transpositions (when intraMode: 1) */
intraTrn?: 0 | 1; // 0
/** max omissions/deletions (when intraMode: 1) */
intraDel?: 0 | 1; // 0
/** can dynamically adjust error tolerance rules per term in needle (when intraMode: 1) */
intraRules?: (term: string) => {
intraSlice?: IntraSliceIdxs;
intraIns: 0 | 1;
intraSub: 0 | 1;
intraTrn: 0 | 1;
intraDel: 0 | 1;
};
/** post-filters matches during .info() based on cmp of term in needle vs partial match */
intraFilt?: (term: string, match: string, index: number) => boolean; // should this also accept WIP info?
/** default: toLocaleUpperCase() */
toUpper?: (str: string) => string;
/** default: toLocaleLowerCase() */
toLower?: (str: string) => string;
/** final sorting cmp when all other match metrics are equal */
compare?: CompareFn;
sort?: (
info: Info,
haystack: string[],
needle: string,
compare?: CompareFn,
) => InfoIdxOrder;
}
export interface Info {
/** matched idxs from haystack */
idx: HaystackIdxs;
/** match offsets */
start: number[];
/** number of left BoundMode.Strict term boundaries found */
interLft2: number[];
/** number of right BoundMode.Strict term boundaries found */
interRgt2: number[];
/** number of left BoundMode.Loose term boundaries found */
interLft1: number[];
/** number of right BoundMode.Loose term boundaries found */
interRgt1: number[];
/** total number of extra chars matched within all terms. higher = matched terms have more fuzz in them */
intraIns: number[];
/** total number of chars found in between matched terms. higher = terms are more sparse, have more fuzz in between them */
interIns: number[];
/** total number of matched contiguous chars (substrs but not necessarily full terms) */
chars: number[];
/** number of exactly-matched terms (intra = 0) where both lft and rgt landed on a BoundMode.Loose or BoundMode.Strict boundary */
terms: number[];
/** number of needle terms with case-sensitive partial matches */
cases: number[];
/** offset ranges within match for highlighting: [startIdx0, endIdx0, startIdx1, endIdx1,...] */
ranges: number[][];
}
}
export as namespace uFuzzy;
@@ -4,7 +4,7 @@
*
* uFuzzy.js (μFuzzy)
* A tiny, efficient fuzzy matcher that doesn't suck
* https://github.com/leeoniya/uFuzzy (v1.0.18)
* https://github.com/leeoniya/uFuzzy (v1.0.19)
*/
const cmp = (a, b) => a > b ? 1 : a < b ? -1 : 0;
@@ -925,40 +925,50 @@ function uFuzzy(opts) {
const latinize = (() => {
let accents = {
A: 'ÁÀÃÂÄĄ',
a: 'áàãâäą',
E: 'ÉÈÊËĖ',
e: 'éèêëę',
I: 'ÍÌÎÏĮ',
i: 'íìîïį',
A: 'ÁÀÃÂÄĄĂÅ',
a: 'áàãâäąăå',
E: 'ÉÈÊËĖĚ',
e: 'éèêëęě',
I: 'ÍÌÎÏĮİ',
i: 'íìîïįı',
O: 'ÓÒÔÕÖ',
o: 'óòôõö',
U: 'ÚÙÛÜŪŲ',
u: 'úùûüūų',
U: 'ÚÙÛÜŪŲŮŰ',
u: 'úùûüūųůű',
C: 'ÇČĆ',
c: 'çčć',
D: 'Ď',
d: 'ď',
G: 'Ğ',
g: 'ğ',
L: 'Ł',
l: 'ł',
N: 'ÑŃ',
n: 'ñń',
S: 'ŠŚ',
s: 'šś',
Z: 'ŻŹ',
z: 'żź'
N: 'ÑŃŇ',
n: 'ñńň',
S: 'ŠŚȘŞ',
s: 'šśșş',
T: 'ŢȚŤ',
t: 'ţțť',
Y: 'Ý',
y: 'ý',
Z: 'ŻŹŽ',
z: 'żźž'
};
let accentsMap = new Map();
// str.normalize("NFD").replace(/\p{Diacritic}/gu, "")
let accentsMap = {};
let accentsTpl = '';
for (let r in accents) {
accents[r].split('').forEach(a => {
accentsTpl += a;
accentsMap.set(a, r);
accentsMap[a] = r;
});
}
let accentsRe = new RegExp(`[${accentsTpl}]`, 'g');
let replacer = m => accentsMap.get(m);
let replacer = m => accentsMap[m];
return strings => {
if (typeof strings == 'string')
@@ -50,6 +50,8 @@ import {
const oklchToRGBA = createOklchToRGBA();
const lineWidth = /** @type {any} */ (1.5);
/**
* @param {Object} args
* @param {string} args.id
@@ -436,9 +438,9 @@ function createChartElement({
value: v,
};
} else {
if (sameTime) {
console.log(data[offsetedI]);
}
// if (sameTime) {
// console.log(data[offsetedI]);
// }
let [open, high, low, close] = v;
data[offsetedI] = {
time,
@@ -690,7 +692,7 @@ function createChartElement({
ichart.addSeries(
/** @type {SeriesDefinition<'Line'>} */ (LineSeries),
{
lineWidth: /** @type {any} */ (1.5),
lineWidth,
visible: defaultActive !== false,
priceLineVisible: false,
color: color(),
@@ -744,7 +746,7 @@ function createChartElement({
ichart.addSeries(
/** @type {SeriesDefinition<'Baseline'>} */ (BaselineSeries),
{
lineWidth: /** @type {any} */ (1.5),
lineWidth,
visible: defaultActive !== false,
baseValue: {
price: options?.baseValue?.price ?? 0,
@@ -0,0 +1 @@
*.js
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
import { domToBlob } from "./4.6.6/dist/index.mjs";
/**
* @param {Element} element
*/
export async function screenshot(element) {
const blob = await domToBlob(element, {
scale: 2,
});
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
setTimeout(() => URL.revokeObjectURL(url), 100);
}
+5 -4
View File
@@ -386,10 +386,10 @@ main() {
# Check if the directory already exists and has content
if [[ -d "$output_dir" ]] && [[ -n "$(ls -A "$output_dir" 2>/dev/null)" ]]; then
print_error "Directory already exists and is not empty: $output_dir"
print_error "Package $package_name@$resolved_version appears to already be downloaded."
print_error "Remove the directory or choose a different output location to proceed."
exit 1
print_warning "Directory already exists and is not empty: $output_dir"
print_warning "Package $package_name@$resolved_version appears to already be downloaded."
print_warning "Remove the directory or choose a different output location to proceed."
return
fi
# Create the base output directory
@@ -409,3 +409,4 @@ main "@solidjs/signals"
main "@leeoniya/ufuzzy"
main "lean-qr"
main "lightweight-charts"
main "modern-screenshot"
+36
View File
@@ -21,6 +21,7 @@ const CANDLE = "candle";
* @param {Elements} args.elements
* @param {VecsResources} args.vecsResources
* @param {VecIdToIndexes} args.vecIdToIndexes
* @param {Packages} args.packages
*/
export function init({
colors,
@@ -32,6 +33,7 @@ export function init({
webSockets,
vecsResources,
vecIdToIndexes,
packages,
}) {
elements.charts.append(utils.dom.createShadow("left"));
elements.charts.append(utils.dom.createShadow("right"));
@@ -95,6 +97,33 @@ export function init({
},
});
const chartBottomRightCanvas = Array.from(
chart.inner.chartElement().getElementsByTagName("tr"),
).at(-1)?.lastChild?.firstChild?.firstChild;
if (chartBottomRightCanvas) {
const charts = elements.charts;
const domain = window.document.createElement("p");
domain.innerText = `${window.location.host}`;
domain.id = "domain";
const screenshotButton = window.document.createElement("button");
screenshotButton.id = "screenshot";
const camera = "[ ◉¯]";
screenshotButton.innerHTML = camera;
screenshotButton.title = "Screenshot";
chartBottomRightCanvas.replaceWith(screenshotButton);
screenshotButton.addEventListener("click", () => {
packages.modernScreenshot().then(async ({ screenshot }) => {
elements.body.dataset.screenshot = "true";
charts.append(domain);
seriesTypeField.hidden = true;
await screenshot(charts);
charts.removeChild(domain);
seriesTypeField.hidden = false;
elements.body.dataset.screenshot = "false";
});
});
}
chart.inner.timeScale().subscribeVisibleLogicalRangeChange(
utils.debounce((t) => {
if (t) {
@@ -500,6 +529,7 @@ function createIndexSelector({ option, vecIdToIndexes, signals, utils }) {
const rawIndexes = new Set(
[Object.values(o.top), Object.values(o.bottom)]
.flat(2)
.filter((blueprint) => !blueprint.key.startsWith("constant_"))
.map((blueprint) => vecIdToIndexes[blueprint.key])
.flat(),
);
@@ -524,6 +554,12 @@ function createIndexSelector({ option, vecIdToIndexes, signals, utils }) {
});
const fieldset = window.document.createElement("fieldset");
fieldset.id = "interval";
const screenshotSpan = window.document.createElement("span");
screenshotSpan.innerText = "interval:";
fieldset.append(screenshotSpan);
fieldset.append(field);
fieldset.dataset.size = "sm";
+21 -15
View File
@@ -3,7 +3,7 @@
/**
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType } from "./options"
* @import { Valued, SingleValueData, CandlestickData, OHLCTuple, Series, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions } from "../packages/lightweight-charts/wrapper"
* @import * as _ from "../packages/leeoniya-ufuzzy/1.0.18/dist/uFuzzy.d.ts"
* @import * as _ from "../packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.d.ts"
* @import { SerializedChartableIndex } from "./chart";
* @import { Signal, Signals, Accessor } from "../packages/solidjs-signals/wrapper";
* @import { DateIndex, DecadeIndex, DifficultyEpoch, Index, HalvingEpoch, Height, MonthIndex, P2PK33AddressIndex, P2PK65AddressIndex, P2PKHAddressIndex, P2SHAddressIndex, P2MSOutputIndex, P2AAddressIndex, P2TRAddressIndex, P2WPKHAddressIndex, P2WSHAddressIndex, TxIndex, InputIndex, OutputIndex, VecId, WeekIndex, SemesterIndex, YearIndex, VecIdToIndexes, QuarterIndex, EmptyOutputIndex, OpReturnIndex, UnknownOutputIndex } from "./vecid-to-indexes"
@@ -28,7 +28,7 @@
* "percentage" |
* "Ratio" |
* "Sats" |
* "Seconds" |
* "secs" |
* "Timestamp" |
* "tx" |
* "Type" |
@@ -78,10 +78,13 @@ function initPackages() {
return import("../packages/lean-qr/2.5.0/index.mjs").then((d) => d);
},
async ufuzzy() {
return import("../packages/leeoniya-ufuzzy/1.0.18/dist/uFuzzy.mjs").then(
return import("../packages/leeoniya-ufuzzy/1.0.19/dist/uFuzzy.mjs").then(
({ default: d }) => d,
);
},
async modernScreenshot() {
return import("../packages/modern-screenshot/wrapper.js").then((d) => d);
},
};
/**
@@ -106,6 +109,7 @@ function initPackages() {
lightweightCharts: importPackage("lightweightCharts"),
leanQr: importPackage("leanQr"),
ufuzzy: importPackage("ufuzzy"),
modernScreenshot: importPackage("modernScreenshot"),
};
}
/**
@@ -727,7 +731,7 @@ function createUtils() {
id.endsWith("stack") ||
(id.endsWith("value") && !id.includes("realized")) ||
((id.includes("coinbase") ||
id.includes("fee") ||
(id.includes("fee") && !id.includes("feerate")) ||
id.includes("subsidy") ||
id.includes("rewards")) &&
!(
@@ -762,7 +766,7 @@ function createUtils() {
((id.includes("realized") || id.includes("true_market_mean")) &&
!id.includes("ratio") &&
!id.includes("relative_to")) ||
((id.endsWith("sma") || id.includes("sma_x")) &&
((id.endsWith("sma") || id.includes("sma_x") || id.endsWith("ema")) &&
!id.includes("ratio")) ||
id === "ath")
) {
@@ -777,7 +781,9 @@ function createUtils() {
((!unit || thoroughUnitCheck) &&
(id.endsWith("ratio") ||
(id.includes("ratio") &&
(id.endsWith("sma") || id.endsWith("zscore"))) ||
(id.endsWith("sma") ||
id.endsWith("ema") ||
id.endsWith("zscore"))) ||
id.endsWith("_5sd") ||
id.endsWith("1sd") ||
id.endsWith("2sd") ||
@@ -806,11 +812,15 @@ function createUtils() {
(id.endsWith("count") ||
id.includes("_count_") ||
id.startsWith("block_count") ||
id.includes("tx_v"))
(id.includes("tx_v") && !id.includes("vsize")))
) {
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
unit = "Count";
}
if ((!unit || thoroughUnitCheck) && id.includes("feerate")) {
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
unit = "sat/vB";
}
if ((!unit || thoroughUnitCheck) && id.startsWith("is_")) {
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
unit = "Bool";
@@ -824,7 +834,7 @@ function createUtils() {
(id === "interval" || id.startsWith("block_interval"))
) {
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
unit = "Seconds";
unit = "secs";
}
if ((!unit || thoroughUnitCheck) && id.endsWith("returns")) {
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
@@ -864,10 +874,7 @@ function createUtils() {
}
if (
(!unit || thoroughUnitCheck) &&
(id.endsWith("vsize") ||
id.endsWith("vbytes") ||
id.endsWith("_vbytes_sum") ||
id.endsWith("_vbytes_cumulative"))
(id.includes("vsize") || id.includes("vbytes"))
) {
if (unit) throw Error(`Unit "${unit}" already assigned "${id}"`);
unit = "vB";
@@ -2279,6 +2286,7 @@ function main() {
webSockets,
vecsResources,
vecIdToIndexes,
packages,
}),
),
),
@@ -2402,9 +2410,7 @@ function main() {
return treeElement;
});
if (localhost) {
setTimeout(scrollToSelected, 10);
}
setTimeout(scrollToSelected, 10);
});
}
initFolders();
+366 -296
View File
@@ -908,7 +908,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
},
{
key: "above_100k_btc",
name: "100K btc+",
name: "100K+ btc",
title: "Above 100K BTC",
color: colors.yellow,
},
@@ -1066,13 +1066,14 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
/**
* @param {Object} args
* @param {number} [args.number]
* @param {string} [args.name]
* @param {boolean} [args.defaultActive]
* @param {Unit} args.unit
*/
function createPriceLine({ number = 0, unit, defaultActive }) {
function createPriceLine({ number = 0, unit, defaultActive, name }) {
return /** @satisfies {FetchedLineSeriesBlueprint} */ ({
key: `constant_${number >= 0 ? number : `minus_${Math.abs(number)}`}`,
title: `${number}`,
title: name ?? `${number}`,
unit,
defaultActive,
color: colors.gray,
@@ -1138,80 +1139,117 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
/**
* @param {Object} args
* @param {VecIdAverageBase} args.concat
* @param {string} [args.title]
*/
function createAverageSeries({ concat }) {
function createAverageSeries({ concat, title = "" }) {
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
key: `${concat}_average`,
title: "Average",
title: `Average ${title}`,
});
}
/**
* @param {Object} args
* @param {CumulativeVecIdBase} args.concat
* @param {string} [args.name]
* @param {Color} [args.sumColor]
* @param {Color} [args.cumulativeColor]
* @param {string} [args.common]
*/
function createSumCumulativeSeries({ concat, name }) {
function createSumCumulativeSeries({
concat,
common,
sumColor,
cumulativeColor,
}) {
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
{
createSumSeries({
key: concat,
title: name ? `${name} Sum` : "Sum",
color: colors.orange,
},
{
key: `${concat}_cumulative`,
title: name ? `Cumulative ${name}` : "Cumulative",
color: colors.red,
defaultActive: false,
},
title: common,
color: sumColor,
}),
createCumulativeSeries({
concat,
title: common,
color: cumulativeColor,
}),
]);
}
/**
* @param {Object} args
* @param {VecIdMinBase & VecIdMaxBase & VecId90pBase & VecId75pBase & VecIdMedianBase & VecId25pBase & VecId10pBase} args.concat
* @param {CumulativeVecIdBase} args.key
* @param {string} [args.title]
* @param {Color} [args.color]
*/
function createMinMaxPercentilesSeries({ concat }) {
function createSumSeries({ key, title = "", color }) {
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
key,
title: `Sum ${title}`,
color: color ?? colors.orange,
});
}
/**
* @param {Object} args
* @param {CumulativeVecIdBase} args.concat
* @param {string} [args.title]
* @param {Color} [args.color]
*/
function createCumulativeSeries({ concat, title = "", color }) {
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
key: `${concat}_cumulative`,
title: `Cumulative ${title}`,
color: color ?? colors.red,
defaultActive: false,
});
}
/**
* @param {Object} args
* @param {VecIdMinBase & VecIdMaxBase & VecId90pBase & VecId75pBase & VecIdMedianBase & VecId25pBase & VecId10pBase} args.concat
* @param {string} [args.title]
*/
function createMinMaxPercentilesSeries({ concat, title = "" }) {
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
{
key: `${concat}_max`,
title: "Max",
title: `Max ${title}`,
color: colors.pink,
defaultActive: false,
},
{
key: `${concat}_min`,
title: "Min",
title: `Min ${title}`,
color: colors.green,
defaultActive: false,
},
{
key: `${concat}_median`,
title: "Median",
title: `Median ${title}`,
color: colors.amber,
defaultActive: false,
},
{
key: `${concat}_75p`,
title: "75p",
title: `75p ${title}`,
color: colors.red,
defaultActive: false,
},
{
key: `${concat}_25p`,
title: "25p",
title: `25p ${title}`,
color: colors.yellow,
defaultActive: false,
},
{
key: `${concat}_90p`,
title: "90p",
title: `90p ${title}`,
color: colors.rose,
defaultActive: false,
},
{
key: `${concat}_10p`,
title: "10p",
title: `10p ${title}`,
color: colors.lime,
defaultActive: false,
},
@@ -1280,6 +1318,66 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
* @param {Color} [args.color]
*/
function createPriceWithRatioOptions({ name, title, legend, key, color }) {
const percentiles = [
{
name: "p1",
color: colors.indigo,
},
{
name: "p2",
color: colors.violet,
},
{
name: "p5",
color: colors.purple,
},
{
name: "p95",
color: colors.amber,
},
{
name: "p98",
color: colors.orange,
},
{
name: "p99",
color: colors.red,
},
];
const ratioAverages = /** @type {const} */ ([
{
name: "1w sma",
key: "1w_sma",
color: colors.lime,
},
{
name: "1m sma",
key: "1m_sma",
color: colors.teal,
},
{
name: "1y sma",
key: "1y_sma",
color: colors.sky,
},
{
name: "2y sma",
key: "2y_sma",
color: colors.indigo,
},
{
name: "4y sma",
key: "4y_sma",
color: colors.purple,
},
{
name: "all sma",
key: "sma",
color: colors.rose,
},
]);
return [
{
name: "price",
@@ -1302,44 +1400,17 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
color,
}),
...(`${key}_ratio_p1sd_as_price` in vecIdToIndexes
? [
? percentiles.map(({ name, color }) =>
createBaseSeries({
key: `${key}_ratio_p1_as_price`,
name: "p1",
color: colors.indigo,
key: `${key}_ratio_${name}_as_price`,
name,
color,
defaultActive: false,
options: {
lineStyle: 1,
},
}),
createBaseSeries({
key: `${key}_ratio_p2_as_price`,
name: "p2",
color: colors.violet,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p5_as_price`,
name: "p5",
color: colors.purple,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p95_as_price`,
name: "p95",
color: colors.amber,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p98_as_price`,
name: "p98",
color: colors.orange,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p99_as_price`,
name: "p99",
color: colors.red,
defaultActive: false,
}),
]
)
: []),
],
bottom: [
@@ -1352,80 +1423,30 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
},
}),
...(`${key}_ratio_p1sd` in vecIdToIndexes
? [
? percentiles.map(({ name, color }) =>
createBaseSeries({
key: `${key}_ratio_p1`,
name: "p1",
color: colors.indigo,
key: `${key}_ratio_${name}`,
name,
color,
defaultActive: false,
options: {
lineStyle: 1,
},
}),
)
: []),
...(`${key}_ratio_sma` in vecIdToIndexes
? ratioAverages.map(({ name, key: keyAddon, color }) =>
createBaseSeries({
key: `${key}_ratio_p2`,
name: "p2",
color: colors.violet,
key: `${key}_ratio_${keyAddon}`,
name,
color,
defaultActive: false,
options: {
lineStyle: 1,
},
}),
createBaseSeries({
key: `${key}_ratio_p5`,
name: "p5",
color: colors.purple,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p95`,
name: "p95",
color: colors.amber,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p98`,
name: "p98",
color: colors.orange,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_p99`,
name: "p99",
color: colors.red,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_1w_sma`,
name: "1w sma",
color: colors.rose,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_1m_sma`,
name: "1m sma",
color: colors.pink,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_1y_sma`,
name: "1y sma",
color: colors.fuchsia,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_2y_sma`,
name: "2y sma",
color: colors.purple,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_4y_sma`,
name: "4y sma",
color: colors.violet,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_sma`,
name: "sma",
color: colors.indigo,
defaultActive: false,
}),
]
)
: []),
createPriceLine({
number: 1,
@@ -1532,89 +1553,84 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
name: legend,
color,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}0sd_as_price`,
name: "0σ",
color: colors.lime,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}p0_5sd_as_price`,
name: "+0.5σ",
color: colors.yellow,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}p1sd_as_price`,
name: "+1σ",
color: colors.amber,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}p1_5sd_as_price`,
name: "+1.5σ",
color: colors.orange,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}p2sd_as_price`,
name: "+2σ",
color: colors.red,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}p2_5sd_as_price`,
name: "+2.5σ",
color: colors.rose,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}p3sd_as_price`,
name: "+3σ",
color: colors.pink,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}m0_5sd_as_price`,
name: "0.5σ",
color: colors.teal,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}m1sd_as_price`,
name: "1σ",
color: colors.cyan,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}m1_5sd_as_price`,
name: "1.5σ",
color: colors.sky,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}m2sd_as_price`,
name: "2σ",
color: colors.blue,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}m2_5sd_as_price`,
name: "2.5σ",
color: colors.indigo,
defaultActive: false,
}),
createBaseSeries({
key: `${key}_ratio_${keyAddon}m3sd_as_price`,
name: "3σ",
color: colors.violet,
defaultActive: false,
}),
...[
{ sdKey: "0sd", name: "0σ", color: colors.lime },
{
sdKey: `p0_5sd`,
name: "+0.5σ",
color: colors.yellow,
},
{
sdKey: `p1sd`,
name: "+1σ",
color: colors.amber,
},
{
sdKey: `p1_5sd`,
name: "+1.5σ",
color: colors.orange,
},
{
sdKey: `p2sd`,
name: "+2σ",
color: colors.red,
},
{
sdKey: `p2_5sd`,
name: "+2.5σ",
color: colors.rose,
},
{
sdKey: `p3sd`,
name: "+3σ",
color: colors.pink,
},
{
sdKey: `m0_5sd`,
name: "0.5σ",
color: colors.teal,
},
{
sdKey: `m1sd`,
name: "1σ",
color: colors.cyan,
},
{
sdKey: `m1_5sd`,
name: "1.5σ",
color: colors.sky,
},
{
sdKey: `m2sd`,
name: "2σ",
color: colors.blue,
},
{
sdKey: `m2_5sd`,
name: "2.5σ",
color: colors.indigo,
},
{
sdKey: `m3sd`,
name: "3σ",
color: colors.violet,
},
].map(({ sdKey, name, color }) =>
createBaseSeries({
key: `${key}_ratio_${keyAddon}${sdKey}_as_price`,
name,
color,
defaultActive: false,
options: {
lineStyle: 1,
},
}),
),
],
bottom: [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
key: `${key}_ratio_${keyAddon}zscore`,
title: "All time",
title: "score",
type: "Baseline",
}),
...createPriceLines({
@@ -2256,7 +2272,19 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: soprKey,
title: "sopr",
title: "normal",
options: {
baseValue: {
price: 1,
},
},
}),
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: `${soprKey}_7d_ema`,
title: "7d ema",
colors: [colors.lime, colors.rose],
defaultActive: false,
options: {
baseValue: {
price: 1,
@@ -2268,8 +2296,21 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: asoprKey,
title: "asopr",
colors: [colors.yellow, colors.pink],
title: "adjusted",
colors: [colors.avocado, colors.pink],
defaultActive: false,
options: {
baseValue: {
price: 1,
},
},
}),
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
key: `${asoprKey}_7d_ema`,
title: "adj. 7d ema",
colors: [colors.yellow, colors.fuchsia],
defaultActive: false,
options: {
baseValue: {
price: 1,
@@ -2519,16 +2560,31 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
],
},
]),
{
name: "Sell Side Risk Ratio",
title: `Sell Side Risk Ratio ${title}`,
bottom: list.flatMap(({ color, name, key }) =>
createBaseSeries({
key: `${fixKey(key)}sell_side_risk_ratio`,
name: useGroupName ? name : "Risk",
color: color,
}),
),
bottom: !("list" in args)
? list.flatMap(({ key }) => [
createBaseSeries({
key: `${fixKey(key)}sell_side_risk_ratio`,
name: "raw",
color: colors.orange,
}),
createBaseSeries({
key: `${fixKey(key)}sell_side_risk_ratio_7d_ema`,
name: "7d ema",
color: colors.red,
defaultActive: false,
}),
])
: list.flatMap(({ color, name, key }) => [
createBaseSeries({
key: `${fixKey(key)}sell_side_risk_ratio`,
name,
color: color,
}),
]),
},
],
},
@@ -2627,11 +2683,11 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
...("list" in args
? [
{
name: "Price paid",
name: "Cost Basis",
tree: [
{
name: "Average",
title: `Average Price Paid ${title}`,
title: `Average Cost Basis ${title}`,
top: list.flatMap(({ color, name, key: _key }) => {
const key = fixKey(_key);
return /** @type {const} */ ([
@@ -2645,7 +2701,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
},
{
name: "Min",
title: `Min Price Paid ${title}`,
title: `Min Cost Basis ${title}`,
top: list.flatMap(({ color, name, key: _key }) => {
const key = fixKey(_key);
return /** @type {const} */ ([
@@ -2659,7 +2715,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
},
{
name: "Max",
title: `Max Price Paid ${title}`,
title: `Max Cost Basis ${title}`,
top: list.flatMap(({ color, name, key: _key }) => {
const key = fixKey(_key);
return /** @type {const} */ ([
@@ -2676,8 +2732,8 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
]
: [
{
name: "Price paid",
title: `Prices Paid ${title}`,
name: "Cost Basis",
title: `Costs Basis ${title}`,
top: [
createBaseSeries({
key: `${fixKey(args.key)}realized_price`,
@@ -2799,30 +2855,42 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
],
},
{
name: "Average",
name: "Averages",
tree: [
{
name: "Compare",
title: "Market Price Moving Averages",
top: averages.map(({ days, key, name, color }) =>
createBaseSeries({
key: `${key}_sma`,
name: key,
nameAddon: "Simple",
keyAddon: "sma",
},
{
nameAddon: "Exponential",
keyAddon: "ema",
},
].map(({ nameAddon, keyAddon }) => ({
name: nameAddon,
tree: [
{
name: "Compare",
title: `Market Price ${nameAddon} Moving Averages`,
top: averages.map(({ days, key, name, color }) =>
createBaseSeries({
key: `${key}_${keyAddon}`,
name: key,
color,
}),
),
},
...averages.map(({ key, name, color }) => ({
name,
tree: createPriceWithRatioOptions({
key: `${key}_${keyAddon}`,
name,
title: `${name} Market Price ${nameAddon} Moving Average`,
legend: "average",
color,
}),
),
},
...averages.map(({ key, name, color }) => ({
name,
tree: createPriceWithRatioOptions({
key: `${key}_sma`,
name,
title: `${name} Market Price Moving Average`,
legend: "average",
color,
}),
})),
],
})),
],
})),
},
{
name: "Performance",
@@ -3107,17 +3175,14 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
name: "Unclaimed Rewards",
title: "Unclaimed Rewards",
bottom: [
...createBaseSumCumulativeSeries({
key: "unclaimed_rewards",
name: "unclaimed",
...createSumCumulativeSeries({
concat: "unclaimed_rewards",
}),
...createBaseSumCumulativeSeries({
key: "unclaimed_rewards_in_btc",
name: "unclaimed",
...createSumCumulativeSeries({
concat: "unclaimed_rewards_in_btc",
}),
...createBaseSumCumulativeSeries({
key: "unclaimed_rewards_in_usd",
name: "unclaimed",
...createSumCumulativeSeries({
concat: "unclaimed_rewards_in_usd",
}),
],
},
@@ -3170,11 +3235,14 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
name: "Count",
title: "Block Count",
bottom: [
createBaseSeries({
key: "block_count",
name: "Count",
...createSumCumulativeSeries({
concat: "block_count",
}),
createPriceLine({
unit: "Count",
name: "Target",
number: 144,
}),
...createSumCumulativeSeries({ concat: "block_count" }),
],
},
{
@@ -3189,6 +3257,11 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
...createMinMaxPercentilesSeries({
concat: "block_interval",
}),
createPriceLine({
unit: "secs",
name: "Target",
number: 600,
}),
],
},
{
@@ -3199,29 +3272,31 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
key: "total_size",
name: "Size",
}),
...createSumCumulativeSeries({ concat: "block_size" }),
...createSumCumulativeSeries({
concat: "block_size",
}),
],
},
{
name: "Weight",
title: "Block Weight",
name: "Bytes",
title: "Block Bytes",
bottom: [
createBaseSeries({
key: "weight",
name: "Weight",
}),
...createSumCumulativeSeries({ concat: "block_weight" }),
],
},
{
name: "Vbytes",
title: "Block Virtual Bytes",
bottom: [
...createSumCumulativeSeries({
concat: "block_weight",
common: "weight",
}),
createBaseSeries({
key: "vbytes",
name: "Vbytes",
}),
...createSumCumulativeSeries({ concat: "block_vbytes" }),
...createSumCumulativeSeries({
concat: "block_vbytes",
common: "Vbytes",
}),
],
},
],
@@ -3238,22 +3313,18 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
}),
},
{
name: "Weight",
title: "Transaction Weight",
name: "Bytes",
title: "Transaction Bytes",
bottom: [
createAverageSeries({ concat: "tx_weight" }),
createAverageSeries({ concat: "tx_weight", title: "Weight" }),
...createMinMaxPercentilesSeries({
concat: "tx_weight",
title: "Weight",
}),
],
},
{
name: "vsize",
title: "Transaction Virtual Size",
bottom: [
createAverageSeries({ concat: "tx_vsize" }),
createAverageSeries({ concat: "tx_vsize", title: "VSize" }),
...createMinMaxPercentilesSeries({
concat: "tx_vsize",
title: "VSize",
}),
],
},
@@ -3261,22 +3332,17 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
name: "Versions",
title: "Transaction Versions",
bottom: [
createBaseSeries({
key: "tx_v1",
name: "v1 Count",
[colors.orange, colors.red],
[colors.cyan, colors.blue],
[colors.lime, colors.green],
].flatMap(([sumColor, cumulativeColor], index) =>
createSumCumulativeSeries({
concat: `tx_v${index + 1}`,
common: `v${index + 1}`,
sumColor,
cumulativeColor,
}),
...createSumCumulativeSeries({ concat: "tx_v1", name: "v1" }),
createBaseSeries({
key: "tx_v2",
name: "v2 Count",
}),
...createSumCumulativeSeries({ concat: "tx_v2", name: "v2" }),
createBaseSeries({
key: "tx_v3",
name: "v3 Count",
}),
...createSumCumulativeSeries({ concat: "tx_v3", name: "v3" }),
],
),
},
],
},
@@ -3288,7 +3354,9 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
title: "Transaction Input Count",
bottom: [
createAverageSeries({ concat: "input_count" }),
...createSumCumulativeSeries({ concat: "input_count" }),
createCumulativeSeries({
concat: "input_count",
}),
...createMinMaxPercentilesSeries({
concat: "input_count",
}),
@@ -3312,7 +3380,9 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
title: "Transaction Output Count",
bottom: [
createAverageSeries({ concat: "output_count" }),
...createSumCumulativeSeries({ concat: "output_count" }),
createCumulativeSeries({
concat: "input_count",
}),
...createMinMaxPercentilesSeries({
concat: "output_count",
}),
@@ -3754,7 +3824,7 @@ function createPartialOptions({ env, colors, vecIdToIndexes }) {
tree: [
{
name: "Status",
url: () => "https://status.bitcoinresearchkit.org/",
url: () => "https://status.bitview.space/",
},
{
name: "Self",
+1 -1
View File
@@ -770,7 +770,7 @@ export function init({
data: bitcoinPriceData,
},
{
title: "Average Price Paid",
title: "Average Cost Basis",
type: "Line",
color: colors.lime,
data: averagePricePaidData,