diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 46d93afb3..7e7d00e26 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -4994,7 +4994,7 @@ pub struct SeriesTree_Market { pub ath: SeriesTree_Market_Ath, pub lookback: SeriesTree_Market_Lookback, pub returns: SeriesTree_Market_Returns, - pub volatility: SeriesTree_Market_Volatility, + pub volatility: _1m1w1y24hPattern, pub range: SeriesTree_Market_Range, pub moving_average: SeriesTree_Market_MovingAverage, pub dca: SeriesTree_Market_Dca, @@ -5007,7 +5007,7 @@ impl SeriesTree_Market { ath: SeriesTree_Market_Ath::new(client.clone(), format!("{base_path}_ath")), lookback: SeriesTree_Market_Lookback::new(client.clone(), format!("{base_path}_lookback")), returns: SeriesTree_Market_Returns::new(client.clone(), format!("{base_path}_returns")), - volatility: SeriesTree_Market_Volatility::new(client.clone(), format!("{base_path}_volatility")), + volatility: _1m1w1y24hPattern::new(client.clone(), "price_volatility".to_string()), range: SeriesTree_Market_Range::new(client.clone(), format!("{base_path}_range")), moving_average: SeriesTree_Market_MovingAverage::new(client.clone(), format!("{base_path}_moving_average")), dca: SeriesTree_Market_Dca::new(client.clone(), format!("{base_path}_dca")), @@ -5132,6 +5132,7 @@ impl SeriesTree_Market_Returns_Periods { /// Series tree node. pub struct SeriesTree_Market_Returns_Sd24h { + pub _24h: SeriesTree_Market_Returns_Sd24h_24h, pub _1w: SeriesTree_Market_Returns_Sd24h_1w, pub _1m: SeriesTree_Market_Returns_Sd24h_1m, pub _1y: SeriesTree_Market_Returns_Sd24h_1y, @@ -5140,6 +5141,7 @@ pub struct SeriesTree_Market_Returns_Sd24h { impl SeriesTree_Market_Returns_Sd24h { pub fn new(client: Arc, base_path: String) -> Self { Self { + _24h: SeriesTree_Market_Returns_Sd24h_24h::new(client.clone(), format!("{base_path}_24h")), _1w: SeriesTree_Market_Returns_Sd24h_1w::new(client.clone(), format!("{base_path}_1w")), _1m: SeriesTree_Market_Returns_Sd24h_1m::new(client.clone(), format!("{base_path}_1m")), _1y: SeriesTree_Market_Returns_Sd24h_1y::new(client.clone(), format!("{base_path}_1y")), @@ -5147,6 +5149,21 @@ impl SeriesTree_Market_Returns_Sd24h { } } +/// Series tree node. +pub struct SeriesTree_Market_Returns_Sd24h_24h { + pub sma: SeriesPattern1, + pub sd: SeriesPattern1, +} + +impl SeriesTree_Market_Returns_Sd24h_24h { + pub fn new(client: Arc, base_path: String) -> Self { + Self { + sma: SeriesPattern1::new(client.clone(), "price_return_24h_sma_24h".to_string()), + sd: SeriesPattern1::new(client.clone(), "price_return_24h_sd_24h".to_string()), + } + } +} + /// Series tree node. pub struct SeriesTree_Market_Returns_Sd24h_1w { pub sma: SeriesPattern1, @@ -5192,23 +5209,6 @@ impl SeriesTree_Market_Returns_Sd24h_1y { } } -/// Series tree node. -pub struct SeriesTree_Market_Volatility { - pub _1w: SeriesPattern1, - pub _1m: SeriesPattern1, - pub _1y: SeriesPattern1, -} - -impl SeriesTree_Market_Volatility { - pub fn new(client: Arc, base_path: String) -> Self { - Self { - _1w: SeriesPattern1::new(client.clone(), "price_volatility_1w".to_string()), - _1m: SeriesPattern1::new(client.clone(), "price_volatility_1m".to_string()), - _1y: SeriesPattern1::new(client.clone(), "price_volatility_1y".to_string()), - } - } -} - /// Series tree node. pub struct SeriesTree_Market_Range { pub min: _1m1w1y2wPattern, diff --git a/crates/brk_computer/src/inputs/compute.rs b/crates/brk_computer/src/inputs/compute.rs index b50b5381c..32c61d2c4 100644 --- a/crates/brk_computer/src/inputs/compute.rs +++ b/crates/brk_computer/src/inputs/compute.rs @@ -16,7 +16,7 @@ impl Vecs { exit: &Exit, ) -> Result<()> { self.spent - .compute(&self.db, indexer, starting_indexes, exit)?; + .compute(indexer, starting_indexes, exit)?; self.count .compute(indexer, indexes, blocks, starting_indexes, exit)?; diff --git a/crates/brk_computer/src/inputs/spent/compute.rs b/crates/brk_computer/src/inputs/spent/compute.rs index a2ce4c5f3..bdd5a1162 100644 --- a/crates/brk_computer/src/inputs/spent/compute.rs +++ b/crates/brk_computer/src/inputs/spent/compute.rs @@ -2,7 +2,7 @@ use brk_error::Result; use brk_indexer::Indexer; use brk_types::{Indexes, Sats, TxIndex, TxOutIndex, Vout}; use tracing::info; -use vecdb::{AnyStoredVec, AnyVec, Database, Exit, ReadableVec, VecIndex, WritableVec}; +use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, VecIndex, WritableVec}; use super::Vecs; @@ -11,7 +11,6 @@ const BATCH_SIZE: usize = 2 * 1024 * 1024 * 1024 / size_of::(); impl Vecs { pub(crate) fn compute( &mut self, - db: &Database, indexer: &Indexer, starting_indexes: &Indexes, exit: &Exit, @@ -105,14 +104,13 @@ impl Vecs { self.value.push(out_value[i]); } - if batch_end < target { - info!("TxIns: {:.2}%", batch_end as f64 / target as f64 * 100.0); - } - let _lock = exit.lock(); self.txout_index.write()?; self.value.write()?; - db.flush()?; + + if batch_end < target { + info!("TxIns: {:.2}%", batch_end as f64 / target as f64 * 100.0); + } batch_start = batch_end; } diff --git a/crates/brk_computer/src/internal/containers/windows.rs b/crates/brk_computer/src/internal/containers/windows.rs index ec381c72c..55e5ef868 100644 --- a/crates/brk_computer/src/internal/containers/windows.rs +++ b/crates/brk_computer/src/internal/containers/windows.rs @@ -10,6 +10,7 @@ pub struct Windows { impl Windows { pub const SUFFIXES: [&'static str; 4] = ["24h", "1w", "1m", "1y"]; + pub const DAYS: [usize; 4] = [1, 7, 30, 365]; pub fn try_from_fn( mut f: impl FnMut(&str) -> std::result::Result, diff --git a/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs b/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs index f9992552f..c1774eb9d 100644 --- a/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs +++ b/crates/brk_computer/src/internal/per_block/ratio/percentiles.rs @@ -136,7 +136,7 @@ impl RatioPerBlockPercentiles { { let _lock = exit.lock(); - self.mut_pct_vecs().try_for_each(|v| v.flush())?; + self.mut_pct_vecs().try_for_each(|v| v.write().map(|_| ()))?; } // Cents bands diff --git a/crates/brk_computer/src/internal/per_block/stddev/extended.rs b/crates/brk_computer/src/internal/per_block/stddev/extended.rs index 882c1ecc2..cf6b4ffae 100644 --- a/crates/brk_computer/src/internal/per_block/stddev/extended.rs +++ b/crates/brk_computer/src/internal/per_block/stddev/extended.rs @@ -162,7 +162,7 @@ impl StdDevPerBlockExtended { { let _lock = exit.lock(); - self.mut_band_height_vecs().try_for_each(|v| v.flush())?; + self.mut_band_height_vecs().try_for_each(|v| v.write().map(|_| ()))?; } self.zscore.height.compute_zscore( diff --git a/crates/brk_computer/src/internal/transform/derived.rs b/crates/brk_computer/src/internal/transform/derived.rs index 352f10ab7..5a83ff3ba 100644 --- a/crates/brk_computer/src/internal/transform/derived.rs +++ b/crates/brk_computer/src/internal/transform/derived.rs @@ -30,6 +30,11 @@ pub trait SqrtDays { const FACTOR: f32; } +pub struct Days1; +impl SqrtDays for Days1 { + const FACTOR: f32 = 1.0; // 1.0_f32.sqrt() +} + pub struct Days7; impl SqrtDays for Days7 { const FACTOR: f32 = 2.6457513; // 7.0_f32.sqrt() diff --git a/crates/brk_computer/src/internal/transform/mod.rs b/crates/brk_computer/src/internal/transform/mod.rs index af6966f75..cfd5ad17c 100644 --- a/crates/brk_computer/src/internal/transform/mod.rs +++ b/crates/brk_computer/src/internal/transform/mod.rs @@ -20,7 +20,7 @@ pub use currency::{ SatsToBitcoin, SatsToCents, }; pub use derived::{ - Days7, Days30, Days365, DaysToYears, PerSec, PriceTimesRatioBp32Cents, PriceTimesRatioCents, + Days1, Days7, Days30, Days365, DaysToYears, PerSec, PriceTimesRatioBp32Cents, PriceTimesRatioCents, RatioCents64, TimesSqrt, }; pub use ratio::{ diff --git a/crates/brk_computer/src/market/returns/compute.rs b/crates/brk_computer/src/market/returns/compute.rs index 78670b9c7..6e9db7eb1 100644 --- a/crates/brk_computer/src/market/returns/compute.rs +++ b/crates/brk_computer/src/market/returns/compute.rs @@ -46,11 +46,7 @@ impl Vecs { let _24h_price_return_ratio = &self.periods._24h.ratio.height; - for sd in [ - &mut self.sd_24h._1w, - &mut self.sd_24h._1m, - &mut self.sd_24h._1y, - ] { + for sd in self.sd_24h.as_mut_array() { sd.compute_all(blocks, starting_indexes, exit, _24h_price_return_ratio)?; } diff --git a/crates/brk_computer/src/market/returns/import.rs b/crates/brk_computer/src/market/returns/import.rs index 8d6fd22d3..421bb7ff1 100644 --- a/crates/brk_computer/src/market/returns/import.rs +++ b/crates/brk_computer/src/market/returns/import.rs @@ -3,10 +3,10 @@ use brk_types::Version; use vecdb::Database; use super::super::lookback::ByLookbackPeriod; -use super::{vecs::PriceReturn24hSdVecs, Vecs}; +use super::Vecs; use crate::{ indexes, - internal::{StdDevPerBlock, PercentPerBlock}, + internal::{StdDevPerBlock, PercentPerBlock, Windows}, market::dca::ByDcaCagr, }; @@ -18,46 +18,27 @@ impl Vecs { ) -> Result { let v1 = Version::ONE; - let price_return = ByLookbackPeriod::try_new(|name, _days| { + let periods = ByLookbackPeriod::try_new(|name, _days| { PercentPerBlock::forced_import(db, &format!("price_return_{name}"), version, indexes) })?; - // CAGR (computed, 2y+ only) - let price_cagr = ByDcaCagr::try_new(|name, _days| { + let cagr = ByDcaCagr::try_new(|name, _days| { PercentPerBlock::forced_import(db, &format!("price_cagr_{name}"), version, indexes) })?; - let price_return_24h_sd = PriceReturn24hSdVecs { - _1w: StdDevPerBlock::forced_import( + let mut days_iter = Windows::<()>::DAYS.iter(); + let sd_24h = Windows::try_from_fn(|suffix| { + let days = *days_iter.next().unwrap(); + StdDevPerBlock::forced_import( db, "price_return_24h", - "1w", - 7, + suffix, + days, version + v1, indexes, - )?, - _1m: StdDevPerBlock::forced_import( - db, - "price_return_24h", - "1m", - 30, - version + v1, - indexes, - )?, - _1y: StdDevPerBlock::forced_import( - db, - "price_return_24h", - "1y", - 365, - version + v1, - indexes, - )?, - }; + ) + })?; - Ok(Self { - periods: price_return, - cagr: price_cagr, - sd_24h: price_return_24h_sd, - }) + Ok(Self { periods, cagr, sd_24h }) } } diff --git a/crates/brk_computer/src/market/returns/vecs.rs b/crates/brk_computer/src/market/returns/vecs.rs index 2839661e5..12d782941 100644 --- a/crates/brk_computer/src/market/returns/vecs.rs +++ b/crates/brk_computer/src/market/returns/vecs.rs @@ -3,20 +3,13 @@ use brk_types::BasisPointsSigned32; use vecdb::{Rw, StorageMode}; use crate::{ - internal::{PercentPerBlock, StdDevPerBlock}, + internal::{PercentPerBlock, StdDevPerBlock, Windows}, market::{dca::ByDcaCagr, lookback::ByLookbackPeriod}, }; -#[derive(Traversable)] -pub struct PriceReturn24hSdVecs { - pub _1w: StdDevPerBlock, - pub _1m: StdDevPerBlock, - pub _1y: StdDevPerBlock, -} - #[derive(Traversable)] pub struct Vecs { pub periods: ByLookbackPeriod>, pub cagr: ByDcaCagr>, - pub sd_24h: PriceReturn24hSdVecs, + pub sd_24h: Windows>, } diff --git a/crates/brk_computer/src/market/technical/compute.rs b/crates/brk_computer/src/market/technical/compute.rs index 7a9530b27..9119b7ce5 100644 --- a/crates/brk_computer/src/market/technical/compute.rs +++ b/crates/brk_computer/src/market/technical/compute.rs @@ -22,54 +22,43 @@ impl Vecs { starting_indexes: &Indexes, exit: &Exit, ) -> Result<()> { - // Stochastic Oscillator: K = (close - low_2w) / (high_2w - low_2w), stored as ratio (0-1) - { - let price = &prices.spot.usd.height; - self.stoch_k.bps.height.compute_transform3( - starting_indexes.height, - price, - &range.min._2w.usd.height, - &range.max._2w.usd.height, - |(h, close, low, high, ..)| { - let range = *high - *low; - let stoch = if range == 0.0 { - BasisPoints16::ZERO - } else { - BasisPoints16::from((*close - *low) / range) - }; - (h, stoch) - }, - exit, - )?; + self.stoch_k.bps.height.compute_transform3( + starting_indexes.height, + &prices.spot.usd.height, + &range.min._2w.usd.height, + &range.max._2w.usd.height, + |(h, close, low, high, ..)| { + let range = *high - *low; + let stoch = if range == 0.0 { + BasisPoints16::ZERO + } else { + BasisPoints16::from((*close - *low) / range) + }; + (h, stoch) + }, + exit, + )?; - self.stoch_d.bps.height.compute_rolling_average( - starting_indexes.height, - &blocks.lookback._3d, - &self.stoch_k.bps.height, - exit, - )?; - } + self.stoch_d.bps.height.compute_rolling_average( + starting_indexes.height, + &blocks.lookback._3d, + &self.stoch_k.bps.height, + exit, + )?; - // RSI per timeframe - let return_sources = [ - &returns.periods._24h.ratio.height, - &returns.periods._1w.ratio.height, - &returns.periods._1m.ratio.height, - &returns.periods._1y.ratio.height, - ]; - for (rsi_chain, ret) in self.rsi.as_mut_array().into_iter().zip(return_sources) { + let daily_returns = &returns.periods._24h.ratio.height; + for (rsi_chain, &m) in self.rsi.as_mut_array().into_iter().zip(&TF_MULTIPLIERS) { rsi::compute( rsi_chain, blocks, - ret, - 14, - 3, + daily_returns, + 14 * m, + 3 * m, starting_indexes, exit, )?; } - // MACD per timeframe for (macd_chain, &m) in self.macd.as_mut_array().into_iter().zip(&TF_MULTIPLIERS) { macd::compute( macd_chain, @@ -83,7 +72,6 @@ impl Vecs { )?; } - // Pi Cycle: sma_111d / sma_350d_x2 self.pi_cycle .bps .compute_binary::( diff --git a/crates/brk_computer/src/market/technical/import.rs b/crates/brk_computer/src/market/technical/import.rs index c171623be..d735c4c07 100644 --- a/crates/brk_computer/src/market/technical/import.rs +++ b/crates/brk_computer/src/market/technical/import.rs @@ -107,7 +107,7 @@ impl Vecs { let v = version + VERSION; let rsi = - Windows::try_from_fn(|tf| RsiChain::forced_import(db, tf, v + Version::ONE, indexes))?; + Windows::try_from_fn(|tf| RsiChain::forced_import(db, tf, v + Version::TWO, indexes))?; let macd = Windows::try_from_fn(|tf| MacdChain::forced_import(db, tf, v, indexes))?; let stoch_k = PercentPerBlock::forced_import(db, "stoch_k", v, indexes)?; diff --git a/crates/brk_computer/src/market/volatility/import.rs b/crates/brk_computer/src/market/volatility/import.rs index d65c0f16a..2ba45f88c 100644 --- a/crates/brk_computer/src/market/volatility/import.rs +++ b/crates/brk_computer/src/market/volatility/import.rs @@ -4,48 +4,40 @@ use vecdb::ReadableCloneableVec; use super::super::returns; use super::Vecs; -use crate::internal::{Days30, Days365, Days7, LazyPerBlock, TimesSqrt}; +use crate::internal::{Days1, Days7, Days30, Days365, LazyPerBlock, TimesSqrt, Windows}; impl Vecs { pub(crate) fn forced_import(version: Version, returns: &returns::Vecs) -> Result { let v2 = Version::TWO; + let _24h = LazyPerBlock::from_computed::>( + "price_volatility_24h", + version + v2, + returns.sd_24h._24h.sd.height.read_only_boxed_clone(), + &returns.sd_24h._24h.sd, + ); + let _1w = LazyPerBlock::from_computed::>( "price_volatility_1w", version + v2, - returns - .sd_24h - ._1w - .sd - .height - .read_only_boxed_clone(), + returns.sd_24h._1w.sd.height.read_only_boxed_clone(), &returns.sd_24h._1w.sd, ); let _1m = LazyPerBlock::from_computed::>( "price_volatility_1m", version + v2, - returns - .sd_24h - ._1m - .sd - .height - .read_only_boxed_clone(), + returns.sd_24h._1m.sd.height.read_only_boxed_clone(), &returns.sd_24h._1m.sd, ); let _1y = LazyPerBlock::from_computed::>( "price_volatility_1y", version + v2, - returns - .sd_24h - ._1y - .sd - .height - .read_only_boxed_clone(), + returns.sd_24h._1y.sd.height.read_only_boxed_clone(), &returns.sd_24h._1y.sd, ); - Ok(Self { _1w, _1m, _1y }) + Ok(Windows { _24h, _1w, _1m, _1y }) } } diff --git a/crates/brk_computer/src/market/volatility/vecs.rs b/crates/brk_computer/src/market/volatility/vecs.rs index ce7ab59b3..f51977a83 100644 --- a/crates/brk_computer/src/market/volatility/vecs.rs +++ b/crates/brk_computer/src/market/volatility/vecs.rs @@ -1,11 +1,5 @@ -use brk_traversable::Traversable; - -use crate::internal::LazyPerBlock; - use brk_types::StoredF32; -#[derive(Clone, Traversable)] -pub struct Vecs { - pub _1w: LazyPerBlock, - pub _1m: LazyPerBlock, - pub _1y: LazyPerBlock, -} + +use crate::internal::{LazyPerBlock, Windows}; + +pub type Vecs = Windows>; diff --git a/crates/brk_computer/src/outputs/compute.rs b/crates/brk_computer/src/outputs/compute.rs index 55e87dd6a..778f8be4a 100644 --- a/crates/brk_computer/src/outputs/compute.rs +++ b/crates/brk_computer/src/outputs/compute.rs @@ -18,8 +18,6 @@ impl Vecs { starting_indexes: &Indexes, exit: &Exit, ) -> Result<()> { - self.spent - .compute(&self.db, indexer, inputs, starting_indexes, exit)?; self.count.compute( indexer, indexes, @@ -29,8 +27,9 @@ impl Vecs { starting_indexes, exit, )?; - - let _lock = exit.lock(); + let _lock = self + .spent + .compute(indexer, inputs, starting_indexes, exit)?; self.db.compact()?; Ok(()) } diff --git a/crates/brk_computer/src/outputs/spent/compute.rs b/crates/brk_computer/src/outputs/spent/compute.rs index 44470b919..b89cff778 100644 --- a/crates/brk_computer/src/outputs/spent/compute.rs +++ b/crates/brk_computer/src/outputs/spent/compute.rs @@ -2,7 +2,7 @@ use brk_error::Result; use brk_indexer::Indexer; use brk_types::{Height, Indexes, TxInIndex, TxOutIndex}; use tracing::info; -use vecdb::{AnyStoredVec, AnyVec, Database, Exit, ReadableVec, Stamp, VecIndex, WritableVec}; +use vecdb::{AnyStoredVec, AnyVec, Exit, ExitGuard, ReadableVec, Stamp, VecIndex, WritableVec}; use super::Vecs; use crate::inputs; @@ -10,17 +10,16 @@ use crate::inputs; const HEIGHT_BATCH: u32 = 10_000; impl Vecs { - pub(crate) fn compute( + pub(crate) fn compute<'a>( &mut self, - db: &Database, indexer: &Indexer, inputs: &inputs::Vecs, starting_indexes: &Indexes, - exit: &Exit, - ) -> Result<()> { + exit: &'a Exit, + ) -> Result> { let target_height = indexer.vecs.blocks.blockhash.len(); if target_height == 0 { - return Ok(()); + return Ok(exit.lock()); } let target_height = Height::from(target_height - 1); @@ -125,17 +124,15 @@ impl Vecs { "TxOuts: {:.2}%", batch_end_height.to_usize() as f64 / target_height.to_usize() as f64 * 100.0 ); - db.flush()?; } batch_start_height = batch_end_height + 1_u32; } - let _lock = exit.lock(); + let lock = exit.lock(); self.txin_index .stamped_write_with_changes(Stamp::from(target_height))?; - db.flush()?; - Ok(()) + Ok(lock) } } diff --git a/crates/brk_computer/src/positions.rs b/crates/brk_computer/src/positions.rs index da492a269..5468bcb61 100644 --- a/crates/brk_computer/src/positions.rs +++ b/crates/brk_computer/src/positions.rs @@ -128,16 +128,16 @@ impl Vecs { if *block.height() % 1_000 == 0 { let _lock = exit.lock(); - self.block.flush()?; - self.tx.flush()?; + self.block.write()?; + self.tx.write()?; } Ok(()) })?; let _lock = exit.lock(); - self.block.flush()?; - self.tx.flush()?; + self.block.write()?; + self.tx.write()?; Ok(()) } diff --git a/crates/brk_store/src/lib.rs b/crates/brk_store/src/lib.rs index 9da55b12a..f7427b62d 100644 --- a/crates/brk_store/src/lib.rs +++ b/crates/brk_store/src/lib.rs @@ -202,7 +202,7 @@ where #[inline] pub fn clear_caches(&mut self) { for cache in &mut self.caches { - cache.clear(); + *cache = FxHashMap::default(); } } @@ -336,9 +336,6 @@ where Self::ingest(&self.keyspace, puts.iter(), dels.iter())?; - // Pre-allocate for next batch based on current batch size - self.puts.reserve(puts.len()); - if !self.caches.is_empty() { self.caches.pop(); self.caches.insert(0, puts); @@ -349,10 +346,10 @@ where fn reset(&mut self) -> Result<()> { self.meta.reset()?; - self.puts.clear(); - self.dels.clear(); + self.puts = FxHashMap::default(); + self.dels = FxHashSet::default(); for cache in &mut self.caches { - cache.clear(); + *cache = FxHashMap::default(); } self.keyspace.clear()?; Ok(()) diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 3c25dabc6..1c95f2009 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -4772,7 +4772,7 @@ function createUnspentPattern(client, acc) { * @property {SeriesTree_Market_Ath} ath * @property {SeriesTree_Market_Lookback} lookback * @property {SeriesTree_Market_Returns} returns - * @property {SeriesTree_Market_Volatility} volatility + * @property {_1m1w1y24hPattern} volatility * @property {SeriesTree_Market_Range} range * @property {SeriesTree_Market_MovingAverage} movingAverage * @property {SeriesTree_Market_Dca} dca @@ -4832,11 +4832,18 @@ function createUnspentPattern(client, acc) { /** * @typedef {Object} SeriesTree_Market_Returns_Sd24h + * @property {SeriesTree_Market_Returns_Sd24h_24h} _24h * @property {SeriesTree_Market_Returns_Sd24h_1w} _1w * @property {SeriesTree_Market_Returns_Sd24h_1m} _1m * @property {SeriesTree_Market_Returns_Sd24h_1y} _1y */ +/** + * @typedef {Object} SeriesTree_Market_Returns_Sd24h_24h + * @property {SeriesPattern1} sma + * @property {SeriesPattern1} sd + */ + /** * @typedef {Object} SeriesTree_Market_Returns_Sd24h_1w * @property {SeriesPattern1} sma @@ -4855,13 +4862,6 @@ function createUnspentPattern(client, acc) { * @property {SeriesPattern1} sd */ -/** - * @typedef {Object} SeriesTree_Market_Volatility - * @property {SeriesPattern1} _1w - * @property {SeriesPattern1} _1m - * @property {SeriesPattern1} _1y - */ - /** * @typedef {Object} SeriesTree_Market_Range * @property {_1m1w1y2wPattern} min @@ -8087,6 +8087,10 @@ class BrkClient extends BrkClientBase { }, cagr: create_10y2y3y4y5y6y8yPattern(this, 'price_cagr'), sd24h: { + _24h: { + sma: createSeriesPattern1(this, 'price_return_24h_sma_24h'), + sd: createSeriesPattern1(this, 'price_return_24h_sd_24h'), + }, _1w: { sma: createSeriesPattern1(this, 'price_return_24h_sma_1w'), sd: createSeriesPattern1(this, 'price_return_24h_sd_1w'), @@ -8101,11 +8105,7 @@ class BrkClient extends BrkClientBase { }, }, }, - volatility: { - _1w: createSeriesPattern1(this, 'price_volatility_1w'), - _1m: createSeriesPattern1(this, 'price_volatility_1m'), - _1y: createSeriesPattern1(this, 'price_volatility_1y'), - }, + volatility: create_1m1w1y24hPattern(this, 'price_volatility'), range: { min: create_1m1w1y2wPattern(this, 'price_min'), max: create_1m1w1y2wPattern(this, 'price_max'), diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 85c44fa23..00fef4ed4 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -4139,6 +4139,13 @@ class SeriesTree_Market_Returns_Periods: self._8y: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'price_return_8y') self._10y: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'price_return_10y') +class SeriesTree_Market_Returns_Sd24h_24h: + """Series tree node.""" + + def __init__(self, client: BrkClientBase, base_path: str = ''): + self.sma: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sma_24h') + self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sd_24h') + class SeriesTree_Market_Returns_Sd24h_1w: """Series tree node.""" @@ -4164,6 +4171,7 @@ class SeriesTree_Market_Returns_Sd24h: """Series tree node.""" def __init__(self, client: BrkClientBase, base_path: str = ''): + self._24h: SeriesTree_Market_Returns_Sd24h_24h = SeriesTree_Market_Returns_Sd24h_24h(client) self._1w: SeriesTree_Market_Returns_Sd24h_1w = SeriesTree_Market_Returns_Sd24h_1w(client) self._1m: SeriesTree_Market_Returns_Sd24h_1m = SeriesTree_Market_Returns_Sd24h_1m(client) self._1y: SeriesTree_Market_Returns_Sd24h_1y = SeriesTree_Market_Returns_Sd24h_1y(client) @@ -4176,14 +4184,6 @@ class SeriesTree_Market_Returns: self.cagr: _10y2y3y4y5y6y8yPattern = _10y2y3y4y5y6y8yPattern(client, 'price_cagr') self.sd_24h: SeriesTree_Market_Returns_Sd24h = SeriesTree_Market_Returns_Sd24h(client) -class SeriesTree_Market_Volatility: - """Series tree node.""" - - def __init__(self, client: BrkClientBase, base_path: str = ''): - self._1w: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_volatility_1w') - self._1m: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_volatility_1m') - self._1y: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_volatility_1y') - class SeriesTree_Market_Range: """Series tree node.""" @@ -4436,7 +4436,7 @@ class SeriesTree_Market: self.ath: SeriesTree_Market_Ath = SeriesTree_Market_Ath(client) self.lookback: SeriesTree_Market_Lookback = SeriesTree_Market_Lookback(client) self.returns: SeriesTree_Market_Returns = SeriesTree_Market_Returns(client) - self.volatility: SeriesTree_Market_Volatility = SeriesTree_Market_Volatility(client) + self.volatility: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'price_volatility') self.range: SeriesTree_Market_Range = SeriesTree_Market_Range(client) self.moving_average: SeriesTree_Market_MovingAverage = SeriesTree_Market_MovingAverage(client) self.dca: SeriesTree_Market_Dca = SeriesTree_Market_Dca(client) diff --git a/website/scripts/options/market.js b/website/scripts/options/market.js index 5d3ea02fa..bc2191e4e 100644 --- a/website/scripts/options/market.js +++ b/website/scripts/options/market.js @@ -93,24 +93,6 @@ function createMaSubSection(label, averages) { }; } -/** - * @param {string} name - * @param {string} title - * @param {Unit} unit - * @param {{ _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} patterns - */ -function volatilityChart(name, title, unit, patterns) { - return { - name, - title, - bottom: [ - line({ series: patterns._1w, name: "1w", color: colors.time._1w, unit }), - line({ series: patterns._1m, name: "1m", color: colors.time._1m, unit }), - line({ series: patterns._1y, name: "1y", color: colors.time._1y, unit }), - ], - }; -} - /** * @param {string} name * @param {Period[]} periods @@ -231,7 +213,6 @@ export function createMarketSection() { range, technical, lookback, - dca, } = market; const shortPeriodsBase = [ @@ -401,8 +382,10 @@ export function createMarketSection() { return { name: "Market", tree: [ + // Price { name: "Price", title: "Bitcoin Price" }, + // Sats/$ { name: "Sats/$", title: "Sats per Dollar", @@ -415,194 +398,7 @@ export function createMarketSection() { ], }, - { - name: "Capitalization", - tree: [ - { - name: "Compare", - title: "Market vs Realized Capitalization", - bottom: [ - line({ - series: supply.marketCap.usd, - name: "Market Cap", - unit: Unit.usd, - }), - line({ - series: cohorts.utxo.all.realized.cap.usd, - name: "Realized Cap", - color: colors.realized, - unit: Unit.usd, - }), - ], - }, - { - name: "Market Cap", - tree: [ - { - name: "Value", - title: "Market Capitalization", - bottom: [ - line({ - series: supply.marketCap.usd, - name: "Market Cap", - unit: Unit.usd, - }), - ], - }, - { - name: "Absolute", - tree: [ - { - name: "Compare", - title: "Market Cap Absolute Change", - bottom: ROLLING_WINDOWS.map((w) => - baseline({ - series: supply.marketCap.delta.absolute[w.key].usd, - name: w.name, - color: w.color, - unit: Unit.usd, - }), - ), - }, - ...ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: `Market Cap Absolute Change ${w.name}`, - bottom: [ - baseline({ - series: supply.marketCap.delta.absolute[w.key].usd, - name: w.name, - unit: Unit.usd, - }), - ], - })), - ], - }, - { - name: "Rate", - tree: [ - { - name: "Compare", - title: "Market Cap Growth Rate", - bottom: ROLLING_WINDOWS.flatMap((w) => - percentRatio({ - pattern: supply.marketCap.delta.rate[w.key], - name: w.name, - color: w.color, - }), - ), - }, - ...ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: `Market Cap Growth Rate ${w.name}`, - bottom: percentRatioBaseline({ - pattern: supply.marketCap.delta.rate[w.key], - name: w.name, - }), - })), - ], - }, - ], - }, - { - name: "Realized Cap", - tree: [ - { - name: "Value", - title: "Realized Capitalization", - bottom: [ - line({ - series: cohorts.utxo.all.realized.cap.usd, - name: "Realized Cap", - color: colors.realized, - unit: Unit.usd, - }), - ], - }, - { - name: "Absolute", - tree: [ - { - name: "Compare", - title: "Realized Cap Absolute Change", - bottom: ROLLING_WINDOWS.map((w) => - baseline({ - series: cohorts.utxo.all.realized.cap.delta.absolute[w.key].usd, - name: w.name, - color: w.color, - unit: Unit.usd, - }), - ), - }, - ...ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: `Realized Cap Absolute Change ${w.name}`, - bottom: [ - baseline({ - series: cohorts.utxo.all.realized.cap.delta.absolute[w.key].usd, - name: w.name, - unit: Unit.usd, - }), - ], - })), - ], - }, - { - name: "Rate", - tree: [ - { - name: "Compare", - title: "Realized Cap Growth Rate", - bottom: ROLLING_WINDOWS.flatMap((w) => - percentRatio({ - pattern: cohorts.utxo.all.realized.cap.delta.rate[w.key], - name: w.name, - color: w.color, - }), - ), - }, - ...ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: `Realized Cap Growth Rate ${w.name}`, - bottom: percentRatioBaseline({ - pattern: cohorts.utxo.all.realized.cap.delta.rate[w.key], - name: w.name, - }), - })), - ], - }, - ], - }, - { - name: "Rate Spread", - tree: [ - { - name: "Compare", - title: "Capitalization Growth Rate Spread", - bottom: ROLLING_WINDOWS.map((w) => - baseline({ - series: supply.marketMinusRealizedCapGrowthRate[w.key], - name: w.name, - color: w.color, - unit: Unit.percentage, - }), - ), - }, - ...ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: `Capitalization Growth Rate Spread ${w.name}`, - bottom: [ - baseline({ - series: supply.marketMinusRealizedCapGrowthRate[w.key], - name: w.name, - unit: Unit.percentage, - }), - ], - })), - ], - }, - ], - }, - + // All Time High { name: "All Time High", tree: [ @@ -648,6 +444,7 @@ export function createMarketSection() { ], }, + // Returns { name: "Returns", tree: [ @@ -668,257 +465,7 @@ export function createMarketSection() { ], }, - { - name: "Volatility", - tree: [ - volatilityChart("Index", "Volatility Index", Unit.percentage, { - _1w: volatility._1w, - _1m: volatility._1m, - _1y: volatility._1y, - }), - { - name: "True Range", - title: "True Range", - bottom: [ - line({ - series: range.trueRange, - name: "Daily", - color: colors.time._24h, - unit: Unit.usd, - }), - line({ - series: range.trueRangeSum2w, - name: "2w Sum", - color: colors.time._1w, - unit: Unit.usd, - defaultActive: false, - }), - ], - }, - { - name: "Choppiness", - title: "Choppiness Index", - bottom: [ - ...percentRatio({ - pattern: range.choppinessIndex2w, - name: "2w", - color: colors.indicator.main, - }), - ...priceLines({ unit: Unit.index, numbers: [61.8, 38.2] }), - ], - }, - ], - }, - - { - name: "Moving Averages", - tree: [ - { - name: "SMA vs EMA", - tree: [ - { - name: "All Periods", - title: "SMA vs EMA Comparison", - top: smaVsEma.flatMap((p) => [ - price({ - series: p.sma, - name: `${p.id} SMA`, - color: p.color, - }), - price({ - series: p.ema, - name: `${p.id} EMA`, - color: p.color, - style: 1, - }), - ]), - }, - ...smaVsEma.map((p) => ({ - name: p.name, - title: `${p.name} SMA vs EMA`, - top: [ - price({ series: p.sma, name: "SMA", color: p.color }), - price({ - series: p.ema, - name: "EMA", - color: p.color, - style: 1, - }), - ], - })), - ], - }, - createMaSubSection("SMA", sma), - createMaSubSection("EMA", ema), - ], - }, - - { - name: "Bands", - tree: [ - { - name: "MinMax", - tree: [ - { - id: "1w", - name: "1 Week", - min: range.min._1w, - max: range.max._1w, - }, - { - id: "2w", - name: "2 Week", - min: range.min._2w, - max: range.max._2w, - }, - { - id: "1m", - name: "1 Month", - min: range.min._1m, - max: range.max._1m, - }, - { - id: "1y", - name: "1 Year", - min: range.min._1y, - max: range.max._1y, - }, - ].map((p) => ({ - name: p.id, - title: `${p.name} MinMax`, - top: [ - price({ - series: p.max, - name: "Max", - key: "price-max", - color: colors.stat.max, - }), - price({ - series: p.min, - name: "Min", - key: "price-min", - color: colors.stat.min, - }), - ], - })), - }, - { - name: "Mayer Multiple", - title: "Mayer Multiple", - top: [ - price({ - series: ma.sma._200d, - name: "200d SMA", - color: colors.indicator.main, - }), - price({ - series: ma.sma._200d.x24, - name: "200d SMA x2.4", - color: colors.indicator.upper, - }), - price({ - series: ma.sma._200d.x08, - name: "200d SMA x0.8", - color: colors.indicator.lower, - }), - ], - }, - ], - }, - - { - name: "Momentum", - tree: [ - { - name: "RSI", - tree: [ - { - name: "Compare", - title: "RSI Comparison", - bottom: [ - ...ROLLING_WINDOWS.flatMap((w) => - indexRatio({ pattern: technical.rsi[w.key].rsi, name: w.name, color: w.color }), - ), - priceLine({ unit: Unit.index, number: 70 }), - priceLine({ unit: Unit.index, number: 30 }), - ], - }, - ...ROLLING_WINDOWS.map((w) => { - const rsi = technical.rsi[w.key]; - return { - name: w.name, - title: `RSI (${w.name})`, - bottom: [ - ...indexRatio({ pattern: rsi.rsi, name: "RSI", color: colors.indicator.main }), - priceLine({ unit: Unit.index, number: 70 }), - priceLine({ unit: Unit.index, number: 50, defaultActive: false }), - priceLine({ unit: Unit.index, number: 30 }), - ], - }; - }), - ], - }, - { - name: "StochRSI", - tree: [ - { - name: "Compare", - title: "Stochastic RSI Comparison", - bottom: [ - ...ROLLING_WINDOWS.flatMap((w) => - indexRatio({ pattern: technical.rsi[w.key].stochRsiK, name: `${w.name} K`, color: w.color }), - ), - ...priceLines({ unit: Unit.index, numbers: [80, 20] }), - ], - }, - ...ROLLING_WINDOWS.map((w) => { - const rsi = technical.rsi[w.key]; - return { - name: w.name, - title: `Stochastic RSI (${w.name})`, - bottom: [ - ...indexRatio({ pattern: rsi.stochRsi, name: "Raw", color: colors.indicator.main, defaultActive: false }), - ...indexRatio({ pattern: rsi.stochRsiK, name: "K", color: colors.indicator.fast }), - ...indexRatio({ pattern: rsi.stochRsiD, name: "D", color: colors.indicator.slow }), - ...priceLines({ unit: Unit.index, numbers: [80, 20] }), - ], - }; - }), - ], - }, - { - name: "Stochastic", - title: "Stochastic Oscillator", - bottom: [ - ...indexRatio({ pattern: technical.stochK, name: "K", color: colors.indicator.fast }), - ...indexRatio({ pattern: technical.stochD, name: "D", color: colors.indicator.slow }), - ...priceLines({ unit: Unit.index, numbers: [80, 20] }), - ], - }, - { - name: "MACD", - tree: [ - { - name: "Compare", - title: "MACD Comparison", - bottom: ROLLING_WINDOWS.map((w) => - line({ series: technical.macd[w.key].line, name: w.name, color: w.color, unit: Unit.usd }), - ), - }, - ...ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: `MACD (${w.name})`, - bottom: [ - line({ series: technical.macd[w.key].line, name: "MACD", color: colors.indicator.fast, unit: Unit.usd }), - line({ series: technical.macd[w.key].signal, name: "Signal", color: colors.indicator.slow, unit: Unit.usd }), - histogram({ series: technical.macd[w.key].histogram, name: "Histogram", unit: Unit.usd }), - ], - })), - ], - }, - ], - }, - + // Historical { name: "Historical", tree: [ @@ -939,67 +486,612 @@ export function createMarketSection() { ], }, + // Capitalization { - name: "DCA", - title: "Dollar Cost Average Sats/Day", - bottom: [ - line({ - series: dca.satsPerDay, - name: "Sats/Day", - unit: Unit.sats, - }), + name: "Capitalization", + tree: [ + { + name: "Compare", + title: "Market vs Realized Capitalization", + bottom: [ + line({ + series: supply.marketCap.usd, + name: "Market Cap", + unit: Unit.usd, + }), + line({ + series: cohorts.utxo.all.realized.cap.usd, + name: "Realized Cap", + color: colors.realized, + unit: Unit.usd, + }), + ], + }, + { + name: "Market Cap", + tree: [ + { + name: "Value", + title: "Market Capitalization", + bottom: [ + line({ + series: supply.marketCap.usd, + name: "Market Cap", + unit: Unit.usd, + }), + ], + }, + { + name: "Change", + tree: [ + { + name: "Compare", + title: "Market Cap Change", + bottom: ROLLING_WINDOWS.map((w) => + baseline({ + series: supply.marketCap.delta.absolute[w.key].usd, + name: w.name, + color: w.color, + unit: Unit.usd, + }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `Market Cap Change ${w.name}`, + bottom: [ + baseline({ + series: supply.marketCap.delta.absolute[w.key].usd, + name: w.name, + unit: Unit.usd, + }), + ], + })), + ], + }, + { + name: "Growth Rate", + tree: [ + { + name: "Compare", + title: "Market Cap Growth Rate", + bottom: ROLLING_WINDOWS.flatMap((w) => + percentRatio({ + pattern: supply.marketCap.delta.rate[w.key], + name: w.name, + color: w.color, + }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `Market Cap Growth Rate ${w.name}`, + bottom: percentRatioBaseline({ + pattern: supply.marketCap.delta.rate[w.key], + name: w.name, + }), + })), + ], + }, + ], + }, + { + name: "Realized Cap", + tree: [ + { + name: "Value", + title: "Realized Capitalization", + bottom: [ + line({ + series: cohorts.utxo.all.realized.cap.usd, + name: "Realized Cap", + color: colors.realized, + unit: Unit.usd, + }), + ], + }, + { + name: "Change", + tree: [ + { + name: "Compare", + title: "Realized Cap Change", + bottom: ROLLING_WINDOWS.map((w) => + baseline({ + series: cohorts.utxo.all.realized.cap.delta.absolute[w.key].usd, + name: w.name, + color: w.color, + unit: Unit.usd, + }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `Realized Cap Change ${w.name}`, + bottom: [ + baseline({ + series: cohorts.utxo.all.realized.cap.delta.absolute[w.key].usd, + name: w.name, + unit: Unit.usd, + }), + ], + })), + ], + }, + { + name: "Growth Rate", + tree: [ + { + name: "Compare", + title: "Realized Cap Growth Rate", + bottom: ROLLING_WINDOWS.flatMap((w) => + percentRatio({ + pattern: cohorts.utxo.all.realized.cap.delta.rate[w.key], + name: w.name, + color: w.color, + }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `Realized Cap Growth Rate ${w.name}`, + bottom: percentRatioBaseline({ + pattern: cohorts.utxo.all.realized.cap.delta.rate[w.key], + name: w.name, + }), + })), + ], + }, + ], + }, + { + name: "Growth Rate Spread", + tree: [ + { + name: "Compare", + title: "Capitalization Growth Rate Spread", + bottom: ROLLING_WINDOWS.map((w) => + baseline({ + series: supply.marketMinusRealizedCapGrowthRate[w.key], + name: w.name, + color: w.color, + unit: Unit.percentage, + }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `Capitalization Growth Rate Spread ${w.name}`, + bottom: [ + baseline({ + series: supply.marketMinusRealizedCapGrowthRate[w.key], + name: w.name, + unit: Unit.percentage, + }), + ], + })), + ], + }, ], }, + // Technical + { + name: "Technical", + tree: [ + // Moving Averages + { + name: "Moving Averages", + tree: [ + { + name: "SMA vs EMA", + tree: [ + { + name: "All Periods", + title: "SMA vs EMA Comparison", + top: smaVsEma.flatMap((p) => [ + price({ + series: p.sma, + name: `${p.id} SMA`, + color: p.color, + }), + price({ + series: p.ema, + name: `${p.id} EMA`, + color: p.color, + style: 1, + }), + ]), + }, + ...smaVsEma.map((p) => ({ + name: p.name, + title: `${p.name} SMA vs EMA`, + top: [ + price({ series: p.sma, name: "SMA", color: p.color }), + price({ + series: p.ema, + name: "EMA", + color: p.color, + style: 1, + }), + ], + })), + ], + }, + createMaSubSection("SMA", sma), + createMaSubSection("EMA", ema), + ], + }, + + // Momentum + { + name: "Momentum", + tree: [ + { + name: "RSI", + tree: [ + { + name: "Compare", + title: "RSI Comparison", + bottom: [ + ...ROLLING_WINDOWS.flatMap((w) => + indexRatio({ pattern: technical.rsi[w.key].rsi, name: w.name, color: w.color }), + ), + priceLine({ unit: Unit.index, number: 70 }), + priceLine({ unit: Unit.index, number: 30 }), + ], + }, + ...ROLLING_WINDOWS.map((w) => { + const rsi = technical.rsi[w.key]; + return { + name: w.name, + title: `RSI (${w.name})`, + bottom: [ + ...indexRatio({ pattern: rsi.rsi, name: "RSI", color: colors.indicator.main }), + priceLine({ unit: Unit.index, number: 70 }), + priceLine({ unit: Unit.index, number: 50, defaultActive: false }), + priceLine({ unit: Unit.index, number: 30 }), + ], + }; + }), + { + name: "Stochastic", + tree: ROLLING_WINDOWS.map((w) => { + const rsi = technical.rsi[w.key]; + return { + name: w.name, + title: `Stochastic RSI (${w.name})`, + bottom: [ + ...indexRatio({ pattern: rsi.stochRsiK, name: "K", color: colors.indicator.fast }), + ...indexRatio({ pattern: rsi.stochRsiD, name: "D", color: colors.indicator.slow }), + ...priceLines({ unit: Unit.index, numbers: [80, 20] }), + ], + }; + }), + }, + ], + }, + { + name: "MACD", + tree: [ + { + name: "Compare", + title: "MACD Comparison", + bottom: ROLLING_WINDOWS.map((w) => + line({ series: technical.macd[w.key].line, name: w.name, color: w.color, unit: Unit.usd }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `MACD (${w.name})`, + bottom: [ + line({ series: technical.macd[w.key].line, name: "MACD", color: colors.indicator.fast, unit: Unit.usd }), + line({ series: technical.macd[w.key].signal, name: "Signal", color: colors.indicator.slow, unit: Unit.usd }), + histogram({ series: technical.macd[w.key].histogram, name: "Histogram", unit: Unit.usd }), + ], + })), + ], + }, + ], + }, + + // Volatility + { + name: "Volatility", + tree: [ + { + name: "Index", + tree: [ + { + name: "Compare", + title: "Volatility Index", + bottom: ROLLING_WINDOWS.map((w) => + line({ series: volatility[w.key], name: w.name, color: w.color, unit: Unit.percentage }), + ), + }, + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: `Volatility Index (${w.name})`, + bottom: [line({ series: volatility[w.key], name: w.name, color: w.color, unit: Unit.percentage })], + })), + ], + }, + { + name: "True Range", + tree: [ + { + name: "Daily", + title: "True Range (Daily)", + bottom: [ + line({ + series: range.trueRange, + name: "Daily", + color: colors.time._24h, + unit: Unit.usd, + }), + ], + }, + { + name: "2 Week Sum", + title: "True Range (2 Week Sum)", + bottom: [ + line({ + series: range.trueRangeSum2w, + name: "2w Sum", + color: colors.time._1w, + unit: Unit.usd, + }), + ], + }, + ], + }, + { + name: "Choppiness", + title: "Choppiness Index", + bottom: [ + ...percentRatio({ + pattern: range.choppinessIndex2w, + name: "2w", + color: colors.indicator.main, + }), + ...priceLines({ unit: Unit.index, numbers: [61.8, 38.2] }), + ], + }, + ], + }, + + // Price Bands + { + name: "Price Bands", + tree: [ + { + name: "Mayer Multiple", + title: "Mayer Multiple", + top: [ + price({ + series: ma.sma._200d, + name: "200d SMA", + color: colors.indicator.main, + }), + price({ + series: ma.sma._200d.x24, + name: "200d SMA x2.4", + color: colors.indicator.upper, + }), + price({ + series: ma.sma._200d.x08, + name: "200d SMA x0.8", + color: colors.indicator.lower, + }), + ], + }, + { + name: "MinMax", + tree: [ + { + id: "1w", + name: "1 Week", + min: range.min._1w, + max: range.max._1w, + }, + { + id: "2w", + name: "2 Week", + min: range.min._2w, + max: range.max._2w, + }, + { + id: "1m", + name: "1 Month", + min: range.min._1m, + max: range.max._1m, + }, + { + id: "1y", + name: "1 Year", + min: range.min._1y, + max: range.max._1y, + }, + ].map((p) => ({ + name: p.id, + title: `${p.name} MinMax`, + top: [ + price({ + series: p.max, + name: "Max", + key: "price-max", + color: colors.stat.max, + }), + price({ + series: p.min, + name: "Min", + key: "price-min", + color: colors.stat.min, + }), + ], + })), + }, + ], + }, + ], + }, + + // Indicators { name: "Indicators", tree: [ { - name: "Pi Cycle", - title: "Pi Cycle", - top: [ - price({ - series: ma.sma._111d, - name: "111d SMA", - color: colors.indicator.upper, - }), - price({ - series: ma.sma._350d.x2, - name: "350d SMA x2", - color: colors.indicator.lower, - }), - ], - bottom: [ - baseline({ - series: technical.piCycle.ratio, - name: "Pi Cycle", - unit: Unit.ratio, - base: 1, - }), - ], - }, - { - name: "Puell Multiple", - title: "Puell Multiple", - bottom: [ - line({ - series: indicators.puellMultiple.ratio, - name: "Puell", - color: colors.usd, - unit: Unit.ratio, - }), - ], - }, - { - name: "NVT", - title: "NVT Ratio", - bottom: [ - line({ - series: indicators.nvt.ratio, + name: "Valuation", + tree: [ + { name: "NVT", - color: colors.bitcoin, - unit: Unit.ratio, - }), + title: "NVT Ratio", + bottom: [ + line({ + series: indicators.nvt.ratio, + name: "NVT", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, + { + name: "Thermocap Multiple", + title: "Thermocap Multiple", + bottom: [ + line({ + series: indicators.thermoCapMultiple.ratio, + name: "Thermocap", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, + ], + }, + { + name: "Cycle", + tree: [ + { + name: "Pi Cycle", + title: "Pi Cycle", + top: [ + price({ + series: ma.sma._111d, + name: "111d SMA", + color: colors.indicator.upper, + }), + price({ + series: ma.sma._350d.x2, + name: "350d SMA x2", + color: colors.indicator.lower, + }), + ], + bottom: [ + baseline({ + series: technical.piCycle.ratio, + name: "Pi Cycle", + unit: Unit.ratio, + base: 1, + }), + ], + }, + { + name: "Stock-to-Flow", + title: "Stock-to-Flow", + bottom: [ + line({ + series: indicators.stockToFlow, + name: "S2F", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, + { + name: "Puell Multiple", + title: "Puell Multiple", + bottom: [ + line({ + series: indicators.puellMultiple.ratio, + name: "Puell", + color: colors.usd, + unit: Unit.ratio, + }), + ], + }, + { + name: "RHODL Ratio", + title: "RHODL Ratio", + bottom: [ + line({ + series: indicators.rhodlRatio.ratio, + name: "RHODL", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, + ], + }, + { + name: "Activity", + tree: [ + { + name: "Dormancy", + title: "Dormancy", + bottom: [ + line({ + series: indicators.dormancy.supplyAdjusted, + name: "Supply Adjusted", + color: colors.bitcoin, + unit: Unit.ratio, + }), + line({ + series: indicators.dormancy.flow, + name: "Flow", + color: colors.usd, + unit: Unit.ratio, + defaultActive: false, + }), + ], + }, + { + name: "Seller Exhaustion", + title: "Seller Exhaustion Constant", + bottom: [ + line({ + series: indicators.sellerExhaustion, + name: "SEC", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, + { + name: "CDD Supply Adjusted", + title: "Coindays Destroyed (Supply Adjusted)", + bottom: [ + line({ + series: indicators.coindaysDestroyedSupplyAdjusted, + name: "CDD SA", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, + { + name: "CYD Supply Adjusted", + title: "Coinyears Destroyed (Supply Adjusted)", + bottom: [ + line({ + series: indicators.coinyearsDestroyedSupplyAdjusted, + name: "CYD SA", + color: colors.bitcoin, + unit: Unit.ratio, + }), + ], + }, ], }, { @@ -1011,97 +1103,6 @@ export function createMarketSection() { color: colors.loss, }), }, - { - name: "RHODL Ratio", - title: "RHODL Ratio", - bottom: [ - line({ - series: indicators.rhodlRatio.ratio, - name: "RHODL", - color: colors.bitcoin, - unit: Unit.ratio, - }), - ], - }, - { - name: "Thermocap Multiple", - title: "Thermocap Multiple", - bottom: [ - line({ - series: indicators.thermoCapMultiple.ratio, - name: "Thermocap", - color: colors.bitcoin, - unit: Unit.ratio, - }), - ], - }, - { - name: "Stock-to-Flow", - title: "Stock-to-Flow", - bottom: [ - line({ - series: indicators.stockToFlow, - name: "S2F", - color: colors.bitcoin, - unit: Unit.ratio, - }), - ], - }, - { - name: "Dormancy", - title: "Dormancy", - bottom: [ - line({ - series: indicators.dormancy.supplyAdjusted, - name: "Supply Adjusted", - color: colors.bitcoin, - unit: Unit.ratio, - }), - line({ - series: indicators.dormancy.flow, - name: "Flow", - color: colors.usd, - unit: Unit.ratio, - defaultActive: false, - }), - ], - }, - { - name: "Seller Exhaustion", - title: "Seller Exhaustion Constant", - bottom: [ - line({ - series: indicators.sellerExhaustion, - name: "SEC", - color: colors.bitcoin, - unit: Unit.ratio, - }), - ], - }, - { - name: "CDD Supply Adjusted", - title: "Coindays Destroyed (Supply Adjusted)", - bottom: [ - line({ - series: indicators.coindaysDestroyedSupplyAdjusted, - name: "CDD SA", - color: colors.bitcoin, - unit: Unit.ratio, - }), - ], - }, - { - name: "CYD Supply Adjusted", - title: "Coinyears Destroyed (Supply Adjusted)", - bottom: [ - line({ - series: indicators.coinyearsDestroyedSupplyAdjusted, - name: "CYD SA", - color: colors.bitcoin, - unit: Unit.ratio, - }), - ], - }, ], }, ],