mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -1596,7 +1596,7 @@ pub struct BaseChangeCumulativeDeltaRelSumPattern {
|
||||
pub base: CentsUsdPattern,
|
||||
pub change_1m: RelPattern,
|
||||
pub cumulative: CentsUsdPattern,
|
||||
pub delta: ChangeRatePattern2,
|
||||
pub delta: AbsoluteRatePattern2,
|
||||
pub rel_to_rcap: BpsPercentRatioPattern,
|
||||
pub sum: _1m1w1y24hPattern3,
|
||||
}
|
||||
@@ -1608,7 +1608,7 @@ impl BaseChangeCumulativeDeltaRelSumPattern {
|
||||
base: CentsUsdPattern::new(client.clone(), _m(&acc, "realized_pnl")),
|
||||
change_1m: RelPattern::new(client.clone(), _m(&acc, "pnl_change_1m_rel_to")),
|
||||
cumulative: CentsUsdPattern::new(client.clone(), _m(&acc, "realized_pnl_cumulative")),
|
||||
delta: ChangeRatePattern2::new(client.clone(), _m(&acc, "realized_pnl_delta")),
|
||||
delta: AbsoluteRatePattern2::new(client.clone(), _m(&acc, "realized_pnl_delta")),
|
||||
rel_to_rcap: BpsPercentRatioPattern::new(client.clone(), _m(&acc, "realized_pnl_rel_to_rcap")),
|
||||
sum: _1m1w1y24hPattern3::new(client.clone(), _m(&acc, "realized_pnl_sum")),
|
||||
}
|
||||
@@ -1713,7 +1713,7 @@ impl CapLossMvrvPriceProfitSoprPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DeltaHalfInRelTotalPattern {
|
||||
pub delta: ChangeRatePattern,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub half: BtcCentsSatsUsdPattern,
|
||||
pub in_loss: BtcCentsRelSatsUsdPattern,
|
||||
pub in_profit: BtcCentsRelSatsUsdPattern,
|
||||
@@ -1725,7 +1725,7 @@ impl DeltaHalfInRelTotalPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
delta: ChangeRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
|
||||
in_loss: BtcCentsRelSatsUsdPattern::new(client.clone(), _m(&acc, "in_loss")),
|
||||
in_profit: BtcCentsRelSatsUsdPattern::new(client.clone(), _m(&acc, "in_profit")),
|
||||
@@ -1737,7 +1737,7 @@ impl DeltaHalfInRelTotalPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DeltaHalfInRelTotalPattern2 {
|
||||
pub delta: ChangeRatePattern,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub half: BtcCentsSatsUsdPattern,
|
||||
pub in_loss: BtcCentsRelSatsUsdPattern3,
|
||||
pub in_profit: BtcCentsRelSatsUsdPattern3,
|
||||
@@ -1749,7 +1749,7 @@ impl DeltaHalfInRelTotalPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
delta: ChangeRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
|
||||
in_loss: BtcCentsRelSatsUsdPattern3::new(client.clone(), _m(&acc, "in_loss")),
|
||||
in_profit: BtcCentsRelSatsUsdPattern3::new(client.clone(), _m(&acc, "in_profit")),
|
||||
@@ -1917,7 +1917,7 @@ impl BtcCentsRelSatsUsdPattern2 {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DeltaHalfInTotalPattern2 {
|
||||
pub delta: ChangeRatePattern,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub half: BtcCentsSatsUsdPattern,
|
||||
pub in_loss: BtcCentsSatsUsdPattern,
|
||||
pub in_profit: BtcCentsSatsUsdPattern,
|
||||
@@ -1928,7 +1928,7 @@ impl DeltaHalfInTotalPattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
delta: ChangeRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
|
||||
in_loss: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "in_loss")),
|
||||
in_profit: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "in_profit")),
|
||||
@@ -2189,7 +2189,7 @@ impl AdjustedRatioValuePattern {
|
||||
pub struct BaseCumulativeDeltaSumPattern {
|
||||
pub base: CentsUsdPattern,
|
||||
pub cumulative: CentsUsdPattern,
|
||||
pub delta: ChangeRatePattern2,
|
||||
pub delta: AbsoluteRatePattern2,
|
||||
pub sum: _1m1w1y24hPattern3,
|
||||
}
|
||||
|
||||
@@ -2199,7 +2199,7 @@ impl BaseCumulativeDeltaSumPattern {
|
||||
Self {
|
||||
base: CentsUsdPattern::new(client.clone(), acc.clone()),
|
||||
cumulative: CentsUsdPattern::new(client.clone(), _m(&acc, "cumulative")),
|
||||
delta: ChangeRatePattern2::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern2::new(client.clone(), _m(&acc, "delta")),
|
||||
sum: _1m1w1y24hPattern3::new(client.clone(), _m(&acc, "sum")),
|
||||
}
|
||||
}
|
||||
@@ -2268,7 +2268,7 @@ impl BtcCentsSatsUsdPattern {
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CentsDeltaRelUsdPattern {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub delta: ChangeRatePattern2,
|
||||
pub delta: AbsoluteRatePattern2,
|
||||
pub rel_to_own_mcap: BpsPercentRatioPattern4,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
}
|
||||
@@ -2278,7 +2278,7 @@ impl CentsDeltaRelUsdPattern {
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), _m(&acc, "cents")),
|
||||
delta: ChangeRatePattern2::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern2::new(client.clone(), _m(&acc, "delta")),
|
||||
rel_to_own_mcap: BpsPercentRatioPattern4::new(client.clone(), _m(&acc, "rel_to_own_mcap")),
|
||||
usd: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
@@ -2606,7 +2606,7 @@ impl CentsSatsUsdPattern3 {
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CentsDeltaUsdPattern {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub delta: ChangeRatePattern2,
|
||||
pub delta: AbsoluteRatePattern2,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
}
|
||||
|
||||
@@ -2615,7 +2615,7 @@ impl CentsDeltaUsdPattern {
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), _m(&acc, "cents")),
|
||||
delta: ChangeRatePattern2::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern2::new(client.clone(), _m(&acc, "delta")),
|
||||
usd: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
@@ -2641,7 +2641,7 @@ impl CentsSatsUsdPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DeltaHalfTotalPattern {
|
||||
pub delta: ChangeRatePattern,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub half: BtcCentsSatsUsdPattern,
|
||||
pub total: BtcCentsSatsUsdPattern,
|
||||
}
|
||||
@@ -2650,7 +2650,7 @@ impl DeltaHalfTotalPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
delta: ChangeRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
half: BtcCentsSatsUsdPattern::new(client.clone(), _m(&acc, "half")),
|
||||
total: BtcCentsSatsUsdPattern::new(client.clone(), acc.clone()),
|
||||
}
|
||||
@@ -2783,6 +2783,38 @@ impl<T: DeserializeOwned> BaseCumulativeSumPattern<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AbsoluteRatePattern {
|
||||
pub absolute: _1m1w1y24hPattern<StoredI64>,
|
||||
pub rate: _1m1w1y24hPattern2,
|
||||
}
|
||||
|
||||
impl AbsoluteRatePattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
absolute: _1m1w1y24hPattern::new(client.clone(), acc.clone()),
|
||||
rate: _1m1w1y24hPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AbsoluteRatePattern2 {
|
||||
pub absolute: _1m1w1y24hPattern3,
|
||||
pub rate: _1m1w1y24hPattern2,
|
||||
}
|
||||
|
||||
impl AbsoluteRatePattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
absolute: _1m1w1y24hPattern3::new(client.clone(), acc.clone()),
|
||||
rate: _1m1w1y24hPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BlocksDominancePattern {
|
||||
pub blocks_mined: BaseCumulativeSumPattern2,
|
||||
@@ -2863,38 +2895,6 @@ impl CentsUsdPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ChangeRatePattern {
|
||||
pub change: _1m1w1y24hPattern<StoredI64>,
|
||||
pub rate: _1m1w1y24hPattern2,
|
||||
}
|
||||
|
||||
impl ChangeRatePattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
change: _1m1w1y24hPattern::new(client.clone(), acc.clone()),
|
||||
rate: _1m1w1y24hPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct ChangeRatePattern2 {
|
||||
pub change: _1m1w1y24hPattern3,
|
||||
pub rate: _1m1w1y24hPattern2,
|
||||
}
|
||||
|
||||
impl ChangeRatePattern2 {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
change: _1m1w1y24hPattern3::new(client.clone(), acc.clone()),
|
||||
rate: _1m1w1y24hPattern2::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CoindaysSentPattern {
|
||||
pub coindays_destroyed: BaseCumulativeSumPattern<StoredF64>,
|
||||
@@ -2913,7 +2913,7 @@ impl CoindaysSentPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct DeltaInnerPattern {
|
||||
pub delta: ChangeRatePattern,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub inner: MetricPattern1<StoredU64>,
|
||||
}
|
||||
|
||||
@@ -2921,7 +2921,7 @@ impl DeltaInnerPattern {
|
||||
/// Create a new pattern node with accumulated metric name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
delta: ChangeRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), _m(&acc, "delta")),
|
||||
inner: MetricPattern1::new(client.clone(), acc.clone()),
|
||||
}
|
||||
}
|
||||
@@ -3937,29 +3937,29 @@ impl MetricsTree_Addresses_New {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Addresses_Delta {
|
||||
pub all: ChangeRatePattern,
|
||||
pub p2pk65: ChangeRatePattern,
|
||||
pub p2pk33: ChangeRatePattern,
|
||||
pub p2pkh: ChangeRatePattern,
|
||||
pub p2sh: ChangeRatePattern,
|
||||
pub p2wpkh: ChangeRatePattern,
|
||||
pub p2wsh: ChangeRatePattern,
|
||||
pub p2tr: ChangeRatePattern,
|
||||
pub p2a: ChangeRatePattern,
|
||||
pub all: AbsoluteRatePattern,
|
||||
pub p2pk65: AbsoluteRatePattern,
|
||||
pub p2pk33: AbsoluteRatePattern,
|
||||
pub p2pkh: AbsoluteRatePattern,
|
||||
pub p2sh: AbsoluteRatePattern,
|
||||
pub p2wpkh: AbsoluteRatePattern,
|
||||
pub p2wsh: AbsoluteRatePattern,
|
||||
pub p2tr: AbsoluteRatePattern,
|
||||
pub p2a: AbsoluteRatePattern,
|
||||
}
|
||||
|
||||
impl MetricsTree_Addresses_Delta {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
all: ChangeRatePattern::new(client.clone(), "address_count".to_string()),
|
||||
p2pk65: ChangeRatePattern::new(client.clone(), "p2pk65_address_count".to_string()),
|
||||
p2pk33: ChangeRatePattern::new(client.clone(), "p2pk33_address_count".to_string()),
|
||||
p2pkh: ChangeRatePattern::new(client.clone(), "p2pkh_address_count".to_string()),
|
||||
p2sh: ChangeRatePattern::new(client.clone(), "p2sh_address_count".to_string()),
|
||||
p2wpkh: ChangeRatePattern::new(client.clone(), "p2wpkh_address_count".to_string()),
|
||||
p2wsh: ChangeRatePattern::new(client.clone(), "p2wsh_address_count".to_string()),
|
||||
p2tr: ChangeRatePattern::new(client.clone(), "p2tr_address_count".to_string()),
|
||||
p2a: ChangeRatePattern::new(client.clone(), "p2a_address_count".to_string()),
|
||||
all: AbsoluteRatePattern::new(client.clone(), "address_count".to_string()),
|
||||
p2pk65: AbsoluteRatePattern::new(client.clone(), "p2pk65_address_count".to_string()),
|
||||
p2pk33: AbsoluteRatePattern::new(client.clone(), "p2pk33_address_count".to_string()),
|
||||
p2pkh: AbsoluteRatePattern::new(client.clone(), "p2pkh_address_count".to_string()),
|
||||
p2sh: AbsoluteRatePattern::new(client.clone(), "p2sh_address_count".to_string()),
|
||||
p2wpkh: AbsoluteRatePattern::new(client.clone(), "p2wpkh_address_count".to_string()),
|
||||
p2wsh: AbsoluteRatePattern::new(client.clone(), "p2wsh_address_count".to_string()),
|
||||
p2tr: AbsoluteRatePattern::new(client.clone(), "p2tr_address_count".to_string()),
|
||||
p2a: AbsoluteRatePattern::new(client.clone(), "p2a_address_count".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4435,16 +4435,16 @@ impl MetricsTree_Cointime_Prices {
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cointime_Adjusted {
|
||||
pub inflation_rate: BpsPercentRatioPattern,
|
||||
pub tx_velocity_btc: MetricPattern1<StoredF64>,
|
||||
pub tx_velocity_usd: MetricPattern1<StoredF64>,
|
||||
pub tx_velocity_native: MetricPattern1<StoredF64>,
|
||||
pub tx_velocity_fiat: MetricPattern1<StoredF64>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Cointime_Adjusted {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
inflation_rate: BpsPercentRatioPattern::new(client.clone(), "cointime_adj_inflation_rate".to_string()),
|
||||
tx_velocity_btc: MetricPattern1::new(client.clone(), "cointime_adj_tx_velocity_btc".to_string()),
|
||||
tx_velocity_usd: MetricPattern1::new(client.clone(), "cointime_adj_tx_velocity_usd".to_string()),
|
||||
tx_velocity_native: MetricPattern1::new(client.clone(), "cointime_adj_tx_velocity".to_string()),
|
||||
tx_velocity_fiat: MetricPattern1::new(client.clone(), "cointime_adj_tx_velocity_fiat".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5434,8 +5434,8 @@ impl MetricsTree_Market_MovingAverage_Sma {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_MovingAverage_Sma_200d {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub sats: MetricPattern1<SatsFract>,
|
||||
pub bps: MetricPattern1<BasisPoints32>,
|
||||
pub ratio: MetricPattern1<StoredF32>,
|
||||
@@ -5446,8 +5446,8 @@ pub struct MetricsTree_Market_MovingAverage_Sma_200d {
|
||||
impl MetricsTree_Market_MovingAverage_Sma_200d {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_200d_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "price_sma_200d".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_200d_cents".to_string()),
|
||||
sats: MetricPattern1::new(client.clone(), "price_sma_200d_sats".to_string()),
|
||||
bps: MetricPattern1::new(client.clone(), "price_sma_200d_ratio_bps".to_string()),
|
||||
ratio: MetricPattern1::new(client.clone(), "price_sma_200d_ratio".to_string()),
|
||||
@@ -5459,16 +5459,16 @@ impl MetricsTree_Market_MovingAverage_Sma_200d {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_MovingAverage_Sma_200d_X24 {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub sats: MetricPattern1<SatsFract>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Market_MovingAverage_Sma_200d_X24 {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_200d_x2_4_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "price_sma_200d_x2_4_usd".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_200d_x2_4_cents".to_string()),
|
||||
sats: MetricPattern1::new(client.clone(), "price_sma_200d_x2_4_sats".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -5476,16 +5476,16 @@ impl MetricsTree_Market_MovingAverage_Sma_200d_X24 {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_MovingAverage_Sma_200d_X08 {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub sats: MetricPattern1<SatsFract>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Market_MovingAverage_Sma_200d_X08 {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_200d_x0_8_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "price_sma_200d_x0_8_usd".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_200d_x0_8_cents".to_string()),
|
||||
sats: MetricPattern1::new(client.clone(), "price_sma_200d_x0_8_sats".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -5493,8 +5493,8 @@ impl MetricsTree_Market_MovingAverage_Sma_200d_X08 {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_MovingAverage_Sma_350d {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub sats: MetricPattern1<SatsFract>,
|
||||
pub bps: MetricPattern1<BasisPoints32>,
|
||||
pub ratio: MetricPattern1<StoredF32>,
|
||||
@@ -5504,8 +5504,8 @@ pub struct MetricsTree_Market_MovingAverage_Sma_350d {
|
||||
impl MetricsTree_Market_MovingAverage_Sma_350d {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_350d_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "price_sma_350d".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_350d_cents".to_string()),
|
||||
sats: MetricPattern1::new(client.clone(), "price_sma_350d_sats".to_string()),
|
||||
bps: MetricPattern1::new(client.clone(), "price_sma_350d_ratio_bps".to_string()),
|
||||
ratio: MetricPattern1::new(client.clone(), "price_sma_350d_ratio".to_string()),
|
||||
@@ -5516,16 +5516,16 @@ impl MetricsTree_Market_MovingAverage_Sma_350d {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Market_MovingAverage_Sma_350d_X2 {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub sats: MetricPattern1<SatsFract>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Market_MovingAverage_Sma_350d_X2 {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_350d_x2_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "price_sma_350d_x2_usd".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "price_sma_350d_x2_cents".to_string()),
|
||||
sats: MetricPattern1::new(client.clone(), "price_sma_350d_x2_sats".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -6387,16 +6387,16 @@ impl MetricsTree_Prices_Split {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Prices_Ohlc {
|
||||
pub cents: MetricPattern2<OHLCCents>,
|
||||
pub usd: MetricPattern2<OHLCDollars>,
|
||||
pub cents: MetricPattern2<OHLCCents>,
|
||||
pub sats: MetricPattern2<OHLCSats>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Prices_Ohlc {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern2::new(client.clone(), "price_ohlc_cents".to_string()),
|
||||
usd: MetricPattern2::new(client.clone(), "price_ohlc".to_string()),
|
||||
cents: MetricPattern2::new(client.clone(), "price_ohlc_cents".to_string()),
|
||||
sats: MetricPattern2::new(client.clone(), "price_ohlc_sats".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -6404,16 +6404,16 @@ impl MetricsTree_Prices_Ohlc {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Prices_Spot {
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<Cents>,
|
||||
pub sats: MetricPattern1<Sats>,
|
||||
}
|
||||
|
||||
impl MetricsTree_Prices_Spot {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "price_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "price".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "price_cents".to_string()),
|
||||
sats: MetricPattern1::new(client.clone(), "price_sats".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -6557,7 +6557,7 @@ impl MetricsTree_Cohorts_Utxo_All {
|
||||
pub struct MetricsTree_Cohorts_Utxo_All_Supply {
|
||||
pub total: BtcCentsSatsUsdPattern,
|
||||
pub half: BtcCentsSatsUsdPattern,
|
||||
pub delta: ChangeRatePattern,
|
||||
pub delta: AbsoluteRatePattern,
|
||||
pub in_profit: BtcCentsRelSatsUsdPattern2,
|
||||
pub in_loss: BtcCentsRelSatsUsdPattern2,
|
||||
}
|
||||
@@ -6567,7 +6567,7 @@ impl MetricsTree_Cohorts_Utxo_All_Supply {
|
||||
Self {
|
||||
total: BtcCentsSatsUsdPattern::new(client.clone(), "supply".to_string()),
|
||||
half: BtcCentsSatsUsdPattern::new(client.clone(), "supply_half".to_string()),
|
||||
delta: ChangeRatePattern::new(client.clone(), "supply_delta".to_string()),
|
||||
delta: AbsoluteRatePattern::new(client.clone(), "supply_delta".to_string()),
|
||||
in_profit: BtcCentsRelSatsUsdPattern2::new(client.clone(), "supply_in_profit".to_string()),
|
||||
in_loss: BtcCentsRelSatsUsdPattern2::new(client.clone(), "supply_in_loss".to_string()),
|
||||
}
|
||||
@@ -6645,16 +6645,16 @@ impl MetricsTree_Cohorts_Utxo_All_Unrealized_Loss {
|
||||
|
||||
/// Metrics tree node.
|
||||
pub struct MetricsTree_Cohorts_Utxo_All_Unrealized_NetPnl {
|
||||
pub cents: MetricPattern1<CentsSigned>,
|
||||
pub usd: MetricPattern1<Dollars>,
|
||||
pub cents: MetricPattern1<CentsSigned>,
|
||||
pub rel_to_own_gross: BpsPercentRatioPattern,
|
||||
}
|
||||
|
||||
impl MetricsTree_Cohorts_Utxo_All_Unrealized_NetPnl {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
cents: MetricPattern1::new(client.clone(), "net_unrealized_pnl_cents".to_string()),
|
||||
usd: MetricPattern1::new(client.clone(), "net_unrealized_pnl".to_string()),
|
||||
cents: MetricPattern1::new(client.clone(), "net_unrealized_pnl_cents".to_string()),
|
||||
rel_to_own_gross: BpsPercentRatioPattern::new(client.clone(), "net_unrealized_pnl_rel_to_own_gross_pnl".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,20 +107,20 @@ impl<T> ByEpoch<T> {
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
pub fn mut_vec_from_height(&mut self, height: Height) -> &mut T {
|
||||
pub fn mut_vec_from_height(&mut self, height: Height) -> Option<&mut T> {
|
||||
let epoch = Halving::from(height);
|
||||
if epoch == Halving::new(0) {
|
||||
&mut self._0
|
||||
Some(&mut self._0)
|
||||
} else if epoch == Halving::new(1) {
|
||||
&mut self._1
|
||||
Some(&mut self._1)
|
||||
} else if epoch == Halving::new(2) {
|
||||
&mut self._2
|
||||
Some(&mut self._2)
|
||||
} else if epoch == Halving::new(3) {
|
||||
&mut self._3
|
||||
Some(&mut self._3)
|
||||
} else if epoch == Halving::new(4) {
|
||||
&mut self._4
|
||||
Some(&mut self._4)
|
||||
} else {
|
||||
todo!("")
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,32 +231,32 @@ impl<T> Class<T> {
|
||||
.into_par_iter()
|
||||
}
|
||||
|
||||
pub fn mut_vec_from_timestamp(&mut self, timestamp: Timestamp) -> &mut T {
|
||||
pub fn mut_vec_from_timestamp(&mut self, timestamp: Timestamp) -> Option<&mut T> {
|
||||
let year = Year::from(timestamp);
|
||||
self.get_mut(year)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, year: Year) -> &mut T {
|
||||
pub fn get_mut(&mut self, year: Year) -> Option<&mut T> {
|
||||
match u16::from(year) {
|
||||
2009 => &mut self._2009,
|
||||
2010 => &mut self._2010,
|
||||
2011 => &mut self._2011,
|
||||
2012 => &mut self._2012,
|
||||
2013 => &mut self._2013,
|
||||
2014 => &mut self._2014,
|
||||
2015 => &mut self._2015,
|
||||
2016 => &mut self._2016,
|
||||
2017 => &mut self._2017,
|
||||
2018 => &mut self._2018,
|
||||
2019 => &mut self._2019,
|
||||
2020 => &mut self._2020,
|
||||
2021 => &mut self._2021,
|
||||
2022 => &mut self._2022,
|
||||
2023 => &mut self._2023,
|
||||
2024 => &mut self._2024,
|
||||
2025 => &mut self._2025,
|
||||
2026 => &mut self._2026,
|
||||
_ => todo!("Year {} not yet supported", u16::from(year)),
|
||||
2009 => Some(&mut self._2009),
|
||||
2010 => Some(&mut self._2010),
|
||||
2011 => Some(&mut self._2011),
|
||||
2012 => Some(&mut self._2012),
|
||||
2013 => Some(&mut self._2013),
|
||||
2014 => Some(&mut self._2014),
|
||||
2015 => Some(&mut self._2015),
|
||||
2016 => Some(&mut self._2016),
|
||||
2017 => Some(&mut self._2017),
|
||||
2018 => Some(&mut self._2018),
|
||||
2019 => Some(&mut self._2019),
|
||||
2020 => Some(&mut self._2020),
|
||||
2021 => Some(&mut self._2021),
|
||||
2022 => Some(&mut self._2022),
|
||||
2023 => Some(&mut self._2023),
|
||||
2024 => Some(&mut self._2024),
|
||||
2025 => Some(&mut self._2025),
|
||||
2026 => Some(&mut self._2026),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use serde::Serialize;
|
||||
|
||||
use super::CohortName;
|
||||
|
||||
/// "At least X% loss" threshold names (10 thresholds).
|
||||
/// "At least X% loss" threshold names (9 thresholds).
|
||||
pub const LOSS_NAMES: Loss<CohortName> = Loss {
|
||||
breakeven: CohortName::new("utxos_in_loss", "<0%", "In Loss (Below Breakeven)"),
|
||||
_10pct: CohortName::new("utxos_over_10pct_in_loss", "≥10%L", "10%+ Loss"),
|
||||
@@ -26,7 +26,7 @@ impl Loss<CohortName> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 10 "at least X% loss" aggregate thresholds.
|
||||
/// 9 "at least X% loss" aggregate thresholds.
|
||||
///
|
||||
/// Each is a suffix sum over the profitability ranges, from most loss-making up.
|
||||
#[derive(Default, Clone, Traversable, Serialize)]
|
||||
|
||||
@@ -4,7 +4,7 @@ use serde::Serialize;
|
||||
|
||||
use super::CohortName;
|
||||
|
||||
/// "At least X% profit" threshold names (15 thresholds).
|
||||
/// "At least X% profit" threshold names (14 thresholds).
|
||||
pub const PROFIT_NAMES: Profit<CohortName> = Profit {
|
||||
breakeven: CohortName::new("utxos_in_profit", "≥0%", "In Profit (Breakeven+)"),
|
||||
_10pct: CohortName::new("utxos_over_10pct_in_profit", "≥10%", "10%+ Profit"),
|
||||
@@ -31,7 +31,7 @@ impl Profit<CohortName> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 15 "at least X% profit" aggregate thresholds.
|
||||
/// 14 "at least X% profit" aggregate thresholds.
|
||||
///
|
||||
/// Each is a prefix sum over the profitability ranges, from most profitable down.
|
||||
#[derive(Default, Clone, Traversable, Serialize)]
|
||||
|
||||
@@ -8,11 +8,11 @@ pub enum TimeFilter {
|
||||
}
|
||||
|
||||
impl TimeFilter {
|
||||
pub fn contains(&self, days: usize) -> bool {
|
||||
pub fn contains(&self, hours: usize) -> bool {
|
||||
match self {
|
||||
TimeFilter::LowerThan(max) => days < *max,
|
||||
TimeFilter::Range(r) => r.contains(&days),
|
||||
TimeFilter::GreaterOrEqual(min) => days >= *min,
|
||||
TimeFilter::LowerThan(max) => hours < *max,
|
||||
TimeFilter::Range(r) => r.contains(&hours),
|
||||
TimeFilter::GreaterOrEqual(min) => hours >= *min,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,14 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.tx_velocity_btc.height.compute_multiply(
|
||||
self.tx_velocity_native.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&activity.ratio.height,
|
||||
&supply.velocity.native.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.tx_velocity_usd.height.compute_multiply(
|
||||
self.tx_velocity_fiat.height.compute_multiply(
|
||||
starting_indexes.height,
|
||||
&activity.ratio.height,
|
||||
&supply.velocity.fiat.height,
|
||||
|
||||
@@ -21,15 +21,15 @@ impl Vecs {
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
tx_velocity_btc: PerBlock::forced_import(
|
||||
tx_velocity_native: PerBlock::forced_import(
|
||||
db,
|
||||
"cointime_adj_tx_velocity_btc",
|
||||
"cointime_adj_tx_velocity",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
tx_velocity_usd: PerBlock::forced_import(
|
||||
tx_velocity_fiat: PerBlock::forced_import(
|
||||
db,
|
||||
"cointime_adj_tx_velocity_usd",
|
||||
"cointime_adj_tx_velocity_fiat",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
|
||||
@@ -7,6 +7,6 @@ use crate::internal::{PerBlock, PercentPerBlock};
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub inflation_rate: PercentPerBlock<BasisPointsSigned32, M>,
|
||||
pub tx_velocity_btc: PerBlock<StoredF64, M>,
|
||||
pub tx_velocity_usd: PerBlock<StoredF64, M>,
|
||||
pub tx_velocity_native: PerBlock<StoredF64, M>,
|
||||
pub tx_velocity_fiat: PerBlock<StoredF64, M>,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use brk_cohort::{AmountBucket, ByAddressType};
|
||||
use brk_error::Result;
|
||||
use brk_types::{Age, Cents, CheckedSub, Height, Sats, Timestamp, TypeIndex};
|
||||
use rustc_hash::FxHashSet;
|
||||
use vecdb::{VecIndex, unlikely};
|
||||
use vecdb::VecIndex;
|
||||
|
||||
use crate::distribution::{
|
||||
address::{AddressTypeToActivityCounts, HeightToAddressTypeToVec},
|
||||
@@ -49,8 +49,7 @@ pub(crate) fn process_sent(
|
||||
let prev_timestamp = height_to_timestamp[receive_height.to_usize()];
|
||||
let age = Age::new(current_timestamp, prev_timestamp);
|
||||
|
||||
// Compute peak price during holding period for peak regret
|
||||
// This is the max HIGH price between receive and send heights
|
||||
// Compute peak spot price during holding period for peak regret
|
||||
let peak_price = price_range_max.max_between(receive_height, current_height);
|
||||
|
||||
for (output_type, vec) in by_type.unwrap().into_iter() {
|
||||
@@ -84,72 +83,33 @@ pub(crate) fn process_sent(
|
||||
let new_bucket = AmountBucket::from(new_balance);
|
||||
let crossing_boundary = prev_bucket != new_bucket;
|
||||
|
||||
let cohort_state = cohorts
|
||||
.amount_range
|
||||
.get_mut_by_bucket(prev_bucket)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
|
||||
cohort_state.send(addr_data, value, current_price, prev_price, peak_price, age)?;
|
||||
|
||||
// If crossing a bucket boundary, remove the (now-updated) address from old bucket
|
||||
if will_be_empty || crossing_boundary {
|
||||
// Subtract from old cohort
|
||||
let cohort_state = cohorts
|
||||
.amount_range
|
||||
.get_mut_by_bucket(prev_bucket)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
|
||||
// Debug info for tracking down underflow issues
|
||||
if unlikely(
|
||||
cohort_state.inner.supply.utxo_count < addr_data.utxo_count() as u64,
|
||||
) {
|
||||
panic!(
|
||||
"process_sent: cohort underflow detected!\n\
|
||||
Block context: receive_height={:?}, output_type={:?}, type_index={:?}\n\
|
||||
prev_balance={}, new_balance={}, value={}\n\
|
||||
will_be_empty={}, crossing_boundary={}\n\
|
||||
Address: {:?}",
|
||||
receive_height,
|
||||
output_type,
|
||||
type_index,
|
||||
prev_balance,
|
||||
new_balance,
|
||||
value,
|
||||
will_be_empty,
|
||||
crossing_boundary,
|
||||
addr_data
|
||||
);
|
||||
}
|
||||
|
||||
cohort_state.subtract(addr_data);
|
||||
}
|
||||
|
||||
// Update address data
|
||||
addr_data.send(value, prev_price)?;
|
||||
|
||||
if will_be_empty {
|
||||
// Address becoming empty - invariant check
|
||||
if new_balance.is_not_zero() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
*type_address_count -= 1;
|
||||
*type_empty_count += 1;
|
||||
|
||||
// Move from funded to empty
|
||||
lookup.move_to_empty(output_type, type_index);
|
||||
} else {
|
||||
// Add to new cohort
|
||||
cohorts
|
||||
.amount_range
|
||||
.get_mut_by_bucket(new_bucket)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.add(addr_data);
|
||||
}
|
||||
} else {
|
||||
// Address staying in same cohort - update in place
|
||||
// Migrate address to new bucket or mark as empty
|
||||
if will_be_empty {
|
||||
*type_address_count -= 1;
|
||||
*type_empty_count += 1;
|
||||
lookup.move_to_empty(output_type, type_index);
|
||||
} else if crossing_boundary {
|
||||
cohorts
|
||||
.amount_range
|
||||
.get_mut_by_bucket(new_bucket)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send(addr_data, value, current_price, prev_price, peak_price, age)?;
|
||||
.add(addr_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,18 +33,12 @@ impl UTXOCohorts<Rw> {
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
self.epoch
|
||||
.mut_vec_from_height(height)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
self.class
|
||||
.mut_vec_from_timestamp(timestamp)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
if let Some(v) = self.epoch.mut_vec_from_height(height) {
|
||||
v.state.as_mut().unwrap().receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
}
|
||||
if let Some(v) = self.class.mut_vec_from_timestamp(timestamp) {
|
||||
v.state.as_mut().unwrap().receive_utxo_snapshot(&supply_state, &snapshot);
|
||||
}
|
||||
|
||||
// Update output type cohorts (skip types with no outputs this block)
|
||||
self.type_.iter_typed_mut().for_each(|(output_type, vecs)| {
|
||||
|
||||
@@ -63,34 +63,22 @@ impl UTXOCohorts<Rw> {
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
self.epoch
|
||||
.mut_vec_from_height(receive_height)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
self.class
|
||||
.mut_vec_from_timestamp(block_state.timestamp)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
if let Some(v) = self.epoch.mut_vec_from_height(receive_height) {
|
||||
v.state.as_mut().unwrap().send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
}
|
||||
if let Some(v) = self.class.mut_vec_from_timestamp(block_state.timestamp) {
|
||||
v.state.as_mut().unwrap().send_utxo_precomputed(&sent.spendable_supply, &pre);
|
||||
}
|
||||
} else if sent.spendable_supply.utxo_count > 0 {
|
||||
// Zero-value UTXOs: just subtract supply
|
||||
self.age_range.get_mut(age).state.as_mut().unwrap().supply -=
|
||||
&sent.spendable_supply;
|
||||
self.epoch
|
||||
.mut_vec_from_height(receive_height)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.supply -= &sent.spendable_supply;
|
||||
self.class
|
||||
.mut_vec_from_timestamp(block_state.timestamp)
|
||||
.state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.supply -= &sent.spendable_supply;
|
||||
if let Some(v) = self.epoch.mut_vec_from_height(receive_height) {
|
||||
v.state.as_mut().unwrap().supply -= &sent.spendable_supply;
|
||||
}
|
||||
if let Some(v) = self.class.mut_vec_from_timestamp(block_state.timestamp) {
|
||||
v.state.as_mut().unwrap().supply -= &sent.spendable_supply;
|
||||
}
|
||||
}
|
||||
|
||||
// Update output type cohorts (skip zero-supply entries)
|
||||
|
||||
@@ -460,7 +460,7 @@ impl RealizedFull {
|
||||
.change_1m_rel_to_rcap
|
||||
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.core.net_pnl.delta.change._1m.cents.height,
|
||||
&self.core.net_pnl.delta.absolute._1m.cents.height,
|
||||
&self.core.minimal.cap.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
@@ -468,7 +468,7 @@ impl RealizedFull {
|
||||
.change_1m_rel_to_mcap
|
||||
.compute_binary::<CentsSigned, Dollars, RatioCentsSignedDollarsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.core.net_pnl.delta.change._1m.cents.height,
|
||||
&self.core.net_pnl.delta.absolute._1m.cents.height,
|
||||
height_to_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -373,6 +373,7 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
let (base, rest) = CostBasisDistribution::deserialize_with_rest(&data)?;
|
||||
self.map = Some(base);
|
||||
self.raw.state = Some(RawState::deserialize(rest)?);
|
||||
debug_assert!(rest.len() >= 32, "CostBasisData state too short: {} bytes", rest.len());
|
||||
self.investor_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
||||
self.pending.clear();
|
||||
self.raw.pending_cap = PendingCapDelta::default();
|
||||
@@ -431,6 +432,14 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
self.apply_map_pending();
|
||||
self.raw.apply_pending_cap();
|
||||
self.investor_cap_raw += self.pending_investor_cap.inc;
|
||||
debug_assert!(
|
||||
self.investor_cap_raw >= self.pending_investor_cap.dec,
|
||||
"CostBasis investor_cap_raw underflow!\n\
|
||||
Path: {:?}\n\
|
||||
Current (after increments): {:?}\n\
|
||||
Trying to decrement by: {:?}",
|
||||
self.raw.pathbuf, self.investor_cap_raw, self.pending_investor_cap.dec
|
||||
);
|
||||
self.investor_cap_raw -= self.pending_investor_cap.dec;
|
||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
||||
}
|
||||
|
||||
@@ -224,20 +224,27 @@ impl SlidingWindowSorted {
|
||||
let rank = p * last;
|
||||
let lo = rank.floor() as usize;
|
||||
let hi = rank.ceil() as usize;
|
||||
let frac = rank - lo as f64;
|
||||
lo_hi[i] = (lo, hi, frac);
|
||||
lo_hi[i] = (lo, hi, rank - lo as f64);
|
||||
|
||||
// Insert unique ranks in sorted order (they're already ~sorted since ps is sorted)
|
||||
if rank_count == 0 || rank_set[rank_count - 1] != lo {
|
||||
rank_set[rank_count] = lo;
|
||||
rank_count += 1;
|
||||
}
|
||||
if hi != lo && (rank_count == 0 || rank_set[rank_count - 1] != hi) {
|
||||
rank_set[rank_count] = lo;
|
||||
rank_count += 1;
|
||||
if hi != lo {
|
||||
rank_set[rank_count] = hi;
|
||||
rank_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and deduplicate (interleaved lo/hi values aren't necessarily sorted)
|
||||
rank_set[..rank_count].sort_unstable();
|
||||
let mut w = 1;
|
||||
for r in 1..rank_count {
|
||||
if rank_set[r] != rank_set[w - 1] {
|
||||
rank_set[w] = rank_set[r];
|
||||
w += 1;
|
||||
}
|
||||
}
|
||||
rank_count = w;
|
||||
|
||||
// Single pass through blocks to get all values
|
||||
let ranks = &rank_set[..rank_count];
|
||||
let mut values = [0.0f64; 10];
|
||||
|
||||
@@ -9,10 +9,10 @@ use crate::internal::AmountPerBlock;
|
||||
/// All fields are lazy transforms from existing sources - no storage.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyAmount<I: VecIndex> {
|
||||
pub sats: LazyVecFrom1<I, Sats, I, Sats>,
|
||||
pub btc: LazyVecFrom1<I, Bitcoin, I, Sats>,
|
||||
pub cents: LazyVecFrom1<I, Cents, I, Cents>,
|
||||
pub sats: LazyVecFrom1<I, Sats, I, Sats>,
|
||||
pub usd: LazyVecFrom1<I, Dollars, I, Dollars>,
|
||||
pub cents: LazyVecFrom1<I, Cents, I, Cents>,
|
||||
}
|
||||
|
||||
impl LazyAmount<Height> {
|
||||
@@ -57,10 +57,10 @@ impl LazyAmount<Height> {
|
||||
);
|
||||
|
||||
Self {
|
||||
sats,
|
||||
btc,
|
||||
cents,
|
||||
sats,
|
||||
usd,
|
||||
cents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ use crate::{
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct AmountPerBlock<M: StorageMode = Rw> {
|
||||
pub sats: PerBlock<Sats, M>,
|
||||
pub btc: LazyPerBlock<Bitcoin, Sats>,
|
||||
pub cents: PerBlock<Cents, M>,
|
||||
pub sats: PerBlock<Sats, M>,
|
||||
pub usd: LazyPerBlock<Dollars, Cents>,
|
||||
pub cents: PerBlock<Cents, M>,
|
||||
}
|
||||
|
||||
impl AmountPerBlock {
|
||||
@@ -47,10 +47,10 @@ impl AmountPerBlock {
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
btc,
|
||||
cents,
|
||||
sats,
|
||||
usd,
|
||||
cents,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ use crate::internal::{AmountPerBlock, DerivedResolutions};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyAmountDerivedResolutions {
|
||||
pub sats: DerivedResolutions<Sats, Sats>,
|
||||
pub btc: DerivedResolutions<Bitcoin, Sats>,
|
||||
pub cents: DerivedResolutions<Cents, Cents>,
|
||||
pub sats: DerivedResolutions<Sats, Sats>,
|
||||
pub usd: DerivedResolutions<Dollars, Dollars>,
|
||||
pub cents: DerivedResolutions<Cents, Cents>,
|
||||
}
|
||||
|
||||
impl LazyAmountDerivedResolutions {
|
||||
@@ -54,10 +54,10 @@ impl LazyAmountDerivedResolutions {
|
||||
);
|
||||
|
||||
Self {
|
||||
sats,
|
||||
btc,
|
||||
cents,
|
||||
sats,
|
||||
usd,
|
||||
cents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ use crate::{
|
||||
/// Single window slot: lazy rolling sum for Amount (sats + btc + cents + usd).
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyRollingSumAmountFromHeight {
|
||||
pub sats: LazyRollingSumFromHeight<Sats>,
|
||||
pub btc: LazyPerBlock<Bitcoin, Sats>,
|
||||
pub cents: LazyRollingSumFromHeight<Cents>,
|
||||
pub sats: LazyRollingSumFromHeight<Sats>,
|
||||
pub usd: LazyPerBlock<Dollars, Cents>,
|
||||
pub cents: LazyRollingSumFromHeight<Cents>,
|
||||
}
|
||||
|
||||
/// Lazy rolling sums for all 4 windows, for Amount (sats + btc + cents + usd).
|
||||
@@ -112,10 +112,10 @@ impl LazyRollingSumsAmountFromHeight {
|
||||
};
|
||||
|
||||
LazyRollingSumAmountFromHeight {
|
||||
sats,
|
||||
btc,
|
||||
cents,
|
||||
sats,
|
||||
usd,
|
||||
cents,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ impl CentsType for CentsSigned {
|
||||
/// Generic over `C` to support both `Cents` (unsigned) and `CentsSigned` (signed).
|
||||
#[derive(Traversable)]
|
||||
pub struct FiatPerBlock<C: CentsType, M: StorageMode = Rw> {
|
||||
pub cents: PerBlock<C, M>,
|
||||
pub usd: LazyPerBlock<Dollars, C>,
|
||||
pub cents: PerBlock<C, M>,
|
||||
}
|
||||
|
||||
impl<C: CentsType> FiatPerBlock<C> {
|
||||
@@ -48,6 +48,6 @@ impl<C: CentsType> FiatPerBlock<C> {
|
||||
cents.height.read_only_boxed_clone(),
|
||||
¢s,
|
||||
);
|
||||
Ok(Self { cents, usd })
|
||||
Ok(Self { usd, cents })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::internal::{CentsType, PerBlock, Identity, LazyPerBlock, NumericValue}
|
||||
/// Zero extra stored vecs.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyFiatPerBlock<C: CentsType> {
|
||||
pub cents: LazyPerBlock<C, C>,
|
||||
pub usd: LazyPerBlock<Dollars, C>,
|
||||
pub cents: LazyPerBlock<C, C>,
|
||||
}
|
||||
|
||||
impl<C: CentsType> LazyFiatPerBlock<C> {
|
||||
@@ -33,6 +33,6 @@ impl<C: CentsType> LazyFiatPerBlock<C> {
|
||||
source.height.read_only_boxed_clone(),
|
||||
source,
|
||||
);
|
||||
Self { cents, usd }
|
||||
Self { usd, cents }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ use crate::{
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyRollingSumFiatFromHeight<C: CentsType> {
|
||||
pub cents: LazyRollingSumFromHeight<C>,
|
||||
pub usd: LazyPerBlock<Dollars, C>,
|
||||
pub cents: LazyRollingSumFromHeight<C>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
@@ -69,7 +69,7 @@ impl<C: CentsType> LazyRollingSumsFiatFromHeight<C> {
|
||||
)),
|
||||
};
|
||||
|
||||
LazyRollingSumFiatFromHeight { cents, usd }
|
||||
LazyRollingSumFiatFromHeight { usd, cents }
|
||||
};
|
||||
|
||||
Self(cached_starts.0.map_with_suffix(make_slot))
|
||||
|
||||
@@ -19,8 +19,8 @@ use crate::{
|
||||
/// Generic price metric with cents, USD, and sats representations.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Price<C> {
|
||||
pub cents: C,
|
||||
pub usd: LazyPerBlock<Dollars, Cents>,
|
||||
pub cents: C,
|
||||
pub sats: LazyPerBlock<SatsFract, Dollars>,
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Price<PerBlock<Cents>> {
|
||||
version,
|
||||
&usd,
|
||||
);
|
||||
Ok(Self { cents, usd, sats })
|
||||
Ok(Self { usd, cents, sats })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,6 @@ where
|
||||
version,
|
||||
&usd,
|
||||
);
|
||||
Self { cents, usd, sats }
|
||||
Self { usd, cents, sats }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ use super::{RatioPerBlock, RatioPerBlockPercentiles};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct PriceWithRatioPerBlock<M: StorageMode = Rw> {
|
||||
pub cents: PerBlock<Cents, M>,
|
||||
pub usd: LazyPerBlock<Dollars, Cents>,
|
||||
pub cents: PerBlock<Cents, M>,
|
||||
pub sats: LazyPerBlock<SatsFract, Dollars>,
|
||||
pub bps: PerBlock<BasisPoints32, M>,
|
||||
pub ratio: LazyPerBlock<StoredF32, BasisPoints32>,
|
||||
@@ -28,8 +28,8 @@ impl PriceWithRatioPerBlock {
|
||||
let price = Price::forced_import(db, name, version, indexes)?;
|
||||
let ratio = RatioPerBlock::forced_import(db, name, version, indexes)?;
|
||||
Ok(Self {
|
||||
cents: price.cents,
|
||||
usd: price.usd,
|
||||
cents: price.cents,
|
||||
sats: price.sats,
|
||||
bps: ratio.bps,
|
||||
ratio: ratio.ratio,
|
||||
|
||||
@@ -44,7 +44,7 @@ where
|
||||
|
||||
/// Lazy rolling deltas for all 4 window durations (24h, 1w, 1m, 1y).
|
||||
///
|
||||
/// Tree shape: `change._24h/...`, `rate._24h/...` — matches old `RollingDelta`.
|
||||
/// Tree shape: `absolute._24h/...`, `rate._24h/...` — matches old `RollingDelta`.
|
||||
///
|
||||
/// Replaces `RollingDelta`, `RollingDelta1m`, and `RollingDeltaExcept1m` — since
|
||||
/// there is no storage cost, all 4 windows are always available.
|
||||
@@ -55,7 +55,7 @@ where
|
||||
C: NumericValue + JsonSchema,
|
||||
B: BpsType,
|
||||
{
|
||||
pub change: Windows<LazyDeltaFromHeight<S, C, DeltaChange>>,
|
||||
pub absolute: Windows<LazyDeltaFromHeight<S, C, DeltaChange>>,
|
||||
pub rate: Windows<LazyDeltaPercentFromHeight<S, B>>,
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ where
|
||||
version,
|
||||
indexes,
|
||||
);
|
||||
let change = LazyDeltaFromHeight {
|
||||
let absolute = LazyDeltaFromHeight {
|
||||
height: change_vec,
|
||||
resolutions: Box::new(change_resolutions),
|
||||
};
|
||||
@@ -154,12 +154,12 @@ where
|
||||
percent,
|
||||
};
|
||||
|
||||
(change, rate)
|
||||
(absolute, rate)
|
||||
};
|
||||
|
||||
let (change, rate) = cached_starts.0.map_with_suffix(make_slot).unzip();
|
||||
let (absolute, rate) = cached_starts.0.map_with_suffix(make_slot).unzip();
|
||||
|
||||
Self { change, rate }
|
||||
Self { absolute, rate }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,13 +174,13 @@ where
|
||||
S: VecValue,
|
||||
C: CentsType,
|
||||
{
|
||||
pub cents: LazyDeltaFromHeight<S, C, DeltaChange>,
|
||||
pub usd: LazyPerBlock<Dollars, C>,
|
||||
pub cents: LazyDeltaFromHeight<S, C, DeltaChange>,
|
||||
}
|
||||
|
||||
/// Lazy fiat rolling deltas for all 4 windows.
|
||||
///
|
||||
/// Tree shape: `change._24h.{cents,usd}/...`, `rate._24h/...` — matches old `FiatRollingDelta`.
|
||||
/// Tree shape: `absolute._24h.{cents,usd}/...`, `rate._24h/...` — matches old `FiatRollingDelta`.
|
||||
///
|
||||
/// Replaces `FiatRollingDelta`, `FiatRollingDelta1m`, and `FiatRollingDeltaExcept1m`.
|
||||
#[derive(Clone, Traversable)]
|
||||
@@ -190,7 +190,7 @@ where
|
||||
C: CentsType,
|
||||
B: BpsType,
|
||||
{
|
||||
pub change: Windows<LazyDeltaFiatFromHeight<S, C>>,
|
||||
pub absolute: Windows<LazyDeltaFiatFromHeight<S, C>>,
|
||||
pub rate: Windows<LazyDeltaPercentFromHeight<S, B>>,
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ where
|
||||
)),
|
||||
};
|
||||
|
||||
let change = LazyDeltaFiatFromHeight { cents, usd };
|
||||
let absolute = LazyDeltaFiatFromHeight { usd, cents };
|
||||
|
||||
// Rate BPS: (source[h] - source[ago]) / source[ago] as B (via f64)
|
||||
let rate_vec = LazyDeltaVec::<Height, S, B, DeltaRate>::new(
|
||||
@@ -303,11 +303,11 @@ where
|
||||
percent,
|
||||
};
|
||||
|
||||
(change, rate)
|
||||
(absolute, rate)
|
||||
};
|
||||
|
||||
let (change, rate) = cached_starts.0.map_with_suffix(make_slot).unzip();
|
||||
let (absolute, rate) = cached_starts.0.map_with_suffix(make_slot).unzip();
|
||||
|
||||
Self { change, rate }
|
||||
Self { absolute, rate }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,28 +18,28 @@ pub struct SplitByUnit<M: StorageMode = Rw> {
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct SplitIndexesByUnit<M: StorageMode = Rw> {
|
||||
pub cents: EagerIndexes<Cents, M>,
|
||||
pub usd: LazyEagerIndexes<Dollars, Cents>,
|
||||
pub cents: EagerIndexes<Cents, M>,
|
||||
pub sats: LazyEagerIndexes<Sats, Cents>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct SplitCloseByUnit {
|
||||
pub cents: Resolutions<Cents>,
|
||||
pub usd: Resolutions<Dollars>,
|
||||
pub cents: Resolutions<Cents>,
|
||||
pub sats: Resolutions<Sats>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct OhlcByUnit<M: StorageMode = Rw> {
|
||||
pub cents: OhlcVecs<OHLCCents, M>,
|
||||
pub usd: LazyOhlcVecs<OHLCDollars, OHLCCents>,
|
||||
pub cents: OhlcVecs<OHLCCents, M>,
|
||||
pub sats: LazyOhlcVecs<OHLCSats, OHLCCents>,
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct PriceByUnit<M: StorageMode = Rw> {
|
||||
pub cents: PerBlock<Cents, M>,
|
||||
pub usd: LazyPerBlock<Dollars, Cents>,
|
||||
pub cents: PerBlock<Cents, M>,
|
||||
pub sats: LazyPerBlock<Sats, Cents>,
|
||||
}
|
||||
|
||||
@@ -142,36 +142,36 @@ impl Vecs {
|
||||
|
||||
let split = SplitByUnit {
|
||||
open: SplitIndexesByUnit {
|
||||
cents: open_cents,
|
||||
usd: open_usd,
|
||||
cents: open_cents,
|
||||
sats: open_sats,
|
||||
},
|
||||
high: SplitIndexesByUnit {
|
||||
cents: high_cents,
|
||||
usd: high_usd,
|
||||
cents: high_cents,
|
||||
sats: high_sats,
|
||||
},
|
||||
low: SplitIndexesByUnit {
|
||||
cents: low_cents,
|
||||
usd: low_usd,
|
||||
cents: low_cents,
|
||||
sats: low_sats,
|
||||
},
|
||||
close: SplitCloseByUnit {
|
||||
cents: close_cents,
|
||||
usd: close_usd,
|
||||
cents: close_cents,
|
||||
sats: close_sats,
|
||||
},
|
||||
};
|
||||
|
||||
let ohlc = OhlcByUnit {
|
||||
cents: ohlc_cents,
|
||||
usd: ohlc_usd,
|
||||
cents: ohlc_cents,
|
||||
sats: ohlc_sats,
|
||||
};
|
||||
|
||||
let spot = PriceByUnit {
|
||||
cents: price_cents,
|
||||
usd: price_usd,
|
||||
cents: price_cents,
|
||||
sats: price_sats,
|
||||
};
|
||||
|
||||
|
||||
@@ -142,6 +142,9 @@ pub enum Error {
|
||||
#[error("Request weight {requested} exceeds maximum {max}")]
|
||||
WeightExceeded { requested: usize, max: usize },
|
||||
|
||||
#[error("Deserialization error: {0}")]
|
||||
Deserialization(String),
|
||||
|
||||
#[error("Fetch failed after retries: {0}")]
|
||||
FetchFailed(String),
|
||||
|
||||
|
||||
@@ -39,8 +39,7 @@ impl Binance {
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> Result<OHLCCents> {
|
||||
// Try live API data first
|
||||
if self._1mn.is_none()
|
||||
|| self._1mn.as_ref().unwrap().last_key_value().unwrap().0 <= ×tamp
|
||||
if self._1mn.as_ref().and_then(|m| m.last_key_value()).is_none_or(|(k, _)| k <= ×tamp)
|
||||
{
|
||||
self._1mn.replace(Self::fetch_1mn()?);
|
||||
}
|
||||
@@ -80,7 +79,7 @@ impl Binance {
|
||||
}
|
||||
|
||||
pub fn get_from_1d(&mut self, date: &Date) -> Result<OHLCCents> {
|
||||
if self._1d.is_none() || self._1d.as_ref().unwrap().last_key_value().unwrap().0 <= date {
|
||||
if self._1d.as_ref().and_then(|m| m.last_key_value()).is_none_or(|(k, _)| k <= date) {
|
||||
self._1d.replace(Self::fetch_1d()?);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ impl Kraken {
|
||||
timestamp: Timestamp,
|
||||
previous_timestamp: Option<Timestamp>,
|
||||
) -> Result<OHLCCents> {
|
||||
if self._1mn.is_none()
|
||||
|| self._1mn.as_ref().unwrap().last_key_value().unwrap().0 <= ×tamp
|
||||
if self._1mn.as_ref().and_then(|m| m.last_key_value()).is_none_or(|(k, _)| k <= ×tamp)
|
||||
{
|
||||
self._1mn.replace(Self::fetch_1mn()?);
|
||||
}
|
||||
@@ -46,7 +45,7 @@ impl Kraken {
|
||||
}
|
||||
|
||||
fn get_from_1d(&mut self, date: &Date) -> Result<OHLCCents> {
|
||||
if self._1d.is_none() || self._1d.as_ref().unwrap().last_key_value().unwrap().0 <= date {
|
||||
if self._1d.as_ref().and_then(|m| m.last_key_value()).is_none_or(|(k, _)| k <= date) {
|
||||
self._1d.replace(Self::fetch_1d()?);
|
||||
}
|
||||
self._1d
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{path::Path, thread::sleep, time::Duration};
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_types::{Date, Height, OHLCCents, Timestamp};
|
||||
use tracing::info;
|
||||
use tracing::{info, warn};
|
||||
|
||||
mod binance;
|
||||
mod brk;
|
||||
@@ -70,14 +70,20 @@ impl Fetcher {
|
||||
where
|
||||
F: FnMut(&mut dyn PriceSource) -> Option<Result<OHLCCents>>,
|
||||
{
|
||||
if let Some(Ok(ohlc)) = fetch(&mut self.binance) {
|
||||
return Some(Ok(ohlc));
|
||||
match fetch(&mut self.binance) {
|
||||
Some(Ok(ohlc)) => return Some(Ok(ohlc)),
|
||||
Some(Err(e)) => warn!("Binance fetch failed: {e}"),
|
||||
None => {}
|
||||
}
|
||||
if let Some(Ok(ohlc)) = fetch(&mut self.kraken) {
|
||||
return Some(Ok(ohlc));
|
||||
match fetch(&mut self.kraken) {
|
||||
Some(Ok(ohlc)) => return Some(Ok(ohlc)),
|
||||
Some(Err(e)) => warn!("Kraken fetch failed: {e}"),
|
||||
None => {}
|
||||
}
|
||||
if let Some(Ok(ohlc)) = fetch(&mut self.brk) {
|
||||
return Some(Ok(ohlc));
|
||||
match fetch(&mut self.brk) {
|
||||
Some(Ok(ohlc)) => return Some(Ok(ohlc)),
|
||||
Some(Err(e)) => warn!("Brk fetch failed: {e}"),
|
||||
None => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -5,13 +5,18 @@ use brk_types::{Cents, Close, Date, Dollars, High, Low, OHLCCents, Open, Timesta
|
||||
|
||||
/// Parse OHLC value from a JSON array element at given index
|
||||
pub fn parse_cents(array: &[serde_json::Value], index: usize) -> Cents {
|
||||
Cents::from(Dollars::from(
|
||||
array
|
||||
.get(index)
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| s.parse::<f64>().ok())
|
||||
.unwrap_or(0.0),
|
||||
))
|
||||
let value = array
|
||||
.get(index)
|
||||
.and_then(|v| v.as_str())
|
||||
.and_then(|s| s.parse::<f64>().ok())
|
||||
.unwrap_or_else(|| {
|
||||
tracing::warn!(
|
||||
"Failed to parse price at index {index}: {:?}",
|
||||
array.get(index)
|
||||
);
|
||||
0.0
|
||||
});
|
||||
Cents::from(Dollars::from(value))
|
||||
}
|
||||
|
||||
/// Build OHLCCentsUnsigned from array indices 1-4 (open, high, low, close)
|
||||
|
||||
@@ -186,6 +186,7 @@ impl Query {
|
||||
.unwrap_or(total),
|
||||
};
|
||||
|
||||
let end = end.max(start);
|
||||
let weight = Self::weight(&vecs, Some(start as i64), Some(end as i64));
|
||||
if weight > max_weight {
|
||||
return Err(Error::WeightExceeded {
|
||||
|
||||
@@ -20,7 +20,7 @@ use crossbeam::channel::bounded;
|
||||
use derive_more::Deref;
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
use rayon::prelude::*;
|
||||
use tracing::error;
|
||||
use tracing::{error, warn};
|
||||
|
||||
mod blk_index_to_blk_path;
|
||||
mod decode;
|
||||
@@ -169,6 +169,10 @@ impl ReaderInner {
|
||||
};
|
||||
i += offset;
|
||||
|
||||
if i + 4 > blk_bytes.len() {
|
||||
warn!("Truncated blk file {blk_index}: not enough bytes for block length at offset {i}");
|
||||
break;
|
||||
}
|
||||
let len = u32::from_le_bytes(
|
||||
xor_i
|
||||
.bytes(&mut blk_bytes[i..(i + 4)], xor_bytes)
|
||||
@@ -177,6 +181,10 @@ impl ReaderInner {
|
||||
) as usize;
|
||||
i += 4;
|
||||
|
||||
if i + len > blk_bytes.len() {
|
||||
warn!("Truncated blk file {blk_index}: block at offset {} claims {len} bytes but only {} remain", i - 4, blk_bytes.len() - i);
|
||||
break;
|
||||
}
|
||||
let position = BlkPosition::new(blk_index, i as u32);
|
||||
let metadata = BlkMetadata::new(position, len as u32);
|
||||
|
||||
@@ -208,12 +216,20 @@ impl ReaderInner {
|
||||
.into_iter()
|
||||
.par_bridge()
|
||||
.try_for_each(|(metadata, bytes, xor_i)| {
|
||||
if let Ok(Some(block)) = decode_block(
|
||||
let position = metadata.position();
|
||||
match decode_block(
|
||||
bytes, metadata, &client, xor_i, xor_bytes, start, end, start_time,
|
||||
end_time,
|
||||
) && send_block.send(block).is_err()
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
) {
|
||||
Ok(Some(block)) => {
|
||||
if send_block.send(block).is_err() {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
Ok(None) => {} // Block filtered out (outside range, unconfirmed)
|
||||
Err(e) => {
|
||||
warn!("Failed to decode block at {position}: {e}");
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
@@ -247,7 +263,7 @@ impl ReaderInner {
|
||||
"Chain discontinuity detected at height {}: expected prev_hash {}, got {}. Stopping iteration.",
|
||||
*block.height(),
|
||||
expected_prev,
|
||||
block.hash()
|
||||
block.header.prev_blockhash
|
||||
);
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ impl std::fmt::Display for Bitcoin {
|
||||
impl Formattable for Bitcoin {
|
||||
#[inline(always)]
|
||||
fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
if !self.0.is_nan() {
|
||||
if self.0.is_finite() {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
}
|
||||
@@ -152,10 +152,10 @@ impl Formattable for Bitcoin {
|
||||
|
||||
#[inline(always)]
|
||||
fn fmt_json(&self, buf: &mut Vec<u8>) {
|
||||
if self.0.is_nan() {
|
||||
buf.extend_from_slice(b"null");
|
||||
} else {
|
||||
if self.0.is_finite() {
|
||||
self.write_to(buf);
|
||||
} else {
|
||||
buf.extend_from_slice(b"null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@ pub type CostBasisFormatted = BTreeMap<Dollars, f64>;
|
||||
impl CostBasisDistribution {
|
||||
/// Deserialize from the pco-compressed format, returning remaining bytes.
|
||||
pub fn deserialize_with_rest(data: &[u8]) -> Result<(Self, &[u8])> {
|
||||
if data.len() < 24 {
|
||||
return Err(brk_error::Error::Deserialization(format!(
|
||||
"CostBasisDistribution: data too short ({} bytes, need >= 24)",
|
||||
data.len()
|
||||
)));
|
||||
}
|
||||
let entry_count = usize::from_bytes(&data[0..8])?;
|
||||
let keys_len = usize::from_bytes(&data[8..16])?;
|
||||
let values_len = usize::from_bytes(&data[16..24])?;
|
||||
@@ -35,6 +41,13 @@ impl CostBasisDistribution {
|
||||
let values_start = keys_start + keys_len;
|
||||
let rest_start = values_start + values_len;
|
||||
|
||||
if data.len() < rest_start {
|
||||
return Err(brk_error::Error::Deserialization(format!(
|
||||
"CostBasisDistribution: data too short ({} bytes, need >= {})",
|
||||
data.len(), rest_start
|
||||
)));
|
||||
}
|
||||
|
||||
let keys: Vec<u32> = simple_decompress(&data[keys_start..values_start])?;
|
||||
let values: Vec<u64> = simple_decompress(&data[values_start..rest_start])?;
|
||||
|
||||
@@ -44,7 +57,7 @@ impl CostBasisDistribution {
|
||||
.map(|(k, v)| (CentsCompact::new(k), Sats::from(v)))
|
||||
.collect();
|
||||
|
||||
assert_eq!(map.len(), entry_count);
|
||||
debug_assert_eq!(map.len(), entry_count);
|
||||
|
||||
Ok((Self { map }, &data[rest_start..]))
|
||||
}
|
||||
|
||||
@@ -213,6 +213,10 @@ impl<'de> Deserialize<'de> for Date {
|
||||
.parse()
|
||||
.map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &self))?;
|
||||
|
||||
if !(1..=12).contains(&month) || !(1..=31).contains(&day) {
|
||||
return Err(E::invalid_value(serde::de::Unexpected::Str(v), &self));
|
||||
}
|
||||
|
||||
Ok(Date::new(year, month, day))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,10 +196,11 @@ impl Div<f64> for Dollars {
|
||||
impl Div<Bitcoin> for Dollars {
|
||||
type Output = Self;
|
||||
fn div(self, rhs: Bitcoin) -> Self::Output {
|
||||
if self.is_nan() {
|
||||
self
|
||||
let rhs = f64::from(rhs);
|
||||
if self.is_nan() || rhs == 0.0 {
|
||||
Dollars::NAN
|
||||
} else {
|
||||
Self(f64::from(self) / f64::from(rhs))
|
||||
Self(f64::from(self) / rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +436,7 @@ impl std::fmt::Display for Dollars {
|
||||
impl Formattable for Dollars {
|
||||
#[inline(always)]
|
||||
fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
if !self.0.is_nan() {
|
||||
if self.0.is_finite() {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
}
|
||||
@@ -443,10 +444,10 @@ impl Formattable for Dollars {
|
||||
|
||||
#[inline(always)]
|
||||
fn fmt_json(&self, buf: &mut Vec<u8>) {
|
||||
if self.0.is_nan() {
|
||||
buf.extend_from_slice(b"null");
|
||||
} else {
|
||||
if self.0.is_finite() {
|
||||
self.write_to(buf);
|
||||
} else {
|
||||
buf.extend_from_slice(b"null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,18 @@ impl std::fmt::Display for FeeRate {
|
||||
impl Formattable for FeeRate {
|
||||
#[inline(always)]
|
||||
fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
if self.0.is_finite() {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn fmt_json(&self, buf: &mut Vec<u8>) {
|
||||
if self.0.is_finite() {
|
||||
self.write_to(buf);
|
||||
} else {
|
||||
buf.extend_from_slice(b"null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ impl<'de> Deserialize<'de> for Metrics {
|
||||
} else if let Some(vec) = value.as_array() {
|
||||
if vec.len() <= MAX_VECS {
|
||||
Ok(Self(
|
||||
sanitize(vec.iter().map(|s| s.as_str().unwrap().to_string()))
|
||||
sanitize(vec.iter().filter_map(|s| s.as_str().map(String::from)))
|
||||
.into_iter()
|
||||
.map(Metric::from)
|
||||
.collect(),
|
||||
|
||||
@@ -29,10 +29,14 @@ impl Pagination {
|
||||
}
|
||||
|
||||
pub fn start(&self, len: usize) -> usize {
|
||||
(self.page() * self.per_page()).clamp(0, len)
|
||||
self.page()
|
||||
.saturating_mul(self.per_page())
|
||||
.min(len)
|
||||
}
|
||||
|
||||
pub fn end(&self, len: usize) -> usize {
|
||||
((self.page() + 1) * self.per_page()).clamp(0, len)
|
||||
(self.page().saturating_add(1))
|
||||
.saturating_mul(self.per_page())
|
||||
.min(len)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,13 @@ impl<'de> Deserialize<'de> for RangeIndex {
|
||||
return Ok(Self::Date(date));
|
||||
}
|
||||
if let Ok(ts) = s.parse::<jiff::Timestamp>() {
|
||||
return Ok(Self::Timestamp(Timestamp::new(ts.as_second() as u32)));
|
||||
let secs = ts.as_second();
|
||||
if secs < 0 || secs > u32::MAX as i64 {
|
||||
return Err(serde::de::Error::custom(format!(
|
||||
"timestamp out of range: {s}"
|
||||
)));
|
||||
}
|
||||
return Ok(Self::Timestamp(Timestamp::new(secs as u32)));
|
||||
}
|
||||
Err(serde::de::Error::custom(format!(
|
||||
"expected integer, YYYY-MM-DD, or ISO 8601 timestamp: {s}"
|
||||
|
||||
@@ -189,7 +189,7 @@ impl std::fmt::Display for SatsFract {
|
||||
impl Formattable for SatsFract {
|
||||
#[inline(always)]
|
||||
fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
if !self.0.is_nan() {
|
||||
if self.0.is_finite() {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
}
|
||||
@@ -197,10 +197,10 @@ impl Formattable for SatsFract {
|
||||
|
||||
#[inline(always)]
|
||||
fn fmt_json(&self, buf: &mut Vec<u8>) {
|
||||
if self.0.is_nan() {
|
||||
buf.extend_from_slice(b"null");
|
||||
} else {
|
||||
if self.0.is_finite() {
|
||||
self.write_to(buf);
|
||||
} else {
|
||||
buf.extend_from_slice(b"null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use derive_more::Deref;
|
||||
use schemars::JsonSchema;
|
||||
use schemars::{JsonSchema, SchemaGenerator};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::{Formattable, Pco, PrintableIndex};
|
||||
|
||||
@@ -17,10 +17,19 @@ use vecdb::{Formattable, Pco, PrintableIndex};
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Pco,
|
||||
JsonSchema,
|
||||
)]
|
||||
pub struct StoredBool(u8);
|
||||
|
||||
impl JsonSchema for StoredBool {
|
||||
fn schema_name() -> std::borrow::Cow<'static, str> {
|
||||
"StoredBool".into()
|
||||
}
|
||||
|
||||
fn json_schema(generator: &mut SchemaGenerator) -> schemars::Schema {
|
||||
bool::json_schema(generator)
|
||||
}
|
||||
}
|
||||
|
||||
impl StoredBool {
|
||||
pub const FALSE: Self = Self(0);
|
||||
pub const TRUE: Self = Self(1);
|
||||
|
||||
@@ -267,7 +267,7 @@ impl std::fmt::Display for StoredF32 {
|
||||
impl Formattable for StoredF32 {
|
||||
#[inline(always)]
|
||||
fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
if !self.0.is_nan() {
|
||||
if self.0.is_finite() {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
}
|
||||
@@ -275,10 +275,10 @@ impl Formattable for StoredF32 {
|
||||
|
||||
#[inline(always)]
|
||||
fn fmt_json(&self, buf: &mut Vec<u8>) {
|
||||
if self.0.is_nan() {
|
||||
buf.extend_from_slice(b"null");
|
||||
} else {
|
||||
if self.0.is_finite() {
|
||||
self.write_to(buf);
|
||||
} else {
|
||||
buf.extend_from_slice(b"null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ impl std::fmt::Display for StoredF64 {
|
||||
impl Formattable for StoredF64 {
|
||||
#[inline(always)]
|
||||
fn write_to(&self, buf: &mut Vec<u8>) {
|
||||
if !self.0.is_nan() {
|
||||
if self.0.is_finite() {
|
||||
let mut b = ryu::Buffer::new();
|
||||
buf.extend_from_slice(b.format(self.0).as_bytes());
|
||||
}
|
||||
@@ -255,10 +255,10 @@ impl Formattable for StoredF64 {
|
||||
|
||||
#[inline(always)]
|
||||
fn fmt_json(&self, buf: &mut Vec<u8>) {
|
||||
if self.0.is_nan() {
|
||||
buf.extend_from_slice(b"null");
|
||||
} else {
|
||||
if self.0.is_finite() {
|
||||
self.write_to(buf);
|
||||
} else {
|
||||
buf.extend_from_slice(b"null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
/**
|
||||
* Closing price value for a time period
|
||||
*
|
||||
* @typedef {Cents} Close
|
||||
* @typedef {Dollars} Close
|
||||
*/
|
||||
/**
|
||||
* Cohort identifier for cost basis distribution.
|
||||
@@ -417,7 +417,7 @@
|
||||
/**
|
||||
* Highest price value for a time period
|
||||
*
|
||||
* @typedef {Cents} High
|
||||
* @typedef {Dollars} High
|
||||
*/
|
||||
/** @typedef {number} Hour1 */
|
||||
/** @typedef {number} Hour12 */
|
||||
@@ -443,7 +443,7 @@
|
||||
/**
|
||||
* Lowest price value for a time period
|
||||
*
|
||||
* @typedef {Cents} Low
|
||||
* @typedef {Dollars} Low
|
||||
*/
|
||||
/**
|
||||
* Block info in a mempool.space like format for fee estimation.
|
||||
@@ -566,7 +566,7 @@
|
||||
/**
|
||||
* Opening price value for a time period
|
||||
*
|
||||
* @typedef {Cents} Open
|
||||
* @typedef {Dollars} Open
|
||||
*/
|
||||
/** @typedef {number} OutPoint */
|
||||
/**
|
||||
@@ -738,11 +738,7 @@
|
||||
* @property {Metric} q - Search query string
|
||||
* @property {Limit=} limit - Maximum number of results
|
||||
*/
|
||||
/**
|
||||
* Fixed-size boolean value optimized for on-disk storage (stored as u8)
|
||||
*
|
||||
* @typedef {number} StoredBool
|
||||
*/
|
||||
/** @typedef {boolean} StoredBool */
|
||||
/**
|
||||
* Stored 32-bit floating point value
|
||||
*
|
||||
@@ -2326,7 +2322,7 @@ function create_1m1w1y2y4yAllPattern(client, acc) {
|
||||
* @property {CentsUsdPattern} base
|
||||
* @property {RelPattern} change1m
|
||||
* @property {CentsUsdPattern} cumulative
|
||||
* @property {ChangeRatePattern2} delta
|
||||
* @property {AbsoluteRatePattern2} delta
|
||||
* @property {BpsPercentRatioPattern} relToRcap
|
||||
* @property {_1m1w1y24hPattern3} sum
|
||||
*/
|
||||
@@ -2342,7 +2338,7 @@ function createBaseChangeCumulativeDeltaRelSumPattern(client, acc) {
|
||||
base: createCentsUsdPattern(client, _m(acc, 'realized_pnl')),
|
||||
change1m: createRelPattern(client, _m(acc, 'pnl_change_1m_rel_to')),
|
||||
cumulative: createCentsUsdPattern(client, _m(acc, 'realized_pnl_cumulative')),
|
||||
delta: createChangeRatePattern2(client, _m(acc, 'realized_pnl_delta')),
|
||||
delta: createAbsoluteRatePattern2(client, _m(acc, 'realized_pnl_delta')),
|
||||
relToRcap: createBpsPercentRatioPattern(client, _m(acc, 'realized_pnl_rel_to_rcap')),
|
||||
sum: create_1m1w1y24hPattern3(client, _m(acc, 'realized_pnl_sum')),
|
||||
};
|
||||
@@ -2458,7 +2454,7 @@ function createCapLossMvrvPriceProfitSoprPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} DeltaHalfInRelTotalPattern
|
||||
* @property {ChangeRatePattern} delta
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {BtcCentsSatsUsdPattern} half
|
||||
* @property {BtcCentsRelSatsUsdPattern} inLoss
|
||||
* @property {BtcCentsRelSatsUsdPattern} inProfit
|
||||
@@ -2474,7 +2470,7 @@ function createCapLossMvrvPriceProfitSoprPattern(client, acc) {
|
||||
*/
|
||||
function createDeltaHalfInRelTotalPattern(client, acc) {
|
||||
return {
|
||||
delta: createChangeRatePattern(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
|
||||
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
|
||||
inLoss: createBtcCentsRelSatsUsdPattern(client, _m(acc, 'in_loss')),
|
||||
inProfit: createBtcCentsRelSatsUsdPattern(client, _m(acc, 'in_profit')),
|
||||
@@ -2485,7 +2481,7 @@ function createDeltaHalfInRelTotalPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} DeltaHalfInRelTotalPattern2
|
||||
* @property {ChangeRatePattern} delta
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {BtcCentsSatsUsdPattern} half
|
||||
* @property {BtcCentsRelSatsUsdPattern3} inLoss
|
||||
* @property {BtcCentsRelSatsUsdPattern3} inProfit
|
||||
@@ -2501,7 +2497,7 @@ function createDeltaHalfInRelTotalPattern(client, acc) {
|
||||
*/
|
||||
function createDeltaHalfInRelTotalPattern2(client, acc) {
|
||||
return {
|
||||
delta: createChangeRatePattern(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
|
||||
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
|
||||
inLoss: createBtcCentsRelSatsUsdPattern3(client, _m(acc, 'in_loss')),
|
||||
inProfit: createBtcCentsRelSatsUsdPattern3(client, _m(acc, 'in_profit')),
|
||||
@@ -2689,7 +2685,7 @@ function createBtcCentsRelSatsUsdPattern2(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} DeltaHalfInTotalPattern2
|
||||
* @property {ChangeRatePattern} delta
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {BtcCentsSatsUsdPattern} half
|
||||
* @property {BtcCentsSatsUsdPattern} inLoss
|
||||
* @property {BtcCentsSatsUsdPattern} inProfit
|
||||
@@ -2704,7 +2700,7 @@ function createBtcCentsRelSatsUsdPattern2(client, acc) {
|
||||
*/
|
||||
function createDeltaHalfInTotalPattern2(client, acc) {
|
||||
return {
|
||||
delta: createChangeRatePattern(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
|
||||
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
|
||||
inLoss: createBtcCentsSatsUsdPattern(client, _m(acc, 'in_loss')),
|
||||
inProfit: createBtcCentsSatsUsdPattern(client, _m(acc, 'in_profit')),
|
||||
@@ -3002,7 +2998,7 @@ function createAdjustedRatioValuePattern(client, acc) {
|
||||
* @typedef {Object} BaseCumulativeDeltaSumPattern
|
||||
* @property {CentsUsdPattern} base
|
||||
* @property {CentsUsdPattern} cumulative
|
||||
* @property {ChangeRatePattern2} delta
|
||||
* @property {AbsoluteRatePattern2} delta
|
||||
* @property {_1m1w1y24hPattern3} sum
|
||||
*/
|
||||
|
||||
@@ -3016,7 +3012,7 @@ function createBaseCumulativeDeltaSumPattern(client, acc) {
|
||||
return {
|
||||
base: createCentsUsdPattern(client, acc),
|
||||
cumulative: createCentsUsdPattern(client, _m(acc, 'cumulative')),
|
||||
delta: createChangeRatePattern2(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern2(client, _m(acc, 'delta')),
|
||||
sum: create_1m1w1y24hPattern3(client, _m(acc, 'sum')),
|
||||
};
|
||||
}
|
||||
@@ -3093,7 +3089,7 @@ function createBtcCentsSatsUsdPattern(client, acc) {
|
||||
/**
|
||||
* @typedef {Object} CentsDeltaRelUsdPattern
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {ChangeRatePattern2} delta
|
||||
* @property {AbsoluteRatePattern2} delta
|
||||
* @property {BpsPercentRatioPattern4} relToOwnMcap
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
*/
|
||||
@@ -3107,7 +3103,7 @@ function createBtcCentsSatsUsdPattern(client, acc) {
|
||||
function createCentsDeltaRelUsdPattern(client, acc) {
|
||||
return {
|
||||
cents: createMetricPattern1(client, _m(acc, 'cents')),
|
||||
delta: createChangeRatePattern2(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern2(client, _m(acc, 'delta')),
|
||||
relToOwnMcap: createBpsPercentRatioPattern4(client, _m(acc, 'rel_to_own_mcap')),
|
||||
usd: createMetricPattern1(client, acc),
|
||||
};
|
||||
@@ -3487,7 +3483,7 @@ function createCentsSatsUsdPattern3(client, acc) {
|
||||
/**
|
||||
* @typedef {Object} CentsDeltaUsdPattern
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {ChangeRatePattern2} delta
|
||||
* @property {AbsoluteRatePattern2} delta
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
*/
|
||||
|
||||
@@ -3500,7 +3496,7 @@ function createCentsSatsUsdPattern3(client, acc) {
|
||||
function createCentsDeltaUsdPattern(client, acc) {
|
||||
return {
|
||||
cents: createMetricPattern1(client, _m(acc, 'cents')),
|
||||
delta: createChangeRatePattern2(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern2(client, _m(acc, 'delta')),
|
||||
usd: createMetricPattern1(client, acc),
|
||||
};
|
||||
}
|
||||
@@ -3528,7 +3524,7 @@ function createCentsSatsUsdPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} DeltaHalfTotalPattern
|
||||
* @property {ChangeRatePattern} delta
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {BtcCentsSatsUsdPattern} half
|
||||
* @property {BtcCentsSatsUsdPattern} total
|
||||
*/
|
||||
@@ -3541,7 +3537,7 @@ function createCentsSatsUsdPattern(client, acc) {
|
||||
*/
|
||||
function createDeltaHalfTotalPattern(client, acc) {
|
||||
return {
|
||||
delta: createChangeRatePattern(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
|
||||
half: createBtcCentsSatsUsdPattern(client, _m(acc, 'half')),
|
||||
total: createBtcCentsSatsUsdPattern(client, acc),
|
||||
};
|
||||
@@ -3698,6 +3694,44 @@ function createBaseCumulativeSumPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AbsoluteRatePattern
|
||||
* @property {_1m1w1y24hPattern<StoredI64>} absolute
|
||||
* @property {_1m1w1y24hPattern2} rate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a AbsoluteRatePattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {AbsoluteRatePattern}
|
||||
*/
|
||||
function createAbsoluteRatePattern(client, acc) {
|
||||
return {
|
||||
absolute: create_1m1w1y24hPattern(client, acc),
|
||||
rate: create_1m1w1y24hPattern2(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AbsoluteRatePattern2
|
||||
* @property {_1m1w1y24hPattern3} absolute
|
||||
* @property {_1m1w1y24hPattern2} rate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a AbsoluteRatePattern2 pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {AbsoluteRatePattern2}
|
||||
*/
|
||||
function createAbsoluteRatePattern2(client, acc) {
|
||||
return {
|
||||
absolute: create_1m1w1y24hPattern3(client, acc),
|
||||
rate: create_1m1w1y24hPattern2(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BlocksDominancePattern
|
||||
* @property {BaseCumulativeSumPattern2} blocksMined
|
||||
@@ -3793,44 +3827,6 @@ function createCentsUsdPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ChangeRatePattern
|
||||
* @property {_1m1w1y24hPattern<StoredI64>} change
|
||||
* @property {_1m1w1y24hPattern2} rate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ChangeRatePattern pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ChangeRatePattern}
|
||||
*/
|
||||
function createChangeRatePattern(client, acc) {
|
||||
return {
|
||||
change: create_1m1w1y24hPattern(client, acc),
|
||||
rate: create_1m1w1y24hPattern2(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ChangeRatePattern2
|
||||
* @property {_1m1w1y24hPattern3} change
|
||||
* @property {_1m1w1y24hPattern2} rate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a ChangeRatePattern2 pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated metric name
|
||||
* @returns {ChangeRatePattern2}
|
||||
*/
|
||||
function createChangeRatePattern2(client, acc) {
|
||||
return {
|
||||
change: create_1m1w1y24hPattern3(client, acc),
|
||||
rate: create_1m1w1y24hPattern2(client, acc),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CoindaysSentPattern
|
||||
* @property {BaseCumulativeSumPattern<StoredF64>} coindaysDestroyed
|
||||
@@ -3852,7 +3848,7 @@ function createCoindaysSentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} DeltaInnerPattern
|
||||
* @property {ChangeRatePattern} delta
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {MetricPattern1<StoredU64>} inner
|
||||
*/
|
||||
|
||||
@@ -3864,7 +3860,7 @@ function createCoindaysSentPattern(client, acc) {
|
||||
*/
|
||||
function createDeltaInnerPattern(client, acc) {
|
||||
return {
|
||||
delta: createChangeRatePattern(client, _m(acc, 'delta')),
|
||||
delta: createAbsoluteRatePattern(client, _m(acc, 'delta')),
|
||||
inner: createMetricPattern1(client, acc),
|
||||
};
|
||||
}
|
||||
@@ -4415,15 +4411,15 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Addresses_Delta
|
||||
* @property {ChangeRatePattern} all
|
||||
* @property {ChangeRatePattern} p2pk65
|
||||
* @property {ChangeRatePattern} p2pk33
|
||||
* @property {ChangeRatePattern} p2pkh
|
||||
* @property {ChangeRatePattern} p2sh
|
||||
* @property {ChangeRatePattern} p2wpkh
|
||||
* @property {ChangeRatePattern} p2wsh
|
||||
* @property {ChangeRatePattern} p2tr
|
||||
* @property {ChangeRatePattern} p2a
|
||||
* @property {AbsoluteRatePattern} all
|
||||
* @property {AbsoluteRatePattern} p2pk65
|
||||
* @property {AbsoluteRatePattern} p2pk33
|
||||
* @property {AbsoluteRatePattern} p2pkh
|
||||
* @property {AbsoluteRatePattern} p2sh
|
||||
* @property {AbsoluteRatePattern} p2wpkh
|
||||
* @property {AbsoluteRatePattern} p2wsh
|
||||
* @property {AbsoluteRatePattern} p2tr
|
||||
* @property {AbsoluteRatePattern} p2a
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -4627,8 +4623,8 @@ function createUnspentPattern(client, acc) {
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cointime_Adjusted
|
||||
* @property {BpsPercentRatioPattern} inflationRate
|
||||
* @property {MetricPattern1<StoredF64>} txVelocityBtc
|
||||
* @property {MetricPattern1<StoredF64>} txVelocityUsd
|
||||
* @property {MetricPattern1<StoredF64>} txVelocityNative
|
||||
* @property {MetricPattern1<StoredF64>} txVelocityFiat
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -5051,8 +5047,8 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Market_MovingAverage_Sma_200d
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<SatsFract>} sats
|
||||
* @property {MetricPattern1<BasisPoints32>} bps
|
||||
* @property {MetricPattern1<StoredF32>} ratio
|
||||
@@ -5062,22 +5058,22 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Market_MovingAverage_Sma_200d_X24
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<SatsFract>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Market_MovingAverage_Sma_200d_X08
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<SatsFract>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Market_MovingAverage_Sma_350d
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<SatsFract>} sats
|
||||
* @property {MetricPattern1<BasisPoints32>} bps
|
||||
* @property {MetricPattern1<StoredF32>} ratio
|
||||
@@ -5086,8 +5082,8 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Market_MovingAverage_Sma_350d_X2
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<SatsFract>} sats
|
||||
*/
|
||||
|
||||
@@ -5487,15 +5483,15 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Prices_Ohlc
|
||||
* @property {MetricPattern2<OHLCCents>} cents
|
||||
* @property {MetricPattern2<OHLCDollars>} usd
|
||||
* @property {MetricPattern2<OHLCCents>} cents
|
||||
* @property {MetricPattern2<OHLCSats>} sats
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Prices_Spot
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<Cents>} cents
|
||||
* @property {MetricPattern1<Sats>} sats
|
||||
*/
|
||||
|
||||
@@ -5561,7 +5557,7 @@ function createUnspentPattern(client, acc) {
|
||||
* @typedef {Object} MetricsTree_Cohorts_Utxo_All_Supply
|
||||
* @property {BtcCentsSatsUsdPattern} total
|
||||
* @property {BtcCentsSatsUsdPattern} half
|
||||
* @property {ChangeRatePattern} delta
|
||||
* @property {AbsoluteRatePattern} delta
|
||||
* @property {BtcCentsRelSatsUsdPattern2} inProfit
|
||||
* @property {BtcCentsRelSatsUsdPattern2} inLoss
|
||||
*/
|
||||
@@ -5598,8 +5594,8 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} MetricsTree_Cohorts_Utxo_All_Unrealized_NetPnl
|
||||
* @property {MetricPattern1<CentsSigned>} cents
|
||||
* @property {MetricPattern1<Dollars>} usd
|
||||
* @property {MetricPattern1<CentsSigned>} cents
|
||||
* @property {BpsPercentRatioPattern} relToOwnGross
|
||||
*/
|
||||
|
||||
@@ -7161,15 +7157,15 @@ class BrkClient extends BrkClientBase {
|
||||
p2a: createBaseCumulativeSumPattern(this, 'p2a_new_address_count'),
|
||||
},
|
||||
delta: {
|
||||
all: createChangeRatePattern(this, 'address_count'),
|
||||
p2pk65: createChangeRatePattern(this, 'p2pk65_address_count'),
|
||||
p2pk33: createChangeRatePattern(this, 'p2pk33_address_count'),
|
||||
p2pkh: createChangeRatePattern(this, 'p2pkh_address_count'),
|
||||
p2sh: createChangeRatePattern(this, 'p2sh_address_count'),
|
||||
p2wpkh: createChangeRatePattern(this, 'p2wpkh_address_count'),
|
||||
p2wsh: createChangeRatePattern(this, 'p2wsh_address_count'),
|
||||
p2tr: createChangeRatePattern(this, 'p2tr_address_count'),
|
||||
p2a: createChangeRatePattern(this, 'p2a_address_count'),
|
||||
all: createAbsoluteRatePattern(this, 'address_count'),
|
||||
p2pk65: createAbsoluteRatePattern(this, 'p2pk65_address_count'),
|
||||
p2pk33: createAbsoluteRatePattern(this, 'p2pk33_address_count'),
|
||||
p2pkh: createAbsoluteRatePattern(this, 'p2pkh_address_count'),
|
||||
p2sh: createAbsoluteRatePattern(this, 'p2sh_address_count'),
|
||||
p2wpkh: createAbsoluteRatePattern(this, 'p2wpkh_address_count'),
|
||||
p2wsh: createAbsoluteRatePattern(this, 'p2wsh_address_count'),
|
||||
p2tr: createAbsoluteRatePattern(this, 'p2tr_address_count'),
|
||||
p2a: createAbsoluteRatePattern(this, 'p2a_address_count'),
|
||||
},
|
||||
},
|
||||
scripts: {
|
||||
@@ -7299,8 +7295,8 @@ class BrkClient extends BrkClientBase {
|
||||
},
|
||||
adjusted: {
|
||||
inflationRate: createBpsPercentRatioPattern(this, 'cointime_adj_inflation_rate'),
|
||||
txVelocityBtc: createMetricPattern1(this, 'cointime_adj_tx_velocity_btc'),
|
||||
txVelocityUsd: createMetricPattern1(this, 'cointime_adj_tx_velocity_usd'),
|
||||
txVelocityNative: createMetricPattern1(this, 'cointime_adj_tx_velocity'),
|
||||
txVelocityFiat: createMetricPattern1(this, 'cointime_adj_tx_velocity_fiat'),
|
||||
},
|
||||
reserveRisk: {
|
||||
value: createMetricPattern1(this, 'reserve_risk'),
|
||||
@@ -7569,31 +7565,31 @@ class BrkClient extends BrkClientBase {
|
||||
_111d: createBpsCentsRatioSatsUsdPattern(this, 'price_sma_111d'),
|
||||
_144d: createBpsCentsRatioSatsUsdPattern(this, 'price_sma_144d'),
|
||||
_200d: {
|
||||
cents: createMetricPattern1(this, 'price_sma_200d_cents'),
|
||||
usd: createMetricPattern1(this, 'price_sma_200d'),
|
||||
cents: createMetricPattern1(this, 'price_sma_200d_cents'),
|
||||
sats: createMetricPattern1(this, 'price_sma_200d_sats'),
|
||||
bps: createMetricPattern1(this, 'price_sma_200d_ratio_bps'),
|
||||
ratio: createMetricPattern1(this, 'price_sma_200d_ratio'),
|
||||
x24: {
|
||||
cents: createMetricPattern1(this, 'price_sma_200d_x2_4_cents'),
|
||||
usd: createMetricPattern1(this, 'price_sma_200d_x2_4_usd'),
|
||||
cents: createMetricPattern1(this, 'price_sma_200d_x2_4_cents'),
|
||||
sats: createMetricPattern1(this, 'price_sma_200d_x2_4_sats'),
|
||||
},
|
||||
x08: {
|
||||
cents: createMetricPattern1(this, 'price_sma_200d_x0_8_cents'),
|
||||
usd: createMetricPattern1(this, 'price_sma_200d_x0_8_usd'),
|
||||
cents: createMetricPattern1(this, 'price_sma_200d_x0_8_cents'),
|
||||
sats: createMetricPattern1(this, 'price_sma_200d_x0_8_sats'),
|
||||
},
|
||||
},
|
||||
_350d: {
|
||||
cents: createMetricPattern1(this, 'price_sma_350d_cents'),
|
||||
usd: createMetricPattern1(this, 'price_sma_350d'),
|
||||
cents: createMetricPattern1(this, 'price_sma_350d_cents'),
|
||||
sats: createMetricPattern1(this, 'price_sma_350d_sats'),
|
||||
bps: createMetricPattern1(this, 'price_sma_350d_ratio_bps'),
|
||||
ratio: createMetricPattern1(this, 'price_sma_350d_ratio'),
|
||||
x2: {
|
||||
cents: createMetricPattern1(this, 'price_sma_350d_x2_cents'),
|
||||
usd: createMetricPattern1(this, 'price_sma_350d_x2_usd'),
|
||||
cents: createMetricPattern1(this, 'price_sma_350d_x2_cents'),
|
||||
sats: createMetricPattern1(this, 'price_sma_350d_x2_sats'),
|
||||
},
|
||||
},
|
||||
@@ -7935,13 +7931,13 @@ class BrkClient extends BrkClientBase {
|
||||
close: createCentsSatsUsdPattern3(this, 'price_close'),
|
||||
},
|
||||
ohlc: {
|
||||
cents: createMetricPattern2(this, 'price_ohlc_cents'),
|
||||
usd: createMetricPattern2(this, 'price_ohlc'),
|
||||
cents: createMetricPattern2(this, 'price_ohlc_cents'),
|
||||
sats: createMetricPattern2(this, 'price_ohlc_sats'),
|
||||
},
|
||||
spot: {
|
||||
cents: createMetricPattern1(this, 'price_cents'),
|
||||
usd: createMetricPattern1(this, 'price'),
|
||||
cents: createMetricPattern1(this, 'price_cents'),
|
||||
sats: createMetricPattern1(this, 'price_sats'),
|
||||
},
|
||||
},
|
||||
@@ -7967,7 +7963,7 @@ class BrkClient extends BrkClientBase {
|
||||
supply: {
|
||||
total: createBtcCentsSatsUsdPattern(this, 'supply'),
|
||||
half: createBtcCentsSatsUsdPattern(this, 'supply_half'),
|
||||
delta: createChangeRatePattern(this, 'supply_delta'),
|
||||
delta: createAbsoluteRatePattern(this, 'supply_delta'),
|
||||
inProfit: createBtcCentsRelSatsUsdPattern2(this, 'supply_in_profit'),
|
||||
inLoss: createBtcCentsRelSatsUsdPattern2(this, 'supply_in_loss'),
|
||||
},
|
||||
@@ -7993,8 +7989,8 @@ class BrkClient extends BrkClientBase {
|
||||
relToOwnGross: createBpsPercentRatioPattern3(this, 'unrealized_loss_rel_to_own_gross_pnl'),
|
||||
},
|
||||
netPnl: {
|
||||
cents: createMetricPattern1(this, 'net_unrealized_pnl_cents'),
|
||||
usd: createMetricPattern1(this, 'net_unrealized_pnl'),
|
||||
cents: createMetricPattern1(this, 'net_unrealized_pnl_cents'),
|
||||
relToOwnGross: createBpsPercentRatioPattern(this, 'net_unrealized_pnl_rel_to_own_gross_pnl'),
|
||||
},
|
||||
grossPnl: createCentsUsdPattern2(this, 'unrealized_gross_pnl'),
|
||||
|
||||
@@ -67,8 +67,10 @@ CentsSigned = int
|
||||
# Used for precise accumulation of investor cap values: Σ(price² × sats).
|
||||
# investor_price = investor_cap_raw / realized_cap_raw
|
||||
CentsSquaredSats = int
|
||||
# US Dollar amount as floating point
|
||||
Dollars = float
|
||||
# Closing price value for a time period
|
||||
Close = Cents
|
||||
Close = Dollars
|
||||
# Cohort identifier for cost basis distribution.
|
||||
Cohort = str
|
||||
# Bucket type for cost basis aggregation.
|
||||
@@ -88,8 +90,6 @@ Limit = int
|
||||
RangeIndex = Union[int, Date, Timestamp]
|
||||
Day1 = int
|
||||
Day3 = int
|
||||
# US Dollar amount as floating point
|
||||
Dollars = float
|
||||
EmptyAddressIndex = TypeIndex
|
||||
EmptyOutputIndex = TypeIndex
|
||||
Epoch = int
|
||||
@@ -100,12 +100,12 @@ Halving = int
|
||||
# Hex-encoded string
|
||||
Hex = str
|
||||
# Highest price value for a time period
|
||||
High = Cents
|
||||
High = Dollars
|
||||
Hour1 = int
|
||||
Hour12 = int
|
||||
Hour4 = int
|
||||
# Lowest price value for a time period
|
||||
Low = Cents
|
||||
Low = Dollars
|
||||
# Virtual size in vbytes (weight / 4, rounded up)
|
||||
VSize = int
|
||||
# Metric name
|
||||
@@ -124,7 +124,7 @@ Month1 = int
|
||||
Month3 = int
|
||||
Month6 = int
|
||||
# Opening price value for a time period
|
||||
Open = Cents
|
||||
Open = Dollars
|
||||
OpReturnIndex = TypeIndex
|
||||
OutPoint = int
|
||||
# Type (P2PKH, P2WPKH, P2SH, P2TR, etc.)
|
||||
@@ -166,8 +166,7 @@ SatsFract = float
|
||||
# Signed satoshis (i64) - for values that can be negative.
|
||||
# Used for changes, deltas, profit/loss calculations, etc.
|
||||
SatsSigned = int
|
||||
# Fixed-size boolean value optimized for on-disk storage (stored as u8)
|
||||
StoredBool = int
|
||||
StoredBool = bool
|
||||
# Stored 32-bit floating point value
|
||||
StoredF32 = float
|
||||
# Fixed-size 64-bit floating point value optimized for on-disk storage
|
||||
@@ -2454,7 +2453,7 @@ class BaseChangeCumulativeDeltaRelSumPattern:
|
||||
self.base: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'realized_pnl'))
|
||||
self.change_1m: RelPattern = RelPattern(client, _m(acc, 'pnl_change_1m_rel_to'))
|
||||
self.cumulative: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'realized_pnl_cumulative'))
|
||||
self.delta: ChangeRatePattern2 = ChangeRatePattern2(client, _m(acc, 'realized_pnl_delta'))
|
||||
self.delta: AbsoluteRatePattern2 = AbsoluteRatePattern2(client, _m(acc, 'realized_pnl_delta'))
|
||||
self.rel_to_rcap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'realized_pnl_rel_to_rcap'))
|
||||
self.sum: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'realized_pnl_sum'))
|
||||
|
||||
@@ -2511,7 +2510,7 @@ class DeltaHalfInRelTotalPattern:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.delta: ChangeRatePattern = ChangeRatePattern(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
|
||||
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
|
||||
self.in_loss: BtcCentsRelSatsUsdPattern = BtcCentsRelSatsUsdPattern(client, _m(acc, 'in_loss'))
|
||||
self.in_profit: BtcCentsRelSatsUsdPattern = BtcCentsRelSatsUsdPattern(client, _m(acc, 'in_profit'))
|
||||
@@ -2523,7 +2522,7 @@ class DeltaHalfInRelTotalPattern2:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.delta: ChangeRatePattern = ChangeRatePattern(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
|
||||
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
|
||||
self.in_loss: BtcCentsRelSatsUsdPattern3 = BtcCentsRelSatsUsdPattern3(client, _m(acc, 'in_loss'))
|
||||
self.in_profit: BtcCentsRelSatsUsdPattern3 = BtcCentsRelSatsUsdPattern3(client, _m(acc, 'in_profit'))
|
||||
@@ -2613,7 +2612,7 @@ class DeltaHalfInTotalPattern2:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.delta: ChangeRatePattern = ChangeRatePattern(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
|
||||
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
|
||||
self.in_loss: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'in_loss'))
|
||||
self.in_profit: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'in_profit'))
|
||||
@@ -2750,7 +2749,7 @@ class BaseCumulativeDeltaSumPattern:
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.base: CentsUsdPattern = CentsUsdPattern(client, acc)
|
||||
self.cumulative: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'cumulative'))
|
||||
self.delta: ChangeRatePattern2 = ChangeRatePattern2(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern2 = AbsoluteRatePattern2(client, _m(acc, 'delta'))
|
||||
self.sum: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'sum'))
|
||||
|
||||
class BaseCumulativeNegativeSumPattern:
|
||||
@@ -2789,7 +2788,7 @@ class CentsDeltaRelUsdPattern:
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, _m(acc, 'cents'))
|
||||
self.delta: ChangeRatePattern2 = ChangeRatePattern2(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern2 = AbsoluteRatePattern2(client, _m(acc, 'delta'))
|
||||
self.rel_to_own_mcap: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, _m(acc, 'rel_to_own_mcap'))
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, acc)
|
||||
|
||||
@@ -2958,7 +2957,7 @@ class CentsDeltaUsdPattern:
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, _m(acc, 'cents'))
|
||||
self.delta: ChangeRatePattern2 = ChangeRatePattern2(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern2 = AbsoluteRatePattern2(client, _m(acc, 'delta'))
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, acc)
|
||||
|
||||
class CentsSatsUsdPattern:
|
||||
@@ -2975,7 +2974,7 @@ class DeltaHalfTotalPattern:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.delta: ChangeRatePattern = ChangeRatePattern(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
|
||||
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'half'))
|
||||
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, acc)
|
||||
|
||||
@@ -3042,6 +3041,22 @@ class BaseCumulativeSumPattern(Generic[T]):
|
||||
self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative'))
|
||||
self.sum: _1m1w1y24hPattern[T] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
|
||||
|
||||
class AbsoluteRatePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.absolute: _1m1w1y24hPattern[StoredI64] = _1m1w1y24hPattern(client, acc)
|
||||
self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc)
|
||||
|
||||
class AbsoluteRatePattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.absolute: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, acc)
|
||||
self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc)
|
||||
|
||||
class BlocksDominancePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -3082,22 +3097,6 @@ class CentsUsdPattern:
|
||||
self.cents: MetricPattern1[CentsSigned] = MetricPattern1(client, acc)
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
|
||||
|
||||
class ChangeRatePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.change: _1m1w1y24hPattern[StoredI64] = _1m1w1y24hPattern(client, acc)
|
||||
self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc)
|
||||
|
||||
class ChangeRatePattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.change: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, acc)
|
||||
self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc)
|
||||
|
||||
class CoindaysSentPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -3111,7 +3110,7 @@ class DeltaInnerPattern:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated metric name."""
|
||||
self.delta: ChangeRatePattern = ChangeRatePattern(client, _m(acc, 'delta'))
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta'))
|
||||
self.inner: MetricPattern1[StoredU64] = MetricPattern1(client, acc)
|
||||
|
||||
class InPattern:
|
||||
@@ -3569,15 +3568,15 @@ class MetricsTree_Addresses_Delta:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.all: ChangeRatePattern = ChangeRatePattern(client, 'address_count')
|
||||
self.p2pk65: ChangeRatePattern = ChangeRatePattern(client, 'p2pk65_address_count')
|
||||
self.p2pk33: ChangeRatePattern = ChangeRatePattern(client, 'p2pk33_address_count')
|
||||
self.p2pkh: ChangeRatePattern = ChangeRatePattern(client, 'p2pkh_address_count')
|
||||
self.p2sh: ChangeRatePattern = ChangeRatePattern(client, 'p2sh_address_count')
|
||||
self.p2wpkh: ChangeRatePattern = ChangeRatePattern(client, 'p2wpkh_address_count')
|
||||
self.p2wsh: ChangeRatePattern = ChangeRatePattern(client, 'p2wsh_address_count')
|
||||
self.p2tr: ChangeRatePattern = ChangeRatePattern(client, 'p2tr_address_count')
|
||||
self.p2a: ChangeRatePattern = ChangeRatePattern(client, 'p2a_address_count')
|
||||
self.all: AbsoluteRatePattern = AbsoluteRatePattern(client, 'address_count')
|
||||
self.p2pk65: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2pk65_address_count')
|
||||
self.p2pk33: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2pk33_address_count')
|
||||
self.p2pkh: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2pkh_address_count')
|
||||
self.p2sh: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2sh_address_count')
|
||||
self.p2wpkh: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2wpkh_address_count')
|
||||
self.p2wsh: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2wsh_address_count')
|
||||
self.p2tr: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2tr_address_count')
|
||||
self.p2a: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2a_address_count')
|
||||
|
||||
class MetricsTree_Addresses:
|
||||
"""Metrics tree node."""
|
||||
@@ -3807,8 +3806,8 @@ class MetricsTree_Cointime_Adjusted:
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.inflation_rate: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'cointime_adj_inflation_rate')
|
||||
self.tx_velocity_btc: MetricPattern1[StoredF64] = MetricPattern1(client, 'cointime_adj_tx_velocity_btc')
|
||||
self.tx_velocity_usd: MetricPattern1[StoredF64] = MetricPattern1(client, 'cointime_adj_tx_velocity_usd')
|
||||
self.tx_velocity_native: MetricPattern1[StoredF64] = MetricPattern1(client, 'cointime_adj_tx_velocity')
|
||||
self.tx_velocity_fiat: MetricPattern1[StoredF64] = MetricPattern1(client, 'cointime_adj_tx_velocity_fiat')
|
||||
|
||||
class MetricsTree_Cointime_ReserveRisk:
|
||||
"""Metrics tree node."""
|
||||
@@ -4253,24 +4252,24 @@ class MetricsTree_Market_MovingAverage_Sma_200d_X24:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_200d_x2_4_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'price_sma_200d_x2_4_usd')
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_200d_x2_4_cents')
|
||||
self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, 'price_sma_200d_x2_4_sats')
|
||||
|
||||
class MetricsTree_Market_MovingAverage_Sma_200d_X08:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_200d_x0_8_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'price_sma_200d_x0_8_usd')
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_200d_x0_8_cents')
|
||||
self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, 'price_sma_200d_x0_8_sats')
|
||||
|
||||
class MetricsTree_Market_MovingAverage_Sma_200d:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_200d_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'price_sma_200d')
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_200d_cents')
|
||||
self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, 'price_sma_200d_sats')
|
||||
self.bps: MetricPattern1[BasisPoints32] = MetricPattern1(client, 'price_sma_200d_ratio_bps')
|
||||
self.ratio: MetricPattern1[StoredF32] = MetricPattern1(client, 'price_sma_200d_ratio')
|
||||
@@ -4281,16 +4280,16 @@ class MetricsTree_Market_MovingAverage_Sma_350d_X2:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_350d_x2_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'price_sma_350d_x2_usd')
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_350d_x2_cents')
|
||||
self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, 'price_sma_350d_x2_sats')
|
||||
|
||||
class MetricsTree_Market_MovingAverage_Sma_350d:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_350d_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'price_sma_350d')
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_sma_350d_cents')
|
||||
self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, 'price_sma_350d_sats')
|
||||
self.bps: MetricPattern1[BasisPoints32] = MetricPattern1(client, 'price_sma_350d_ratio_bps')
|
||||
self.ratio: MetricPattern1[StoredF32] = MetricPattern1(client, 'price_sma_350d_ratio')
|
||||
@@ -4749,16 +4748,16 @@ class MetricsTree_Prices_Ohlc:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern2[OHLCCents] = MetricPattern2(client, 'price_ohlc_cents')
|
||||
self.usd: MetricPattern2[OHLCDollars] = MetricPattern2(client, 'price_ohlc')
|
||||
self.cents: MetricPattern2[OHLCCents] = MetricPattern2(client, 'price_ohlc_cents')
|
||||
self.sats: MetricPattern2[OHLCSats] = MetricPattern2(client, 'price_ohlc_sats')
|
||||
|
||||
class MetricsTree_Prices_Spot:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'price')
|
||||
self.cents: MetricPattern1[Cents] = MetricPattern1(client, 'price_cents')
|
||||
self.sats: MetricPattern1[Sats] = MetricPattern1(client, 'price_sats')
|
||||
|
||||
class MetricsTree_Prices:
|
||||
@@ -4802,7 +4801,7 @@ class MetricsTree_Cohorts_Utxo_All_Supply:
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.total: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'supply')
|
||||
self.half: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'supply_half')
|
||||
self.delta: ChangeRatePattern = ChangeRatePattern(client, 'supply_delta')
|
||||
self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, 'supply_delta')
|
||||
self.in_profit: BtcCentsRelSatsUsdPattern2 = BtcCentsRelSatsUsdPattern2(client, 'supply_in_profit')
|
||||
self.in_loss: BtcCentsRelSatsUsdPattern2 = BtcCentsRelSatsUsdPattern2(client, 'supply_in_loss')
|
||||
|
||||
@@ -4831,8 +4830,8 @@ class MetricsTree_Cohorts_Utxo_All_Unrealized_NetPnl:
|
||||
"""Metrics tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.cents: MetricPattern1[CentsSigned] = MetricPattern1(client, 'net_unrealized_pnl_cents')
|
||||
self.usd: MetricPattern1[Dollars] = MetricPattern1(client, 'net_unrealized_pnl')
|
||||
self.cents: MetricPattern1[CentsSigned] = MetricPattern1(client, 'net_unrealized_pnl_cents')
|
||||
self.rel_to_own_gross: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'net_unrealized_pnl_rel_to_own_gross_pnl')
|
||||
|
||||
class MetricsTree_Cohorts_Utxo_All_Unrealized:
|
||||
|
||||
@@ -550,7 +550,7 @@ export function createCointimeSection() {
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: adjusted.txVelocityBtc,
|
||||
metric: adjusted.txVelocityNative,
|
||||
name: "Cointime-Adjusted",
|
||||
color: colors.adjusted,
|
||||
unit: Unit.ratio,
|
||||
@@ -568,7 +568,7 @@ export function createCointimeSection() {
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: adjusted.txVelocityUsd,
|
||||
metric: adjusted.txVelocityFiat,
|
||||
name: "Cointime-Adjusted",
|
||||
color: colors.vaulted,
|
||||
unit: Unit.ratio,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline, dotsBaseline, dots } from "../series.js";
|
||||
import { line, baseline, dotsBaseline, percentRatio, percentRatioDots } from "../series.js";
|
||||
import {
|
||||
mapCohortsWithAll,
|
||||
flatMapCohortsWithAll,
|
||||
@@ -35,31 +35,18 @@ function volumeAndCoinsTree(activity, color, title) {
|
||||
name: "Sum",
|
||||
title: title("Sent Volume"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: activity.sent.sum._24h,
|
||||
name: "24h",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: activity.sent.base,
|
||||
name: "Sum",
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({ metric: activity.sent.base, name: "Sum", color, unit: Unit.sats }),
|
||||
line({ metric: activity.sent.sum._24h, name: "24h", color: colors.time._24h, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: activity.sent.sum._1w, name: "1w", color: colors.time._1w, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: activity.sent.sum._1m, name: "1m", color: colors.time._1m, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: activity.sent.sum._1y, name: "1y", color: colors.time._1y, unit: Unit.sats, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Sent Volume (Total)"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: activity.sent.cumulative,
|
||||
name: "All-time",
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({ metric: activity.sent.cumulative, name: "All-time", color, unit: Unit.sats }),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -68,27 +55,21 @@ function volumeAndCoinsTree(activity, color, title) {
|
||||
name: "Coins Destroyed",
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
name: "Base",
|
||||
title: title("Coindays Destroyed"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: activity.coindaysDestroyed.sum._24h,
|
||||
name: "24h",
|
||||
color,
|
||||
unit: Unit.coindays,
|
||||
}),
|
||||
line({ metric: activity.coindaysDestroyed.base, name: "Base", color, unit: Unit.coindays }),
|
||||
line({ metric: activity.coindaysDestroyed.sum._24h, name: "24h", color: colors.time._24h, unit: Unit.coindays, defaultActive: false }),
|
||||
line({ metric: activity.coindaysDestroyed.sum._1w, name: "1w", color: colors.time._1w, unit: Unit.coindays, defaultActive: false }),
|
||||
line({ metric: activity.coindaysDestroyed.sum._1m, name: "1m", color: colors.time._1m, unit: Unit.coindays, defaultActive: false }),
|
||||
line({ metric: activity.coindaysDestroyed.sum._1y, name: "1y", color: colors.time._1y, unit: Unit.coindays, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Coindays Destroyed"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: activity.coindaysDestroyed.cumulative,
|
||||
name: "All-time",
|
||||
color,
|
||||
unit: Unit.coindays,
|
||||
}),
|
||||
line({ metric: activity.coindaysDestroyed.cumulative, name: "All-time", color, unit: Unit.coindays }),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -156,31 +137,31 @@ function singleSellSideRiskTree(sellSideRisk, title) {
|
||||
name: "Compare",
|
||||
title: title("Rolling Sell Side Risk"),
|
||||
bottom: [
|
||||
line({ metric: sellSideRisk._24h.ratio, name: "24h", color: colors.time._24h, unit: Unit.ratio }),
|
||||
line({ metric: sellSideRisk._1w.ratio, name: "7d", color: colors.time._1w, unit: Unit.ratio }),
|
||||
line({ metric: sellSideRisk._1m.ratio, name: "30d", color: colors.time._1m, unit: Unit.ratio }),
|
||||
line({ metric: sellSideRisk._1y.ratio, name: "1y", color: colors.time._1y, unit: Unit.ratio }),
|
||||
...percentRatioDots({ pattern: sellSideRisk._24h, name: "24h", color: colors.time._24h }),
|
||||
...percentRatio({ pattern: sellSideRisk._1w, name: "7d", color: colors.time._1w }),
|
||||
...percentRatio({ pattern: sellSideRisk._1m, name: "30d", color: colors.time._1m }),
|
||||
...percentRatio({ pattern: sellSideRisk._1y, name: "1y", color: colors.time._1y }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "24h",
|
||||
title: title("Sell Side Risk (24h)"),
|
||||
bottom: [dots({ metric: sellSideRisk._24h.ratio, name: "Raw", color: colors.bitcoin, unit: Unit.ratio })],
|
||||
bottom: percentRatioDots({ pattern: sellSideRisk._24h, name: "Raw", color: colors.bitcoin }),
|
||||
},
|
||||
{
|
||||
name: "7d",
|
||||
title: title("Sell Side Risk (7d)"),
|
||||
bottom: [line({ metric: sellSideRisk._1w.ratio, name: "Risk", unit: Unit.ratio })],
|
||||
bottom: percentRatio({ pattern: sellSideRisk._1w, name: "Risk" }),
|
||||
},
|
||||
{
|
||||
name: "30d",
|
||||
title: title("Sell Side Risk (30d)"),
|
||||
bottom: [line({ metric: sellSideRisk._1m.ratio, name: "Risk", unit: Unit.ratio })],
|
||||
bottom: percentRatio({ pattern: sellSideRisk._1m, name: "Risk" }),
|
||||
},
|
||||
{
|
||||
name: "1y",
|
||||
title: title("Sell Side Risk (1y)"),
|
||||
bottom: [line({ metric: sellSideRisk._1y.ratio, name: "Risk", unit: Unit.ratio })],
|
||||
bottom: percentRatio({ pattern: sellSideRisk._1y, name: "Risk" }),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -255,6 +236,14 @@ function singleRollingValueTree(valueCreated, valueDestroyed, title, prefix = ""
|
||||
line({ metric: valueDestroyed.sum._1y, name: "Destroyed", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title(`${prefix}Value Created & Destroyed (Total)`),
|
||||
bottom: [
|
||||
line({ metric: valueCreated.cumulative, name: "Created", color: colors.usd, unit: Unit.usd }),
|
||||
line({ metric: valueDestroyed.cumulative, name: "Destroyed", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ import { colors } from "../../utils/colors.js";
|
||||
import { entries } from "../../utils/array.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { priceLines } from "../constants.js";
|
||||
import { line, price } from "../series.js";
|
||||
import { mapCohortsWithAll } from "../shared.js";
|
||||
import { price, percentRatio } from "../series.js";
|
||||
import { mapCohortsWithAll, flatMapCohortsWithAll } from "../shared.js";
|
||||
|
||||
const ACTIVE_PCTS = new Set(["pct75", "pct50", "pct25"]);
|
||||
|
||||
@@ -129,11 +129,10 @@ function createSingleByCapitalSeries(cohort) {
|
||||
function createSingleSupplyDensitySeries(cohort) {
|
||||
const { tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.costBasis.supplyDensity.percent,
|
||||
...percentRatio({
|
||||
pattern: tree.costBasis.supplyDensity,
|
||||
name: "Supply Density",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
];
|
||||
@@ -269,12 +268,11 @@ export function createGroupedCostBasisSectionWithPercentiles({
|
||||
{
|
||||
name: "Supply Density",
|
||||
title: title("Cost Basis Supply Density"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.costBasis.supplyDensity.percent,
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({
|
||||
pattern: tree.costBasis.supplyDensity,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline } from "../series.js";
|
||||
import { ROLLING_WINDOWS, line, baseline, rollingWindowsTree, rollingPercentRatioTree, percentRatio } from "../series.js";
|
||||
import {
|
||||
satsBtcUsd,
|
||||
mapCohorts,
|
||||
@@ -74,23 +74,15 @@ function fullSupplySeries(supply) {
|
||||
|
||||
/**
|
||||
* % of Own Supply series (profit/loss relative to own supply)
|
||||
* @param {{ inProfit: { relToOwn: { percent: AnyMetricPattern } }, inLoss: { relToOwn: { percent: AnyMetricPattern } } }} supply
|
||||
* @param {{ inProfit: { relToOwn: { percent: AnyMetricPattern, ratio: AnyMetricPattern } }, inLoss: { relToOwn: { percent: AnyMetricPattern, ratio: AnyMetricPattern } } }} supply
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function ownSupplyPctSeries(supply) {
|
||||
return [
|
||||
line({
|
||||
metric: supply.inProfit.relToOwn.percent,
|
||||
name: "In Profit",
|
||||
color: colors.profit,
|
||||
unit: Unit.pctOwn,
|
||||
}),
|
||||
line({
|
||||
metric: supply.inLoss.relToOwn.percent,
|
||||
name: "In Loss",
|
||||
color: colors.loss,
|
||||
unit: Unit.pctOwn,
|
||||
}),
|
||||
line({ metric: supply.inProfit.relToOwn.percent, name: "In Profit", color: colors.profit, unit: Unit.pctOwn }),
|
||||
line({ metric: supply.inLoss.relToOwn.percent, name: "In Loss", color: colors.loss, unit: Unit.pctOwn }),
|
||||
line({ metric: supply.inProfit.relToOwn.ratio, name: "In Profit", color: colors.profit, unit: Unit.ratio }),
|
||||
line({ metric: supply.inLoss.relToOwn.ratio, name: "In Loss", color: colors.loss, unit: Unit.ratio }),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.pctOwn }),
|
||||
];
|
||||
}
|
||||
@@ -144,42 +136,58 @@ function groupedUtxoCountChart(list, all, title) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly (UtxoCohortObject | CohortWithoutRelative)[]} list
|
||||
* @param {CohortAll} all
|
||||
* @param {{ absolute: { _24h: AnyMetricPattern, _1w: AnyMetricPattern, _1m: AnyMetricPattern, _1y: AnyMetricPattern }, rate: { _24h: { percent: AnyMetricPattern, ratio: AnyMetricPattern }, _1w: { percent: AnyMetricPattern, ratio: AnyMetricPattern }, _1m: { percent: AnyMetricPattern, ratio: AnyMetricPattern }, _1y: { percent: AnyMetricPattern, ratio: AnyMetricPattern } } }} delta
|
||||
* @param {Unit} unit
|
||||
* @param {(metric: string) => string} title
|
||||
* @param {string} name
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function grouped30dSupplyChangeChart(list, all, title) {
|
||||
function singleDeltaTree(delta, unit, title, name) {
|
||||
return {
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.supply.delta.change._1m,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
),
|
||||
name,
|
||||
tree: [
|
||||
{ ...rollingWindowsTree({ windows: delta.absolute, title: title(`${name} Change`), unit, series: baseline }), name: "Absolute" },
|
||||
{ ...rollingPercentRatioTree({ windows: delta.rate, title: title(`${name} Rate`) }), name: "Rate" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly (UtxoCohortObject | CohortWithoutRelative)[]} list
|
||||
* @param {CohortAll} all
|
||||
* @template {{ name: string, color: Color }} T
|
||||
* @template {{ name: string, color: Color }} A
|
||||
* @param {readonly T[]} list
|
||||
* @param {A} all
|
||||
* @param {(c: T | A) => ChangeRatePattern | ChangeRatePattern2} getDelta
|
||||
* @param {Unit} unit
|
||||
* @param {(metric: string) => string} title
|
||||
* @param {string} name
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function grouped30dUtxoCountChangeChart(list, all, title) {
|
||||
function groupedDeltaTree(list, all, getDelta, unit, title, name) {
|
||||
return {
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.outputs.unspentCount.delta.change._1m,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Absolute",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`${name} Change (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, (c) =>
|
||||
baseline({ metric: getDelta(c).absolute[w.key], name: c.name, color: c.color, unit }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Rate",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`${name} Rate (${w.name})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, (c) =>
|
||||
percentRatio({ pattern: getDelta(c).rate[w.key], name: c.name, color: c.color }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -203,43 +211,6 @@ function singleUtxoCountChart(cohort, title) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function single30dSupplyChangeChart(cohort, title) {
|
||||
return {
|
||||
name: "Supply",
|
||||
title: title("Supply 30d Change"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: cohort.tree.supply.delta.change._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function single30dUtxoCountChangeChart(cohort, title) {
|
||||
return {
|
||||
name: "UTXO Count",
|
||||
title: title("UTXO Count 30d Change"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: cohort.tree.outputs.unspentCount.delta.change._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortAddress | AddressCohortObject} cohort
|
||||
@@ -261,24 +232,6 @@ function singleAddressCountChart(cohort, title) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortAddress | AddressCohortObject} cohort
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function single30dAddressCountChangeChart(cohort, title) {
|
||||
return {
|
||||
name: "Address Count",
|
||||
title: title("Address Count 30d Change"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: cohort.addressCount.delta.change._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Single Cohort Holdings Sections
|
||||
@@ -301,10 +254,10 @@ export function createHoldingsSection({ cohort, title }) {
|
||||
},
|
||||
singleUtxoCountChart(cohort, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
single30dSupplyChangeChart(cohort, title),
|
||||
single30dUtxoCountChangeChart(cohort, title),
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -332,11 +285,11 @@ export function createHoldingsSectionAll({ cohort, title }) {
|
||||
singleUtxoCountChart(cohort, title),
|
||||
singleAddressCountChart(cohort, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
single30dSupplyChangeChart(cohort, title),
|
||||
single30dUtxoCountChangeChart(cohort, title),
|
||||
single30dAddressCountChangeChart(cohort, title),
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
singleDeltaTree(cohort.addressCount.delta, Unit.count, title, "Address Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -365,10 +318,10 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
|
||||
},
|
||||
singleUtxoCountChart(cohort, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
single30dSupplyChangeChart(cohort, title),
|
||||
single30dUtxoCountChangeChart(cohort, title),
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -396,10 +349,10 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
|
||||
},
|
||||
singleUtxoCountChart(cohort, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
single30dSupplyChangeChart(cohort, title),
|
||||
single30dUtxoCountChangeChart(cohort, title),
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -423,11 +376,11 @@ export function createHoldingsSectionAddress({ cohort, title }) {
|
||||
singleUtxoCountChart(cohort, title),
|
||||
singleAddressCountChart(cohort, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
single30dSupplyChangeChart(cohort, title),
|
||||
single30dUtxoCountChangeChart(cohort, title),
|
||||
single30dAddressCountChangeChart(cohort, title),
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
singleDeltaTree(cohort.addressCount.delta, Unit.count, title, "Address Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -451,11 +404,11 @@ export function createHoldingsSectionAddressAmount({ cohort, title }) {
|
||||
singleUtxoCountChart(cohort, title),
|
||||
singleAddressCountChart(cohort, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
single30dSupplyChangeChart(cohort, title),
|
||||
single30dUtxoCountChangeChart(cohort, title),
|
||||
single30dAddressCountChangeChart(cohort, title),
|
||||
singleDeltaTree(cohort.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
singleDeltaTree(cohort.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
singleDeltaTree(cohort.addressCount.delta, Unit.count, title, "Address Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -517,22 +470,11 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
grouped30dSupplyChangeChart(list, all, title),
|
||||
grouped30dUtxoCountChangeChart(list, all, title),
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
|
||||
baseline({
|
||||
metric: addressCount.delta.change._1m,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
groupedDeltaTree(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
groupedDeltaTree(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -573,22 +515,11 @@ export function createGroupedHoldingsSectionAddressAmount({
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
grouped30dSupplyChangeChart(list, all, title),
|
||||
grouped30dUtxoCountChangeChart(list, all, title),
|
||||
{
|
||||
name: "Address Count",
|
||||
title: title("Address Count 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
|
||||
baseline({
|
||||
metric: addressCount.delta.change._1m,
|
||||
name,
|
||||
unit: Unit.count,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
groupedDeltaTree(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
groupedDeltaTree(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -618,10 +549,10 @@ export function createGroupedHoldingsSection({ list, all, title }) {
|
||||
},
|
||||
groupedUtxoCountChart(list, all, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
grouped30dSupplyChangeChart(list, all, title),
|
||||
grouped30dUtxoCountChangeChart(list, all, title),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -708,10 +639,10 @@ export function createGroupedHoldingsSectionWithOwnSupply({
|
||||
},
|
||||
groupedUtxoCountChart(list, all, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
grouped30dSupplyChangeChart(list, all, title),
|
||||
grouped30dUtxoCountChangeChart(list, all, title),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -812,10 +743,10 @@ export function createGroupedHoldingsSectionWithRelative({ list, all, title }) {
|
||||
},
|
||||
groupedUtxoCountChart(list, all, title),
|
||||
{
|
||||
name: "30d Changes",
|
||||
name: "Change",
|
||||
tree: [
|
||||
grouped30dSupplyChangeChart(list, all, title),
|
||||
grouped30dUtxoCountChangeChart(list, all, title),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
groupedDeltaTree(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline, dots, dotsBaseline } from "../series.js";
|
||||
import { ROLLING_WINDOWS, line, baseline, dots, dotsBaseline, percentRatio, percentRatioBaseline } from "../series.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { priceLine, priceLines } from "../constants.js";
|
||||
import { priceLine } from "../constants.js";
|
||||
import {
|
||||
mapCohorts,
|
||||
mapCohortsWithAll,
|
||||
flatMapCohorts,
|
||||
flatMapCohortsWithAll,
|
||||
} from "../shared.js";
|
||||
|
||||
// ============================================================================
|
||||
@@ -62,69 +63,82 @@ function netBaseline(metric, unit) {
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Unrealized P&L (USD + relToMcap) for All cohort
|
||||
* @param {{ profit: { base: { usd: AnyMetricPattern } }, loss: { base: { usd: AnyMetricPattern }, negative: AnyMetricPattern }, grossPnl: { usd: AnyMetricPattern } }} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function unrealizedUsdSeries(u) {
|
||||
return [
|
||||
...pnlLines(
|
||||
{ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative, gross: u.grossPnl.usd },
|
||||
Unit.usd,
|
||||
),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ percent: AnyMetricPattern, ratio: AnyMetricPattern }} profit
|
||||
* @param {{ percent: AnyMetricPattern, ratio: AnyMetricPattern }} loss
|
||||
* @param {string} name
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {AnyPartialOption}
|
||||
*/
|
||||
function relPnlChart(profit, loss, name, title) {
|
||||
return {
|
||||
name,
|
||||
title: title(`Unrealized P&L (${name})`),
|
||||
bottom: [
|
||||
...percentRatio({ pattern: profit, name: "Profit", color: colors.profit }),
|
||||
...percentRatio({ pattern: loss, name: "Loss", color: colors.loss }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrealized P&L tree for All cohort
|
||||
* @param {Brk.MetricsTree_Cohorts_Utxo_All_Unrealized} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function unrealizedAll(u) {
|
||||
function unrealizedPnlTreeAll(u, title) {
|
||||
return [
|
||||
...pnlLines(
|
||||
{ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative, gross: u.grossPnl.usd },
|
||||
Unit.usd,
|
||||
),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: u.profit.relToMcap.ratio, name: "Profit", color: colors.profit, unit: Unit.pctMcap }),
|
||||
line({ metric: u.loss.relToMcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctMcap }),
|
||||
priceLine({ unit: Unit.pctMcap, defaultActive: false }),
|
||||
line({ metric: u.profit.relToOwnGross.ratio, name: "Profit", color: colors.profit, unit: Unit.pctOwnPnl }),
|
||||
line({ metric: u.loss.relToOwnGross.ratio, name: "Loss", color: colors.loss, unit: Unit.pctOwnPnl }),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.pctOwnPnl }),
|
||||
{ name: "USD", title: title("Unrealized P&L"), bottom: unrealizedUsdSeries(u) },
|
||||
relPnlChart(u.profit.relToMcap, u.loss.relToMcap, "% of Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnGross, u.loss.relToOwnGross, "% of Own P&L", title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrealized P&L (USD + relToMcap + relToOwnMcap + relToOwnGross) for Full cohorts (STH/LTH)
|
||||
* Unrealized P&L tree for Full cohorts (STH)
|
||||
* @param {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function unrealizedFull(u) {
|
||||
function unrealizedPnlTreeFull(u, title) {
|
||||
return [
|
||||
...pnlLines(
|
||||
{ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative, gross: u.grossPnl.usd },
|
||||
Unit.usd,
|
||||
),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: u.profit.relToMcap.ratio, name: "Profit", color: colors.profit, unit: Unit.pctMcap }),
|
||||
line({ metric: u.loss.relToMcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctMcap }),
|
||||
priceLine({ unit: Unit.pctMcap, defaultActive: false }),
|
||||
line({ metric: u.profit.relToOwnMcap.ratio, name: "Profit", color: colors.profit, unit: Unit.pctOwnMcap }),
|
||||
line({ metric: u.loss.relToOwnMcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctOwnMcap }),
|
||||
priceLine({ unit: Unit.pctOwnMcap, defaultActive: false }),
|
||||
line({ metric: u.profit.relToOwnGross.ratio, name: "Profit", color: colors.profit, unit: Unit.pctOwnPnl }),
|
||||
line({ metric: u.loss.relToOwnGross.ratio, name: "Loss", color: colors.loss, unit: Unit.pctOwnPnl }),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.pctOwnPnl }),
|
||||
{ name: "USD", title: title("Unrealized P&L"), bottom: unrealizedUsdSeries(u) },
|
||||
relPnlChart(u.profit.relToMcap, u.loss.relToMcap, "% of Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnMcap, u.loss.relToOwnMcap, "% of Own Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnGross, u.loss.relToOwnGross, "% of Own P&L", title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrealized P&L for LTH (loss relToMcap only + relToOwnMcap + relToOwnGross)
|
||||
* Unrealized P&L tree for LTH (loss relToMcap only)
|
||||
* @param {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function unrealizedLongTerm(u) {
|
||||
function unrealizedPnlTreeLongTerm(u, title) {
|
||||
return [
|
||||
...pnlLines(
|
||||
{ profit: u.profit.base.usd, loss: u.loss.base.usd, negLoss: u.loss.negative, gross: u.grossPnl.usd },
|
||||
Unit.usd,
|
||||
),
|
||||
priceLine({ unit: Unit.usd, defaultActive: false }),
|
||||
line({ metric: u.loss.relToMcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctMcap }),
|
||||
line({ metric: u.profit.relToOwnMcap.ratio, name: "Profit", color: colors.profit, unit: Unit.pctOwnMcap }),
|
||||
line({ metric: u.loss.relToOwnMcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctOwnMcap }),
|
||||
priceLine({ unit: Unit.pctOwnMcap, defaultActive: false }),
|
||||
line({ metric: u.profit.relToOwnGross.ratio, name: "Profit", color: colors.profit, unit: Unit.pctOwnPnl }),
|
||||
line({ metric: u.loss.relToOwnGross.ratio, name: "Loss", color: colors.loss, unit: Unit.pctOwnPnl }),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.pctOwnPnl }),
|
||||
{ name: "USD", title: title("Unrealized P&L"), bottom: unrealizedUsdSeries(u) },
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Unrealized Loss (% of Mcap)"),
|
||||
bottom: percentRatio({ pattern: u.loss.relToMcap, name: "Loss", color: colors.loss }),
|
||||
},
|
||||
relPnlChart(u.profit.relToOwnMcap, u.loss.relToOwnMcap, "% of Own Mcap", title),
|
||||
relPnlChart(u.profit.relToOwnGross, u.loss.relToOwnGross, "% of Own P&L", title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -148,27 +162,39 @@ function unrealizedMid(u) {
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Net P&L for All cohort
|
||||
* @param {Brk.MetricsTree_Cohorts_Utxo_All_Unrealized} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function netUnrealizedAll(u) {
|
||||
function netUnrealizedTreeAll(u, title) {
|
||||
return [
|
||||
netBaseline(u.netPnl.usd, Unit.usd),
|
||||
netBaseline(u.netPnl.relToOwnGross.ratio, Unit.pctOwnPnl),
|
||||
{ name: "USD", title: title("Net Unrealized P&L"), bottom: [netBaseline(u.netPnl.usd, Unit.usd)] },
|
||||
{
|
||||
name: "% of Own P&L",
|
||||
title: title("Net Unrealized P&L (% of Own P&L)"),
|
||||
bottom: percentRatioBaseline({ pattern: u.netPnl.relToOwnGross, name: "Net P&L" }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Net P&L for Full cohorts (STH/LTH)
|
||||
* @param {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} u
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function netUnrealizedFull(u) {
|
||||
function netUnrealizedTreeFull(u, title) {
|
||||
return [
|
||||
netBaseline(u.netPnl.usd, Unit.usd),
|
||||
netBaseline(u.netPnl.relToOwnMcap.ratio, Unit.pctOwnMcap),
|
||||
netBaseline(u.netPnl.relToOwnGross.ratio, Unit.pctOwnPnl),
|
||||
{ name: "USD", title: title("Net Unrealized P&L"), bottom: [netBaseline(u.netPnl.usd, Unit.usd)] },
|
||||
{
|
||||
name: "% of Own Mcap",
|
||||
title: title("Net Unrealized P&L (% of Own Mcap)"),
|
||||
bottom: percentRatioBaseline({ pattern: u.netPnl.relToOwnMcap, name: "Net P&L" }),
|
||||
},
|
||||
{
|
||||
name: "% of Own P&L",
|
||||
title: title("Net Unrealized P&L (% of Own P&L)"),
|
||||
bottom: percentRatioBaseline({ pattern: u.netPnl.relToOwnGross, name: "Net P&L" }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -225,52 +251,92 @@ function nuplSeries(nupl) {
|
||||
|
||||
/**
|
||||
* @param {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern | Brk.MetricsTree_Cohorts_Utxo_Lth_Realized} r
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function realizedPnlSumFull(r) {
|
||||
function realizedPnlSumTreeFull(r, title) {
|
||||
return [
|
||||
dots({ metric: r.profit.base.usd, name: "Profit", color: colors.profit, unit: Unit.usd }),
|
||||
dots({ metric: r.loss.negative, name: "Negative Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
dots({ metric: r.loss.base.usd, name: "Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
baseline({ metric: r.profit.relToRcap.ratio, name: "Profit", color: colors.profit, unit: Unit.pctRcap }),
|
||||
baseline({ metric: r.loss.relToRcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctRcap }),
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Realized P&L"),
|
||||
bottom: [
|
||||
dots({ metric: r.profit.base.usd, name: "Profit", color: colors.profit, unit: Unit.usd }),
|
||||
dots({ metric: r.loss.negative, name: "Negative Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
dots({ metric: r.loss.base.usd, name: "Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Realized P&L (% of Realized Cap)"),
|
||||
bottom: [
|
||||
...percentRatioBaseline({ pattern: r.profit.relToRcap, name: "Profit", color: colors.profit }),
|
||||
...percentRatioBaseline({ pattern: r.loss.relToRcap, name: "Loss", color: colors.loss }),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern | Brk.MetricsTree_Cohorts_Utxo_Lth_Realized} r
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function realizedNetPnlSumFull(r) {
|
||||
function realizedNetPnlSumTreeFull(r, title) {
|
||||
return [
|
||||
dotsBaseline({ metric: r.netPnl.base.usd, name: "Net", unit: Unit.usd, defaultActive: false }),
|
||||
baseline({ metric: r.netPnl.relToRcap.ratio, name: "Net", unit: Unit.pctRcap }),
|
||||
{ name: "USD", title: title("Net Realized P&L"), bottom: [dotsBaseline({ metric: r.netPnl.base.usd, name: "Net", unit: Unit.usd })] },
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Net Realized P&L (% of Realized Cap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.relToRcap, name: "Net" }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern | Brk.MetricsTree_Cohorts_Utxo_Lth_Realized} r
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function realizedPnlCumulativeFull(r) {
|
||||
function realizedPnlCumulativeTreeFull(r, title) {
|
||||
return [
|
||||
line({ metric: r.profit.cumulative.usd, name: "Profit", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: r.loss.cumulative.usd, name: "Loss", color: colors.loss, unit: Unit.usd }),
|
||||
line({ metric: r.loss.negative, name: "Negative Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
baseline({ metric: r.profit.relToRcap.ratio, name: "Profit", color: colors.profit, unit: Unit.pctRcap }),
|
||||
baseline({ metric: r.loss.relToRcap.ratio, name: "Loss", color: colors.loss, unit: Unit.pctRcap }),
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Cumulative Realized P&L"),
|
||||
bottom: [
|
||||
line({ metric: r.profit.cumulative.usd, name: "Profit", color: colors.profit, unit: Unit.usd }),
|
||||
line({ metric: r.loss.cumulative.usd, name: "Loss", color: colors.loss, unit: Unit.usd }),
|
||||
line({ metric: r.loss.negative, name: "Negative Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Cumulative Realized P&L (% of Realized Cap)"),
|
||||
bottom: [
|
||||
...percentRatioBaseline({ pattern: r.profit.relToRcap, name: "Profit", color: colors.profit }),
|
||||
...percentRatioBaseline({ pattern: r.loss.relToRcap, name: "Loss", color: colors.loss }),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern | Brk.MetricsTree_Cohorts_Utxo_Lth_Realized} r
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function realized30dChangeFull(r) {
|
||||
function realized30dChangeTreeFull(r, title) {
|
||||
return [
|
||||
baseline({ metric: r.netPnl.delta.change._1m.usd, name: "30d Change", unit: Unit.usd }),
|
||||
baseline({ metric: r.netPnl.change1m.relToMcap.ratio, name: "30d Change", unit: Unit.pctMcap }),
|
||||
baseline({ metric: r.netPnl.change1m.relToRcap.ratio, name: "30d Change", unit: Unit.pctRcap }),
|
||||
{ name: "USD", title: title("Realized P&L 30d Change"), bottom: [baseline({ metric: r.netPnl.delta.absolute._1m.usd, name: "30d Change", unit: Unit.usd })] },
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Realized 30d Change (% of Mcap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.change1m.relToMcap, name: "30d Change" }),
|
||||
},
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Realized 30d Change (% of Realized Cap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.change1m.relToRcap, name: "30d Change" }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -288,17 +354,15 @@ function singleRollingRealizedTreeFull(r, title) {
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Realized Profit"),
|
||||
bottom: [
|
||||
line({ metric: r.profit.sum._24h.usd, name: "24h", color: colors.time._24h, unit: Unit.usd }),
|
||||
line({ metric: r.profit.sum._1w.usd, name: "7d", color: colors.time._1w, unit: Unit.usd }),
|
||||
line({ metric: r.profit.sum._1m.usd, name: "30d", color: colors.time._1m, unit: Unit.usd }),
|
||||
line({ metric: r.profit.sum._1y.usd, name: "1y", color: colors.time._1y, unit: Unit.usd }),
|
||||
],
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ metric: r.profit.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{ name: "24h", title: title("Realized Profit (24h)"), bottom: [line({ metric: r.profit.sum._24h.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
{ name: "7d", title: title("Realized Profit (7d)"), bottom: [line({ metric: r.profit.sum._1w.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
{ name: "30d", title: title("Realized Profit (30d)"), bottom: [line({ metric: r.profit.sum._1m.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
{ name: "1y", title: title("Realized Profit (1y)"), bottom: [line({ metric: r.profit.sum._1y.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Profit (${w.name})`),
|
||||
bottom: [line({ metric: r.profit.sum[w.key].usd, name: "Profit", color: colors.profit, unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -307,17 +371,32 @@ function singleRollingRealizedTreeFull(r, title) {
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Realized Loss"),
|
||||
bottom: [
|
||||
line({ metric: r.loss.sum._24h.usd, name: "24h", color: colors.time._24h, unit: Unit.usd }),
|
||||
line({ metric: r.loss.sum._1w.usd, name: "7d", color: colors.time._1w, unit: Unit.usd }),
|
||||
line({ metric: r.loss.sum._1m.usd, name: "30d", color: colors.time._1m, unit: Unit.usd }),
|
||||
line({ metric: r.loss.sum._1y.usd, name: "1y", color: colors.time._1y, unit: Unit.usd }),
|
||||
],
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ metric: r.loss.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{ name: "24h", title: title("Realized Loss (24h)"), bottom: [line({ metric: r.loss.sum._24h.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
{ name: "7d", title: title("Realized Loss (7d)"), bottom: [line({ metric: r.loss.sum._1w.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
{ name: "30d", title: title("Realized Loss (30d)"), bottom: [line({ metric: r.loss.sum._1m.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
{ name: "1y", title: title("Realized Loss (1y)"), bottom: [line({ metric: r.loss.sum._1y.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Loss (${w.name})`),
|
||||
bottom: [line({ metric: r.loss.sum[w.key].usd, name: "Loss", color: colors.loss, unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Net",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Net Realized P&L"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({ metric: r.netPnl.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L (${w.name})`),
|
||||
bottom: [baseline({ metric: r.netPnl.sum[w.key].usd, name: "Net", unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -326,17 +405,15 @@ function singleRollingRealizedTreeFull(r, title) {
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Realized P/L Ratio"),
|
||||
bottom: [
|
||||
baseline({ metric: r.profitToLossRatio._24h, name: "24h", color: colors.time._24h, unit: Unit.ratio, base: 1 }),
|
||||
baseline({ metric: r.profitToLossRatio._1w, name: "7d", color: colors.time._1w, unit: Unit.ratio, base: 1 }),
|
||||
baseline({ metric: r.profitToLossRatio._1m, name: "30d", color: colors.time._1m, unit: Unit.ratio, base: 1 }),
|
||||
baseline({ metric: r.profitToLossRatio._1y, name: "1y", color: colors.time._1y, unit: Unit.ratio, base: 1 }),
|
||||
],
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({ metric: r.profitToLossRatio[w.key], name: w.name, color: w.color, unit: Unit.ratio, base: 1 }),
|
||||
),
|
||||
},
|
||||
{ name: "24h", title: title("Realized P/L Ratio (24h)"), bottom: [baseline({ metric: r.profitToLossRatio._24h, name: "P/L Ratio", unit: Unit.ratio, base: 1 })] },
|
||||
{ name: "7d", title: title("Realized P/L Ratio (7d)"), bottom: [baseline({ metric: r.profitToLossRatio._1w, name: "P/L Ratio", unit: Unit.ratio, base: 1 })] },
|
||||
{ name: "30d", title: title("Realized P/L Ratio (30d)"), bottom: [baseline({ metric: r.profitToLossRatio._1m, name: "P/L Ratio", unit: Unit.ratio, base: 1 })] },
|
||||
{ name: "1y", title: title("Realized P/L Ratio (1y)"), bottom: [baseline({ metric: r.profitToLossRatio._1y, name: "P/L Ratio", unit: Unit.ratio, base: 1 })] },
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized P/L Ratio (${w.name})`),
|
||||
bottom: [baseline({ metric: r.profitToLossRatio[w.key], name: "P/L Ratio", unit: Unit.ratio, base: 1 })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -353,21 +430,19 @@ function singleRollingRealizedTreeBasic(profit, loss, title) {
|
||||
return [
|
||||
{
|
||||
name: "Profit",
|
||||
tree: [
|
||||
{ name: "24h", title: title("Realized Profit (24h)"), bottom: [line({ metric: profit.sum._24h.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
{ name: "7d", title: title("Realized Profit (7d)"), bottom: [line({ metric: profit.sum._1w.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
{ name: "30d", title: title("Realized Profit (30d)"), bottom: [line({ metric: profit.sum._1m.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
{ name: "1y", title: title("Realized Profit (1y)"), bottom: [line({ metric: profit.sum._1y.usd, name: "Profit", color: colors.profit, unit: Unit.usd })] },
|
||||
],
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Profit (${w.name})`),
|
||||
bottom: [line({ metric: profit.sum[w.key].usd, name: "Profit", color: colors.profit, unit: Unit.usd })],
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
tree: [
|
||||
{ name: "24h", title: title("Realized Loss (24h)"), bottom: [line({ metric: loss.sum._24h.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
{ name: "7d", title: title("Realized Loss (7d)"), bottom: [line({ metric: loss.sum._1w.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
{ name: "30d", title: title("Realized Loss (30d)"), bottom: [line({ metric: loss.sum._1m.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
{ name: "1y", title: title("Realized Loss (1y)"), bottom: [line({ metric: loss.sum._1y.usd, name: "Loss", color: colors.loss, unit: Unit.usd })] },
|
||||
],
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Loss (${w.name})`),
|
||||
bottom: [line({ metric: loss.sum[w.key].usd, name: "Loss", color: colors.loss, unit: Unit.usd })],
|
||||
})),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -386,13 +461,32 @@ function realizedSubfolderFull(r, title) {
|
||||
return {
|
||||
name: "Realized",
|
||||
tree: [
|
||||
{ name: "P&L", title: title("Realized P&L"), bottom: realizedPnlSumFull(r) },
|
||||
{ name: "Net", title: title("Net Realized P&L"), bottom: realizedNetPnlSumFull(r) },
|
||||
{ name: "30d Change", title: title("Realized P&L 30d Change"), bottom: realized30dChangeFull(r) },
|
||||
{ name: "P&L", tree: realizedPnlSumTreeFull(r, title) },
|
||||
{ name: "Net", tree: realizedNetPnlSumTreeFull(r, title) },
|
||||
{ name: "30d Change", tree: realized30dChangeTreeFull(r, title) },
|
||||
{
|
||||
name: "Total",
|
||||
title: title("Total Realized P&L"),
|
||||
bottom: [line({ metric: r.grossPnl.cumulative.usd, name: "Total", unit: Unit.usd, color: colors.bitcoin })],
|
||||
name: "Gross P&L",
|
||||
tree: [
|
||||
{ name: "Base", title: title("Gross Realized P&L"), bottom: [dots({ metric: r.grossPnl.base.usd, name: "Gross P&L", color: colors.bitcoin, unit: Unit.usd })] },
|
||||
{
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Rolling Gross Realized P&L"),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ metric: r.grossPnl.sum[w.key].usd, name: w.name, color: w.color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Gross Realized P&L (${w.name})`),
|
||||
bottom: [line({ metric: r.grossPnl.sum[w.key].usd, name: "Gross P&L", color: colors.bitcoin, unit: Unit.usd })],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{ name: "Cumulative", title: title("Total Realized P&L"), bottom: [line({ metric: r.grossPnl.cumulative.usd, name: "Total", unit: Unit.usd, color: colors.bitcoin })] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "P/L Ratio",
|
||||
@@ -402,29 +496,33 @@ function realizedSubfolderFull(r, title) {
|
||||
{
|
||||
name: "Peak Regret",
|
||||
title: title("Realized Peak Regret"),
|
||||
bottom: [
|
||||
line({ metric: r.peakRegret.base, name: "Peak Regret", unit: Unit.usd }),
|
||||
],
|
||||
bottom: [line({ metric: r.peakRegret.base, name: "Peak Regret", unit: Unit.usd })],
|
||||
},
|
||||
{ name: "Rolling", tree: singleRollingRealizedTreeFull(r, title) },
|
||||
{
|
||||
name: "Cumulative",
|
||||
tree: [
|
||||
{ name: "P&L", title: title("Cumulative Realized P&L"), bottom: realizedPnlCumulativeFull(r) },
|
||||
{ name: "P&L", tree: realizedPnlCumulativeTreeFull(r, title) },
|
||||
{
|
||||
name: "Net",
|
||||
title: title("Cumulative Net Realized P&L"),
|
||||
bottom: [
|
||||
baseline({ metric: r.netPnl.cumulative.usd, name: "Net", unit: Unit.usd }),
|
||||
baseline({ metric: r.netPnl.relToRcap.ratio, name: "Net", unit: Unit.pctRcap }),
|
||||
tree: [
|
||||
{ name: "USD", title: title("Cumulative Net Realized P&L"), bottom: [baseline({ metric: r.netPnl.cumulative.usd, name: "Net", unit: Unit.usd })] },
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Cumulative Net P&L (% of Realized Cap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.netPnl.relToRcap, name: "Net" }),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Peak Regret",
|
||||
title: title("Cumulative Realized Peak Regret"),
|
||||
bottom: [
|
||||
line({ metric: r.peakRegret.cumulative, name: "Peak Regret", unit: Unit.usd }),
|
||||
line({ metric: r.peakRegret.relToRcap.ratio, name: "Peak Regret", unit: Unit.pctRcap }),
|
||||
tree: [
|
||||
{ name: "USD", title: title("Cumulative Peak Regret"), bottom: [line({ metric: r.peakRegret.cumulative, name: "Peak Regret", unit: Unit.usd })] },
|
||||
{
|
||||
name: "% of Rcap",
|
||||
title: title("Cumulative Peak Regret (% of Realized Cap)"),
|
||||
bottom: percentRatioBaseline({ pattern: r.peakRegret.relToRcap, name: "Peak Regret" }),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -460,7 +558,7 @@ function realizedSubfolderMid(r, title) {
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized P&L 30d Change"),
|
||||
bottom: [baseline({ metric: r.netPnl.delta.change._1m.usd, name: "30d Change", unit: Unit.usd })],
|
||||
bottom: [baseline({ metric: r.netPnl.delta.absolute._1m.usd, name: "30d Change", unit: Unit.usd })],
|
||||
},
|
||||
{ name: "Rolling", tree: singleRollingRealizedTreeBasic(r.profit, r.loss, title) },
|
||||
{
|
||||
@@ -555,8 +653,8 @@ export function createProfitabilitySectionAll({ cohort, title }) {
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{ name: "P&L", title: title("Unrealized P&L"), bottom: unrealizedAll(u) },
|
||||
{ name: "Net P&L", title: title("Net Unrealized P&L"), bottom: netUnrealizedAll(u) },
|
||||
{ name: "P&L", tree: unrealizedPnlTreeAll(u, title) },
|
||||
{ name: "Net P&L", tree: netUnrealizedTreeAll(u, title) },
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
],
|
||||
},
|
||||
@@ -585,8 +683,8 @@ export function createProfitabilitySectionFull({ cohort, title }) {
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{ name: "P&L", title: title("Unrealized P&L"), bottom: unrealizedFull(u) },
|
||||
{ name: "Net P&L", title: title("Net Unrealized P&L"), bottom: netUnrealizedFull(u) },
|
||||
{ name: "P&L", tree: unrealizedPnlTreeFull(u, title) },
|
||||
{ name: "Net P&L", tree: netUnrealizedTreeFull(u, title) },
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
],
|
||||
},
|
||||
@@ -636,8 +734,8 @@ export function createProfitabilitySectionLongTerm({ cohort, title }) {
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{ name: "P&L", title: title("Unrealized P&L"), bottom: unrealizedLongTerm(u) },
|
||||
{ name: "Net P&L", title: title("Net Unrealized P&L"), bottom: netUnrealizedFull(u) },
|
||||
{ name: "P&L", tree: unrealizedPnlTreeLongTerm(u, title) },
|
||||
{ name: "Net P&L", tree: netUnrealizedTreeFull(u, title) },
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
],
|
||||
},
|
||||
@@ -769,21 +867,19 @@ function groupedRollingRealizedCharts(list, all, title) {
|
||||
return [
|
||||
{
|
||||
name: "Profit",
|
||||
tree: [
|
||||
{ name: "24h", title: title("Realized Profit (24h)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.profit.sum._24h.usd, name, color, unit: Unit.usd })) },
|
||||
{ name: "7d", title: title("Realized Profit (7d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.profit.sum._1w.usd, name, color, unit: Unit.usd })) },
|
||||
{ name: "30d", title: title("Realized Profit (30d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.profit.sum._1m.usd, name, color, unit: Unit.usd })) },
|
||||
{ name: "1y", title: title("Realized Profit (1y)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.profit.sum._1y.usd, name, color, unit: Unit.usd })) },
|
||||
],
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Profit (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.profit.sum[w.key].usd, name, color, unit: Unit.usd })),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
tree: [
|
||||
{ name: "24h", title: title("Realized Loss (24h)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.loss.sum._24h.usd, name, color, unit: Unit.usd })) },
|
||||
{ name: "7d", title: title("Realized Loss (7d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.loss.sum._1w.usd, name, color, unit: Unit.usd })) },
|
||||
{ name: "30d", title: title("Realized Loss (30d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.loss.sum._1m.usd, name, color, unit: Unit.usd })) },
|
||||
{ name: "1y", title: title("Realized Loss (1y)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.loss.sum._1y.usd, name, color, unit: Unit.usd })) },
|
||||
],
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Loss (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ metric: tree.realized.loss.sum[w.key].usd, name, color, unit: Unit.usd })),
|
||||
})),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -800,12 +896,13 @@ function groupedRollingRealizedChartsFull(list, all, title) {
|
||||
...groupedRollingRealizedCharts(list, all, title),
|
||||
{
|
||||
name: "P/L Ratio",
|
||||
tree: [
|
||||
{ name: "24h", title: title("Realized P/L Ratio (24h)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => baseline({ metric: tree.realized.profitToLossRatio._24h, name, color, unit: Unit.ratio, base: 1 })) },
|
||||
{ name: "7d", title: title("Realized P/L Ratio (7d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => baseline({ metric: tree.realized.profitToLossRatio._1w, name, color, unit: Unit.ratio, base: 1 })) },
|
||||
{ name: "30d", title: title("Realized P/L Ratio (30d)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => baseline({ metric: tree.realized.profitToLossRatio._1m, name, color, unit: Unit.ratio, base: 1 })) },
|
||||
{ name: "1y", title: title("Realized P/L Ratio (1y)"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => baseline({ metric: tree.realized.profitToLossRatio._1y, name, color, unit: Unit.ratio, base: 1 })) },
|
||||
],
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized P/L Ratio (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.realized.profitToLossRatio[w.key], name, color, unit: Unit.ratio, base: 1 }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -871,7 +968,7 @@ function groupedRealizedSubfolderFull(list, all, title) {
|
||||
name: "30d Change",
|
||||
title: title("Realized P&L 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.realized.netPnl.delta.change._1m.usd, name, color, unit: Unit.usd }),
|
||||
baseline({ metric: tree.realized.netPnl.delta.absolute._1m.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{ name: "Rolling", tree: groupedRollingRealizedChartsFull(list, all, title) },
|
||||
@@ -937,26 +1034,40 @@ function groupedPnlChartsWithMarketCap(list, all, title) {
|
||||
return [
|
||||
{
|
||||
name: "Profit",
|
||||
title: title("Unrealized Profit"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.unrealized.profit.relToMcap.ratio, name, color, unit: Unit.pctMcap }),
|
||||
),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized Profit"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Unrealized Profit (% of Mcap)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.profit.relToMcap, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
title: title("Unrealized Loss"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.unrealized.loss.relToMcap.ratio, name, color, unit: Unit.pctMcap }),
|
||||
),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized Loss"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Unrealized Loss (% of Mcap)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.loss.relToMcap, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1013,50 +1124,87 @@ function groupedPnlChartsLongTerm(list, all, title) {
|
||||
return [
|
||||
{
|
||||
name: "Profit",
|
||||
title: title("Unrealized Profit"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
...mapCohorts(list, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.relToOwnMcap.ratio, name, color, unit: Unit.pctOwnMcap }),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.relToOwnGross.ratio, name, color, unit: Unit.pctOwnPnl }),
|
||||
),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized Profit"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.profit.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own Mcap",
|
||||
title: title("Unrealized Profit (% of Own Mcap)"),
|
||||
bottom: flatMapCohorts(list, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.profit.relToOwnMcap, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own P&L",
|
||||
title: title("Unrealized Profit (% of Own P&L)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.profit.relToOwnGross, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
title: title("Unrealized Loss"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.relToMcap.ratio, name, color, unit: Unit.pctMcap }),
|
||||
),
|
||||
...mapCohorts(list, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.relToOwnMcap.ratio, name, color, unit: Unit.pctOwnMcap }),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.relToOwnGross.ratio, name, color, unit: Unit.pctOwnPnl }),
|
||||
),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Unrealized Loss"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.unrealized.loss.base.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Mcap",
|
||||
title: title("Unrealized Loss (% of Mcap)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.loss.relToMcap, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own Mcap",
|
||||
title: title("Unrealized Loss (% of Own Mcap)"),
|
||||
bottom: flatMapCohorts(list, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.loss.relToOwnMcap, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own P&L",
|
||||
title: title("Unrealized Loss (% of Own P&L)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.unrealized.loss.relToOwnGross, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Net P&L",
|
||||
title: title("Net Unrealized P&L"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.unrealized.netPnl.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
...mapCohorts(list, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.unrealized.netPnl.relToOwnMcap.ratio, name, color, unit: Unit.pctOwnMcap }),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.unrealized.netPnl.relToOwnGross.ratio, name, color, unit: Unit.pctOwnPnl }),
|
||||
),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Net Unrealized P&L"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.unrealized.netPnl.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own Mcap",
|
||||
title: title("Net Unrealized P&L (% of Own Mcap)"),
|
||||
bottom: flatMapCohorts(list, ({ name, color, tree }) =>
|
||||
percentRatioBaseline({ pattern: tree.unrealized.netPnl.relToOwnMcap, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own P&L",
|
||||
title: title("Net Unrealized P&L (% of Own P&L)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatioBaseline({ pattern: tree.unrealized.netPnl.relToOwnGross, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline } from "../series.js";
|
||||
import { createRatioChart, mapCohortsWithAll } from "../shared.js";
|
||||
import { ROLLING_WINDOWS, line, baseline, mapWindows, rollingWindowsTree, rollingPercentRatioTree, percentRatio, percentRatioBaseline } from "../series.js";
|
||||
import { createRatioChart, mapCohortsWithAll, flatMapCohortsWithAll } from "../shared.js";
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
@@ -30,20 +30,6 @@ function createSingleRealizedCapSeries(cohort) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingle30dChangeSeries(cohort) {
|
||||
return [
|
||||
baseline({
|
||||
metric: cohort.tree.realized.cap.delta.change._1m.usd,
|
||||
name: "30d Change",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create valuation section for cohorts with full ratio patterns
|
||||
* (CohortAll, CohortFull, CohortWithPercentiles)
|
||||
@@ -57,21 +43,25 @@ export function createValuationSectionFull({ cohort, title }) {
|
||||
tree: [
|
||||
{
|
||||
name: "Realized Cap",
|
||||
title: title("Realized Cap"),
|
||||
bottom: [
|
||||
...createSingleRealizedCapSeries(cohort),
|
||||
baseline({
|
||||
metric: tree.realized.cap.relToOwnMcap.percent,
|
||||
name: "Rel. to Own M.Cap",
|
||||
color,
|
||||
unit: Unit.pctOwnMcap,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Realized Cap"),
|
||||
bottom: createSingleRealizedCapSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "% of Own Mcap",
|
||||
title: title("Realized Cap (% of Own Mcap)"),
|
||||
bottom: percentRatioBaseline({ pattern: tree.realized.cap.relToOwnMcap, name: "Rel. to Own M.Cap", color }),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
name: "Change",
|
||||
tree: [
|
||||
{ ...rollingWindowsTree({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title: title("Realized Cap Change"), unit: Unit.usd, series: baseline }), name: "Absolute" },
|
||||
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title: title("Realized Cap Rate") }), name: "Rate" },
|
||||
],
|
||||
},
|
||||
createRatioChart({
|
||||
title,
|
||||
@@ -101,9 +91,11 @@ export function createValuationSection({ cohort, title }) {
|
||||
bottom: createSingleRealizedCapSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: createSingle30dChangeSeries(cohort),
|
||||
name: "Change",
|
||||
tree: [
|
||||
{ ...rollingWindowsTree({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title: title("Realized Cap Change"), unit: Unit.usd, series: baseline }), name: "Absolute" },
|
||||
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title: title("Realized Cap Rate") }), name: "Rate" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "MVRV",
|
||||
@@ -142,16 +134,29 @@ export function createGroupedValuationSection({ list, all, title }) {
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.cap.delta.change._1m.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Absolute",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Cap Change (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.realized.cap.delta.absolute[w.key].usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Rate",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Cap Rate (${w.name})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.realized.cap.delta.rate[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "MVRV",
|
||||
@@ -184,37 +189,47 @@ export function createGroupedValuationSectionWithOwnMarketCap({
|
||||
tree: [
|
||||
{
|
||||
name: "Realized Cap",
|
||||
title: title("Realized Cap"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.realized.cap.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.cap.relToOwnMcap.percent,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctOwnMcap,
|
||||
}),
|
||||
),
|
||||
tree: [
|
||||
{
|
||||
name: "USD",
|
||||
title: title("Realized Cap"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ metric: tree.realized.cap.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "% of Own Mcap",
|
||||
title: title("Realized Cap (% of Own Mcap)"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.realized.cap.relToOwnMcap, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.cap.delta.change._1m.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Absolute",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Cap Change (${w.name})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ metric: tree.realized.cap.delta.absolute[w.key].usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Rate",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Cap Rate (${w.name})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatio({ pattern: tree.realized.cap.delta.rate[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "MVRV",
|
||||
|
||||
@@ -175,7 +175,7 @@ export function createNetworkSection() {
|
||||
name: "Trends",
|
||||
tree: [
|
||||
rollingWindowsTree({
|
||||
windows: addresses.delta[key].change,
|
||||
windows: addresses.delta[key].absolute,
|
||||
title: `${titlePrefix}Address Count Change`,
|
||||
unit: Unit.count,
|
||||
series: baseline,
|
||||
@@ -487,7 +487,7 @@ export function createNetworkSection() {
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: mapWindows(
|
||||
supply.marketCap.delta.change,
|
||||
supply.marketCap.delta.absolute,
|
||||
(c) => c.usd,
|
||||
),
|
||||
title: "Market Cap Change",
|
||||
@@ -1074,7 +1074,7 @@ export function createNetworkSection() {
|
||||
title: "UTXO Count 30d Change",
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: cohorts.utxo.all.outputs.unspentCount.delta.change._1m,
|
||||
metric: cohorts.utxo.all.outputs.unspentCount.delta.absolute._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user