mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -682,6 +682,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
"tower-layer",
|
||||
"tracing",
|
||||
"vecdb",
|
||||
]
|
||||
@@ -1822,9 +1823,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "importmap"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f650142f4c83e122ae10a49dbaa88593dc4b20158124409856adbae847775938"
|
||||
dependencies = [
|
||||
"include_dir",
|
||||
"rapidhash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -1,90 +1,73 @@
|
||||
# brk_cli
|
||||
|
||||
Command-line interface for running the Bitcoin Research Kit.
|
||||
Command-line interface for running a Bitcoin Research Kit instance.
|
||||
|
||||
## What It Enables
|
||||
## Preview
|
||||
|
||||
Run a full BRK instance: index the blockchain, compute metrics, serve the API, and optionally host a web interface. Continuously syncs with new blocks.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **All-in-one**: Single binary runs indexer, computer, mempool monitor, and server
|
||||
- **Auto-sync**: Waits for new blocks and processes them automatically
|
||||
- **Web interface**: Downloads and bundles frontend from GitHub releases
|
||||
- **Configurable**: TOML config for RPC, paths, and features
|
||||
- **Collision checking**: Optional TXID collision validation mode
|
||||
- **Memory optimized**: Uses mimalloc allocator, 512MB stack for deep recursion
|
||||
|
||||
## Install
|
||||
|
||||
First, ensure Rust is up to date:
|
||||
|
||||
```bash
|
||||
rustup update
|
||||
```
|
||||
|
||||
Recommended (optimized for your CPU, auto-finds latest version):
|
||||
|
||||
```bash
|
||||
RUSTFLAGS="-C target-cpu=native" cargo install --locked brk_cli --version "$(cargo search brk_cli | head -1 | awk -F'"' '{print $2}')"
|
||||
```
|
||||
|
||||
**Variants:**
|
||||
|
||||
```bash
|
||||
# Standard install (portable, latest stable only)
|
||||
cargo install --locked brk_cli
|
||||
|
||||
# Specific version
|
||||
cargo install --locked brk_cli --version "0.1.0-alpha.2"
|
||||
```
|
||||
|
||||
See [crates.io/crates/brk_cli/versions](https://crates.io/crates/brk_cli/versions) for all versions.
|
||||
|
||||
## Update
|
||||
|
||||
Same as install - cargo replaces the existing binary:
|
||||
|
||||
```bash
|
||||
rustup update
|
||||
RUSTFLAGS="-C target-cpu=native" cargo install --locked brk_cli --version "$(cargo search brk_cli | head -1 | awk -F'"' '{print $2}')"
|
||||
```
|
||||
- https://bitview.space - web interface
|
||||
- https://bitview.space/api - API docs
|
||||
|
||||
## Requirements
|
||||
|
||||
- Bitcoin Core with accessible `blk*.dat` files
|
||||
- Bitcoin Core running with RPC enabled
|
||||
- Access to `blk*.dat` files
|
||||
- ~400 GB disk space
|
||||
- 12+ GB RAM recommended
|
||||
- 12+ GB RAM
|
||||
|
||||
## Usage
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# See all options
|
||||
brk --help
|
||||
|
||||
# The CLI will:
|
||||
# 1. Index new blocks
|
||||
# 2. Compute derived metrics
|
||||
# 3. Start mempool monitor
|
||||
# 4. Launch API server (port 3110)
|
||||
# 5. Wait for new blocks and repeat
|
||||
rustup update
|
||||
RUSTFLAGS="-C target-cpu=native" cargo install --locked brk_cli --version "$(cargo search brk_cli | head -1 | awk -F'"' '{print $2}')"
|
||||
```
|
||||
|
||||
## Components
|
||||
Portable build (without native CPU optimizations):
|
||||
|
||||
1. **Indexer**: Processes blocks into queryable indexes
|
||||
2. **Computer**: Derives 1000+ on-chain metrics
|
||||
3. **Mempool**: Real-time fee estimation
|
||||
4. **Server**: REST API with OpenAPI docs
|
||||
5. **Bundler**: JS bundling for web interface (if enabled)
|
||||
```bash
|
||||
cargo install --locked brk_cli
|
||||
```
|
||||
|
||||
## Performance
|
||||
## Run
|
||||
|
||||
See [brk_computer](https://docs.rs/brk_computer) for full pipeline benchmarks.
|
||||
```bash
|
||||
brk
|
||||
```
|
||||
|
||||
## Built On
|
||||
Indexes the blockchain, computes datasets, starts the server on `localhost:3110`, and waits for new blocks.
|
||||
|
||||
- `brk_indexer` for blockchain indexing
|
||||
- `brk_computer` for metric computation
|
||||
- `brk_mempool` for mempool monitoring
|
||||
- `brk_server` for HTTP API
|
||||
## Help
|
||||
|
||||
```
|
||||
brk -h Show all options
|
||||
brk -V Show version
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
Options are saved to `~/.brk/config.toml` after first use.
|
||||
|
||||
```
|
||||
--bitcoindir <PATH> Bitcoin data directory
|
||||
--blocksdir <PATH> Blocks directory (default: bitcoindir/blocks)
|
||||
--brkdir <PATH> Output directory (default: ~/.brk)
|
||||
|
||||
--rpcconnect <IP> RPC host (default: localhost)
|
||||
--rpcport <PORT> RPC port (default: 8332)
|
||||
--rpccookiefile <PATH> RPC cookie file (default: bitcoindir/.cookie)
|
||||
--rpcuser <USERNAME> RPC username
|
||||
--rpcpassword <PASSWORD> RPC password
|
||||
|
||||
-F, --fetch <BOOL> Fetch price data (default: true)
|
||||
--exchanges <BOOL> Fetch from exchange APIs (default: true)
|
||||
-w, --website <BOOL|PATH> Serve web interface (default: true)
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
~/.brk/
|
||||
├── config.toml Configuration
|
||||
└── log/ Logs
|
||||
|
||||
<brkdir>/ Indexed data (default: ~/.brk)
|
||||
```
|
||||
|
||||
@@ -9,7 +9,7 @@ use brk_rpc::{Auth, Client};
|
||||
use clap::Parser;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use crate::{default_brk_path, dot_brk_path, fix_user_path, website::Website};
|
||||
use crate::{default_brk_path, dot_brk_path, fix_user_path, website::WebsiteArg};
|
||||
|
||||
#[derive(Parser, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
#[command(version, about)]
|
||||
@@ -42,7 +42,7 @@ pub struct Config {
|
||||
/// Website served by the server: true (default), false, or PATH, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
#[arg(short, long, value_name = "BOOL|PATH")]
|
||||
website: Option<Website>,
|
||||
website: Option<WebsiteArg>,
|
||||
|
||||
/// Bitcoin RPC ip, default: localhost, saved
|
||||
#[serde(default, deserialize_with = "default_on_error")]
|
||||
@@ -258,7 +258,7 @@ Finally, you can run the program with '-h' for help."
|
||||
)
|
||||
}
|
||||
|
||||
pub fn website(&self) -> Website {
|
||||
pub fn website(&self) -> WebsiteArg {
|
||||
self.website.clone().unwrap_or_default()
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use brk_iterator::Blocks;
|
||||
use brk_mempool::Mempool;
|
||||
use brk_query::AsyncQuery;
|
||||
use brk_reader::Reader;
|
||||
use brk_server::{Server, WebsiteSource};
|
||||
use brk_server::{Server, Website};
|
||||
use tracing::info;
|
||||
use vecdb::Exit;
|
||||
|
||||
@@ -22,7 +22,7 @@ mod config;
|
||||
mod paths;
|
||||
mod website;
|
||||
|
||||
use crate::{config::Config, paths::*, website::Website};
|
||||
use crate::{config::Config, paths::*, website::WebsiteArg};
|
||||
|
||||
pub fn main() -> color_eyre::Result<()> {
|
||||
// Can't increase main thread's stack size, thus we need to use another thread
|
||||
@@ -66,23 +66,14 @@ pub fn run() -> color_eyre::Result<()> {
|
||||
|
||||
let data_path = config.brkdir();
|
||||
|
||||
let website_source = match config.website() {
|
||||
Website::Enabled(false) => {
|
||||
info!("Website: disabled");
|
||||
WebsiteSource::Disabled
|
||||
}
|
||||
Website::Path(p) => {
|
||||
info!("Website: filesystem ({})", p.display());
|
||||
WebsiteSource::Filesystem(p)
|
||||
}
|
||||
Website::Enabled(true) => {
|
||||
info!("Website: embedded");
|
||||
WebsiteSource::Embedded
|
||||
}
|
||||
let website = match config.website() {
|
||||
WebsiteArg::Enabled(false) => Website::Disabled,
|
||||
WebsiteArg::Enabled(true) => Website::Default,
|
||||
WebsiteArg::Path(p) => Website::Filesystem(p),
|
||||
};
|
||||
|
||||
let future = async move {
|
||||
let server = Server::new(&query, data_path, website_source);
|
||||
let server = Server::new(&query, data_path, website);
|
||||
|
||||
tokio::spawn(async move {
|
||||
server.serve().await.unwrap();
|
||||
|
||||
@@ -10,18 +10,18 @@ use crate::paths::fix_user_path;
|
||||
/// - `"/path/to/website"`: serve custom website from path
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Website {
|
||||
pub enum WebsiteArg {
|
||||
Enabled(bool),
|
||||
Path(PathBuf),
|
||||
}
|
||||
|
||||
impl Default for Website {
|
||||
impl Default for WebsiteArg {
|
||||
fn default() -> Self {
|
||||
Self::Enabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Website {
|
||||
impl FromStr for WebsiteArg {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
@@ -1268,56 +1268,6 @@ impl Price111dSmaPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActivePriceRatioPattern {
|
||||
pub ratio: MetricPattern4<StoredF32>,
|
||||
pub ratio_1m_sma: MetricPattern4<StoredF32>,
|
||||
pub ratio_1w_sma: MetricPattern4<StoredF32>,
|
||||
pub ratio_1y_sd: Ratio1ySdPattern,
|
||||
pub ratio_2y_sd: Ratio1ySdPattern,
|
||||
pub ratio_4y_sd: Ratio1ySdPattern,
|
||||
pub ratio_pct1: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct1_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct2: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct2_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct5: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct5_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct95: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct95_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct98: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct98_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct99: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct99_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_sd: Ratio1ySdPattern,
|
||||
}
|
||||
|
||||
impl ActivePriceRatioPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
ratio: MetricPattern4::new(client.clone(), acc.clone()),
|
||||
ratio_1m_sma: MetricPattern4::new(client.clone(), _m(&acc, "1m_sma")),
|
||||
ratio_1w_sma: MetricPattern4::new(client.clone(), _m(&acc, "1w_sma")),
|
||||
ratio_1y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "1y")),
|
||||
ratio_2y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "2y")),
|
||||
ratio_4y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "4y")),
|
||||
ratio_pct1: MetricPattern4::new(client.clone(), _m(&acc, "pct1")),
|
||||
ratio_pct1_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct1_usd")),
|
||||
ratio_pct2: MetricPattern4::new(client.clone(), _m(&acc, "pct2")),
|
||||
ratio_pct2_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct2_usd")),
|
||||
ratio_pct5: MetricPattern4::new(client.clone(), _m(&acc, "pct5")),
|
||||
ratio_pct5_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct5_usd")),
|
||||
ratio_pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")),
|
||||
ratio_pct95_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct95_usd")),
|
||||
ratio_pct98: MetricPattern4::new(client.clone(), _m(&acc, "pct98")),
|
||||
ratio_pct98_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct98_usd")),
|
||||
ratio_pct99: MetricPattern4::new(client.clone(), _m(&acc, "pct99")),
|
||||
ratio_pct99_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct99_usd")),
|
||||
ratio_sd: Ratio1ySdPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct PercentilesPattern {
|
||||
pub pct05: MetricPattern4<Dollars>,
|
||||
@@ -1368,6 +1318,56 @@ impl PercentilesPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActivePriceRatioPattern {
|
||||
pub ratio: MetricPattern4<StoredF32>,
|
||||
pub ratio_1m_sma: MetricPattern4<StoredF32>,
|
||||
pub ratio_1w_sma: MetricPattern4<StoredF32>,
|
||||
pub ratio_1y_sd: Ratio1ySdPattern,
|
||||
pub ratio_2y_sd: Ratio1ySdPattern,
|
||||
pub ratio_4y_sd: Ratio1ySdPattern,
|
||||
pub ratio_pct1: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct1_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct2: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct2_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct5: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct5_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct95: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct95_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct98: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct98_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_pct99: MetricPattern4<StoredF32>,
|
||||
pub ratio_pct99_usd: MetricPattern4<Dollars>,
|
||||
pub ratio_sd: Ratio1ySdPattern,
|
||||
}
|
||||
|
||||
impl ActivePriceRatioPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
ratio: MetricPattern4::new(client.clone(), acc.clone()),
|
||||
ratio_1m_sma: MetricPattern4::new(client.clone(), _m(&acc, "1m_sma")),
|
||||
ratio_1w_sma: MetricPattern4::new(client.clone(), _m(&acc, "1w_sma")),
|
||||
ratio_1y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "1y")),
|
||||
ratio_2y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "2y")),
|
||||
ratio_4y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "4y")),
|
||||
ratio_pct1: MetricPattern4::new(client.clone(), _m(&acc, "pct1")),
|
||||
ratio_pct1_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct1_usd")),
|
||||
ratio_pct2: MetricPattern4::new(client.clone(), _m(&acc, "pct2")),
|
||||
ratio_pct2_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct2_usd")),
|
||||
ratio_pct5: MetricPattern4::new(client.clone(), _m(&acc, "pct5")),
|
||||
ratio_pct5_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct5_usd")),
|
||||
ratio_pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")),
|
||||
ratio_pct95_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct95_usd")),
|
||||
ratio_pct98: MetricPattern4::new(client.clone(), _m(&acc, "pct98")),
|
||||
ratio_pct98_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct98_usd")),
|
||||
ratio_pct99: MetricPattern4::new(client.clone(), _m(&acc, "pct99")),
|
||||
ratio_pct99_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct99_usd")),
|
||||
ratio_sd: Ratio1ySdPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern5 {
|
||||
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
@@ -1600,40 +1600,6 @@ impl BitcoinPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ClassAveragePricePattern<T> {
|
||||
pub _2015: MetricPattern4<T>,
|
||||
pub _2016: MetricPattern4<T>,
|
||||
pub _2017: MetricPattern4<T>,
|
||||
pub _2018: MetricPattern4<T>,
|
||||
pub _2019: MetricPattern4<T>,
|
||||
pub _2020: MetricPattern4<T>,
|
||||
pub _2021: MetricPattern4<T>,
|
||||
pub _2022: MetricPattern4<T>,
|
||||
pub _2023: MetricPattern4<T>,
|
||||
pub _2024: MetricPattern4<T>,
|
||||
pub _2025: MetricPattern4<T>,
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned> ClassAveragePricePattern<T> {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
_2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_average_price")),
|
||||
_2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_average_price")),
|
||||
_2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_average_price")),
|
||||
_2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_average_price")),
|
||||
_2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_average_price")),
|
||||
_2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_average_price")),
|
||||
_2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_average_price")),
|
||||
_2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_average_price")),
|
||||
_2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_average_price")),
|
||||
_2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_average_price")),
|
||||
_2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_average_price")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DollarsPattern<T> {
|
||||
pub average: MetricPattern2<T>,
|
||||
@@ -1669,33 +1635,35 @@ impl<T: DeserializeOwned> DollarsPattern<T> {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern {
|
||||
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub net_unrealized_pnl_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub nupl: MetricPattern1<StoredF32>,
|
||||
pub supply_in_loss_rel_to_circulating_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_circulating_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_rel_to_circulating_supply: MetricPattern4<StoredF64>,
|
||||
pub unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub unrealized_profit_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub struct ClassAveragePricePattern<T> {
|
||||
pub _2015: MetricPattern4<T>,
|
||||
pub _2016: MetricPattern4<T>,
|
||||
pub _2017: MetricPattern4<T>,
|
||||
pub _2018: MetricPattern4<T>,
|
||||
pub _2019: MetricPattern4<T>,
|
||||
pub _2020: MetricPattern4<T>,
|
||||
pub _2021: MetricPattern4<T>,
|
||||
pub _2022: MetricPattern4<T>,
|
||||
pub _2023: MetricPattern4<T>,
|
||||
pub _2024: MetricPattern4<T>,
|
||||
pub _2025: MetricPattern4<T>,
|
||||
}
|
||||
|
||||
impl RelativePattern {
|
||||
impl<T: DeserializeOwned> ClassAveragePricePattern<T> {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_market_cap")),
|
||||
net_unrealized_pnl_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_market_cap")),
|
||||
nupl: MetricPattern1::new(client.clone(), _m(&acc, "nupl")),
|
||||
supply_in_loss_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_circulating_supply")),
|
||||
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")),
|
||||
supply_in_profit_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_circulating_supply")),
|
||||
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")),
|
||||
supply_rel_to_circulating_supply: MetricPattern4::new(client.clone(), _m(&acc, "supply_rel_to_circulating_supply")),
|
||||
unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_market_cap")),
|
||||
unrealized_profit_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_market_cap")),
|
||||
_2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_average_price")),
|
||||
_2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_average_price")),
|
||||
_2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_average_price")),
|
||||
_2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_average_price")),
|
||||
_2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_average_price")),
|
||||
_2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_average_price")),
|
||||
_2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_average_price")),
|
||||
_2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_average_price")),
|
||||
_2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_average_price")),
|
||||
_2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_average_price")),
|
||||
_2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_average_price")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1732,6 +1700,38 @@ impl RelativePattern2 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct RelativePattern {
|
||||
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub net_unrealized_pnl_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub nupl: MetricPattern1<StoredF32>,
|
||||
pub supply_in_loss_rel_to_circulating_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_circulating_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
|
||||
pub supply_rel_to_circulating_supply: MetricPattern4<StoredF64>,
|
||||
pub unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
pub unrealized_profit_rel_to_market_cap: MetricPattern1<StoredF32>,
|
||||
}
|
||||
|
||||
impl RelativePattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_market_cap")),
|
||||
net_unrealized_pnl_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_market_cap")),
|
||||
nupl: MetricPattern1::new(client.clone(), _m(&acc, "nupl")),
|
||||
supply_in_loss_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_circulating_supply")),
|
||||
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")),
|
||||
supply_in_profit_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_circulating_supply")),
|
||||
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")),
|
||||
supply_rel_to_circulating_supply: MetricPattern4::new(client.clone(), _m(&acc, "supply_rel_to_circulating_supply")),
|
||||
unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_market_cap")),
|
||||
unrealized_profit_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_market_cap")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CountPattern2<T> {
|
||||
pub average: MetricPattern1<T>,
|
||||
@@ -1911,53 +1911,27 @@ impl<T: DeserializeOwned> PhaseDailyCentsPattern<T> {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct PeriodCagrPattern {
|
||||
pub _10y: MetricPattern4<StoredF32>,
|
||||
pub _2y: MetricPattern4<StoredF32>,
|
||||
pub _3y: MetricPattern4<StoredF32>,
|
||||
pub _4y: MetricPattern4<StoredF32>,
|
||||
pub _5y: MetricPattern4<StoredF32>,
|
||||
pub _6y: MetricPattern4<StoredF32>,
|
||||
pub _8y: MetricPattern4<StoredF32>,
|
||||
pub struct UnrealizedPattern {
|
||||
pub neg_unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub net_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub supply_in_loss: ActiveSupplyPattern,
|
||||
pub supply_in_profit: ActiveSupplyPattern,
|
||||
pub total_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub unrealized_profit: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl PeriodCagrPattern {
|
||||
impl UnrealizedPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
_10y: MetricPattern4::new(client.clone(), _p("10y", &acc)),
|
||||
_2y: MetricPattern4::new(client.clone(), _p("2y", &acc)),
|
||||
_3y: MetricPattern4::new(client.clone(), _p("3y", &acc)),
|
||||
_4y: MetricPattern4::new(client.clone(), _p("4y", &acc)),
|
||||
_5y: MetricPattern4::new(client.clone(), _p("5y", &acc)),
|
||||
_6y: MetricPattern4::new(client.clone(), _p("6y", &acc)),
|
||||
_8y: MetricPattern4::new(client.clone(), _p("8y", &acc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _0satsPattern2 {
|
||||
pub activity: ActivityPattern2,
|
||||
pub cost_basis: CostBasisPattern,
|
||||
pub outputs: OutputsPattern,
|
||||
pub realized: RealizedPattern,
|
||||
pub relative: RelativePattern4,
|
||||
pub supply: SupplyPattern2,
|
||||
pub unrealized: UnrealizedPattern,
|
||||
}
|
||||
|
||||
impl _0satsPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
activity: ActivityPattern2::new(client.clone(), acc.clone()),
|
||||
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
|
||||
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
|
||||
realized: RealizedPattern::new(client.clone(), acc.clone()),
|
||||
relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")),
|
||||
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
|
||||
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")),
|
||||
net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
|
||||
supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")),
|
||||
supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")),
|
||||
total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")),
|
||||
unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")),
|
||||
unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1989,27 +1963,53 @@ impl _100btcPattern {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UnrealizedPattern {
|
||||
pub neg_unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub net_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub supply_in_loss: ActiveSupplyPattern,
|
||||
pub supply_in_profit: ActiveSupplyPattern,
|
||||
pub total_unrealized_pnl: MetricPattern1<Dollars>,
|
||||
pub unrealized_loss: MetricPattern1<Dollars>,
|
||||
pub unrealized_profit: MetricPattern1<Dollars>,
|
||||
pub struct _0satsPattern2 {
|
||||
pub activity: ActivityPattern2,
|
||||
pub cost_basis: CostBasisPattern,
|
||||
pub outputs: OutputsPattern,
|
||||
pub realized: RealizedPattern,
|
||||
pub relative: RelativePattern4,
|
||||
pub supply: SupplyPattern2,
|
||||
pub unrealized: UnrealizedPattern,
|
||||
}
|
||||
|
||||
impl UnrealizedPattern {
|
||||
impl _0satsPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")),
|
||||
net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
|
||||
supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")),
|
||||
supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")),
|
||||
total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")),
|
||||
unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")),
|
||||
unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")),
|
||||
activity: ActivityPattern2::new(client.clone(), acc.clone()),
|
||||
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
|
||||
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
|
||||
realized: RealizedPattern::new(client.clone(), acc.clone()),
|
||||
relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")),
|
||||
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
|
||||
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _10yPattern {
|
||||
pub activity: ActivityPattern2,
|
||||
pub cost_basis: CostBasisPattern,
|
||||
pub outputs: OutputsPattern,
|
||||
pub realized: RealizedPattern4,
|
||||
pub relative: RelativePattern,
|
||||
pub supply: SupplyPattern2,
|
||||
pub unrealized: UnrealizedPattern,
|
||||
}
|
||||
|
||||
impl _10yPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
activity: ActivityPattern2::new(client.clone(), acc.clone()),
|
||||
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
|
||||
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
|
||||
realized: RealizedPattern4::new(client.clone(), acc.clone()),
|
||||
relative: RelativePattern::new(client.clone(), acc.clone()),
|
||||
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
|
||||
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2041,27 +2041,27 @@ impl _10yTo12yPattern {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct _10yPattern {
|
||||
pub activity: ActivityPattern2,
|
||||
pub cost_basis: CostBasisPattern,
|
||||
pub outputs: OutputsPattern,
|
||||
pub realized: RealizedPattern4,
|
||||
pub relative: RelativePattern,
|
||||
pub supply: SupplyPattern2,
|
||||
pub unrealized: UnrealizedPattern,
|
||||
pub struct PeriodCagrPattern {
|
||||
pub _10y: MetricPattern4<StoredF32>,
|
||||
pub _2y: MetricPattern4<StoredF32>,
|
||||
pub _3y: MetricPattern4<StoredF32>,
|
||||
pub _4y: MetricPattern4<StoredF32>,
|
||||
pub _5y: MetricPattern4<StoredF32>,
|
||||
pub _6y: MetricPattern4<StoredF32>,
|
||||
pub _8y: MetricPattern4<StoredF32>,
|
||||
}
|
||||
|
||||
impl _10yPattern {
|
||||
impl PeriodCagrPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
activity: ActivityPattern2::new(client.clone(), acc.clone()),
|
||||
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
|
||||
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
|
||||
realized: RealizedPattern4::new(client.clone(), acc.clone()),
|
||||
relative: RelativePattern::new(client.clone(), acc.clone()),
|
||||
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
|
||||
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
|
||||
_10y: MetricPattern4::new(client.clone(), _p("10y", &acc)),
|
||||
_2y: MetricPattern4::new(client.clone(), _p("2y", &acc)),
|
||||
_3y: MetricPattern4::new(client.clone(), _p("3y", &acc)),
|
||||
_4y: MetricPattern4::new(client.clone(), _p("4y", &acc)),
|
||||
_5y: MetricPattern4::new(client.clone(), _p("5y", &acc)),
|
||||
_6y: MetricPattern4::new(client.clone(), _p("6y", &acc)),
|
||||
_8y: MetricPattern4::new(client.clone(), _p("8y", &acc)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2109,19 +2109,55 @@ impl<T: DeserializeOwned> SplitPattern2<T> {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActiveSupplyPattern {
|
||||
pub bitcoin: MetricPattern1<Bitcoin>,
|
||||
pub dollars: MetricPattern1<Dollars>,
|
||||
pub sats: MetricPattern1<Sats>,
|
||||
pub struct UnclaimedRewardsPattern {
|
||||
pub bitcoin: BitcoinPattern2<Bitcoin>,
|
||||
pub dollars: BlockCountPattern<Dollars>,
|
||||
pub sats: BlockCountPattern<Sats>,
|
||||
}
|
||||
|
||||
impl ActiveSupplyPattern {
|
||||
impl UnclaimedRewardsPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
bitcoin: BitcoinPattern2::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: BlockCountPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CoinbasePattern {
|
||||
pub bitcoin: BitcoinPattern,
|
||||
pub dollars: DollarsPattern<Dollars>,
|
||||
pub sats: DollarsPattern<Sats>,
|
||||
}
|
||||
|
||||
impl CoinbasePattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: BitcoinPattern::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: DollarsPattern::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: DollarsPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct SegwitAdoptionPattern {
|
||||
pub base: MetricPattern11<StoredF32>,
|
||||
pub cumulative: MetricPattern2<StoredF32>,
|
||||
pub sum: MetricPattern2<StoredF32>,
|
||||
}
|
||||
|
||||
impl SegwitAdoptionPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
base: MetricPattern11::new(client.clone(), acc.clone()),
|
||||
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
|
||||
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2144,6 +2180,24 @@ impl _2015Pattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ActiveSupplyPattern {
|
||||
pub bitcoin: MetricPattern1<Bitcoin>,
|
||||
pub dollars: MetricPattern1<Dollars>,
|
||||
pub sats: MetricPattern1<Sats>,
|
||||
}
|
||||
|
||||
impl ActiveSupplyPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CoinbasePattern2 {
|
||||
pub bitcoin: BlockCountPattern<Bitcoin>,
|
||||
@@ -2181,55 +2235,17 @@ impl CostBasisPattern2 {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct SegwitAdoptionPattern {
|
||||
pub base: MetricPattern11<StoredF32>,
|
||||
pub cumulative: MetricPattern2<StoredF32>,
|
||||
pub sum: MetricPattern2<StoredF32>,
|
||||
pub struct CostBasisPattern {
|
||||
pub max: MetricPattern1<Dollars>,
|
||||
pub min: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl SegwitAdoptionPattern {
|
||||
impl CostBasisPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
base: MetricPattern11::new(client.clone(), acc.clone()),
|
||||
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
|
||||
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct UnclaimedRewardsPattern {
|
||||
pub bitcoin: BitcoinPattern2<Bitcoin>,
|
||||
pub dollars: BlockCountPattern<Dollars>,
|
||||
pub sats: BlockCountPattern<Sats>,
|
||||
}
|
||||
|
||||
impl UnclaimedRewardsPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: BitcoinPattern2::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: BlockCountPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CoinbasePattern {
|
||||
pub bitcoin: BitcoinPattern,
|
||||
pub dollars: DollarsPattern<Dollars>,
|
||||
pub sats: DollarsPattern<Sats>,
|
||||
}
|
||||
|
||||
impl CoinbasePattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
bitcoin: BitcoinPattern::new(client.clone(), _m(&acc, "btc")),
|
||||
dollars: DollarsPattern::new(client.clone(), _m(&acc, "usd")),
|
||||
sats: DollarsPattern::new(client.clone(), acc.clone()),
|
||||
max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")),
|
||||
min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2266,22 +2282,6 @@ impl _1dReturns1mSdPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CostBasisPattern {
|
||||
pub max: MetricPattern1<Dollars>,
|
||||
pub min: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
impl CostBasisPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")),
|
||||
min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct SupplyPattern2 {
|
||||
pub halved: ActiveSupplyPattern,
|
||||
@@ -2299,17 +2299,17 @@ impl SupplyPattern2 {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BlockCountPattern<T> {
|
||||
pub cumulative: MetricPattern1<T>,
|
||||
pub sum: MetricPattern1<T>,
|
||||
pub struct SatsPattern<T> {
|
||||
pub ohlc: MetricPattern1<T>,
|
||||
pub split: SplitPattern2<T>,
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned> BlockCountPattern<T> {
|
||||
impl<T: DeserializeOwned> SatsPattern<T> {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
cumulative: MetricPattern1::new(client.clone(), _m(&acc, "cumulative")),
|
||||
sum: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
ohlc: MetricPattern1::new(client.clone(), _m(&acc, "ohlc")),
|
||||
split: SplitPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2331,17 +2331,17 @@ impl<T: DeserializeOwned> BitcoinPattern2<T> {
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct SatsPattern<T> {
|
||||
pub ohlc: MetricPattern1<T>,
|
||||
pub split: SplitPattern2<T>,
|
||||
pub struct BlockCountPattern<T> {
|
||||
pub cumulative: MetricPattern1<T>,
|
||||
pub sum: MetricPattern1<T>,
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned> SatsPattern<T> {
|
||||
impl<T: DeserializeOwned> BlockCountPattern<T> {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
ohlc: MetricPattern1::new(client.clone(), _m(&acc, "ohlc_sats")),
|
||||
split: SplitPattern2::new(client.clone(), _m(&acc, "sats")),
|
||||
cumulative: MetricPattern1::new(client.clone(), _m(&acc, "cumulative")),
|
||||
sum: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4942,8 +4942,8 @@ impl MetricsTree_Positions {
|
||||
pub struct MetricsTree_Price {
|
||||
pub cents: MetricsTree_Price_Cents,
|
||||
pub oracle: MetricsTree_Price_Oracle,
|
||||
pub sats: SatsPattern<OHLCSats>,
|
||||
pub usd: MetricsTree_Price_Usd,
|
||||
pub sats: MetricsTree_Price_Sats,
|
||||
pub usd: SatsPattern<OHLCDollars>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Price {
|
||||
@@ -4951,8 +4951,8 @@ impl MetricsTree_Price {
|
||||
Self {
|
||||
cents: MetricsTree_Price_Cents::new(client.clone(), format!("{base_path}_cents")),
|
||||
oracle: MetricsTree_Price_Oracle::new(client.clone(), format!("{base_path}_oracle")),
|
||||
sats: SatsPattern::new(client.clone(), "price".to_string()),
|
||||
usd: MetricsTree_Price_Usd::new(client.clone(), format!("{base_path}_usd")),
|
||||
sats: MetricsTree_Price_Sats::new(client.clone(), format!("{base_path}_sats")),
|
||||
usd: SatsPattern::new(client.clone(), "price".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5055,16 +5055,16 @@ impl MetricsTree_Price_Oracle {
|
||||
}
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Price_Usd {
|
||||
pub ohlc: MetricPattern1<OHLCDollars>,
|
||||
pub split: SplitPattern2<Dollars>,
|
||||
pub struct MetricsTree_Price_Sats {
|
||||
pub ohlc: MetricPattern1<OHLCSats>,
|
||||
pub split: SplitPattern2<Sats>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Price_Usd {
|
||||
impl MetricsTree_Price_Sats {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
ohlc: MetricPattern1::new(client.clone(), "price_ohlc".to_string()),
|
||||
split: SplitPattern2::new(client.clone(), "price".to_string()),
|
||||
ohlc: MetricPattern1::new(client.clone(), "price_ohlc_sats".to_string()),
|
||||
split: SplitPattern2::new(client.clone(), "price_sats".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1456,9 +1456,9 @@ impl Vecs {
|
||||
let price_cents = if histogram.total_count() >= 10 {
|
||||
// Downsample 200 bins to 100 bins
|
||||
let mut bins100 = [0u32; 100];
|
||||
for i in 0..100 {
|
||||
(0..100).for_each(|i| {
|
||||
bins100[i] = histogram.bins[i * 2] as u32 + histogram.bins[i * 2 + 1] as u32;
|
||||
}
|
||||
});
|
||||
|
||||
// Find peak bin, skipping bin 0 (round BTC amounts cluster there)
|
||||
let peak_bin = bins100
|
||||
@@ -1553,10 +1553,10 @@ impl Vecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute Phase Oracle V3 - Step 1: Per-block histograms with uniqueVal filtering
|
||||
/// Compute Phase Oracle V3 - Step 1: Per-block histograms with uniqueVal + noP2TR filtering
|
||||
///
|
||||
/// Filters: >= 1000 sats, only outputs with unique values within their transaction.
|
||||
/// This reduces spurious peaks from exchange batched payouts and inscription spam.
|
||||
/// Filters: >= 1000 sats, no P2TR outputs, only outputs with unique values within their tx.
|
||||
/// This reduces spurious peaks from inscription spam and exchange batched payouts.
|
||||
fn compute_phase_v3_histograms(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
@@ -1564,7 +1564,8 @@ impl Vecs {
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let source_version = indexer.vecs.outputs.value.version();
|
||||
let source_version =
|
||||
indexer.vecs.outputs.value.version() + indexer.vecs.outputs.outputtype.version();
|
||||
self.phase_v3_histogram
|
||||
.validate_computed_version_or_reset(source_version)?;
|
||||
|
||||
@@ -1592,12 +1593,13 @@ impl Vecs {
|
||||
indexer.vecs.transactions.first_txoutindex.into_iter();
|
||||
let mut txindex_to_output_count_iter = indexes.txindex.output_count.iter();
|
||||
let mut txoutindex_to_value_iter = indexer.vecs.outputs.value.into_iter();
|
||||
let mut txoutindex_to_outputtype_iter = indexer.vecs.outputs.outputtype.into_iter();
|
||||
|
||||
let total_txs = indexer.vecs.transactions.height.len();
|
||||
let mut last_progress = (start_height * 100 / total_heights.max(1)) as u8;
|
||||
|
||||
// Reusable buffer for collecting output values per transaction
|
||||
let mut tx_values: Vec<Sats> = Vec::with_capacity(16);
|
||||
// Reusable buffer for collecting output values per transaction (sats, is_p2tr)
|
||||
let mut tx_outputs: Vec<(Sats, bool)> = Vec::with_capacity(16);
|
||||
|
||||
for height in start_height..total_heights {
|
||||
// Get transaction range for this block
|
||||
@@ -1606,7 +1608,7 @@ impl Vecs {
|
||||
.get_at(height + 1)
|
||||
.unwrap_or(TxIndex::from(total_txs));
|
||||
|
||||
// Build phase histogram with uniqueVal filtering
|
||||
// Build phase histogram with uniqueVal + noP2TR filtering
|
||||
let mut histogram = OracleBinsV2::ZERO;
|
||||
|
||||
// Skip coinbase (first tx in block)
|
||||
@@ -1616,17 +1618,25 @@ impl Vecs {
|
||||
let output_count: StoredU64 =
|
||||
txindex_to_output_count_iter.get_unwrap(TxIndex::from(txindex));
|
||||
|
||||
// Collect all output values for this transaction
|
||||
tx_values.clear();
|
||||
// Collect all output values and types for this transaction
|
||||
tx_outputs.clear();
|
||||
for i in 0..*output_count as usize {
|
||||
let txoutindex = first_txoutindex.to_usize() + i;
|
||||
let sats: Sats = txoutindex_to_value_iter.get_at_unwrap(txoutindex);
|
||||
tx_values.push(sats);
|
||||
let outputtype: OutputType =
|
||||
txoutindex_to_outputtype_iter.get_at_unwrap(txoutindex);
|
||||
let is_p2tr = outputtype == OutputType::P2TR;
|
||||
tx_outputs.push((sats, is_p2tr));
|
||||
}
|
||||
|
||||
// Count occurrences of each value to determine uniqueness
|
||||
// For small output counts, simple nested loop is faster than HashMap
|
||||
for (i, &sats) in tx_values.iter().enumerate() {
|
||||
for (i, &(sats, is_p2tr)) in tx_outputs.iter().enumerate() {
|
||||
// Skip P2TR outputs (inscription spam)
|
||||
if is_p2tr {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if below minimum (BASE filter: >= 1000 sats)
|
||||
if sats < Sats::_1K {
|
||||
continue;
|
||||
@@ -1634,7 +1644,7 @@ impl Vecs {
|
||||
|
||||
// Check if this value is unique within the transaction
|
||||
let mut is_unique = true;
|
||||
for (j, &other_sats) in tx_values.iter().enumerate() {
|
||||
for (j, &(other_sats, _)) in tx_outputs.iter().enumerate() {
|
||||
if i != j && sats == other_sats {
|
||||
is_unique = false;
|
||||
break;
|
||||
|
||||
@@ -72,9 +72,9 @@ impl Vecs {
|
||||
&phase_v2_peak_daily_cents,
|
||||
);
|
||||
|
||||
// Phase Oracle V3 (BASE + uniqueVal filter)
|
||||
// v4: Peak prices use 100 bins (downsampled from 200)
|
||||
let phase_v3_version = version + Version::new(4);
|
||||
// Phase Oracle V3 (BASE + noP2TR + uniqueVal filter)
|
||||
// v5: Added noP2TR filter to reduce inscription spam
|
||||
let phase_v3_version = version + Version::new(5);
|
||||
let phase_v3_histogram =
|
||||
BytesVec::forced_import(db, "phase_v3_histogram", phase_v3_version)?;
|
||||
let phase_v3_price_cents =
|
||||
|
||||
@@ -67,6 +67,7 @@ impl Default for PhaseHistogramV2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl PhaseHistogramV2 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -153,9 +154,7 @@ pub fn find_best_phase(
|
||||
(min_bin..=max_bin).collect()
|
||||
} else {
|
||||
// Wraps around
|
||||
(min_bin..PHASE_BINS_V2)
|
||||
.chain(0..=max_bin)
|
||||
.collect()
|
||||
(min_bin..PHASE_BINS_V2).chain(0..=max_bin).collect()
|
||||
}
|
||||
} else {
|
||||
(0..PHASE_BINS_V2).collect()
|
||||
|
||||
@@ -6,8 +6,7 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
build = "build.rs"
|
||||
include = ["src/**/*", "website/**/*", "examples/**/*", "build.rs", "Cargo.toml", "README.md"]
|
||||
include = ["src/**/*", "website/**/*", "examples/**/*", "Cargo.toml", "README.md"]
|
||||
|
||||
[dependencies]
|
||||
aide = { workspace = true }
|
||||
@@ -25,6 +24,8 @@ brk_types = { workspace = true }
|
||||
brk_traversable = { workspace = true }
|
||||
derive_more = { workspace = true }
|
||||
include_dir = "0.7"
|
||||
importmap = { path = "../../../importmap", features = ["embedded"] }
|
||||
# importmap = { version = "0.3", features = ["embedded"] }
|
||||
vecdb = { workspace = true }
|
||||
jiff = { workspace = true }
|
||||
quick_cache = "0.6.18"
|
||||
@@ -34,13 +35,7 @@ serde_json = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tower-http = { version = "0.6.8", features = ["catch-panic", "compression-full", "cors", "normalize-path", "timeout", "trace"] }
|
||||
|
||||
[build-dependencies]
|
||||
# importmap = { path = "../../../importmap" }
|
||||
importmap = "0.2"
|
||||
tower-layer = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
brk_mempool = { workspace = true }
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["importmap"]
|
||||
|
||||
@@ -13,8 +13,8 @@ HTTP API server for Bitcoin on-chain analytics.
|
||||
## Usage
|
||||
|
||||
```rust,ignore
|
||||
let server = Server::new(&async_query, data_path, WebsiteSource::Filesystem(files_path));
|
||||
// Or WebsiteSource::Embedded, or WebsiteSource::Disabled
|
||||
let server = Server::new(&async_query, data_path, Website::Filesystem(files_path));
|
||||
// Or Website::Default, or Website::Disabled
|
||||
server.serve().await?;
|
||||
```
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
use std::{env, path::Path};
|
||||
|
||||
fn main() {
|
||||
let is_dev = env::var("PROFILE").is_ok_and(|p| p == "debug");
|
||||
|
||||
// Generate importmap for website (updates index.html in place)
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
// Use ./website (symlink in repo, real dir in published crate)
|
||||
let website_path = Path::new(&manifest_dir).join("website");
|
||||
|
||||
println!("cargo:rerun-if-changed=website");
|
||||
println!("cargo::warning=build.rs: website_path={website_path:?}, exists={}", website_path.exists());
|
||||
|
||||
if website_path.exists() {
|
||||
// Skip importmap hashing in dev mode (files change often)
|
||||
let map = if is_dev {
|
||||
println!("cargo::warning=build.rs: dev mode, skipping importmap");
|
||||
importmap::ImportMap::empty()
|
||||
} else {
|
||||
match importmap::ImportMap::scan(&website_path, "") {
|
||||
Ok(map) => {
|
||||
println!("cargo::warning=build.rs: importmap scanned {} entries", map.imports.len());
|
||||
map
|
||||
}
|
||||
Err(e) => {
|
||||
println!("cargo::warning=build.rs: importmap scan failed: {e}");
|
||||
importmap::ImportMap::empty()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let index_path = website_path.join("index.html");
|
||||
if let Err(e) = map.update_html_file(&index_path) {
|
||||
println!("cargo::warning=build.rs: failed to update index.html: {e}");
|
||||
}
|
||||
} else {
|
||||
println!("cargo::warning=build.rs: website path does not exist!");
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use brk_mempool::Mempool;
|
||||
use brk_query::AsyncQuery;
|
||||
use brk_reader::Reader;
|
||||
use brk_rpc::{Auth, Client};
|
||||
use brk_server::{Server, WebsiteSource};
|
||||
use brk_server::{Server, Website};
|
||||
use tracing::info;
|
||||
use vecdb::Exit;
|
||||
|
||||
@@ -54,7 +54,7 @@ fn run() -> Result<()> {
|
||||
|
||||
// Option 1: block_on to run and properly propagate errors
|
||||
runtime.block_on(async move {
|
||||
let server = Server::new(&query, outputs_dir, WebsiteSource::Disabled);
|
||||
let server = Server::new(&query, outputs_dir, Website::Disabled);
|
||||
|
||||
let handle = tokio::spawn(async move { server.serve().await });
|
||||
|
||||
|
||||
@@ -1,35 +1,17 @@
|
||||
use std::{
|
||||
path::Path,
|
||||
time::{self, Duration},
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
use axum::http::{
|
||||
HeaderMap,
|
||||
header::{self, IF_MODIFIED_SINCE, IF_NONE_MATCH},
|
||||
header::{self, IF_NONE_MATCH},
|
||||
};
|
||||
use brk_error::Result;
|
||||
use jiff::{Timestamp, civil::DateTime, fmt::strtime, tz::TimeZone};
|
||||
|
||||
const MODIFIED_SINCE_FORMAT: &str = "%a, %d %b %Y %H:%M:%S GMT";
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ModifiedState {
|
||||
ModifiedSince,
|
||||
NotModifiedSince,
|
||||
}
|
||||
|
||||
pub trait HeaderMapExtended {
|
||||
fn has_etag(&self, etag: &str) -> bool;
|
||||
|
||||
fn get_if_modified_since(&self) -> Option<DateTime>;
|
||||
fn check_if_modified_since(&self, path: &Path) -> Result<(ModifiedState, DateTime)>;
|
||||
fn check_if_modified_since_(&self, duration: Duration) -> Result<(ModifiedState, DateTime)>;
|
||||
|
||||
fn insert_cache_control(&mut self, value: &str);
|
||||
fn insert_cache_control_must_revalidate(&mut self);
|
||||
fn insert_cache_control_immutable(&mut self);
|
||||
fn insert_etag(&mut self, etag: &str);
|
||||
fn insert_last_modified(&mut self, date: DateTime);
|
||||
|
||||
fn insert_content_disposition_attachment(&mut self);
|
||||
|
||||
@@ -66,56 +48,10 @@ impl HeaderMapExtended for HeaderMap {
|
||||
self.insert(header::CONTENT_DISPOSITION, "attachment".parse().unwrap());
|
||||
}
|
||||
|
||||
fn insert_last_modified(&mut self, date: DateTime) {
|
||||
let formatted = date
|
||||
.to_zoned(TimeZone::system())
|
||||
.unwrap()
|
||||
.strftime(MODIFIED_SINCE_FORMAT)
|
||||
.to_string();
|
||||
|
||||
self.insert(header::LAST_MODIFIED, formatted.parse().unwrap());
|
||||
}
|
||||
|
||||
fn insert_etag(&mut self, etag: &str) {
|
||||
self.insert(header::ETAG, etag.parse().unwrap());
|
||||
}
|
||||
|
||||
fn check_if_modified_since(&self, path: &Path) -> Result<(ModifiedState, DateTime)> {
|
||||
self.check_if_modified_since_(
|
||||
path.metadata()?
|
||||
.modified()?
|
||||
.duration_since(time::UNIX_EPOCH)?,
|
||||
)
|
||||
}
|
||||
|
||||
fn check_if_modified_since_(&self, duration: Duration) -> Result<(ModifiedState, DateTime)> {
|
||||
let date = Timestamp::new(duration.as_secs() as i64, 0)
|
||||
.unwrap()
|
||||
.to_zoned(TimeZone::UTC)
|
||||
.datetime();
|
||||
|
||||
if let Some(if_modified_since) = self.get_if_modified_since()
|
||||
&& if_modified_since == date
|
||||
{
|
||||
return Ok((ModifiedState::NotModifiedSince, date));
|
||||
}
|
||||
|
||||
Ok((ModifiedState::ModifiedSince, date))
|
||||
}
|
||||
|
||||
fn get_if_modified_since(&self) -> Option<DateTime> {
|
||||
if let Some(modified_since) = self.get(IF_MODIFIED_SINCE)
|
||||
&& let Ok(modified_since) = modified_since.to_str()
|
||||
{
|
||||
return strtime::parse(MODIFIED_SINCE_FORMAT, modified_since)
|
||||
.unwrap()
|
||||
.to_datetime()
|
||||
.ok();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn has_etag(&self, etag: &str) -> bool {
|
||||
self.get(IF_NONE_MATCH)
|
||||
.is_some_and(|prev_etag| etag == prev_etag)
|
||||
|
||||
@@ -1,217 +1,47 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::{self, State},
|
||||
http::HeaderMap,
|
||||
response::Response,
|
||||
};
|
||||
use quick_cache::sync::GuardResult;
|
||||
use tracing::{error, info};
|
||||
use axum::{body::Body, extract::State, response::Response};
|
||||
|
||||
use crate::{
|
||||
AppState, EMBEDDED_WEBSITE, Error, HeaderMapExtended, ModifiedState, ResponseExtended, Result,
|
||||
WebsiteSource,
|
||||
};
|
||||
use crate::{AppState, HeaderMapExtended, Result};
|
||||
|
||||
pub async fn file_handler(
|
||||
headers: HeaderMap,
|
||||
State(state): State<AppState>,
|
||||
path: extract::Path<String>,
|
||||
path: axum::extract::Path<String>,
|
||||
) -> Result<Response> {
|
||||
any_handler(headers, state, Some(path.0))
|
||||
serve(&state, &path.0)
|
||||
}
|
||||
|
||||
pub async fn index_handler(headers: HeaderMap, State(state): State<AppState>) -> Result<Response> {
|
||||
any_handler(headers, state, None)
|
||||
pub async fn index_handler(State(state): State<AppState>) -> Result<Response> {
|
||||
serve(&state, "")
|
||||
}
|
||||
|
||||
fn any_handler(headers: HeaderMap, state: AppState, path: Option<String>) -> Result<Response> {
|
||||
match &state.website {
|
||||
WebsiteSource::Disabled => unreachable!("routes not added when disabled"),
|
||||
WebsiteSource::Embedded => embedded_handler(&state, path),
|
||||
WebsiteSource::Filesystem(files_path) => {
|
||||
filesystem_handler(headers, &state, files_path, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sanitize path to prevent traversal attacks
|
||||
fn sanitize_path(path: &str) -> String {
|
||||
path.split('/')
|
||||
.filter(|c| !c.is_empty() && *c != "." && *c != "..")
|
||||
.collect::<Vec<_>>()
|
||||
.join("/")
|
||||
}
|
||||
|
||||
/// Check if path requires revalidation (HTML files, service worker)
|
||||
fn must_revalidate(path: &Path) -> bool {
|
||||
path.extension().is_some_and(|ext| ext == "html")
|
||||
|| path
|
||||
.to_str()
|
||||
.is_some_and(|p| p.ends_with("service-worker.js"))
|
||||
}
|
||||
|
||||
/// Build response with proper headers and caching
|
||||
fn build_response(state: &AppState, path: &Path, content: Vec<u8>, cache_key: &str) -> Response {
|
||||
let must_revalidate = must_revalidate(path);
|
||||
|
||||
// Use cache for non-HTML files in release mode
|
||||
let guard_res = if !cfg!(debug_assertions) && !must_revalidate {
|
||||
Some(
|
||||
state
|
||||
.cache
|
||||
.get_value_or_guard(&cache_key.to_owned(), Some(Duration::from_millis(50))),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut response = if let Some(GuardResult::Value(v)) = guard_res {
|
||||
Response::new(Body::from(v))
|
||||
} else {
|
||||
if let Some(GuardResult::Guard(g)) = guard_res {
|
||||
let _ = g.insert(content.clone().into());
|
||||
}
|
||||
Response::new(Body::from(content))
|
||||
};
|
||||
fn serve(state: &AppState, path: &str) -> Result<Response> {
|
||||
let path = sanitize(path);
|
||||
let content = state.website.get_file(&path)?;
|
||||
|
||||
let mut response = Response::new(Body::from(content));
|
||||
let headers = response.headers_mut();
|
||||
headers.insert_content_type(path);
|
||||
|
||||
if cfg!(debug_assertions) || must_revalidate {
|
||||
// Empty path or no extension = index.html (SPA fallback)
|
||||
if path.is_empty() || Path::new(&path).extension().is_none() {
|
||||
headers.insert_content_type_text_html();
|
||||
} else {
|
||||
headers.insert_content_type(Path::new(&path));
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
headers.insert_cache_control_must_revalidate();
|
||||
} else {
|
||||
headers.insert_cache_control_immutable();
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
fn embedded_handler(state: &AppState, path: Option<String>) -> Result<Response> {
|
||||
let path = path.unwrap_or_else(|| "index.html".to_string());
|
||||
let sanitized = sanitize_path(&path);
|
||||
|
||||
// Try to get file, with importmap hash stripping and SPA fallback
|
||||
let file = EMBEDDED_WEBSITE
|
||||
.get_file(&sanitized)
|
||||
.or_else(|| {
|
||||
strip_importmap_hash(Path::new(&sanitized))
|
||||
.and_then(|unhashed| EMBEDDED_WEBSITE.get_file(unhashed.to_str()?))
|
||||
})
|
||||
.or_else(|| {
|
||||
// If no extension, serve index.html (SPA routing)
|
||||
if Path::new(&sanitized).extension().is_none() {
|
||||
EMBEDDED_WEBSITE.get_file("index.html")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let Some(file) = file else {
|
||||
return Err(Error::not_found("File not found"));
|
||||
};
|
||||
|
||||
Ok(build_response(
|
||||
state,
|
||||
Path::new(file.path()),
|
||||
file.contents().to_vec(),
|
||||
&file.path().to_string_lossy(),
|
||||
))
|
||||
}
|
||||
|
||||
fn filesystem_handler(
|
||||
headers: HeaderMap,
|
||||
state: &AppState,
|
||||
files_path: &Path,
|
||||
path: Option<String>,
|
||||
) -> Result<Response> {
|
||||
let path = if let Some(path) = path {
|
||||
let sanitized = sanitize_path(&path);
|
||||
let mut path = files_path.join(&sanitized);
|
||||
|
||||
// Canonicalize and verify the path stays within the project root
|
||||
// (allows symlinks to modules/ which is outside the website directory)
|
||||
if let Ok(canonical) = path.canonicalize()
|
||||
&& let Ok(canonical_base) = files_path.canonicalize()
|
||||
{
|
||||
let project_root = canonical_base.parent().and_then(|p| p.parent());
|
||||
let allowed = canonical.starts_with(&canonical_base)
|
||||
|| project_root.is_some_and(|root| canonical.starts_with(root));
|
||||
if !allowed {
|
||||
return Err(Error::forbidden("Access denied"));
|
||||
}
|
||||
}
|
||||
|
||||
// Strip hash from import-mapped URLs
|
||||
if !path.exists()
|
||||
&& let Some(unhashed) = strip_importmap_hash(&path)
|
||||
&& unhashed.exists()
|
||||
{
|
||||
path = unhashed;
|
||||
}
|
||||
|
||||
// SPA fallback
|
||||
if !path.exists() || path.is_dir() {
|
||||
if path.extension().is_some() {
|
||||
return Err(Error::not_found("File doesn't exist"));
|
||||
} else {
|
||||
path = files_path.join("index.html");
|
||||
}
|
||||
}
|
||||
|
||||
path
|
||||
} else {
|
||||
files_path.join("index.html")
|
||||
};
|
||||
|
||||
path_to_response(&headers, state, &path)
|
||||
}
|
||||
|
||||
fn path_to_response(headers: &HeaderMap, state: &AppState, path: &Path) -> Result<Response> {
|
||||
let (modified, date) = headers.check_if_modified_since(path)?;
|
||||
if !cfg!(debug_assertions) && modified == ModifiedState::NotModifiedSince {
|
||||
return Ok(Response::new_not_modified());
|
||||
}
|
||||
|
||||
let content = fs::read(path).unwrap_or_else(|error| {
|
||||
error!("{error}");
|
||||
let path = path.to_str().unwrap();
|
||||
info!("Can't read file {path}");
|
||||
panic!("")
|
||||
});
|
||||
|
||||
let cache_key = path.to_str().unwrap();
|
||||
let mut response = build_response(state, path, content, cache_key);
|
||||
response.headers_mut().insert_last_modified(date);
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
/// Strip importmap hash from filename: `foo.abc12345.js` -> `foo.js`
|
||||
/// Hash is 8 hex characters between the name and extension.
|
||||
fn strip_importmap_hash(path: &Path) -> Option<PathBuf> {
|
||||
let stem = path.file_stem()?.to_str()?;
|
||||
let ext = path.extension()?.to_str()?;
|
||||
|
||||
// Only process js/mjs/css files
|
||||
if !matches!(ext, "js" | "mjs" | "css") {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Look for pattern: name.HASH where HASH is 8 hex chars
|
||||
let dot_pos = stem.rfind('.')?;
|
||||
let hash = &stem[dot_pos + 1..];
|
||||
|
||||
if hash.len() == 8 && hash.chars().all(|c| c.is_ascii_hexdigit()) {
|
||||
let name = &stem[..dot_pos];
|
||||
let new_name = format!("{}.{}", name, ext);
|
||||
Some(path.with_file_name(new_name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
/// Sanitize path to prevent directory traversal attacks
|
||||
fn sanitize(path: &str) -> String {
|
||||
path.split('/')
|
||||
.filter(|s| !s.is_empty() && *s != "." && *s != "..")
|
||||
.collect::<Vec<_>>()
|
||||
.join("/")
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
use aide::axum::ApiRouter;
|
||||
use axum::{response::Redirect, routing::get};
|
||||
|
||||
use super::{AppState, WebsiteSource};
|
||||
use super::AppState;
|
||||
|
||||
mod file;
|
||||
mod website;
|
||||
|
||||
use file::{file_handler, index_handler};
|
||||
pub use website::*;
|
||||
|
||||
pub trait FilesRoutes {
|
||||
fn add_files_routes(self, website: &WebsiteSource) -> Self;
|
||||
fn add_files_routes(self, website: &Website) -> Self;
|
||||
}
|
||||
|
||||
impl FilesRoutes for ApiRouter<AppState> {
|
||||
fn add_files_routes(self, website: &WebsiteSource) -> Self {
|
||||
fn add_files_routes(self, website: &Website) -> Self {
|
||||
if website.is_enabled() {
|
||||
self.route("/{*path}", get(file_handler))
|
||||
.route("/", get(index_handler))
|
||||
|
||||
193
crates/brk_server/src/files/website.rs
Normal file
193
crates/brk_server/src/files/website.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use importmap::ImportMap;
|
||||
use tracing::{error, info};
|
||||
|
||||
use crate::{EMBEDDED_WEBSITE, Error, Result};
|
||||
|
||||
/// Cached index.html with importmap injected
|
||||
static INDEX_HTML: OnceLock<String> = OnceLock::new();
|
||||
|
||||
/// Source for serving the website
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Website {
|
||||
Disabled,
|
||||
Default,
|
||||
Filesystem(PathBuf),
|
||||
}
|
||||
|
||||
impl Website {
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
!matches!(self, Self::Disabled)
|
||||
}
|
||||
|
||||
/// Returns the filesystem path if available, None means use embedded
|
||||
pub fn filesystem_path(&self) -> Option<PathBuf> {
|
||||
match self {
|
||||
Self::Disabled => None,
|
||||
Self::Default => {
|
||||
if cfg!(debug_assertions) {
|
||||
let local = PathBuf::from("./website");
|
||||
local.exists().then_some(local)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Self::Filesystem(p) => Some(p.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get file content by path (handles hash-stripping, SPA fallback, importmap)
|
||||
pub fn get_file(&self, path: &str) -> Result<Vec<u8>> {
|
||||
match self.filesystem_path() {
|
||||
None => self.get_embedded(path),
|
||||
Some(base) => self.get_filesystem(&base, path),
|
||||
}
|
||||
}
|
||||
|
||||
/// Log which website source is being used (call once at startup)
|
||||
pub fn log(&self) {
|
||||
match self {
|
||||
Self::Disabled => info!("Website: disabled"),
|
||||
Self::Default => {
|
||||
if let Some(p) = self.filesystem_path() {
|
||||
info!("Website: filesystem ({})", p.display());
|
||||
} else {
|
||||
info!("Website: embedded");
|
||||
}
|
||||
}
|
||||
Self::Filesystem(p) => info!("Website: filesystem ({})", p.display()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_index(&self) -> Result<Vec<u8>> {
|
||||
// Debug mode: no importmap, no cache
|
||||
if cfg!(debug_assertions) {
|
||||
return match self.filesystem_path() {
|
||||
Some(base) => {
|
||||
fs::read(base.join("index.html")).map_err(|e| Error::not_found(e.to_string()))
|
||||
}
|
||||
None => {
|
||||
let file = EMBEDDED_WEBSITE
|
||||
.get_file("index.html")
|
||||
.expect("index.html must exist in embedded website");
|
||||
Ok(file.contents().to_vec())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Release mode: cache with importmap
|
||||
let html = INDEX_HTML.get_or_init(|| match self.filesystem_path() {
|
||||
None => {
|
||||
let file = EMBEDDED_WEBSITE
|
||||
.get_file("index.html")
|
||||
.expect("index.html must exist in embedded website");
|
||||
|
||||
let html =
|
||||
std::str::from_utf8(file.contents()).expect("index.html must be valid UTF-8");
|
||||
|
||||
let importmap = ImportMap::scan_embedded(&EMBEDDED_WEBSITE, "");
|
||||
importmap
|
||||
.transform_html(html)
|
||||
.unwrap_or_else(|| html.to_string())
|
||||
}
|
||||
Some(base) => {
|
||||
let html =
|
||||
fs::read_to_string(base.join("index.html")).expect("index.html must exist");
|
||||
|
||||
match ImportMap::scan(&base, "") {
|
||||
Ok(importmap) => importmap.transform_html(&html).unwrap_or(html),
|
||||
Err(e) => {
|
||||
error!("Failed to scan for importmap: {e}");
|
||||
html
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(html.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
fn get_embedded(&self, path: &str) -> Result<Vec<u8>> {
|
||||
// Index.html
|
||||
if path.is_empty() || path == "index.html" {
|
||||
return self.get_index();
|
||||
}
|
||||
|
||||
// Try direct lookup, then with hash stripped
|
||||
let file = EMBEDDED_WEBSITE.get_file(path).or_else(|| {
|
||||
strip_importmap_hash(Path::new(path))
|
||||
.and_then(|unhashed| EMBEDDED_WEBSITE.get_file(unhashed.to_str()?))
|
||||
});
|
||||
|
||||
if let Some(file) = file {
|
||||
return Ok(file.contents().to_vec());
|
||||
}
|
||||
|
||||
// SPA fallback: no extension -> index.html
|
||||
if Path::new(path).extension().is_none() {
|
||||
return self.get_index();
|
||||
}
|
||||
|
||||
Err(Error::not_found("File not found"))
|
||||
}
|
||||
|
||||
fn get_filesystem(&self, base: &Path, path: &str) -> Result<Vec<u8>> {
|
||||
// Index.html
|
||||
if path.is_empty() {
|
||||
return self.get_index();
|
||||
}
|
||||
|
||||
let mut file_path = base.join(path);
|
||||
|
||||
// Try with hash stripped
|
||||
if !file_path.exists()
|
||||
&& let Some(unhashed) = strip_importmap_hash(&file_path)
|
||||
&& unhashed.exists()
|
||||
{
|
||||
file_path = unhashed;
|
||||
}
|
||||
|
||||
// SPA fallback or missing file
|
||||
if !file_path.exists() || file_path.is_dir() {
|
||||
if file_path.extension().is_some() {
|
||||
return Err(Error::not_found("File not found"));
|
||||
}
|
||||
return self.get_index();
|
||||
}
|
||||
|
||||
// Explicit index.html request
|
||||
if file_path.file_name().is_some_and(|n| n == "index.html") {
|
||||
return self.get_index();
|
||||
}
|
||||
|
||||
fs::read(&file_path).map_err(|e| {
|
||||
error!("{e}");
|
||||
Error::not_found("File not found")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip importmap hash from filename: `foo.abc12345.js` -> `foo.js`
|
||||
fn strip_importmap_hash(path: &Path) -> Option<PathBuf> {
|
||||
let stem = path.file_stem()?.to_str()?;
|
||||
let ext = path.extension()?.to_str()?;
|
||||
|
||||
if !matches!(ext, "js" | "mjs" | "css") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let dot_pos = stem.rfind('.')?;
|
||||
let hash = &stem[dot_pos + 1..];
|
||||
|
||||
if hash.len() == 8 && hash.chars().all(|c| c.is_ascii_hexdigit()) {
|
||||
let name = &stem[..dot_pos];
|
||||
Some(path.with_file_name(format!("{}.{}", name, ext)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use std::{
|
||||
|
||||
use aide::axum::ApiRouter;
|
||||
use axum::{
|
||||
Extension,
|
||||
Extension, ServiceExt,
|
||||
body::Body,
|
||||
http::{Request, Response, StatusCode, Uri},
|
||||
middleware::Next,
|
||||
@@ -26,25 +26,12 @@ use tower_http::{
|
||||
compression::CompressionLayer, cors::CorsLayer, normalize_path::NormalizePathLayer,
|
||||
timeout::TimeoutLayer, trace::TraceLayer,
|
||||
};
|
||||
use tower_layer::Layer;
|
||||
use tracing::{error, info};
|
||||
|
||||
/// Embedded website assets
|
||||
pub static EMBEDDED_WEBSITE: Dir = include_dir!("$CARGO_MANIFEST_DIR/website");
|
||||
|
||||
/// Source for serving the website
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WebsiteSource {
|
||||
Disabled,
|
||||
Embedded,
|
||||
Filesystem(PathBuf),
|
||||
}
|
||||
|
||||
impl WebsiteSource {
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
!matches!(self, Self::Disabled)
|
||||
}
|
||||
}
|
||||
|
||||
mod api;
|
||||
pub mod cache;
|
||||
mod error;
|
||||
@@ -57,6 +44,7 @@ pub use cache::{CacheParams, CacheStrategy};
|
||||
pub use error::{Error, Result};
|
||||
use extended::*;
|
||||
use files::FilesRoutes;
|
||||
pub use files::Website;
|
||||
use state::*;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -64,7 +52,8 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub struct Server(AppState);
|
||||
|
||||
impl Server {
|
||||
pub fn new(query: &AsyncQuery, data_path: PathBuf, website: WebsiteSource) -> Self {
|
||||
pub fn new(query: &AsyncQuery, data_path: PathBuf, website: Website) -> Self {
|
||||
website.log();
|
||||
Self(AppState {
|
||||
client: query.client().clone(),
|
||||
query: query.clone(),
|
||||
@@ -147,8 +136,7 @@ impl Server {
|
||||
.layer(response_uri_layer)
|
||||
.layer(trace_layer)
|
||||
.layer(TimeoutLayer::with_status_code(StatusCode::GATEWAY_TIMEOUT, Duration::from_secs(5)))
|
||||
.layer(CorsLayer::permissive())
|
||||
.layer(NormalizePathLayer::trim_trailing_slash());
|
||||
.layer(CorsLayer::permissive());
|
||||
|
||||
const BASE_PORT: u16 = 3110;
|
||||
const MAX_PORT: u16 = BASE_PORT + 100;
|
||||
@@ -190,12 +178,15 @@ impl Server {
|
||||
Err(_) => error!("Client generation panicked"),
|
||||
}
|
||||
|
||||
let router = router
|
||||
.layer(Extension(Arc::new(openapi)))
|
||||
.layer(Extension(openapi_trimmed));
|
||||
|
||||
let service = NormalizePathLayer::trim_trailing_slash().layer(router);
|
||||
|
||||
serve(
|
||||
listener,
|
||||
router
|
||||
.layer(Extension(Arc::new(openapi)))
|
||||
.layer(Extension(openapi_trimmed))
|
||||
.into_make_service(),
|
||||
ServiceExt::<Request<Body>>::into_make_service(service),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use quick_cache::sync::Cache;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
CacheParams, CacheStrategy, WebsiteSource,
|
||||
CacheParams, CacheStrategy, Website,
|
||||
extended::{ResponseExtended, ResultExtended},
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct AppState {
|
||||
#[deref]
|
||||
pub query: AsyncQuery,
|
||||
pub data_path: PathBuf,
|
||||
pub website: WebsiteSource,
|
||||
pub website: Website,
|
||||
pub cache: Arc<Cache<String, Bytes>>,
|
||||
pub client: Client,
|
||||
pub started_at: Timestamp,
|
||||
|
||||
@@ -5,6 +5,8 @@ use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use vecdb::AnySerializableVec;
|
||||
|
||||
use super::Timestamp;
|
||||
|
||||
/// Metric data with range information.
|
||||
///
|
||||
/// All metric data endpoints return this structure when format is JSON.
|
||||
@@ -19,12 +21,14 @@ pub struct MetricData<T = Value> {
|
||||
pub start: usize,
|
||||
/// End index (exclusive) of the returned range
|
||||
pub end: usize,
|
||||
/// ISO 8601 timestamp of when the response was generated
|
||||
pub stamp: String,
|
||||
/// The metric data
|
||||
pub data: Vec<T>,
|
||||
}
|
||||
|
||||
impl MetricData {
|
||||
/// Write metric data as JSON to buffer: `{"version":N,"total":N,"start":N,"end":N,"data":[...]}`
|
||||
/// Write metric data as JSON to buffer: `{"version":N,"total":N,"start":N,"end":N,"stamp":"...","data":[...]}`
|
||||
pub fn serialize(
|
||||
vec: &dyn AnySerializableVec,
|
||||
start: usize,
|
||||
@@ -35,10 +39,11 @@ impl MetricData {
|
||||
let total = vec.len();
|
||||
let end = end.min(total);
|
||||
let start = start.min(end);
|
||||
let stamp = Timestamp::now().to_iso8601();
|
||||
|
||||
write!(
|
||||
buf,
|
||||
r#"{{"version":{version},"total":{total},"start":{start},"end":{end},"data":"#,
|
||||
r#"{{"version":{version},"total":{total},"start":{start},"end":{end},"stamp":"{stamp}","data":"#,
|
||||
)?;
|
||||
vec.write_json(Some(start), Some(end), buf)?;
|
||||
buf.push(b'}');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Self-Hosting
|
||||
|
||||
BRK is designed to be self-hosted. See the [brk_cli documentation](https://docs.rs/brk_cli) for installation, requirements, and configuration options.
|
||||
BRK is designed to be self-hosted. See the [brk_cli documentation](https://github.com/bitcoinresearchkit/brk/blob/main/crates/brk_cli/README.md#brk_cli) for installation, requirements, and configuration options.
|
||||
|
||||
## Professional Hosting
|
||||
|
||||
|
||||
@@ -1645,59 +1645,6 @@ function createPrice111dSmaPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ActivePriceRatioPattern
|
||||
* @property {MetricPattern4<StoredF32>} ratio
|
||||
* @property {MetricPattern4<StoredF32>} ratio1mSma
|
||||
* @property {MetricPattern4<StoredF32>} ratio1wSma
|
||||
* @property {Ratio1ySdPattern} ratio1ySd
|
||||
* @property {Ratio1ySdPattern} ratio2ySd
|
||||
* @property {Ratio1ySdPattern} ratio4ySd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct1
|
||||
* @property {MetricPattern4<Dollars>} ratioPct1Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct2
|
||||
* @property {MetricPattern4<Dollars>} ratioPct2Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct5
|
||||
* @property {MetricPattern4<Dollars>} ratioPct5Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct95
|
||||
* @property {MetricPattern4<Dollars>} ratioPct95Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct98
|
||||
* @property {MetricPattern4<Dollars>} ratioPct98Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct99
|
||||
* @property {MetricPattern4<Dollars>} ratioPct99Usd
|
||||
* @property {Ratio1ySdPattern} ratioSd
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ActivePriceRatioPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ActivePriceRatioPattern}
|
||||
*/
|
||||
function createActivePriceRatioPattern(client, acc) {
|
||||
return {
|
||||
ratio: createMetricPattern4(client, acc),
|
||||
ratio1mSma: createMetricPattern4(client, _m(acc, '1m_sma')),
|
||||
ratio1wSma: createMetricPattern4(client, _m(acc, '1w_sma')),
|
||||
ratio1ySd: createRatio1ySdPattern(client, _m(acc, '1y')),
|
||||
ratio2ySd: createRatio1ySdPattern(client, _m(acc, '2y')),
|
||||
ratio4ySd: createRatio1ySdPattern(client, _m(acc, '4y')),
|
||||
ratioPct1: createMetricPattern4(client, _m(acc, 'pct1')),
|
||||
ratioPct1Usd: createMetricPattern4(client, _m(acc, 'pct1_usd')),
|
||||
ratioPct2: createMetricPattern4(client, _m(acc, 'pct2')),
|
||||
ratioPct2Usd: createMetricPattern4(client, _m(acc, 'pct2_usd')),
|
||||
ratioPct5: createMetricPattern4(client, _m(acc, 'pct5')),
|
||||
ratioPct5Usd: createMetricPattern4(client, _m(acc, 'pct5_usd')),
|
||||
ratioPct95: createMetricPattern4(client, _m(acc, 'pct95')),
|
||||
ratioPct95Usd: createMetricPattern4(client, _m(acc, 'pct95_usd')),
|
||||
ratioPct98: createMetricPattern4(client, _m(acc, 'pct98')),
|
||||
ratioPct98Usd: createMetricPattern4(client, _m(acc, 'pct98_usd')),
|
||||
ratioPct99: createMetricPattern4(client, _m(acc, 'pct99')),
|
||||
ratioPct99Usd: createMetricPattern4(client, _m(acc, 'pct99_usd')),
|
||||
ratioSd: createRatio1ySdPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PercentilesPattern
|
||||
* @property {MetricPattern4<Dollars>} pct05
|
||||
@@ -1751,6 +1698,59 @@ function createPercentilesPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ActivePriceRatioPattern
|
||||
* @property {MetricPattern4<StoredF32>} ratio
|
||||
* @property {MetricPattern4<StoredF32>} ratio1mSma
|
||||
* @property {MetricPattern4<StoredF32>} ratio1wSma
|
||||
* @property {Ratio1ySdPattern} ratio1ySd
|
||||
* @property {Ratio1ySdPattern} ratio2ySd
|
||||
* @property {Ratio1ySdPattern} ratio4ySd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct1
|
||||
* @property {MetricPattern4<Dollars>} ratioPct1Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct2
|
||||
* @property {MetricPattern4<Dollars>} ratioPct2Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct5
|
||||
* @property {MetricPattern4<Dollars>} ratioPct5Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct95
|
||||
* @property {MetricPattern4<Dollars>} ratioPct95Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct98
|
||||
* @property {MetricPattern4<Dollars>} ratioPct98Usd
|
||||
* @property {MetricPattern4<StoredF32>} ratioPct99
|
||||
* @property {MetricPattern4<Dollars>} ratioPct99Usd
|
||||
* @property {Ratio1ySdPattern} ratioSd
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ActivePriceRatioPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ActivePriceRatioPattern}
|
||||
*/
|
||||
function createActivePriceRatioPattern(client, acc) {
|
||||
return {
|
||||
ratio: createMetricPattern4(client, acc),
|
||||
ratio1mSma: createMetricPattern4(client, _m(acc, '1m_sma')),
|
||||
ratio1wSma: createMetricPattern4(client, _m(acc, '1w_sma')),
|
||||
ratio1ySd: createRatio1ySdPattern(client, _m(acc, '1y')),
|
||||
ratio2ySd: createRatio1ySdPattern(client, _m(acc, '2y')),
|
||||
ratio4ySd: createRatio1ySdPattern(client, _m(acc, '4y')),
|
||||
ratioPct1: createMetricPattern4(client, _m(acc, 'pct1')),
|
||||
ratioPct1Usd: createMetricPattern4(client, _m(acc, 'pct1_usd')),
|
||||
ratioPct2: createMetricPattern4(client, _m(acc, 'pct2')),
|
||||
ratioPct2Usd: createMetricPattern4(client, _m(acc, 'pct2_usd')),
|
||||
ratioPct5: createMetricPattern4(client, _m(acc, 'pct5')),
|
||||
ratioPct5Usd: createMetricPattern4(client, _m(acc, 'pct5_usd')),
|
||||
ratioPct95: createMetricPattern4(client, _m(acc, 'pct95')),
|
||||
ratioPct95Usd: createMetricPattern4(client, _m(acc, 'pct95_usd')),
|
||||
ratioPct98: createMetricPattern4(client, _m(acc, 'pct98')),
|
||||
ratioPct98Usd: createMetricPattern4(client, _m(acc, 'pct98_usd')),
|
||||
ratioPct99: createMetricPattern4(client, _m(acc, 'pct99')),
|
||||
ratioPct99Usd: createMetricPattern4(client, _m(acc, 'pct99_usd')),
|
||||
ratioSd: createRatio1ySdPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} RelativePattern5
|
||||
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
|
||||
@@ -2005,45 +2005,6 @@ function createBitcoinPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} ClassAveragePricePattern
|
||||
* @property {MetricPattern4<T>} _2015
|
||||
* @property {MetricPattern4<T>} _2016
|
||||
* @property {MetricPattern4<T>} _2017
|
||||
* @property {MetricPattern4<T>} _2018
|
||||
* @property {MetricPattern4<T>} _2019
|
||||
* @property {MetricPattern4<T>} _2020
|
||||
* @property {MetricPattern4<T>} _2021
|
||||
* @property {MetricPattern4<T>} _2022
|
||||
* @property {MetricPattern4<T>} _2023
|
||||
* @property {MetricPattern4<T>} _2024
|
||||
* @property {MetricPattern4<T>} _2025
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ClassAveragePricePattern pattern node
|
||||
* @template T
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ClassAveragePricePattern<T>}
|
||||
*/
|
||||
function createClassAveragePricePattern(client, acc) {
|
||||
return {
|
||||
_2015: createMetricPattern4(client, _m(acc, '2015_average_price')),
|
||||
_2016: createMetricPattern4(client, _m(acc, '2016_average_price')),
|
||||
_2017: createMetricPattern4(client, _m(acc, '2017_average_price')),
|
||||
_2018: createMetricPattern4(client, _m(acc, '2018_average_price')),
|
||||
_2019: createMetricPattern4(client, _m(acc, '2019_average_price')),
|
||||
_2020: createMetricPattern4(client, _m(acc, '2020_average_price')),
|
||||
_2021: createMetricPattern4(client, _m(acc, '2021_average_price')),
|
||||
_2022: createMetricPattern4(client, _m(acc, '2022_average_price')),
|
||||
_2023: createMetricPattern4(client, _m(acc, '2023_average_price')),
|
||||
_2024: createMetricPattern4(client, _m(acc, '2024_average_price')),
|
||||
_2025: createMetricPattern4(client, _m(acc, '2025_average_price')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} DollarsPattern
|
||||
@@ -2084,37 +2045,41 @@ function createDollarsPattern(client, acc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} RelativePattern
|
||||
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
|
||||
* @property {MetricPattern1<StoredF32>} netUnrealizedPnlRelToMarketCap
|
||||
* @property {MetricPattern1<StoredF32>} nupl
|
||||
* @property {MetricPattern1<StoredF64>} supplyInLossRelToCirculatingSupply
|
||||
* @property {MetricPattern1<StoredF64>} supplyInLossRelToOwnSupply
|
||||
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToCirculatingSupply
|
||||
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToOwnSupply
|
||||
* @property {MetricPattern4<StoredF64>} supplyRelToCirculatingSupply
|
||||
* @property {MetricPattern1<StoredF32>} unrealizedLossRelToMarketCap
|
||||
* @property {MetricPattern1<StoredF32>} unrealizedProfitRelToMarketCap
|
||||
* @template T
|
||||
* @typedef {Object} ClassAveragePricePattern
|
||||
* @property {MetricPattern4<T>} _2015
|
||||
* @property {MetricPattern4<T>} _2016
|
||||
* @property {MetricPattern4<T>} _2017
|
||||
* @property {MetricPattern4<T>} _2018
|
||||
* @property {MetricPattern4<T>} _2019
|
||||
* @property {MetricPattern4<T>} _2020
|
||||
* @property {MetricPattern4<T>} _2021
|
||||
* @property {MetricPattern4<T>} _2022
|
||||
* @property {MetricPattern4<T>} _2023
|
||||
* @property {MetricPattern4<T>} _2024
|
||||
* @property {MetricPattern4<T>} _2025
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a RelativePattern pattern node
|
||||
* Create a ClassAveragePricePattern pattern node
|
||||
* @template T
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {RelativePattern}
|
||||
* @returns {ClassAveragePricePattern<T>}
|
||||
*/
|
||||
function createRelativePattern(client, acc) {
|
||||
function createClassAveragePricePattern(client, acc) {
|
||||
return {
|
||||
negUnrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap')),
|
||||
netUnrealizedPnlRelToMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap')),
|
||||
nupl: createMetricPattern1(client, _m(acc, 'nupl')),
|
||||
supplyInLossRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply')),
|
||||
supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')),
|
||||
supplyInProfitRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply')),
|
||||
supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')),
|
||||
supplyRelToCirculatingSupply: createMetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply')),
|
||||
unrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap')),
|
||||
unrealizedProfitRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap')),
|
||||
_2015: createMetricPattern4(client, _m(acc, '2015_average_price')),
|
||||
_2016: createMetricPattern4(client, _m(acc, '2016_average_price')),
|
||||
_2017: createMetricPattern4(client, _m(acc, '2017_average_price')),
|
||||
_2018: createMetricPattern4(client, _m(acc, '2018_average_price')),
|
||||
_2019: createMetricPattern4(client, _m(acc, '2019_average_price')),
|
||||
_2020: createMetricPattern4(client, _m(acc, '2020_average_price')),
|
||||
_2021: createMetricPattern4(client, _m(acc, '2021_average_price')),
|
||||
_2022: createMetricPattern4(client, _m(acc, '2022_average_price')),
|
||||
_2023: createMetricPattern4(client, _m(acc, '2023_average_price')),
|
||||
_2024: createMetricPattern4(client, _m(acc, '2024_average_price')),
|
||||
_2025: createMetricPattern4(client, _m(acc, '2025_average_price')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2153,6 +2118,41 @@ function createRelativePattern2(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} RelativePattern
|
||||
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
|
||||
* @property {MetricPattern1<StoredF32>} netUnrealizedPnlRelToMarketCap
|
||||
* @property {MetricPattern1<StoredF32>} nupl
|
||||
* @property {MetricPattern1<StoredF64>} supplyInLossRelToCirculatingSupply
|
||||
* @property {MetricPattern1<StoredF64>} supplyInLossRelToOwnSupply
|
||||
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToCirculatingSupply
|
||||
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToOwnSupply
|
||||
* @property {MetricPattern4<StoredF64>} supplyRelToCirculatingSupply
|
||||
* @property {MetricPattern1<StoredF32>} unrealizedLossRelToMarketCap
|
||||
* @property {MetricPattern1<StoredF32>} unrealizedProfitRelToMarketCap
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a RelativePattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {RelativePattern}
|
||||
*/
|
||||
function createRelativePattern(client, acc) {
|
||||
return {
|
||||
negUnrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap')),
|
||||
netUnrealizedPnlRelToMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap')),
|
||||
nupl: createMetricPattern1(client, _m(acc, 'nupl')),
|
||||
supplyInLossRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply')),
|
||||
supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')),
|
||||
supplyInProfitRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply')),
|
||||
supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')),
|
||||
supplyRelToCirculatingSupply: createMetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply')),
|
||||
unrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap')),
|
||||
unrealizedProfitRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} CountPattern2
|
||||
@@ -2358,60 +2358,31 @@ function createPhaseDailyCentsPattern(client, acc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} PeriodCagrPattern
|
||||
* @property {MetricPattern4<StoredF32>} _10y
|
||||
* @property {MetricPattern4<StoredF32>} _2y
|
||||
* @property {MetricPattern4<StoredF32>} _3y
|
||||
* @property {MetricPattern4<StoredF32>} _4y
|
||||
* @property {MetricPattern4<StoredF32>} _5y
|
||||
* @property {MetricPattern4<StoredF32>} _6y
|
||||
* @property {MetricPattern4<StoredF32>} _8y
|
||||
* @typedef {Object} UnrealizedPattern
|
||||
* @property {MetricPattern1<Dollars>} negUnrealizedLoss
|
||||
* @property {MetricPattern1<Dollars>} netUnrealizedPnl
|
||||
* @property {ActiveSupplyPattern} supplyInLoss
|
||||
* @property {ActiveSupplyPattern} supplyInProfit
|
||||
* @property {MetricPattern1<Dollars>} totalUnrealizedPnl
|
||||
* @property {MetricPattern1<Dollars>} unrealizedLoss
|
||||
* @property {MetricPattern1<Dollars>} unrealizedProfit
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a PeriodCagrPattern pattern node
|
||||
* Create a UnrealizedPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {PeriodCagrPattern}
|
||||
* @returns {UnrealizedPattern}
|
||||
*/
|
||||
function createPeriodCagrPattern(client, acc) {
|
||||
function createUnrealizedPattern(client, acc) {
|
||||
return {
|
||||
_10y: createMetricPattern4(client, _p('10y', acc)),
|
||||
_2y: createMetricPattern4(client, _p('2y', acc)),
|
||||
_3y: createMetricPattern4(client, _p('3y', acc)),
|
||||
_4y: createMetricPattern4(client, _p('4y', acc)),
|
||||
_5y: createMetricPattern4(client, _p('5y', acc)),
|
||||
_6y: createMetricPattern4(client, _p('6y', acc)),
|
||||
_8y: createMetricPattern4(client, _p('8y', acc)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} _0satsPattern2
|
||||
* @property {ActivityPattern2} activity
|
||||
* @property {CostBasisPattern} costBasis
|
||||
* @property {OutputsPattern} outputs
|
||||
* @property {RealizedPattern} realized
|
||||
* @property {RelativePattern4} relative
|
||||
* @property {SupplyPattern2} supply
|
||||
* @property {UnrealizedPattern} unrealized
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a _0satsPattern2 pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {_0satsPattern2}
|
||||
*/
|
||||
function create_0satsPattern2(client, acc) {
|
||||
return {
|
||||
activity: createActivityPattern2(client, acc),
|
||||
costBasis: createCostBasisPattern(client, acc),
|
||||
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
|
||||
realized: createRealizedPattern(client, acc),
|
||||
relative: createRelativePattern4(client, _m(acc, 'supply_in')),
|
||||
supply: createSupplyPattern2(client, _m(acc, 'supply')),
|
||||
unrealized: createUnrealizedPattern(client, acc),
|
||||
negUnrealizedLoss: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss')),
|
||||
netUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl')),
|
||||
supplyInLoss: createActiveSupplyPattern(client, _m(acc, 'supply_in_loss')),
|
||||
supplyInProfit: createActiveSupplyPattern(client, _m(acc, 'supply_in_profit')),
|
||||
totalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'total_unrealized_pnl')),
|
||||
unrealizedLoss: createMetricPattern1(client, _m(acc, 'unrealized_loss')),
|
||||
unrealizedProfit: createMetricPattern1(client, _m(acc, 'unrealized_profit')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2445,31 +2416,60 @@ function create_100btcPattern(client, acc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} UnrealizedPattern
|
||||
* @property {MetricPattern1<Dollars>} negUnrealizedLoss
|
||||
* @property {MetricPattern1<Dollars>} netUnrealizedPnl
|
||||
* @property {ActiveSupplyPattern} supplyInLoss
|
||||
* @property {ActiveSupplyPattern} supplyInProfit
|
||||
* @property {MetricPattern1<Dollars>} totalUnrealizedPnl
|
||||
* @property {MetricPattern1<Dollars>} unrealizedLoss
|
||||
* @property {MetricPattern1<Dollars>} unrealizedProfit
|
||||
* @typedef {Object} _0satsPattern2
|
||||
* @property {ActivityPattern2} activity
|
||||
* @property {CostBasisPattern} costBasis
|
||||
* @property {OutputsPattern} outputs
|
||||
* @property {RealizedPattern} realized
|
||||
* @property {RelativePattern4} relative
|
||||
* @property {SupplyPattern2} supply
|
||||
* @property {UnrealizedPattern} unrealized
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a UnrealizedPattern pattern node
|
||||
* Create a _0satsPattern2 pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {UnrealizedPattern}
|
||||
* @returns {_0satsPattern2}
|
||||
*/
|
||||
function createUnrealizedPattern(client, acc) {
|
||||
function create_0satsPattern2(client, acc) {
|
||||
return {
|
||||
negUnrealizedLoss: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss')),
|
||||
netUnrealizedPnl: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl')),
|
||||
supplyInLoss: createActiveSupplyPattern(client, _m(acc, 'supply_in_loss')),
|
||||
supplyInProfit: createActiveSupplyPattern(client, _m(acc, 'supply_in_profit')),
|
||||
totalUnrealizedPnl: createMetricPattern1(client, _m(acc, 'total_unrealized_pnl')),
|
||||
unrealizedLoss: createMetricPattern1(client, _m(acc, 'unrealized_loss')),
|
||||
unrealizedProfit: createMetricPattern1(client, _m(acc, 'unrealized_profit')),
|
||||
activity: createActivityPattern2(client, acc),
|
||||
costBasis: createCostBasisPattern(client, acc),
|
||||
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
|
||||
realized: createRealizedPattern(client, acc),
|
||||
relative: createRelativePattern4(client, _m(acc, 'supply_in')),
|
||||
supply: createSupplyPattern2(client, _m(acc, 'supply')),
|
||||
unrealized: createUnrealizedPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} _10yPattern
|
||||
* @property {ActivityPattern2} activity
|
||||
* @property {CostBasisPattern} costBasis
|
||||
* @property {OutputsPattern} outputs
|
||||
* @property {RealizedPattern4} realized
|
||||
* @property {RelativePattern} relative
|
||||
* @property {SupplyPattern2} supply
|
||||
* @property {UnrealizedPattern} unrealized
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a _10yPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {_10yPattern}
|
||||
*/
|
||||
function create_10yPattern(client, acc) {
|
||||
return {
|
||||
activity: createActivityPattern2(client, acc),
|
||||
costBasis: createCostBasisPattern(client, acc),
|
||||
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
|
||||
realized: createRealizedPattern4(client, acc),
|
||||
relative: createRelativePattern(client, acc),
|
||||
supply: createSupplyPattern2(client, _m(acc, 'supply')),
|
||||
unrealized: createUnrealizedPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2503,31 +2503,31 @@ function create_10yTo12yPattern(client, acc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} _10yPattern
|
||||
* @property {ActivityPattern2} activity
|
||||
* @property {CostBasisPattern} costBasis
|
||||
* @property {OutputsPattern} outputs
|
||||
* @property {RealizedPattern4} realized
|
||||
* @property {RelativePattern} relative
|
||||
* @property {SupplyPattern2} supply
|
||||
* @property {UnrealizedPattern} unrealized
|
||||
* @typedef {Object} PeriodCagrPattern
|
||||
* @property {MetricPattern4<StoredF32>} _10y
|
||||
* @property {MetricPattern4<StoredF32>} _2y
|
||||
* @property {MetricPattern4<StoredF32>} _3y
|
||||
* @property {MetricPattern4<StoredF32>} _4y
|
||||
* @property {MetricPattern4<StoredF32>} _5y
|
||||
* @property {MetricPattern4<StoredF32>} _6y
|
||||
* @property {MetricPattern4<StoredF32>} _8y
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a _10yPattern pattern node
|
||||
* Create a PeriodCagrPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {_10yPattern}
|
||||
* @returns {PeriodCagrPattern}
|
||||
*/
|
||||
function create_10yPattern(client, acc) {
|
||||
function createPeriodCagrPattern(client, acc) {
|
||||
return {
|
||||
activity: createActivityPattern2(client, acc),
|
||||
costBasis: createCostBasisPattern(client, acc),
|
||||
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
|
||||
realized: createRealizedPattern4(client, acc),
|
||||
relative: createRelativePattern(client, acc),
|
||||
supply: createSupplyPattern2(client, _m(acc, 'supply')),
|
||||
unrealized: createUnrealizedPattern(client, acc),
|
||||
_10y: createMetricPattern4(client, _p('10y', acc)),
|
||||
_2y: createMetricPattern4(client, _p('2y', acc)),
|
||||
_3y: createMetricPattern4(client, _p('3y', acc)),
|
||||
_4y: createMetricPattern4(client, _p('4y', acc)),
|
||||
_5y: createMetricPattern4(client, _p('5y', acc)),
|
||||
_6y: createMetricPattern4(client, _p('6y', acc)),
|
||||
_8y: createMetricPattern4(client, _p('8y', acc)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2582,23 +2582,65 @@ function createSplitPattern2(client, acc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ActiveSupplyPattern
|
||||
* @property {MetricPattern1<Bitcoin>} bitcoin
|
||||
* @property {MetricPattern1<Dollars>} dollars
|
||||
* @property {MetricPattern1<Sats>} sats
|
||||
* @typedef {Object} UnclaimedRewardsPattern
|
||||
* @property {BitcoinPattern2<Bitcoin>} bitcoin
|
||||
* @property {BlockCountPattern<Dollars>} dollars
|
||||
* @property {BlockCountPattern<Sats>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ActiveSupplyPattern pattern node
|
||||
* Create a UnclaimedRewardsPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ActiveSupplyPattern}
|
||||
* @returns {UnclaimedRewardsPattern}
|
||||
*/
|
||||
function createActiveSupplyPattern(client, acc) {
|
||||
function createUnclaimedRewardsPattern(client, acc) {
|
||||
return {
|
||||
bitcoin: createMetricPattern1(client, _m(acc, 'btc')),
|
||||
dollars: createMetricPattern1(client, _m(acc, 'usd')),
|
||||
sats: createMetricPattern1(client, acc),
|
||||
bitcoin: createBitcoinPattern2(client, _m(acc, 'btc')),
|
||||
dollars: createBlockCountPattern(client, _m(acc, 'usd')),
|
||||
sats: createBlockCountPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CoinbasePattern
|
||||
* @property {BitcoinPattern} bitcoin
|
||||
* @property {DollarsPattern<Dollars>} dollars
|
||||
* @property {DollarsPattern<Sats>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a CoinbasePattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {CoinbasePattern}
|
||||
*/
|
||||
function createCoinbasePattern(client, acc) {
|
||||
return {
|
||||
bitcoin: createBitcoinPattern(client, _m(acc, 'btc')),
|
||||
dollars: createDollarsPattern(client, _m(acc, 'usd')),
|
||||
sats: createDollarsPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SegwitAdoptionPattern
|
||||
* @property {MetricPattern11<StoredF32>} base
|
||||
* @property {MetricPattern2<StoredF32>} cumulative
|
||||
* @property {MetricPattern2<StoredF32>} sum
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a SegwitAdoptionPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {SegwitAdoptionPattern}
|
||||
*/
|
||||
function createSegwitAdoptionPattern(client, acc) {
|
||||
return {
|
||||
base: createMetricPattern11(client, acc),
|
||||
cumulative: createMetricPattern2(client, _m(acc, 'cumulative')),
|
||||
sum: createMetricPattern2(client, _m(acc, 'sum')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2623,6 +2665,27 @@ function create_2015Pattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ActiveSupplyPattern
|
||||
* @property {MetricPattern1<Bitcoin>} bitcoin
|
||||
* @property {MetricPattern1<Dollars>} dollars
|
||||
* @property {MetricPattern1<Sats>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ActiveSupplyPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ActiveSupplyPattern}
|
||||
*/
|
||||
function createActiveSupplyPattern(client, acc) {
|
||||
return {
|
||||
bitcoin: createMetricPattern1(client, _m(acc, 'btc')),
|
||||
dollars: createMetricPattern1(client, _m(acc, 'usd')),
|
||||
sats: createMetricPattern1(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CoinbasePattern2
|
||||
* @property {BlockCountPattern<Bitcoin>} bitcoin
|
||||
@@ -2666,65 +2729,21 @@ function createCostBasisPattern2(client, acc) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SegwitAdoptionPattern
|
||||
* @property {MetricPattern11<StoredF32>} base
|
||||
* @property {MetricPattern2<StoredF32>} cumulative
|
||||
* @property {MetricPattern2<StoredF32>} sum
|
||||
* @typedef {Object} CostBasisPattern
|
||||
* @property {MetricPattern1<Dollars>} max
|
||||
* @property {MetricPattern1<Dollars>} min
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a SegwitAdoptionPattern pattern node
|
||||
* Create a CostBasisPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {SegwitAdoptionPattern}
|
||||
* @returns {CostBasisPattern}
|
||||
*/
|
||||
function createSegwitAdoptionPattern(client, acc) {
|
||||
function createCostBasisPattern(client, acc) {
|
||||
return {
|
||||
base: createMetricPattern11(client, acc),
|
||||
cumulative: createMetricPattern2(client, _m(acc, 'cumulative')),
|
||||
sum: createMetricPattern2(client, _m(acc, 'sum')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} UnclaimedRewardsPattern
|
||||
* @property {BitcoinPattern2<Bitcoin>} bitcoin
|
||||
* @property {BlockCountPattern<Dollars>} dollars
|
||||
* @property {BlockCountPattern<Sats>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a UnclaimedRewardsPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {UnclaimedRewardsPattern}
|
||||
*/
|
||||
function createUnclaimedRewardsPattern(client, acc) {
|
||||
return {
|
||||
bitcoin: createBitcoinPattern2(client, _m(acc, 'btc')),
|
||||
dollars: createBlockCountPattern(client, _m(acc, 'usd')),
|
||||
sats: createBlockCountPattern(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CoinbasePattern
|
||||
* @property {BitcoinPattern} bitcoin
|
||||
* @property {DollarsPattern<Dollars>} dollars
|
||||
* @property {DollarsPattern<Sats>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a CoinbasePattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {CoinbasePattern}
|
||||
*/
|
||||
function createCoinbasePattern(client, acc) {
|
||||
return {
|
||||
bitcoin: createBitcoinPattern(client, _m(acc, 'btc')),
|
||||
dollars: createDollarsPattern(client, _m(acc, 'usd')),
|
||||
sats: createDollarsPattern(client, acc),
|
||||
max: createMetricPattern1(client, _m(acc, 'max_cost_basis')),
|
||||
min: createMetricPattern1(client, _m(acc, 'min_cost_basis')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2766,25 +2785,6 @@ function create_1dReturns1mSdPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CostBasisPattern
|
||||
* @property {MetricPattern1<Dollars>} max
|
||||
* @property {MetricPattern1<Dollars>} min
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a CostBasisPattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {CostBasisPattern}
|
||||
*/
|
||||
function createCostBasisPattern(client, acc) {
|
||||
return {
|
||||
max: createMetricPattern1(client, _m(acc, 'max_cost_basis')),
|
||||
min: createMetricPattern1(client, _m(acc, 'min_cost_basis')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} SupplyPattern2
|
||||
* @property {ActiveSupplyPattern} halved
|
||||
@@ -2806,22 +2806,22 @@ function createSupplyPattern2(client, acc) {
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} BlockCountPattern
|
||||
* @property {MetricPattern1<T>} cumulative
|
||||
* @property {MetricPattern1<T>} sum
|
||||
* @typedef {Object} SatsPattern
|
||||
* @property {MetricPattern1<T>} ohlc
|
||||
* @property {SplitPattern2<T>} split
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a BlockCountPattern pattern node
|
||||
* Create a SatsPattern pattern node
|
||||
* @template T
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {BlockCountPattern<T>}
|
||||
* @returns {SatsPattern<T>}
|
||||
*/
|
||||
function createBlockCountPattern(client, acc) {
|
||||
function createSatsPattern(client, acc) {
|
||||
return {
|
||||
cumulative: createMetricPattern1(client, _m(acc, 'cumulative')),
|
||||
sum: createMetricPattern1(client, acc),
|
||||
ohlc: createMetricPattern1(client, _m(acc, 'ohlc')),
|
||||
split: createSplitPattern2(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2848,22 +2848,22 @@ function createBitcoinPattern2(client, acc) {
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} SatsPattern
|
||||
* @property {MetricPattern1<T>} ohlc
|
||||
* @property {SplitPattern2<T>} split
|
||||
* @typedef {Object} BlockCountPattern
|
||||
* @property {MetricPattern1<T>} cumulative
|
||||
* @property {MetricPattern1<T>} sum
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a SatsPattern pattern node
|
||||
* Create a BlockCountPattern pattern node
|
||||
* @template T
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {SatsPattern<T>}
|
||||
* @returns {BlockCountPattern<T>}
|
||||
*/
|
||||
function createSatsPattern(client, acc) {
|
||||
function createBlockCountPattern(client, acc) {
|
||||
return {
|
||||
ohlc: createMetricPattern1(client, _m(acc, 'ohlc_sats')),
|
||||
split: createSplitPattern2(client, _m(acc, 'sats')),
|
||||
cumulative: createMetricPattern1(client, _m(acc, 'cumulative')),
|
||||
sum: createMetricPattern1(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4056,8 +4056,8 @@ function createRealizedPriceExtraPattern(client, acc) {
|
||||
* @typedef {Object} MetricsTree_Price
|
||||
* @property {MetricsTree_Price_Cents} cents
|
||||
* @property {MetricsTree_Price_Oracle} oracle
|
||||
* @property {SatsPattern<OHLCSats>} sats
|
||||
* @property {MetricsTree_Price_Usd} usd
|
||||
* @property {MetricsTree_Price_Sats} sats
|
||||
* @property {SatsPattern<OHLCDollars>} usd
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -4105,9 +4105,9 @@ function createRealizedPriceExtraPattern(client, acc) {
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Price_Usd
|
||||
* @property {MetricPattern1<OHLCDollars>} ohlc
|
||||
* @property {SplitPattern2<Dollars>} split
|
||||
* @typedef {Object} MetricsTree_Price_Sats
|
||||
* @property {MetricPattern1<OHLCSats>} ohlc
|
||||
* @property {SplitPattern2<Sats>} split
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -6087,11 +6087,11 @@ class BrkClient extends BrkClientBase {
|
||||
priceCents: createMetricPattern11(this, 'oracle_price_cents'),
|
||||
txCount: createMetricPattern6(this, 'oracle_tx_count'),
|
||||
},
|
||||
sats: createSatsPattern(this, 'price'),
|
||||
usd: {
|
||||
ohlc: createMetricPattern1(this, 'price_ohlc'),
|
||||
split: createSplitPattern2(this, 'price'),
|
||||
sats: {
|
||||
ohlc: createMetricPattern1(this, 'price_ohlc_sats'),
|
||||
split: createSplitPattern2(this, 'price_sats'),
|
||||
},
|
||||
usd: createSatsPattern(this, 'price'),
|
||||
},
|
||||
scripts: {
|
||||
count: {
|
||||
|
||||
@@ -1883,31 +1883,6 @@ class Price111dSmaPattern:
|
||||
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct99_usd'))
|
||||
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio'))
|
||||
|
||||
class ActivePriceRatioPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
|
||||
self.ratio_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1m_sma'))
|
||||
self.ratio_1w_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1w_sma'))
|
||||
self.ratio_1y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '1y'))
|
||||
self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '2y'))
|
||||
self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '4y'))
|
||||
self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct1'))
|
||||
self.ratio_pct1_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct1_usd'))
|
||||
self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct2'))
|
||||
self.ratio_pct2_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct2_usd'))
|
||||
self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct5'))
|
||||
self.ratio_pct5_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct5_usd'))
|
||||
self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct95'))
|
||||
self.ratio_pct95_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95_usd'))
|
||||
self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct98'))
|
||||
self.ratio_pct98_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct98_usd'))
|
||||
self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct99'))
|
||||
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd'))
|
||||
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc)
|
||||
|
||||
class PercentilesPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -1933,6 +1908,31 @@ class PercentilesPattern:
|
||||
self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90'))
|
||||
self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95'))
|
||||
|
||||
class ActivePriceRatioPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
|
||||
self.ratio_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1m_sma'))
|
||||
self.ratio_1w_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1w_sma'))
|
||||
self.ratio_1y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '1y'))
|
||||
self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '2y'))
|
||||
self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '4y'))
|
||||
self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct1'))
|
||||
self.ratio_pct1_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct1_usd'))
|
||||
self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct2'))
|
||||
self.ratio_pct2_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct2_usd'))
|
||||
self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct5'))
|
||||
self.ratio_pct5_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct5_usd'))
|
||||
self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct95'))
|
||||
self.ratio_pct95_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95_usd'))
|
||||
self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct98'))
|
||||
self.ratio_pct98_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct98_usd'))
|
||||
self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct99'))
|
||||
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd'))
|
||||
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc)
|
||||
|
||||
class RelativePattern5:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2049,23 +2049,6 @@ class BitcoinPattern:
|
||||
self.pct90: MetricPattern6[Bitcoin] = MetricPattern6(client, _m(acc, 'pct90'))
|
||||
self.sum: MetricPattern2[Bitcoin] = MetricPattern2(client, _m(acc, 'sum'))
|
||||
|
||||
class ClassAveragePricePattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_average_price'))
|
||||
self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_average_price'))
|
||||
self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_average_price'))
|
||||
self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_average_price'))
|
||||
self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_average_price'))
|
||||
self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_average_price'))
|
||||
self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_average_price'))
|
||||
self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_average_price'))
|
||||
self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_average_price'))
|
||||
self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_average_price'))
|
||||
self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_average_price'))
|
||||
|
||||
class DollarsPattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2083,21 +2066,22 @@ class DollarsPattern(Generic[T]):
|
||||
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
|
||||
self.sum: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'sum'))
|
||||
|
||||
class RelativePattern:
|
||||
class ClassAveragePricePattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.neg_unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap'))
|
||||
self.net_unrealized_pnl_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap'))
|
||||
self.nupl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'nupl'))
|
||||
self.supply_in_loss_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply'))
|
||||
self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply'))
|
||||
self.supply_in_profit_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply'))
|
||||
self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply'))
|
||||
self.supply_rel_to_circulating_supply: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply'))
|
||||
self.unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap'))
|
||||
self.unrealized_profit_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap'))
|
||||
self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_average_price'))
|
||||
self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_average_price'))
|
||||
self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_average_price'))
|
||||
self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_average_price'))
|
||||
self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_average_price'))
|
||||
self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_average_price'))
|
||||
self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_average_price'))
|
||||
self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_average_price'))
|
||||
self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_average_price'))
|
||||
self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_average_price'))
|
||||
self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_average_price'))
|
||||
|
||||
class RelativePattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2115,6 +2099,22 @@ class RelativePattern2:
|
||||
self.unrealized_profit_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap'))
|
||||
self.unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl'))
|
||||
|
||||
class RelativePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.neg_unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap'))
|
||||
self.net_unrealized_pnl_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap'))
|
||||
self.nupl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'nupl'))
|
||||
self.supply_in_loss_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply'))
|
||||
self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply'))
|
||||
self.supply_in_profit_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply'))
|
||||
self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply'))
|
||||
self.supply_rel_to_circulating_supply: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply'))
|
||||
self.unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap'))
|
||||
self.unrealized_profit_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap'))
|
||||
|
||||
class CountPattern2(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2204,31 +2204,18 @@ class PhaseDailyCentsPattern(Generic[T]):
|
||||
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
|
||||
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
|
||||
|
||||
class PeriodCagrPattern:
|
||||
class UnrealizedPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self._10y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('10y', acc))
|
||||
self._2y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('2y', acc))
|
||||
self._3y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('3y', acc))
|
||||
self._4y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('4y', acc))
|
||||
self._5y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('5y', acc))
|
||||
self._6y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('6y', acc))
|
||||
self._8y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('8y', acc))
|
||||
|
||||
class _0satsPattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
|
||||
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
|
||||
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
|
||||
self.realized: RealizedPattern = RealizedPattern(client, acc)
|
||||
self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in'))
|
||||
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
|
||||
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
|
||||
self.neg_unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss'))
|
||||
self.net_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl'))
|
||||
self.supply_in_loss: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_loss'))
|
||||
self.supply_in_profit: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_profit'))
|
||||
self.total_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'total_unrealized_pnl'))
|
||||
self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss'))
|
||||
self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit'))
|
||||
|
||||
class _100btcPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2243,29 +2230,16 @@ class _100btcPattern:
|
||||
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
|
||||
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
|
||||
|
||||
class UnrealizedPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.neg_unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss'))
|
||||
self.net_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl'))
|
||||
self.supply_in_loss: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_loss'))
|
||||
self.supply_in_profit: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_profit'))
|
||||
self.total_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'total_unrealized_pnl'))
|
||||
self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss'))
|
||||
self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit'))
|
||||
|
||||
class _10yTo12yPattern:
|
||||
class _0satsPattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
|
||||
self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc)
|
||||
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
|
||||
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
|
||||
self.realized: RealizedPattern2 = RealizedPattern2(client, acc)
|
||||
self.relative: RelativePattern2 = RelativePattern2(client, acc)
|
||||
self.realized: RealizedPattern = RealizedPattern(client, acc)
|
||||
self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in'))
|
||||
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
|
||||
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
|
||||
|
||||
@@ -2282,6 +2256,32 @@ class _10yPattern:
|
||||
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
|
||||
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
|
||||
|
||||
class _10yTo12yPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
|
||||
self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc)
|
||||
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
|
||||
self.realized: RealizedPattern2 = RealizedPattern2(client, acc)
|
||||
self.relative: RelativePattern2 = RelativePattern2(client, acc)
|
||||
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
|
||||
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
|
||||
|
||||
class PeriodCagrPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self._10y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('10y', acc))
|
||||
self._2y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('2y', acc))
|
||||
self._3y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('3y', acc))
|
||||
self._4y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('4y', acc))
|
||||
self._5y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('5y', acc))
|
||||
self._6y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('6y', acc))
|
||||
self._8y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('8y', acc))
|
||||
|
||||
class ActivityPattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2303,14 +2303,32 @@ class SplitPattern2(Generic[T]):
|
||||
self.low: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'low'))
|
||||
self.open: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'open'))
|
||||
|
||||
class ActiveSupplyPattern:
|
||||
class UnclaimedRewardsPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc'))
|
||||
self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
|
||||
self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc)
|
||||
self.bitcoin: BitcoinPattern2[Bitcoin] = BitcoinPattern2(client, _m(acc, 'btc'))
|
||||
self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd'))
|
||||
self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc)
|
||||
|
||||
class CoinbasePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.bitcoin: BitcoinPattern = BitcoinPattern(client, _m(acc, 'btc'))
|
||||
self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd'))
|
||||
self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc)
|
||||
|
||||
class SegwitAdoptionPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.base: MetricPattern11[StoredF32] = MetricPattern11(client, acc)
|
||||
self.cumulative: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'cumulative'))
|
||||
self.sum: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'sum'))
|
||||
|
||||
class _2015Pattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2321,6 +2339,15 @@ class _2015Pattern:
|
||||
self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd'))
|
||||
self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc)
|
||||
|
||||
class ActiveSupplyPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc'))
|
||||
self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
|
||||
self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc)
|
||||
|
||||
class CoinbasePattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2339,32 +2366,13 @@ class CostBasisPattern2:
|
||||
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis'))
|
||||
self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis'))
|
||||
|
||||
class SegwitAdoptionPattern:
|
||||
class CostBasisPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.base: MetricPattern11[StoredF32] = MetricPattern11(client, acc)
|
||||
self.cumulative: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'cumulative'))
|
||||
self.sum: MetricPattern2[StoredF32] = MetricPattern2(client, _m(acc, 'sum'))
|
||||
|
||||
class UnclaimedRewardsPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.bitcoin: BitcoinPattern2[Bitcoin] = BitcoinPattern2(client, _m(acc, 'btc'))
|
||||
self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd'))
|
||||
self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc)
|
||||
|
||||
class CoinbasePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.bitcoin: BitcoinPattern = BitcoinPattern(client, _m(acc, 'btc'))
|
||||
self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd'))
|
||||
self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc)
|
||||
self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis'))
|
||||
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis'))
|
||||
|
||||
class RelativePattern4:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2382,14 +2390,6 @@ class _1dReturns1mSdPattern:
|
||||
self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd'))
|
||||
self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma'))
|
||||
|
||||
class CostBasisPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis'))
|
||||
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis'))
|
||||
|
||||
class SupplyPattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -2398,13 +2398,13 @@ class SupplyPattern2:
|
||||
self.halved: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'halved'))
|
||||
self.total: ActiveSupplyPattern = ActiveSupplyPattern(client, acc)
|
||||
|
||||
class BlockCountPattern(Generic[T]):
|
||||
class SatsPattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative'))
|
||||
self.sum: MetricPattern1[T] = MetricPattern1(client, acc)
|
||||
self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc'))
|
||||
self.split: SplitPattern2[T] = SplitPattern2(client, acc)
|
||||
|
||||
class BitcoinPattern2(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2414,13 +2414,13 @@ class BitcoinPattern2(Generic[T]):
|
||||
self.cumulative: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'cumulative'))
|
||||
self.sum: MetricPattern1[T] = MetricPattern1(client, acc)
|
||||
|
||||
class SatsPattern(Generic[T]):
|
||||
class BlockCountPattern(Generic[T]):
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc_sats'))
|
||||
self.split: SplitPattern2[T] = SplitPattern2(client, _m(acc, 'sats'))
|
||||
self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative'))
|
||||
self.sum: MetricPattern1[T] = MetricPattern1(client, acc)
|
||||
|
||||
class OutputsPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -3702,12 +3702,12 @@ class MetricsTree_Price_Oracle:
|
||||
self.price_cents: MetricPattern11[Cents] = MetricPattern11(client, 'oracle_price_cents')
|
||||
self.tx_count: MetricPattern6[StoredU32] = MetricPattern6(client, 'oracle_tx_count')
|
||||
|
||||
class MetricsTree_Price_Usd:
|
||||
class MetricsTree_Price_Sats:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.ohlc: MetricPattern1[OHLCDollars] = MetricPattern1(client, 'price_ohlc')
|
||||
self.split: SplitPattern2[Dollars] = SplitPattern2(client, 'price')
|
||||
self.ohlc: MetricPattern1[OHLCSats] = MetricPattern1(client, 'price_ohlc_sats')
|
||||
self.split: SplitPattern2[Sats] = SplitPattern2(client, 'price_sats')
|
||||
|
||||
class MetricsTree_Price:
|
||||
"""Metrics tree node."""
|
||||
@@ -3715,8 +3715,8 @@ class MetricsTree_Price:
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricsTree_Price_Cents = MetricsTree_Price_Cents(client)
|
||||
self.oracle: MetricsTree_Price_Oracle = MetricsTree_Price_Oracle(client)
|
||||
self.sats: SatsPattern[OHLCSats] = SatsPattern(client, 'price')
|
||||
self.usd: MetricsTree_Price_Usd = MetricsTree_Price_Usd(client)
|
||||
self.sats: MetricsTree_Price_Sats = MetricsTree_Price_Sats(client)
|
||||
self.usd: SatsPattern[OHLCDollars] = SatsPattern(client, 'price')
|
||||
|
||||
class MetricsTree_Scripts_Count:
|
||||
"""Metrics tree node."""
|
||||
|
||||
@@ -1519,9 +1519,10 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- --------- -->
|
||||
<!-- IMPORTMAP -->
|
||||
|
||||
<!-- /IMPORTMAP -->
|
||||
<!-- ---------- -->
|
||||
|
||||
<!-- ------- -->
|
||||
<!-- Scripts -->
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from "../modules/lightweight-charts/5.1.0/dist/lightweight-charts.standalone.production.mjs";
|
||||
|
||||
const createChart = /** @type {CreateChart} */ (_createChart);
|
||||
|
||||
import {
|
||||
createChoiceField,
|
||||
createLabeledInput,
|
||||
@@ -502,7 +501,8 @@ export function createChartElement({
|
||||
if (!hasData()) {
|
||||
setData(data);
|
||||
hasData.set(true);
|
||||
lastTime = /** @type {number} */ (data.at(-1)?.time) ?? -Infinity;
|
||||
lastTime =
|
||||
/** @type {number} */ (data.at(-1)?.time) ?? -Infinity;
|
||||
|
||||
if (fitContent) {
|
||||
ichart.timeScale().fitContent();
|
||||
|
||||
Reference in New Issue
Block a user