global: snapshot

This commit is contained in:
nym21
2026-01-16 15:17:42 +01:00
parent f39681bb2b
commit 3b00a92fa4
23 changed files with 4904 additions and 845 deletions

2
Cargo.lock generated
View File

@@ -3325,8 +3325,10 @@ dependencies = [
"bitflags 2.10.0",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"pin-project-lite",
"tokio",
"tokio-util",

View File

@@ -1268,56 +1268,6 @@ impl Price111dSmaPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct ActivePriceRatioPattern {
pub ratio: MetricPattern4<StoredF32>,
pub ratio_1m_sma: MetricPattern4<StoredF32>,
pub ratio_1w_sma: MetricPattern4<StoredF32>,
pub ratio_1y_sd: Ratio1ySdPattern,
pub ratio_2y_sd: Ratio1ySdPattern,
pub ratio_4y_sd: Ratio1ySdPattern,
pub ratio_pct1: MetricPattern4<StoredF32>,
pub ratio_pct1_usd: MetricPattern4<Dollars>,
pub ratio_pct2: MetricPattern4<StoredF32>,
pub ratio_pct2_usd: MetricPattern4<Dollars>,
pub ratio_pct5: MetricPattern4<StoredF32>,
pub ratio_pct5_usd: MetricPattern4<Dollars>,
pub ratio_pct95: MetricPattern4<StoredF32>,
pub ratio_pct95_usd: MetricPattern4<Dollars>,
pub ratio_pct98: MetricPattern4<StoredF32>,
pub ratio_pct98_usd: MetricPattern4<Dollars>,
pub ratio_pct99: MetricPattern4<StoredF32>,
pub ratio_pct99_usd: MetricPattern4<Dollars>,
pub ratio_sd: Ratio1ySdPattern,
}
impl ActivePriceRatioPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ratio: MetricPattern4::new(client.clone(), acc.clone()),
ratio_1m_sma: MetricPattern4::new(client.clone(), _m(&acc, "1m_sma")),
ratio_1w_sma: MetricPattern4::new(client.clone(), _m(&acc, "1w_sma")),
ratio_1y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "1y")),
ratio_2y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "2y")),
ratio_4y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "4y")),
ratio_pct1: MetricPattern4::new(client.clone(), _m(&acc, "pct1")),
ratio_pct1_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct1_usd")),
ratio_pct2: MetricPattern4::new(client.clone(), _m(&acc, "pct2")),
ratio_pct2_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct2_usd")),
ratio_pct5: MetricPattern4::new(client.clone(), _m(&acc, "pct5")),
ratio_pct5_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct5_usd")),
ratio_pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")),
ratio_pct95_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct95_usd")),
ratio_pct98: MetricPattern4::new(client.clone(), _m(&acc, "pct98")),
ratio_pct98_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct98_usd")),
ratio_pct99: MetricPattern4::new(client.clone(), _m(&acc, "pct99")),
ratio_pct99_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct99_usd")),
ratio_sd: Ratio1ySdPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct PercentilesPattern {
pub pct05: MetricPattern4<Dollars>,
@@ -1368,6 +1318,56 @@ impl PercentilesPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct ActivePriceRatioPattern {
pub ratio: MetricPattern4<StoredF32>,
pub ratio_1m_sma: MetricPattern4<StoredF32>,
pub ratio_1w_sma: MetricPattern4<StoredF32>,
pub ratio_1y_sd: Ratio1ySdPattern,
pub ratio_2y_sd: Ratio1ySdPattern,
pub ratio_4y_sd: Ratio1ySdPattern,
pub ratio_pct1: MetricPattern4<StoredF32>,
pub ratio_pct1_usd: MetricPattern4<Dollars>,
pub ratio_pct2: MetricPattern4<StoredF32>,
pub ratio_pct2_usd: MetricPattern4<Dollars>,
pub ratio_pct5: MetricPattern4<StoredF32>,
pub ratio_pct5_usd: MetricPattern4<Dollars>,
pub ratio_pct95: MetricPattern4<StoredF32>,
pub ratio_pct95_usd: MetricPattern4<Dollars>,
pub ratio_pct98: MetricPattern4<StoredF32>,
pub ratio_pct98_usd: MetricPattern4<Dollars>,
pub ratio_pct99: MetricPattern4<StoredF32>,
pub ratio_pct99_usd: MetricPattern4<Dollars>,
pub ratio_sd: Ratio1ySdPattern,
}
impl ActivePriceRatioPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ratio: MetricPattern4::new(client.clone(), acc.clone()),
ratio_1m_sma: MetricPattern4::new(client.clone(), _m(&acc, "1m_sma")),
ratio_1w_sma: MetricPattern4::new(client.clone(), _m(&acc, "1w_sma")),
ratio_1y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "1y")),
ratio_2y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "2y")),
ratio_4y_sd: Ratio1ySdPattern::new(client.clone(), _m(&acc, "4y")),
ratio_pct1: MetricPattern4::new(client.clone(), _m(&acc, "pct1")),
ratio_pct1_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct1_usd")),
ratio_pct2: MetricPattern4::new(client.clone(), _m(&acc, "pct2")),
ratio_pct2_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct2_usd")),
ratio_pct5: MetricPattern4::new(client.clone(), _m(&acc, "pct5")),
ratio_pct5_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct5_usd")),
ratio_pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")),
ratio_pct95_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct95_usd")),
ratio_pct98: MetricPattern4::new(client.clone(), _m(&acc, "pct98")),
ratio_pct98_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct98_usd")),
ratio_pct99: MetricPattern4::new(client.clone(), _m(&acc, "pct99")),
ratio_pct99_usd: MetricPattern4::new(client.clone(), _m(&acc, "pct99_usd")),
ratio_sd: Ratio1ySdPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct RelativePattern5 {
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
@@ -1600,40 +1600,6 @@ impl BitcoinPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct DollarsPattern<T> {
pub average: MetricPattern2<T>,
pub base: MetricPattern11<T>,
pub cumulative: MetricPattern1<T>,
pub max: MetricPattern2<T>,
pub median: MetricPattern6<T>,
pub min: MetricPattern2<T>,
pub pct10: MetricPattern6<T>,
pub pct25: MetricPattern6<T>,
pub pct75: MetricPattern6<T>,
pub pct90: MetricPattern6<T>,
pub sum: MetricPattern2<T>,
}
impl<T: DeserializeOwned> DollarsPattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
average: MetricPattern2::new(client.clone(), _m(&acc, "average")),
base: MetricPattern11::new(client.clone(), acc.clone()),
cumulative: MetricPattern1::new(client.clone(), _m(&acc, "cumulative")),
max: MetricPattern2::new(client.clone(), _m(&acc, "max")),
median: MetricPattern6::new(client.clone(), _m(&acc, "median")),
min: MetricPattern2::new(client.clone(), _m(&acc, "min")),
pct10: MetricPattern6::new(client.clone(), _m(&acc, "pct10")),
pct25: MetricPattern6::new(client.clone(), _m(&acc, "pct25")),
pct75: MetricPattern6::new(client.clone(), _m(&acc, "pct75")),
pct90: MetricPattern6::new(client.clone(), _m(&acc, "pct90")),
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct ClassAveragePricePattern<T> {
pub _2015: MetricPattern4<T>,
@@ -1669,33 +1635,35 @@ impl<T: DeserializeOwned> ClassAveragePricePattern<T> {
}
/// Pattern struct for repeated tree structure.
pub struct RelativePattern {
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
pub net_unrealized_pnl_rel_to_market_cap: MetricPattern1<StoredF32>,
pub nupl: MetricPattern1<StoredF32>,
pub supply_in_loss_rel_to_circulating_supply: MetricPattern1<StoredF64>,
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
pub supply_in_profit_rel_to_circulating_supply: MetricPattern1<StoredF64>,
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
pub supply_rel_to_circulating_supply: MetricPattern4<StoredF64>,
pub unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
pub unrealized_profit_rel_to_market_cap: MetricPattern1<StoredF32>,
pub struct DollarsPattern<T> {
pub average: MetricPattern2<T>,
pub base: MetricPattern11<T>,
pub cumulative: MetricPattern1<T>,
pub max: MetricPattern2<T>,
pub median: MetricPattern6<T>,
pub min: MetricPattern2<T>,
pub pct10: MetricPattern6<T>,
pub pct25: MetricPattern6<T>,
pub pct75: MetricPattern6<T>,
pub pct90: MetricPattern6<T>,
pub sum: MetricPattern2<T>,
}
impl RelativePattern {
impl<T: DeserializeOwned> DollarsPattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
neg_unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_market_cap")),
net_unrealized_pnl_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_market_cap")),
nupl: MetricPattern1::new(client.clone(), _m(&acc, "nupl")),
supply_in_loss_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_circulating_supply")),
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")),
supply_in_profit_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_circulating_supply")),
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")),
supply_rel_to_circulating_supply: MetricPattern4::new(client.clone(), _m(&acc, "supply_rel_to_circulating_supply")),
unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_market_cap")),
unrealized_profit_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_market_cap")),
average: MetricPattern2::new(client.clone(), _m(&acc, "average")),
base: MetricPattern11::new(client.clone(), acc.clone()),
cumulative: MetricPattern1::new(client.clone(), _m(&acc, "cumulative")),
max: MetricPattern2::new(client.clone(), _m(&acc, "max")),
median: MetricPattern6::new(client.clone(), _m(&acc, "median")),
min: MetricPattern2::new(client.clone(), _m(&acc, "min")),
pct10: MetricPattern6::new(client.clone(), _m(&acc, "pct10")),
pct25: MetricPattern6::new(client.clone(), _m(&acc, "pct25")),
pct75: MetricPattern6::new(client.clone(), _m(&acc, "pct75")),
pct90: MetricPattern6::new(client.clone(), _m(&acc, "pct90")),
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
}
}
}
@@ -1732,6 +1700,38 @@ impl RelativePattern2 {
}
}
/// Pattern struct for repeated tree structure.
pub struct RelativePattern {
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
pub net_unrealized_pnl_rel_to_market_cap: MetricPattern1<StoredF32>,
pub nupl: MetricPattern1<StoredF32>,
pub supply_in_loss_rel_to_circulating_supply: MetricPattern1<StoredF64>,
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
pub supply_in_profit_rel_to_circulating_supply: MetricPattern1<StoredF64>,
pub supply_in_profit_rel_to_own_supply: MetricPattern1<StoredF64>,
pub supply_rel_to_circulating_supply: MetricPattern4<StoredF64>,
pub unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
pub unrealized_profit_rel_to_market_cap: MetricPattern1<StoredF32>,
}
impl RelativePattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
neg_unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss_rel_to_market_cap")),
net_unrealized_pnl_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl_rel_to_market_cap")),
nupl: MetricPattern1::new(client.clone(), _m(&acc, "nupl")),
supply_in_loss_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_circulating_supply")),
supply_in_loss_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_loss_rel_to_own_supply")),
supply_in_profit_rel_to_circulating_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_circulating_supply")),
supply_in_profit_rel_to_own_supply: MetricPattern1::new(client.clone(), _m(&acc, "supply_in_profit_rel_to_own_supply")),
supply_rel_to_circulating_supply: MetricPattern4::new(client.clone(), _m(&acc, "supply_rel_to_circulating_supply")),
unrealized_loss_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss_rel_to_market_cap")),
unrealized_profit_rel_to_market_cap: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit_rel_to_market_cap")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CountPattern2<T> {
pub average: MetricPattern1<T>,
@@ -1794,36 +1794,6 @@ impl AddrCountPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct FeeRatePattern<T> {
pub average: MetricPattern1<T>,
pub max: MetricPattern1<T>,
pub median: MetricPattern11<T>,
pub min: MetricPattern1<T>,
pub pct10: MetricPattern11<T>,
pub pct25: MetricPattern11<T>,
pub pct75: MetricPattern11<T>,
pub pct90: MetricPattern11<T>,
pub txindex: MetricPattern27<T>,
}
impl<T: DeserializeOwned> FeeRatePattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
average: MetricPattern1::new(client.clone(), _m(&acc, "average")),
max: MetricPattern1::new(client.clone(), _m(&acc, "max")),
median: MetricPattern11::new(client.clone(), _m(&acc, "median")),
min: MetricPattern1::new(client.clone(), _m(&acc, "min")),
pct10: MetricPattern11::new(client.clone(), _m(&acc, "pct10")),
pct25: MetricPattern11::new(client.clone(), _m(&acc, "pct25")),
pct75: MetricPattern11::new(client.clone(), _m(&acc, "pct75")),
pct90: MetricPattern11::new(client.clone(), _m(&acc, "pct90")),
txindex: MetricPattern27::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct FullnessPattern<T> {
pub average: MetricPattern2<T>,
@@ -1854,6 +1824,36 @@ impl<T: DeserializeOwned> FullnessPattern<T> {
}
}
/// Pattern struct for repeated tree structure.
pub struct FeeRatePattern<T> {
pub average: MetricPattern1<T>,
pub max: MetricPattern1<T>,
pub median: MetricPattern11<T>,
pub min: MetricPattern1<T>,
pub pct10: MetricPattern11<T>,
pub pct25: MetricPattern11<T>,
pub pct75: MetricPattern11<T>,
pub pct90: MetricPattern11<T>,
pub txindex: MetricPattern27<T>,
}
impl<T: DeserializeOwned> FeeRatePattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
average: MetricPattern1::new(client.clone(), _m(&acc, "average")),
max: MetricPattern1::new(client.clone(), _m(&acc, "max")),
median: MetricPattern11::new(client.clone(), _m(&acc, "median")),
min: MetricPattern1::new(client.clone(), _m(&acc, "min")),
pct10: MetricPattern11::new(client.clone(), _m(&acc, "pct10")),
pct25: MetricPattern11::new(client.clone(), _m(&acc, "pct25")),
pct75: MetricPattern11::new(client.clone(), _m(&acc, "pct75")),
pct90: MetricPattern11::new(client.clone(), _m(&acc, "pct90")),
txindex: MetricPattern27::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _0satsPattern {
pub activity: ActivityPattern2,
@@ -1910,32 +1910,6 @@ impl<T: DeserializeOwned> PhaseDailyCentsPattern<T> {
}
}
/// Pattern struct for repeated tree structure.
pub struct _10yPattern {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern4,
pub relative: RelativePattern,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _10yPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern4::new(client.clone(), acc.clone()),
relative: RelativePattern::new(client.clone(), acc.clone()),
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct UnrealizedPattern {
pub neg_unrealized_loss: MetricPattern1<Dollars>,
@@ -1963,25 +1937,25 @@ impl UnrealizedPattern {
}
/// Pattern struct for repeated tree structure.
pub struct _0satsPattern2 {
pub struct _10yTo12yPattern {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub cost_basis: CostBasisPattern2,
pub outputs: OutputsPattern,
pub realized: RealizedPattern,
pub relative: RelativePattern4,
pub realized: RealizedPattern2,
pub relative: RelativePattern2,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _0satsPattern2 {
impl _10yTo12yPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern2::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern::new(client.clone(), acc.clone()),
relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")),
realized: RealizedPattern2::new(client.clone(), acc.clone()),
relative: RelativePattern2::new(client.clone(), acc.clone()),
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
}
@@ -2014,6 +1988,32 @@ impl PeriodCagrPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct _10yPattern {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern4,
pub relative: RelativePattern,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _10yPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern4::new(client.clone(), acc.clone()),
relative: RelativePattern::new(client.clone(), acc.clone()),
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _100btcPattern {
pub activity: ActivityPattern2,
@@ -2041,25 +2041,25 @@ impl _100btcPattern {
}
/// Pattern struct for repeated tree structure.
pub struct _10yTo12yPattern {
pub struct _0satsPattern2 {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern2,
pub relative: RelativePattern2,
pub realized: RealizedPattern,
pub relative: RelativePattern4,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _10yTo12yPattern {
impl _0satsPattern2 {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern2::new(client.clone(), acc.clone()),
relative: RelativePattern2::new(client.clone(), acc.clone()),
realized: RealizedPattern::new(client.clone(), acc.clone()),
relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")),
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
}
@@ -2108,6 +2108,42 @@ impl<T: DeserializeOwned> SplitPattern2<T> {
}
}
/// Pattern struct for repeated tree structure.
pub struct _2015Pattern {
pub bitcoin: MetricPattern4<Bitcoin>,
pub dollars: MetricPattern4<Dollars>,
pub sats: MetricPattern4<Sats>,
}
impl _2015Pattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: MetricPattern4::new(client.clone(), _m(&acc, "btc")),
dollars: MetricPattern4::new(client.clone(), _m(&acc, "usd")),
sats: MetricPattern4::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct ActiveSupplyPattern {
pub bitcoin: MetricPattern1<Bitcoin>,
pub dollars: MetricPattern1<Dollars>,
pub sats: MetricPattern1<Sats>,
}
impl ActiveSupplyPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")),
dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")),
sats: MetricPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CostBasisPattern2 {
pub max: MetricPattern1<Dollars>,
@@ -2126,6 +2162,24 @@ impl CostBasisPattern2 {
}
}
/// Pattern struct for repeated tree structure.
pub struct CoinbasePattern2 {
pub bitcoin: BlockCountPattern<Bitcoin>,
pub dollars: BlockCountPattern<Dollars>,
pub sats: BlockCountPattern<Sats>,
}
impl CoinbasePattern2 {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: BlockCountPattern::new(client.clone(), _m(&acc, "btc")),
dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")),
sats: BlockCountPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct SegwitAdoptionPattern {
pub base: MetricPattern11<StoredF32>,
@@ -2162,60 +2216,6 @@ impl CoinbasePattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct _2015Pattern {
pub bitcoin: MetricPattern4<Bitcoin>,
pub dollars: MetricPattern4<Dollars>,
pub sats: MetricPattern4<Sats>,
}
impl _2015Pattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: MetricPattern4::new(client.clone(), _m(&acc, "btc")),
dollars: MetricPattern4::new(client.clone(), _m(&acc, "usd")),
sats: MetricPattern4::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CoinbasePattern2 {
pub bitcoin: BlockCountPattern<Bitcoin>,
pub dollars: BlockCountPattern<Dollars>,
pub sats: BlockCountPattern<Sats>,
}
impl CoinbasePattern2 {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: BlockCountPattern::new(client.clone(), _m(&acc, "btc")),
dollars: BlockCountPattern::new(client.clone(), _m(&acc, "usd")),
sats: BlockCountPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct ActiveSupplyPattern {
pub bitcoin: MetricPattern1<Bitcoin>,
pub dollars: MetricPattern1<Dollars>,
pub sats: MetricPattern1<Sats>,
}
impl ActiveSupplyPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: MetricPattern1::new(client.clone(), _m(&acc, "btc")),
dollars: MetricPattern1::new(client.clone(), _m(&acc, "usd")),
sats: MetricPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct UnclaimedRewardsPattern {
pub bitcoin: BitcoinPattern2<Bitcoin>,
@@ -2234,22 +2234,6 @@ impl UnclaimedRewardsPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct _1dReturns1mSdPattern {
pub sd: MetricPattern4<StoredF32>,
pub sma: MetricPattern4<StoredF32>,
}
impl _1dReturns1mSdPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
sd: MetricPattern4::new(client.clone(), _m(&acc, "sd")),
sma: MetricPattern4::new(client.clone(), _m(&acc, "sma")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct SupplyPattern2 {
pub halved: ActiveSupplyPattern,
@@ -2298,6 +2282,22 @@ impl CostBasisPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct _1dReturns1mSdPattern {
pub sd: MetricPattern4<StoredF32>,
pub sma: MetricPattern4<StoredF32>,
}
impl _1dReturns1mSdPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
sd: MetricPattern4::new(client.clone(), _m(&acc, "sd")),
sma: MetricPattern4::new(client.clone(), _m(&acc, "sma")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct BitcoinPattern2<T> {
pub cumulative: MetricPattern2<T>,
@@ -2340,22 +2340,8 @@ impl<T: DeserializeOwned> SatsPattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
ohlc: MetricPattern1::new(client.clone(), _m(&acc, "ohlc_sats")),
split: SplitPattern2::new(client.clone(), _m(&acc, "sats")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct OutputsPattern {
pub utxo_count: MetricPattern1<StoredU64>,
}
impl OutputsPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
utxo_count: MetricPattern1::new(client.clone(), acc.clone()),
ohlc: MetricPattern1::new(client.clone(), _m(&acc, "ohlc")),
split: SplitPattern2::new(client.clone(), acc.clone()),
}
}
}
@@ -2374,6 +2360,20 @@ impl RealizedPriceExtraPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct OutputsPattern {
pub utxo_count: MetricPattern1<StoredU64>,
}
impl OutputsPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
utxo_count: MetricPattern1::new(client.clone(), acc.clone()),
}
}
}
// Metrics tree
/// Metrics tree node.
@@ -3932,7 +3932,7 @@ pub struct MetricsTree_Indexes_Height {
impl MetricsTree_Indexes_Height {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
dateindex: MetricPattern11::new(client.clone(), "height_dateindex".to_string()),
dateindex: MetricPattern11::new(client.clone(), "dateindex".to_string()),
difficultyepoch: MetricPattern11::new(client.clone(), "difficultyepoch".to_string()),
halvingepoch: MetricPattern11::new(client.clone(), "halvingepoch".to_string()),
identity: MetricPattern11::new(client.clone(), "height".to_string()),
@@ -4942,8 +4942,8 @@ impl MetricsTree_Positions {
pub struct MetricsTree_Price {
pub cents: MetricsTree_Price_Cents,
pub oracle: MetricsTree_Price_Oracle,
pub sats: SatsPattern<OHLCSats>,
pub usd: MetricsTree_Price_Usd,
pub sats: MetricsTree_Price_Sats,
pub usd: SatsPattern<OHLCDollars>,
}
impl MetricsTree_Price {
@@ -4951,8 +4951,8 @@ impl MetricsTree_Price {
Self {
cents: MetricsTree_Price_Cents::new(client.clone(), format!("{base_path}_cents")),
oracle: MetricsTree_Price_Oracle::new(client.clone(), format!("{base_path}_oracle")),
sats: SatsPattern::new(client.clone(), "price".to_string()),
usd: MetricsTree_Price_Usd::new(client.clone(), format!("{base_path}_usd")),
sats: MetricsTree_Price_Sats::new(client.clone(), format!("{base_path}_sats")),
usd: SatsPattern::new(client.clone(), "price".to_string()),
}
}
}
@@ -5027,16 +5027,16 @@ impl MetricsTree_Price_Oracle {
}
/// Metrics tree node.
pub struct MetricsTree_Price_Usd {
pub ohlc: MetricPattern1<OHLCDollars>,
pub split: SplitPattern2<Dollars>,
pub struct MetricsTree_Price_Sats {
pub ohlc: MetricPattern1<OHLCSats>,
pub split: SplitPattern2<Sats>,
}
impl MetricsTree_Price_Usd {
impl MetricsTree_Price_Sats {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
ohlc: MetricPattern1::new(client.clone(), "price_ohlc".to_string()),
split: SplitPattern2::new(client.clone(), "price".to_string()),
ohlc: MetricPattern1::new(client.clone(), "price_ohlc_sats".to_string()),
split: SplitPattern2::new(client.clone(), "price_sats".to_string()),
}
}
}

View File

@@ -17,7 +17,7 @@ impl Vecs {
pub fn forced_import(db: &Database, version: Version) -> Result<Self> {
Ok(Self {
identity: EagerVec::forced_import(db, "height", version)?,
dateindex: EagerVec::forced_import(db, "height_dateindex", version)?,
dateindex: EagerVec::forced_import(db, "dateindex", version)?,
difficultyepoch: EagerVec::forced_import(db, "difficultyepoch", version)?,
halvingepoch: EagerVec::forced_import(db, "halvingepoch", version)?,
txindex_count: EagerVec::forced_import(db, "txindex_count", version)?,

View File

@@ -24,8 +24,8 @@ impl Vecs {
let phase_histogram = BytesVec::forced_import(db, "phase_histogram", version)?;
// Layer 5: Phase Oracle prices
// v32: Revert to simple anchor-based decade selection (no prev_price tracking)
let phase_version = version + Version::new(25);
// v45: Back to decades (10x) + anchor only
let phase_version = version + Version::new(38);
let phase_price_cents = PcoVec::forced_import(db, "phase_price_cents", phase_version)?;
let phase_daily_cents = Distribution::forced_import(db, "phase_daily", phase_version)?;
let phase_daily_dollars = LazyTransformDistribution::from_distribution::<CentsToDollars>(

View File

@@ -33,7 +33,7 @@ serde = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
tower-http = { version = "0.6.8", features = ["compression-full", "trace"] }
tower-http = { version = "0.6.8", features = ["catch-panic", "compression-full", "cors", "normalize-path", "timeout", "trace"] }
[build-dependencies]
# importmap = { path = "../../../importmap" }

View File

@@ -14,7 +14,8 @@ fn main() {
let map = if is_dev {
importmap::ImportMap::empty()
} else {
importmap::ImportMap::scan(&website_path, "").unwrap_or_else(|_| importmap::ImportMap::empty())
importmap::ImportMap::scan(&website_path, "")
.unwrap_or_else(|_| importmap::ImportMap::empty())
};
let _ = map.update_html_file(&website_path.join("index.html"));

View File

@@ -1,10 +1,5 @@
use aide::axum::{ApiRouter, routing::get_with};
use axum::{
extract::State,
http::HeaderMap,
response::Redirect,
routing::get,
};
use axum::{extract::State, http::HeaderMap, response::Redirect, routing::get};
use brk_types::{MempoolBlock, MempoolInfo, RecommendedFees, Txid};
use crate::{CacheStrategy, extended::TransformResponseExtended};

View File

@@ -6,6 +6,7 @@ use axum::{
http::{HeaderMap, StatusCode, Uri},
response::{IntoResponse, Response},
};
use brk_error::Result;
use brk_types::{Format, MetricSelection, Output};
use quick_cache::sync::GuardResult;
@@ -24,12 +25,7 @@ pub async fn handler(
) -> Response {
match req_to_response_res(uri, headers, query, state).await {
Ok(response) => response,
Err(error) => {
let mut response =
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
response.headers_mut().insert_cors();
response
}
Err(error) => (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(),
}
}
@@ -38,19 +34,16 @@ async fn req_to_response_res(
headers: HeaderMap,
Query(params): Query<MetricSelection>,
AppState { query, cache, .. }: AppState,
) -> brk_error::Result<Response> {
) -> Result<Response> {
// Phase 1: Search and resolve metadata (cheap)
let resolved = query
.run(move |q| q.resolve(params, MAX_WEIGHT))
.await?;
let resolved = query.run(move |q| q.resolve(params, MAX_WEIGHT)).await?;
let format = resolved.format();
let etag = resolved.etag();
// Check if client has fresh cache
if headers.has_etag(etag.as_str()) {
let mut response = (StatusCode::NOT_MODIFIED, "").into_response();
response.headers_mut().insert_cors();
let response = (StatusCode::NOT_MODIFIED, "").into_response();
return Ok(response);
}
@@ -81,7 +74,6 @@ async fn req_to_response_res(
};
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_etag(etag.as_str());
headers.insert_cache_control(CACHE_CONTROL);

View File

@@ -6,6 +6,7 @@ use axum::{
http::{HeaderMap, StatusCode, Uri},
response::{IntoResponse, Response},
};
use brk_error::Result;
use brk_types::{Format, MetricSelection, Output};
use quick_cache::sync::GuardResult;
@@ -24,12 +25,7 @@ pub async fn handler(
) -> Response {
match req_to_response_res(uri, headers, query, state).await {
Ok(response) => response,
Err(error) => {
let mut response =
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
response.headers_mut().insert_cors();
response
}
Err(error) => (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(),
}
}
@@ -38,19 +34,16 @@ async fn req_to_response_res(
headers: HeaderMap,
Query(params): Query<MetricSelection>,
AppState { query, cache, .. }: AppState,
) -> brk_error::Result<Response> {
) -> Result<Response> {
// Phase 1: Search and resolve metadata (cheap)
let resolved = query
.run(move |q| q.resolve(params, MAX_WEIGHT))
.await?;
let resolved = query.run(move |q| q.resolve(params, MAX_WEIGHT)).await?;
let format = resolved.format();
let etag = resolved.etag();
// Check if client has fresh cache
if headers.has_etag(etag.as_str()) {
let mut response = (StatusCode::NOT_MODIFIED, "").into_response();
response.headers_mut().insert_cors();
let response = (StatusCode::NOT_MODIFIED, "").into_response();
return Ok(response);
}
@@ -81,7 +74,6 @@ async fn req_to_response_res(
};
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_etag(etag.as_str());
headers.insert_cache_control(CACHE_CONTROL);

View File

@@ -6,6 +6,7 @@ use axum::{
http::{HeaderMap, StatusCode, Uri},
response::{IntoResponse, Response},
};
use brk_error::Result;
use brk_types::{Format, MetricSelection, OutputLegacy};
use quick_cache::sync::GuardResult;
@@ -24,12 +25,7 @@ pub async fn handler(
) -> Response {
match req_to_response_res(uri, headers, query, state).await {
Ok(response) => response,
Err(error) => {
let mut response =
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
response.headers_mut().insert_cors();
response
}
Err(error) => (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(),
}
}
@@ -38,19 +34,16 @@ async fn req_to_response_res(
headers: HeaderMap,
Query(params): Query<MetricSelection>,
AppState { query, cache, .. }: AppState,
) -> brk_error::Result<Response> {
) -> Result<Response> {
// Phase 1: Search and resolve metadata (cheap)
let resolved = query
.run(move |q| q.resolve(params, MAX_WEIGHT))
.await?;
let resolved = query.run(move |q| q.resolve(params, MAX_WEIGHT)).await?;
let format = resolved.format();
let etag = resolved.etag();
// Check if client has fresh cache
if headers.has_etag(etag.as_str()) {
let mut response = (StatusCode::NOT_MODIFIED, "").into_response();
response.headers_mut().insert_cors();
let response = (StatusCode::NOT_MODIFIED, "").into_response();
return Ok(response);
}
@@ -62,9 +55,7 @@ async fn req_to_response_res(
Response::new(Body::from(v))
} else {
// Phase 2: Format (expensive, only on cache miss)
let metric_output = query
.run(move |q| q.format_legacy(resolved))
.await?;
let metric_output = query.run(move |q| q.format_legacy(resolved)).await?;
match metric_output.output {
OutputLegacy::CSV(s) => {
@@ -84,7 +75,6 @@ async fn req_to_response_res(
};
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_etag(etag.as_str());
headers.insert_cache_control(CACHE_CONTROL);

View File

@@ -6,9 +6,9 @@ use axum::{
routing::get,
};
use brk_types::{
BlockCountParam, BlockFeesEntry, BlockRewardsEntry, BlockSizesWeights,
DifficultyAdjustment, DifficultyAdjustmentEntry, HashrateSummary, PoolDetail, PoolInfo,
PoolSlugParam, PoolsSummary, RewardStats, TimePeriodParam,
BlockCountParam, BlockFeesEntry, BlockRewardsEntry, BlockSizesWeights, DifficultyAdjustment,
DifficultyAdjustmentEntry, HashrateSummary, PoolDetail, PoolInfo, PoolSlugParam, PoolsSummary,
RewardStats, TimePeriodParam,
};
use crate::{CacheStrategy, extended::TransformResponseExtended};

View File

@@ -377,7 +377,10 @@ mod tests {
let props = &parsed["components"]["schemas"]["AddressStats"]["properties"];
assert_eq!(props["address"], "Address", "address should be simplified");
assert_eq!(props["chain_stats"], "AddressChainStats", "chain_stats should be simplified");
assert_eq!(
props["chain_stats"], "AddressChainStats",
"chain_stats should be simplified"
);
}
#[test]

View File

@@ -19,8 +19,6 @@ pub enum ModifiedState {
}
pub trait HeaderMapExtended {
fn insert_cors(&mut self);
fn has_etag(&self, etag: &str) -> bool;
fn get_if_modified_since(&self) -> Option<DateTime>;
@@ -52,11 +50,6 @@ pub trait HeaderMapExtended {
}
impl HeaderMapExtended for HeaderMap {
fn insert_cors(&mut self) {
self.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*".parse().unwrap());
self.insert(header::ACCESS_CONTROL_ALLOW_HEADERS, "*".parse().unwrap());
}
fn insert_cache_control(&mut self, value: &str) {
self.insert(header::CACHE_CONTROL, value.parse().unwrap());
}

View File

@@ -36,8 +36,7 @@ where
impl ResponseExtended for Response<Body> {
fn new_not_modified() -> Response<Body> {
let mut response = (StatusCode::NOT_MODIFIED, "").into_response();
let headers = response.headers_mut();
headers.insert_cors();
let _headers = response.headers_mut();
response
}
@@ -56,7 +55,6 @@ impl ResponseExtended for Response<Body> {
let mut response = Response::builder().body(bytes.into()).unwrap();
*response.status_mut() = status;
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type_application_json();
headers.insert_cache_control_must_revalidate();
headers.insert_etag(etag);
@@ -68,12 +66,9 @@ impl ResponseExtended for Response<Body> {
}
fn new_text_with(status: StatusCode, value: &str, etag: &str) -> Self {
let mut response = Response::builder()
.body(value.to_string().into())
.unwrap();
let mut response = Response::builder().body(value.to_string().into()).unwrap();
*response.status_mut() = status;
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type_text_plain();
headers.insert_cache_control_must_revalidate();
headers.insert_etag(etag);
@@ -88,7 +83,6 @@ impl ResponseExtended for Response<Body> {
let mut response = Response::builder().body(value.into()).unwrap();
*response.status_mut() = status;
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type_octet_stream();
headers.insert_cache_control_must_revalidate();
headers.insert_etag(etag);
@@ -102,7 +96,6 @@ impl ResponseExtended for Response<Body> {
let bytes = serde_json::to_vec(&value).unwrap();
let mut response = Response::builder().body(bytes.into()).unwrap();
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type_application_json();
headers.insert_cache_control(&params.cache_control);
if let Some(etag) = &params.etag {
@@ -123,11 +116,8 @@ impl ResponseExtended for Response<Body> {
}
fn new_text_cached(value: &str, params: &CacheParams) -> Self {
let mut response = Response::builder()
.body(value.to_string().into())
.unwrap();
let mut response = Response::builder().body(value.to_string().into()).unwrap();
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type_text_plain();
headers.insert_cache_control(&params.cache_control);
if let Some(etag) = &params.etag {
@@ -139,7 +129,6 @@ impl ResponseExtended for Response<Body> {
fn new_bytes_cached(value: Vec<u8>, params: &CacheParams) -> Self {
let mut response = Response::builder().body(value.into()).unwrap();
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type_octet_stream();
headers.insert_cache_control(&params.cache_control);
if let Some(etag) = &params.etag {

View File

@@ -81,7 +81,6 @@ fn build_response(state: &AppState, path: &Path, content: Vec<u8>, cache_key: &s
};
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type(path);
if cfg!(debug_assertions) || must_revalidate {
@@ -114,9 +113,8 @@ fn embedded_handler(state: &AppState, path: Option<String>) -> Response {
});
let Some(file) = file else {
let mut response: Response<Body> =
let response: Response<Body> =
(StatusCode::NOT_FOUND, "File not found".to_string()).into_response();
response.headers_mut().insert_cors();
return response;
};
@@ -147,9 +145,8 @@ fn filesystem_handler(
let allowed = canonical.starts_with(&canonical_base)
|| project_root.is_some_and(|root| canonical.starts_with(root));
if !allowed {
let mut response: Response<Body> =
let response: Response<Body> =
(StatusCode::FORBIDDEN, "Access denied".to_string()).into_response();
response.headers_mut().insert_cors();
return response;
}
}
@@ -165,12 +162,11 @@ fn filesystem_handler(
// SPA fallback
if !path.exists() || path.is_dir() {
if path.extension().is_some() {
let mut response: Response<Body> = (
let response: Response<Body> = (
StatusCode::INTERNAL_SERVER_ERROR,
"File doesn't exist".to_string(),
)
.into_response();
response.headers_mut().insert_cors();
return response;
} else {
path = files_path.join("index.html");
@@ -188,12 +184,7 @@ fn filesystem_handler(
fn path_to_response(headers: &HeaderMap, state: &AppState, path: &Path) -> Response {
match path_to_response_(headers, state, path) {
Ok(response) => response,
Err(error) => {
let mut response =
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
response.headers_mut().insert_cors();
response
}
Err(error) => (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(),
}
}

View File

@@ -1,6 +1,11 @@
#![doc = include_str!("../README.md")]
use std::{panic, path::PathBuf, sync::Arc, time::{Duration, Instant}};
use std::{
panic,
path::PathBuf,
sync::Arc,
time::{Duration, Instant},
};
use aide::axum::ApiRouter;
use axum::{
@@ -14,10 +19,14 @@ use axum::{
};
use brk_error::Result;
use brk_query::AsyncQuery;
use include_dir::{include_dir, Dir};
use include_dir::{Dir, include_dir};
use quick_cache::sync::Cache;
use tokio::net::TcpListener;
use tower_http::{compression::CompressionLayer, trace::TraceLayer};
use tower_http::{
catch_panic::CatchPanicLayer, classify::ServerErrorsFailureClass,
compression::CompressionLayer, cors::CorsLayer, normalize_path::NormalizePathLayer,
timeout::TimeoutLayer, trace::TraceLayer,
};
use tracing::{error, info};
/// Embedded website assets
@@ -86,19 +95,25 @@ impl Server {
let trace_layer = TraceLayer::new_for_http()
.on_request(())
.on_response(|response: &Response<Body>, latency: Duration, _: &tracing::Span| {
let status = response.status().as_u16();
let uri = response.extensions().get::<Uri>().unwrap();
match response.status() {
StatusCode::OK => info!(status, %uri, ?latency),
StatusCode::NOT_MODIFIED
| StatusCode::TEMPORARY_REDIRECT
| StatusCode::PERMANENT_REDIRECT => info!(status, %uri, ?latency),
_ => error!(status, %uri, ?latency),
}
})
.on_response(
|response: &Response<Body>, latency: Duration, _: &tracing::Span| {
let status = response.status().as_u16();
let uri = response.extensions().get::<Uri>().unwrap();
match response.status() {
StatusCode::OK => info!(status, %uri, ?latency),
StatusCode::NOT_MODIFIED
| StatusCode::TEMPORARY_REDIRECT
| StatusCode::PERMANENT_REDIRECT => info!(status, %uri, ?latency),
_ => error!(status, %uri, ?latency),
}
},
)
.on_body_chunk(())
.on_failure(())
.on_failure(
|error: ServerErrorsFailureClass, latency: Duration, _: &tracing::Span| {
error!(?error, ?latency, "request failed");
},
)
.on_eos(());
let vecs = state.query.inner().vecs();
@@ -126,9 +141,13 @@ impl Server {
)
.route("/nostr", get(Redirect::temporary("https://primal.net/p/npub1jagmm3x39lmwfnrtvxcs9ac7g300y3dusv9lgzhk2e4x5frpxlrqa73v44")))
.with_state(state)
.layer(CatchPanicLayer::new())
.layer(compression_layer)
.layer(response_uri_layer)
.layer(trace_layer);
.layer(trace_layer)
.layer(TimeoutLayer::with_status_code(StatusCode::GATEWAY_TIMEOUT, Duration::from_secs(5)))
.layer(CorsLayer::permissive())
.layer(NormalizePathLayer::trim_trailing_slash());
const BASE_PORT: u16 = 3110;
const MAX_PORT: u16 = BASE_PORT + 100;

View File

@@ -1644,59 +1644,6 @@ function createPrice111dSmaPattern(client, acc) {
};
}
/**
* @typedef {Object} ActivePriceRatioPattern
* @property {MetricPattern4<StoredF32>} ratio
* @property {MetricPattern4<StoredF32>} ratio1mSma
* @property {MetricPattern4<StoredF32>} ratio1wSma
* @property {Ratio1ySdPattern} ratio1ySd
* @property {Ratio1ySdPattern} ratio2ySd
* @property {Ratio1ySdPattern} ratio4ySd
* @property {MetricPattern4<StoredF32>} ratioPct1
* @property {MetricPattern4<Dollars>} ratioPct1Usd
* @property {MetricPattern4<StoredF32>} ratioPct2
* @property {MetricPattern4<Dollars>} ratioPct2Usd
* @property {MetricPattern4<StoredF32>} ratioPct5
* @property {MetricPattern4<Dollars>} ratioPct5Usd
* @property {MetricPattern4<StoredF32>} ratioPct95
* @property {MetricPattern4<Dollars>} ratioPct95Usd
* @property {MetricPattern4<StoredF32>} ratioPct98
* @property {MetricPattern4<Dollars>} ratioPct98Usd
* @property {MetricPattern4<StoredF32>} ratioPct99
* @property {MetricPattern4<Dollars>} ratioPct99Usd
* @property {Ratio1ySdPattern} ratioSd
*/
/**
* Create a ActivePriceRatioPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {ActivePriceRatioPattern}
*/
function createActivePriceRatioPattern(client, acc) {
return {
ratio: createMetricPattern4(client, acc),
ratio1mSma: createMetricPattern4(client, _m(acc, '1m_sma')),
ratio1wSma: createMetricPattern4(client, _m(acc, '1w_sma')),
ratio1ySd: createRatio1ySdPattern(client, _m(acc, '1y')),
ratio2ySd: createRatio1ySdPattern(client, _m(acc, '2y')),
ratio4ySd: createRatio1ySdPattern(client, _m(acc, '4y')),
ratioPct1: createMetricPattern4(client, _m(acc, 'pct1')),
ratioPct1Usd: createMetricPattern4(client, _m(acc, 'pct1_usd')),
ratioPct2: createMetricPattern4(client, _m(acc, 'pct2')),
ratioPct2Usd: createMetricPattern4(client, _m(acc, 'pct2_usd')),
ratioPct5: createMetricPattern4(client, _m(acc, 'pct5')),
ratioPct5Usd: createMetricPattern4(client, _m(acc, 'pct5_usd')),
ratioPct95: createMetricPattern4(client, _m(acc, 'pct95')),
ratioPct95Usd: createMetricPattern4(client, _m(acc, 'pct95_usd')),
ratioPct98: createMetricPattern4(client, _m(acc, 'pct98')),
ratioPct98Usd: createMetricPattern4(client, _m(acc, 'pct98_usd')),
ratioPct99: createMetricPattern4(client, _m(acc, 'pct99')),
ratioPct99Usd: createMetricPattern4(client, _m(acc, 'pct99_usd')),
ratioSd: createRatio1ySdPattern(client, acc),
};
}
/**
* @typedef {Object} PercentilesPattern
* @property {MetricPattern4<Dollars>} pct05
@@ -1750,6 +1697,59 @@ function createPercentilesPattern(client, acc) {
};
}
/**
* @typedef {Object} ActivePriceRatioPattern
* @property {MetricPattern4<StoredF32>} ratio
* @property {MetricPattern4<StoredF32>} ratio1mSma
* @property {MetricPattern4<StoredF32>} ratio1wSma
* @property {Ratio1ySdPattern} ratio1ySd
* @property {Ratio1ySdPattern} ratio2ySd
* @property {Ratio1ySdPattern} ratio4ySd
* @property {MetricPattern4<StoredF32>} ratioPct1
* @property {MetricPattern4<Dollars>} ratioPct1Usd
* @property {MetricPattern4<StoredF32>} ratioPct2
* @property {MetricPattern4<Dollars>} ratioPct2Usd
* @property {MetricPattern4<StoredF32>} ratioPct5
* @property {MetricPattern4<Dollars>} ratioPct5Usd
* @property {MetricPattern4<StoredF32>} ratioPct95
* @property {MetricPattern4<Dollars>} ratioPct95Usd
* @property {MetricPattern4<StoredF32>} ratioPct98
* @property {MetricPattern4<Dollars>} ratioPct98Usd
* @property {MetricPattern4<StoredF32>} ratioPct99
* @property {MetricPattern4<Dollars>} ratioPct99Usd
* @property {Ratio1ySdPattern} ratioSd
*/
/**
* Create a ActivePriceRatioPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {ActivePriceRatioPattern}
*/
function createActivePriceRatioPattern(client, acc) {
return {
ratio: createMetricPattern4(client, acc),
ratio1mSma: createMetricPattern4(client, _m(acc, '1m_sma')),
ratio1wSma: createMetricPattern4(client, _m(acc, '1w_sma')),
ratio1ySd: createRatio1ySdPattern(client, _m(acc, '1y')),
ratio2ySd: createRatio1ySdPattern(client, _m(acc, '2y')),
ratio4ySd: createRatio1ySdPattern(client, _m(acc, '4y')),
ratioPct1: createMetricPattern4(client, _m(acc, 'pct1')),
ratioPct1Usd: createMetricPattern4(client, _m(acc, 'pct1_usd')),
ratioPct2: createMetricPattern4(client, _m(acc, 'pct2')),
ratioPct2Usd: createMetricPattern4(client, _m(acc, 'pct2_usd')),
ratioPct5: createMetricPattern4(client, _m(acc, 'pct5')),
ratioPct5Usd: createMetricPattern4(client, _m(acc, 'pct5_usd')),
ratioPct95: createMetricPattern4(client, _m(acc, 'pct95')),
ratioPct95Usd: createMetricPattern4(client, _m(acc, 'pct95_usd')),
ratioPct98: createMetricPattern4(client, _m(acc, 'pct98')),
ratioPct98Usd: createMetricPattern4(client, _m(acc, 'pct98_usd')),
ratioPct99: createMetricPattern4(client, _m(acc, 'pct99')),
ratioPct99Usd: createMetricPattern4(client, _m(acc, 'pct99_usd')),
ratioSd: createRatio1ySdPattern(client, acc),
};
}
/**
* @typedef {Object} RelativePattern5
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
@@ -2004,45 +2004,6 @@ function createBitcoinPattern(client, acc) {
};
}
/**
* @template T
* @typedef {Object} DollarsPattern
* @property {MetricPattern2<T>} average
* @property {MetricPattern11<T>} base
* @property {MetricPattern1<T>} cumulative
* @property {MetricPattern2<T>} max
* @property {MetricPattern6<T>} median
* @property {MetricPattern2<T>} min
* @property {MetricPattern6<T>} pct10
* @property {MetricPattern6<T>} pct25
* @property {MetricPattern6<T>} pct75
* @property {MetricPattern6<T>} pct90
* @property {MetricPattern2<T>} sum
*/
/**
* Create a DollarsPattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {DollarsPattern<T>}
*/
function createDollarsPattern(client, acc) {
return {
average: createMetricPattern2(client, _m(acc, 'average')),
base: createMetricPattern11(client, acc),
cumulative: createMetricPattern1(client, _m(acc, 'cumulative')),
max: createMetricPattern2(client, _m(acc, 'max')),
median: createMetricPattern6(client, _m(acc, 'median')),
min: createMetricPattern2(client, _m(acc, 'min')),
pct10: createMetricPattern6(client, _m(acc, 'pct10')),
pct25: createMetricPattern6(client, _m(acc, 'pct25')),
pct75: createMetricPattern6(client, _m(acc, 'pct75')),
pct90: createMetricPattern6(client, _m(acc, 'pct90')),
sum: createMetricPattern2(client, _m(acc, 'sum')),
};
}
/**
* @template T
* @typedef {Object} ClassAveragePricePattern
@@ -2083,37 +2044,41 @@ function createClassAveragePricePattern(client, acc) {
}
/**
* @typedef {Object} RelativePattern
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} netUnrealizedPnlRelToMarketCap
* @property {MetricPattern1<StoredF32>} nupl
* @property {MetricPattern1<StoredF64>} supplyInLossRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInLossRelToOwnSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToOwnSupply
* @property {MetricPattern4<StoredF64>} supplyRelToCirculatingSupply
* @property {MetricPattern1<StoredF32>} unrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} unrealizedProfitRelToMarketCap
* @template T
* @typedef {Object} DollarsPattern
* @property {MetricPattern2<T>} average
* @property {MetricPattern11<T>} base
* @property {MetricPattern1<T>} cumulative
* @property {MetricPattern2<T>} max
* @property {MetricPattern6<T>} median
* @property {MetricPattern2<T>} min
* @property {MetricPattern6<T>} pct10
* @property {MetricPattern6<T>} pct25
* @property {MetricPattern6<T>} pct75
* @property {MetricPattern6<T>} pct90
* @property {MetricPattern2<T>} sum
*/
/**
* Create a RelativePattern pattern node
* Create a DollarsPattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {RelativePattern}
* @returns {DollarsPattern<T>}
*/
function createRelativePattern(client, acc) {
function createDollarsPattern(client, acc) {
return {
negUnrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap')),
netUnrealizedPnlRelToMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap')),
nupl: createMetricPattern1(client, _m(acc, 'nupl')),
supplyInLossRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply')),
supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')),
supplyInProfitRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply')),
supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')),
supplyRelToCirculatingSupply: createMetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply')),
unrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap')),
unrealizedProfitRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap')),
average: createMetricPattern2(client, _m(acc, 'average')),
base: createMetricPattern11(client, acc),
cumulative: createMetricPattern1(client, _m(acc, 'cumulative')),
max: createMetricPattern2(client, _m(acc, 'max')),
median: createMetricPattern6(client, _m(acc, 'median')),
min: createMetricPattern2(client, _m(acc, 'min')),
pct10: createMetricPattern6(client, _m(acc, 'pct10')),
pct25: createMetricPattern6(client, _m(acc, 'pct25')),
pct75: createMetricPattern6(client, _m(acc, 'pct75')),
pct90: createMetricPattern6(client, _m(acc, 'pct90')),
sum: createMetricPattern2(client, _m(acc, 'sum')),
};
}
@@ -2152,6 +2117,41 @@ function createRelativePattern2(client, acc) {
};
}
/**
* @typedef {Object} RelativePattern
* @property {MetricPattern1<StoredF32>} negUnrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} netUnrealizedPnlRelToMarketCap
* @property {MetricPattern1<StoredF32>} nupl
* @property {MetricPattern1<StoredF64>} supplyInLossRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInLossRelToOwnSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToCirculatingSupply
* @property {MetricPattern1<StoredF64>} supplyInProfitRelToOwnSupply
* @property {MetricPattern4<StoredF64>} supplyRelToCirculatingSupply
* @property {MetricPattern1<StoredF32>} unrealizedLossRelToMarketCap
* @property {MetricPattern1<StoredF32>} unrealizedProfitRelToMarketCap
*/
/**
* Create a RelativePattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {RelativePattern}
*/
function createRelativePattern(client, acc) {
return {
negUnrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap')),
netUnrealizedPnlRelToMarketCap: createMetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap')),
nupl: createMetricPattern1(client, _m(acc, 'nupl')),
supplyInLossRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply')),
supplyInLossRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply')),
supplyInProfitRelToCirculatingSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply')),
supplyInProfitRelToOwnSupply: createMetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply')),
supplyRelToCirculatingSupply: createMetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply')),
unrealizedLossRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap')),
unrealizedProfitRelToMarketCap: createMetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap')),
};
}
/**
* @template T
* @typedef {Object} CountPattern2
@@ -2222,41 +2222,6 @@ function createAddrCountPattern(client, acc) {
};
}
/**
* @template T
* @typedef {Object} FeeRatePattern
* @property {MetricPattern1<T>} average
* @property {MetricPattern1<T>} max
* @property {MetricPattern11<T>} median
* @property {MetricPattern1<T>} min
* @property {MetricPattern11<T>} pct10
* @property {MetricPattern11<T>} pct25
* @property {MetricPattern11<T>} pct75
* @property {MetricPattern11<T>} pct90
* @property {MetricPattern27<T>} txindex
*/
/**
* Create a FeeRatePattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {FeeRatePattern<T>}
*/
function createFeeRatePattern(client, acc) {
return {
average: createMetricPattern1(client, _m(acc, 'average')),
max: createMetricPattern1(client, _m(acc, 'max')),
median: createMetricPattern11(client, _m(acc, 'median')),
min: createMetricPattern1(client, _m(acc, 'min')),
pct10: createMetricPattern11(client, _m(acc, 'pct10')),
pct25: createMetricPattern11(client, _m(acc, 'pct25')),
pct75: createMetricPattern11(client, _m(acc, 'pct75')),
pct90: createMetricPattern11(client, _m(acc, 'pct90')),
txindex: createMetricPattern27(client, acc),
};
}
/**
* @template T
* @typedef {Object} FullnessPattern
@@ -2292,6 +2257,41 @@ function createFullnessPattern(client, acc) {
};
}
/**
* @template T
* @typedef {Object} FeeRatePattern
* @property {MetricPattern1<T>} average
* @property {MetricPattern1<T>} max
* @property {MetricPattern11<T>} median
* @property {MetricPattern1<T>} min
* @property {MetricPattern11<T>} pct10
* @property {MetricPattern11<T>} pct25
* @property {MetricPattern11<T>} pct75
* @property {MetricPattern11<T>} pct90
* @property {MetricPattern27<T>} txindex
*/
/**
* Create a FeeRatePattern pattern node
* @template T
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {FeeRatePattern<T>}
*/
function createFeeRatePattern(client, acc) {
return {
average: createMetricPattern1(client, _m(acc, 'average')),
max: createMetricPattern1(client, _m(acc, 'max')),
median: createMetricPattern11(client, _m(acc, 'median')),
min: createMetricPattern1(client, _m(acc, 'min')),
pct10: createMetricPattern11(client, _m(acc, 'pct10')),
pct25: createMetricPattern11(client, _m(acc, 'pct25')),
pct75: createMetricPattern11(client, _m(acc, 'pct75')),
pct90: createMetricPattern11(client, _m(acc, 'pct90')),
txindex: createMetricPattern27(client, acc),
};
}
/**
* @typedef {Object} _0satsPattern
* @property {ActivityPattern2} activity
@@ -2356,35 +2356,6 @@ function createPhaseDailyCentsPattern(client, acc) {
};
}
/**
* @typedef {Object} _10yPattern
* @property {ActivityPattern2} activity
* @property {CostBasisPattern} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern4} realized
* @property {RelativePattern} relative
* @property {SupplyPattern2} supply
* @property {UnrealizedPattern} unrealized
*/
/**
* Create a _10yPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_10yPattern}
*/
function create_10yPattern(client, acc) {
return {
activity: createActivityPattern2(client, acc),
costBasis: createCostBasisPattern(client, acc),
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
realized: createRealizedPattern4(client, acc),
relative: createRelativePattern(client, acc),
supply: createSupplyPattern2(client, _m(acc, 'supply')),
unrealized: createUnrealizedPattern(client, acc),
};
}
/**
* @typedef {Object} UnrealizedPattern
* @property {MetricPattern1<Dollars>} negUnrealizedLoss
@@ -2415,29 +2386,29 @@ function createUnrealizedPattern(client, acc) {
}
/**
* @typedef {Object} _0satsPattern2
* @typedef {Object} _10yTo12yPattern
* @property {ActivityPattern2} activity
* @property {CostBasisPattern} costBasis
* @property {CostBasisPattern2} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern} realized
* @property {RelativePattern4} relative
* @property {RealizedPattern2} realized
* @property {RelativePattern2} relative
* @property {SupplyPattern2} supply
* @property {UnrealizedPattern} unrealized
*/
/**
* Create a _0satsPattern2 pattern node
* Create a _10yTo12yPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_0satsPattern2}
* @returns {_10yTo12yPattern}
*/
function create_0satsPattern2(client, acc) {
function create_10yTo12yPattern(client, acc) {
return {
activity: createActivityPattern2(client, acc),
costBasis: createCostBasisPattern(client, acc),
costBasis: createCostBasisPattern2(client, acc),
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
realized: createRealizedPattern(client, acc),
relative: createRelativePattern4(client, _m(acc, 'supply_in')),
realized: createRealizedPattern2(client, acc),
relative: createRelativePattern2(client, acc),
supply: createSupplyPattern2(client, _m(acc, 'supply')),
unrealized: createUnrealizedPattern(client, acc),
};
@@ -2472,6 +2443,35 @@ function createPeriodCagrPattern(client, acc) {
};
}
/**
* @typedef {Object} _10yPattern
* @property {ActivityPattern2} activity
* @property {CostBasisPattern} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern4} realized
* @property {RelativePattern} relative
* @property {SupplyPattern2} supply
* @property {UnrealizedPattern} unrealized
*/
/**
* Create a _10yPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_10yPattern}
*/
function create_10yPattern(client, acc) {
return {
activity: createActivityPattern2(client, acc),
costBasis: createCostBasisPattern(client, acc),
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
realized: createRealizedPattern4(client, acc),
relative: createRelativePattern(client, acc),
supply: createSupplyPattern2(client, _m(acc, 'supply')),
unrealized: createUnrealizedPattern(client, acc),
};
}
/**
* @typedef {Object} _100btcPattern
* @property {ActivityPattern2} activity
@@ -2502,29 +2502,29 @@ function create_100btcPattern(client, acc) {
}
/**
* @typedef {Object} _10yTo12yPattern
* @typedef {Object} _0satsPattern2
* @property {ActivityPattern2} activity
* @property {CostBasisPattern2} costBasis
* @property {CostBasisPattern} costBasis
* @property {OutputsPattern} outputs
* @property {RealizedPattern2} realized
* @property {RelativePattern2} relative
* @property {RealizedPattern} realized
* @property {RelativePattern4} relative
* @property {SupplyPattern2} supply
* @property {UnrealizedPattern} unrealized
*/
/**
* Create a _10yTo12yPattern pattern node
* Create a _0satsPattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_10yTo12yPattern}
* @returns {_0satsPattern2}
*/
function create_10yTo12yPattern(client, acc) {
function create_0satsPattern2(client, acc) {
return {
activity: createActivityPattern2(client, acc),
costBasis: createCostBasisPattern2(client, acc),
costBasis: createCostBasisPattern(client, acc),
outputs: createOutputsPattern(client, _m(acc, 'utxo_count')),
realized: createRealizedPattern2(client, acc),
relative: createRelativePattern2(client, acc),
realized: createRealizedPattern(client, acc),
relative: createRelativePattern4(client, _m(acc, 'supply_in')),
supply: createSupplyPattern2(client, _m(acc, 'supply')),
unrealized: createUnrealizedPattern(client, acc),
};
@@ -2580,6 +2580,48 @@ function createSplitPattern2(client, acc) {
};
}
/**
* @typedef {Object} _2015Pattern
* @property {MetricPattern4<Bitcoin>} bitcoin
* @property {MetricPattern4<Dollars>} dollars
* @property {MetricPattern4<Sats>} sats
*/
/**
* Create a _2015Pattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_2015Pattern}
*/
function create_2015Pattern(client, acc) {
return {
bitcoin: createMetricPattern4(client, _m(acc, 'btc')),
dollars: createMetricPattern4(client, _m(acc, 'usd')),
sats: createMetricPattern4(client, acc),
};
}
/**
* @typedef {Object} ActiveSupplyPattern
* @property {MetricPattern1<Bitcoin>} bitcoin
* @property {MetricPattern1<Dollars>} dollars
* @property {MetricPattern1<Sats>} sats
*/
/**
* Create a ActiveSupplyPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {ActiveSupplyPattern}
*/
function createActiveSupplyPattern(client, acc) {
return {
bitcoin: createMetricPattern1(client, _m(acc, 'btc')),
dollars: createMetricPattern1(client, _m(acc, 'usd')),
sats: createMetricPattern1(client, acc),
};
}
/**
* @typedef {Object} CostBasisPattern2
* @property {MetricPattern1<Dollars>} max
@@ -2601,6 +2643,27 @@ function createCostBasisPattern2(client, acc) {
};
}
/**
* @typedef {Object} CoinbasePattern2
* @property {BlockCountPattern<Bitcoin>} bitcoin
* @property {BlockCountPattern<Dollars>} dollars
* @property {BlockCountPattern<Sats>} sats
*/
/**
* Create a CoinbasePattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CoinbasePattern2}
*/
function createCoinbasePattern2(client, acc) {
return {
bitcoin: createBlockCountPattern(client, _m(acc, 'btc')),
dollars: createBlockCountPattern(client, _m(acc, 'usd')),
sats: createBlockCountPattern(client, acc),
};
}
/**
* @typedef {Object} SegwitAdoptionPattern
* @property {MetricPattern11<StoredF32>} base
@@ -2643,69 +2706,6 @@ function createCoinbasePattern(client, acc) {
};
}
/**
* @typedef {Object} _2015Pattern
* @property {MetricPattern4<Bitcoin>} bitcoin
* @property {MetricPattern4<Dollars>} dollars
* @property {MetricPattern4<Sats>} sats
*/
/**
* Create a _2015Pattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_2015Pattern}
*/
function create_2015Pattern(client, acc) {
return {
bitcoin: createMetricPattern4(client, _m(acc, 'btc')),
dollars: createMetricPattern4(client, _m(acc, 'usd')),
sats: createMetricPattern4(client, acc),
};
}
/**
* @typedef {Object} CoinbasePattern2
* @property {BlockCountPattern<Bitcoin>} bitcoin
* @property {BlockCountPattern<Dollars>} dollars
* @property {BlockCountPattern<Sats>} sats
*/
/**
* Create a CoinbasePattern2 pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {CoinbasePattern2}
*/
function createCoinbasePattern2(client, acc) {
return {
bitcoin: createBlockCountPattern(client, _m(acc, 'btc')),
dollars: createBlockCountPattern(client, _m(acc, 'usd')),
sats: createBlockCountPattern(client, acc),
};
}
/**
* @typedef {Object} ActiveSupplyPattern
* @property {MetricPattern1<Bitcoin>} bitcoin
* @property {MetricPattern1<Dollars>} dollars
* @property {MetricPattern1<Sats>} sats
*/
/**
* Create a ActiveSupplyPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {ActiveSupplyPattern}
*/
function createActiveSupplyPattern(client, acc) {
return {
bitcoin: createMetricPattern1(client, _m(acc, 'btc')),
dollars: createMetricPattern1(client, _m(acc, 'usd')),
sats: createMetricPattern1(client, acc),
};
}
/**
* @typedef {Object} UnclaimedRewardsPattern
* @property {BitcoinPattern2<Bitcoin>} bitcoin
@@ -2727,25 +2727,6 @@ function createUnclaimedRewardsPattern(client, acc) {
};
}
/**
* @typedef {Object} _1dReturns1mSdPattern
* @property {MetricPattern4<StoredF32>} sd
* @property {MetricPattern4<StoredF32>} sma
*/
/**
* Create a _1dReturns1mSdPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_1dReturns1mSdPattern}
*/
function create_1dReturns1mSdPattern(client, acc) {
return {
sd: createMetricPattern4(client, _m(acc, 'sd')),
sma: createMetricPattern4(client, _m(acc, 'sma')),
};
}
/**
* @typedef {Object} SupplyPattern2
* @property {ActiveSupplyPattern} halved
@@ -2803,6 +2784,25 @@ function createCostBasisPattern(client, acc) {
};
}
/**
* @typedef {Object} _1dReturns1mSdPattern
* @property {MetricPattern4<StoredF32>} sd
* @property {MetricPattern4<StoredF32>} sma
*/
/**
* Create a _1dReturns1mSdPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {_1dReturns1mSdPattern}
*/
function create_1dReturns1mSdPattern(client, acc) {
return {
sd: createMetricPattern4(client, _m(acc, 'sd')),
sma: createMetricPattern4(client, _m(acc, 'sma')),
};
}
/**
* @template T
* @typedef {Object} BitcoinPattern2
@@ -2861,25 +2861,8 @@ function createBlockCountPattern(client, acc) {
*/
function createSatsPattern(client, acc) {
return {
ohlc: createMetricPattern1(client, _m(acc, 'ohlc_sats')),
split: createSplitPattern2(client, _m(acc, 'sats')),
};
}
/**
* @typedef {Object} OutputsPattern
* @property {MetricPattern1<StoredU64>} utxoCount
*/
/**
* Create a OutputsPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {OutputsPattern}
*/
function createOutputsPattern(client, acc) {
return {
utxoCount: createMetricPattern1(client, acc),
ohlc: createMetricPattern1(client, _m(acc, 'ohlc')),
split: createSplitPattern2(client, acc),
};
}
@@ -2900,6 +2883,23 @@ function createRealizedPriceExtraPattern(client, acc) {
};
}
/**
* @typedef {Object} OutputsPattern
* @property {MetricPattern1<StoredU64>} utxoCount
*/
/**
* Create a OutputsPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated metric name
* @returns {OutputsPattern}
*/
function createOutputsPattern(client, acc) {
return {
utxoCount: createMetricPattern1(client, acc),
};
}
// Catalog tree typedefs
/**
@@ -4055,8 +4055,8 @@ function createRealizedPriceExtraPattern(client, acc) {
* @typedef {Object} MetricsTree_Price
* @property {MetricsTree_Price_Cents} cents
* @property {MetricsTree_Price_Oracle} oracle
* @property {SatsPattern<OHLCSats>} sats
* @property {MetricsTree_Price_Usd} usd
* @property {MetricsTree_Price_Sats} sats
* @property {SatsPattern<OHLCDollars>} usd
*/
/**
@@ -4090,9 +4090,9 @@ function createRealizedPriceExtraPattern(client, acc) {
*/
/**
* @typedef {Object} MetricsTree_Price_Usd
* @property {MetricPattern1<OHLCDollars>} ohlc
* @property {SplitPattern2<Dollars>} split
* @typedef {Object} MetricsTree_Price_Sats
* @property {MetricPattern1<OHLCSats>} ohlc
* @property {SplitPattern2<Sats>} split
*/
/**
@@ -5637,7 +5637,7 @@ class BrkClient extends BrkClientBase {
identity: createMetricPattern10(this, 'halvingepoch'),
},
height: {
dateindex: createMetricPattern11(this, 'height_dateindex'),
dateindex: createMetricPattern11(this, 'dateindex'),
difficultyepoch: createMetricPattern11(this, 'difficultyepoch'),
halvingepoch: createMetricPattern11(this, 'halvingepoch'),
identity: createMetricPattern11(this, 'height'),
@@ -6058,11 +6058,11 @@ class BrkClient extends BrkClientBase {
priceCents: createMetricPattern11(this, 'oracle_price_cents'),
txCount: createMetricPattern6(this, 'oracle_tx_count'),
},
sats: createSatsPattern(this, 'price'),
usd: {
ohlc: createMetricPattern1(this, 'price_ohlc'),
split: createSplitPattern2(this, 'price'),
sats: {
ohlc: createMetricPattern1(this, 'price_ohlc_sats'),
split: createSplitPattern2(this, 'price_sats'),
},
usd: createSatsPattern(this, 'price'),
},
scripts: {
count: {

View File

@@ -1882,31 +1882,6 @@ class Price111dSmaPattern:
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct99_usd'))
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio'))
class ActivePriceRatioPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
self.ratio_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1m_sma'))
self.ratio_1w_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1w_sma'))
self.ratio_1y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '1y'))
self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '2y'))
self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '4y'))
self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct1'))
self.ratio_pct1_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct1_usd'))
self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct2'))
self.ratio_pct2_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct2_usd'))
self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct5'))
self.ratio_pct5_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct5_usd'))
self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct95'))
self.ratio_pct95_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95_usd'))
self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct98'))
self.ratio_pct98_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct98_usd'))
self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct99'))
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd'))
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc)
class PercentilesPattern:
"""Pattern struct for repeated tree structure."""
@@ -1932,6 +1907,31 @@ class PercentilesPattern:
self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90'))
self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95'))
class ActivePriceRatioPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
self.ratio_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1m_sma'))
self.ratio_1w_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, '1w_sma'))
self.ratio_1y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '1y'))
self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '2y'))
self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '4y'))
self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct1'))
self.ratio_pct1_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct1_usd'))
self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct2'))
self.ratio_pct2_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct2_usd'))
self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct5'))
self.ratio_pct5_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct5_usd'))
self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct95'))
self.ratio_pct95_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95_usd'))
self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct98'))
self.ratio_pct98_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct98_usd'))
self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct99'))
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd'))
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc)
class RelativePattern5:
"""Pattern struct for repeated tree structure."""
@@ -2048,23 +2048,6 @@ class BitcoinPattern:
self.pct90: MetricPattern6[Bitcoin] = MetricPattern6(client, _m(acc, 'pct90'))
self.sum: MetricPattern2[Bitcoin] = MetricPattern2(client, _m(acc, 'sum'))
class DollarsPattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.average: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'average'))
self.base: MetricPattern11[T] = MetricPattern11(client, acc)
self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative'))
self.max: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'max'))
self.median: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'median'))
self.min: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'min'))
self.pct10: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct10'))
self.pct25: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct25'))
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
self.sum: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'sum'))
class ClassAveragePricePattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
@@ -2082,21 +2065,22 @@ class ClassAveragePricePattern(Generic[T]):
self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_average_price'))
self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_average_price'))
class RelativePattern:
class DollarsPattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.neg_unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap'))
self.net_unrealized_pnl_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap'))
self.nupl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'nupl'))
self.supply_in_loss_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply'))
self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply'))
self.supply_in_profit_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply'))
self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply'))
self.supply_rel_to_circulating_supply: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply'))
self.unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap'))
self.unrealized_profit_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap'))
self.average: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'average'))
self.base: MetricPattern11[T] = MetricPattern11(client, acc)
self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative'))
self.max: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'max'))
self.median: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'median'))
self.min: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'min'))
self.pct10: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct10'))
self.pct25: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct25'))
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
self.sum: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'sum'))
class RelativePattern2:
"""Pattern struct for repeated tree structure."""
@@ -2114,6 +2098,22 @@ class RelativePattern2:
self.unrealized_profit_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_market_cap'))
self.unrealized_profit_rel_to_own_total_unrealized_pnl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_own_total_unrealized_pnl'))
class RelativePattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.neg_unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss_rel_to_market_cap'))
self.net_unrealized_pnl_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl_rel_to_market_cap'))
self.nupl: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'nupl'))
self.supply_in_loss_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_circulating_supply'))
self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_loss_rel_to_own_supply'))
self.supply_in_profit_rel_to_circulating_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_circulating_supply'))
self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'supply_in_profit_rel_to_own_supply'))
self.supply_rel_to_circulating_supply: MetricPattern4[StoredF64] = MetricPattern4(client, _m(acc, 'supply_rel_to_circulating_supply'))
self.unrealized_loss_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_loss_rel_to_market_cap'))
self.unrealized_profit_rel_to_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'unrealized_profit_rel_to_market_cap'))
class CountPattern2(Generic[T]):
"""Pattern struct for repeated tree structure."""
@@ -2145,21 +2145,6 @@ class AddrCountPattern:
self.p2wpkh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2wpkh', acc))
self.p2wsh: MetricPattern1[StoredU64] = MetricPattern1(client, _p('p2wsh', acc))
class FeeRatePattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.average: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'average'))
self.max: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'max'))
self.median: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'median'))
self.min: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'min'))
self.pct10: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct10'))
self.pct25: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct25'))
self.pct75: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct75'))
self.pct90: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct90'))
self.txindex: MetricPattern27[T] = MetricPattern27(client, acc)
class FullnessPattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
@@ -2175,6 +2160,21 @@ class FullnessPattern(Generic[T]):
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
class FeeRatePattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.average: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'average'))
self.max: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'max'))
self.median: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'median'))
self.min: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'min'))
self.pct10: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct10'))
self.pct25: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct25'))
self.pct75: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct75'))
self.pct90: MetricPattern11[T] = MetricPattern11(client, _m(acc, 'pct90'))
self.txindex: MetricPattern27[T] = MetricPattern27(client, acc)
class _0satsPattern:
"""Pattern struct for repeated tree structure."""
@@ -2203,19 +2203,6 @@ class PhaseDailyCentsPattern(Generic[T]):
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
class _10yPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern4 = RealizedPattern4(client, acc)
self.relative: RelativePattern = RelativePattern(client, acc)
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class UnrealizedPattern:
"""Pattern struct for repeated tree structure."""
@@ -2229,16 +2216,16 @@ class UnrealizedPattern:
self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss'))
self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit'))
class _0satsPattern2:
class _10yTo12yPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern = RealizedPattern(client, acc)
self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in'))
self.realized: RealizedPattern2 = RealizedPattern2(client, acc)
self.relative: RelativePattern2 = RelativePattern2(client, acc)
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
@@ -2255,6 +2242,19 @@ class PeriodCagrPattern:
self._6y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('6y', acc))
self._8y: MetricPattern4[StoredF32] = MetricPattern4(client, _p('8y', acc))
class _10yPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern4 = RealizedPattern4(client, acc)
self.relative: RelativePattern = RelativePattern(client, acc)
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class _100btcPattern:
"""Pattern struct for repeated tree structure."""
@@ -2268,16 +2268,16 @@ class _100btcPattern:
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class _10yTo12yPattern:
class _0satsPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern2 = CostBasisPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern2 = RealizedPattern2(client, acc)
self.relative: RelativePattern2 = RelativePattern2(client, acc)
self.realized: RealizedPattern = RealizedPattern(client, acc)
self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in'))
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
@@ -2302,6 +2302,24 @@ class SplitPattern2(Generic[T]):
self.low: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'low'))
self.open: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'open'))
class _2015Pattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: MetricPattern4[Bitcoin] = MetricPattern4(client, _m(acc, 'btc'))
self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd'))
self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc)
class ActiveSupplyPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc'))
self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc)
class CostBasisPattern2:
"""Pattern struct for repeated tree structure."""
@@ -2311,6 +2329,15 @@ class CostBasisPattern2:
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis'))
self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis'))
class CoinbasePattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: BlockCountPattern[Bitcoin] = BlockCountPattern(client, _m(acc, 'btc'))
self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd'))
self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc)
class SegwitAdoptionPattern:
"""Pattern struct for repeated tree structure."""
@@ -2329,33 +2356,6 @@ class CoinbasePattern:
self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd'))
self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc)
class _2015Pattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: MetricPattern4[Bitcoin] = MetricPattern4(client, _m(acc, 'btc'))
self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd'))
self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc)
class CoinbasePattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: BlockCountPattern[Bitcoin] = BlockCountPattern(client, _m(acc, 'btc'))
self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd'))
self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc)
class ActiveSupplyPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: MetricPattern1[Bitcoin] = MetricPattern1(client, _m(acc, 'btc'))
self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc)
class UnclaimedRewardsPattern:
"""Pattern struct for repeated tree structure."""
@@ -2365,14 +2365,6 @@ class UnclaimedRewardsPattern:
self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd'))
self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc)
class _1dReturns1mSdPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd'))
self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma'))
class SupplyPattern2:
"""Pattern struct for repeated tree structure."""
@@ -2397,6 +2389,14 @@ class CostBasisPattern:
self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis'))
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis'))
class _1dReturns1mSdPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd'))
self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma'))
class BitcoinPattern2(Generic[T]):
"""Pattern struct for repeated tree structure."""
@@ -2418,15 +2418,8 @@ class SatsPattern(Generic[T]):
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc_sats'))
self.split: SplitPattern2[T] = SplitPattern2(client, _m(acc, 'sats'))
class OutputsPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc)
self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc'))
self.split: SplitPattern2[T] = SplitPattern2(client, acc)
class RealizedPriceExtraPattern:
"""Pattern struct for repeated tree structure."""
@@ -2435,6 +2428,13 @@ class RealizedPriceExtraPattern:
"""Create pattern node with accumulated metric name."""
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
class OutputsPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc)
# Metrics tree classes
class MetricsTree_Addresses:
@@ -3144,7 +3144,7 @@ class MetricsTree_Indexes_Height:
"""Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.dateindex: MetricPattern11[DateIndex] = MetricPattern11(client, 'height_dateindex')
self.dateindex: MetricPattern11[DateIndex] = MetricPattern11(client, 'dateindex')
self.difficultyepoch: MetricPattern11[DifficultyEpoch] = MetricPattern11(client, 'difficultyepoch')
self.halvingepoch: MetricPattern11[HalvingEpoch] = MetricPattern11(client, 'halvingepoch')
self.identity: MetricPattern11[Height] = MetricPattern11(client, 'height')
@@ -3687,12 +3687,12 @@ class MetricsTree_Price_Oracle:
self.price_cents: MetricPattern11[Cents] = MetricPattern11(client, 'oracle_price_cents')
self.tx_count: MetricPattern6[StoredU32] = MetricPattern6(client, 'oracle_tx_count')
class MetricsTree_Price_Usd:
class MetricsTree_Price_Sats:
"""Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.ohlc: MetricPattern1[OHLCDollars] = MetricPattern1(client, 'price_ohlc')
self.split: SplitPattern2[Dollars] = SplitPattern2(client, 'price')
self.ohlc: MetricPattern1[OHLCSats] = MetricPattern1(client, 'price_ohlc_sats')
self.split: SplitPattern2[Sats] = SplitPattern2(client, 'price_sats')
class MetricsTree_Price:
"""Metrics tree node."""
@@ -3700,8 +3700,8 @@ class MetricsTree_Price:
def __init__(self, client: BrkClientBase, base_path: str = ''):
self.cents: MetricsTree_Price_Cents = MetricsTree_Price_Cents(client)
self.oracle: MetricsTree_Price_Oracle = MetricsTree_Price_Oracle(client)
self.sats: SatsPattern[OHLCSats] = SatsPattern(client, 'price')
self.usd: MetricsTree_Price_Usd = MetricsTree_Price_Usd(client)
self.sats: MetricsTree_Price_Sats = MetricsTree_Price_Sats(client)
self.usd: SatsPattern[OHLCDollars] = SatsPattern(client, 'price')
class MetricsTree_Scripts_Count:
"""Metrics tree node."""

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,174 @@
# Oracle Filter Analysis
## Summary
Analysis of ~20M outputs across 2017-2018 to find filters that distinguish accurate price signals from noise.
## Key Finding: Round USD is the Only Reliable Filter
| Filter | Accuracy Advantage | Consistency |
|--------|-------------------|-------------|
| **Round USD = True** | **+20% to +29%** | **12/12 months** |
| Round BTC | +12% to -8% | Flips with price |
| Value range/Decade | varies | Shifts with price |
| Same-day spend | ~3% | Weak |
| Micro-round sats | 0-5% | Inconsistent |
| Tx pattern | <5% | Weak |
| Is smaller output | ~3-4% | Weak |
## Why Other Filters Fail
### Round BTC (Unreliable)
- Jan-Mar 2017 ($1k): Round BTC = True is GOOD (+10-12%)
- Jun-Jul 2017 ($2.5k): Round BTC = True is BAD (-7%)
- Reason: Round BTC only correlates with accuracy when it happens to align with round USD at current price
### Value Range / Decade (Price-Dependent)
- At $1,000/BTC: Decade 5 (100k-1M sats) is good
- At $10,000/BTC: Decade 6 (1M-10M sats) is good
- At $100,000/BTC: Decade 7 (10M-100M sats) would be good
- These shift with price, making them useless as static filters
## The Round USD Insight
Round USD amounts ($1, $5, $10, $20, $50, $100, etc.) always map to the **same phase bins** regardless of price level:
```
$100 at $10,000/BTC = 1,000,000 sats → log10 = 6.0 → phase = 0.0 → bin 0
$100 at $100,000/BTC = 100,000 sats → log10 = 5.0 → phase = 0.0 → bin 0
$100 at $1,000/BTC = 10,000,000 sats → log10 = 7.0 → phase = 0.0 → bin 0
```
The phase = `frac(log10(sats))` is **invariant** to price decade!
## Round USD Phase Bins
| USD Amount | log10(USD) | Phase = frac(log10) | Bin (×100) |
|------------|------------|---------------------|------------|
| $1, $10, $100, $1000 | 0, 1, 2, 3 | 0.00 | 0 |
| $1.50, $15, $150 | 0.18, 1.18, 2.18 | 0.18 | 18 |
| $2, $20, $200 | 0.30, 1.30, 2.30 | 0.30 | 30 |
| $2.50, $25, $250 | 0.40, 1.40, 2.40 | 0.40 | 40 |
| $3, $30, $300 | 0.48, 1.48, 2.48 | 0.48 | 48 |
| $4, $40, $400 | 0.60, 1.60, 2.60 | 0.60 | 60 |
| $5, $50, $500 | 0.70, 1.70, 2.70 | 0.70 | 70 |
| $6, $60, $600 | 0.78, 1.78, 2.78 | 0.78 | 78 |
| $7, $70, $700 | 0.85, 1.85, 2.85 | 0.85 | 85 |
| $8, $80, $800 | 0.90, 1.90, 2.90 | 0.90 | 90 |
| $9, $90, $900 | 0.95, 1.95, 2.95 | 0.95 | 95 |
## Implementation Plan
### Approach: Phase-Based Round USD Filtering
Filter outputs to only those whose phase bin corresponds to a round USD amount. No price knowledge needed.
```rust
/// Phase bins where round USD amounts cluster
/// Computed as: bin = round(frac(log10(usd_cents)) * 100)
const ROUND_USD_BINS: &[u8] = &[
0, // $1, $10, $100, $1000 (and $0.10, $0.01)
18, // $1.50, $15, $150
30, // $2, $20, $200
40, // $2.50, $25, $250
48, // $3, $30, $300
60, // $4, $40, $400
70, // $5, $50, $500
78, // $6, $60, $600
85, // $7, $70, $700
90, // $8, $80, $800
95, // $9, $90, $900
];
/// Check if a histogram bin corresponds to a round USD amount
fn is_round_usd_bin(bin: usize, tolerance: u8) -> bool {
let phase_bin = (bin % 100) as u8;
ROUND_USD_BINS.iter().any(|&round_bin| {
let diff = if phase_bin >= round_bin {
phase_bin - round_bin
} else {
round_bin - phase_bin
};
// Handle wraparound (bin 99 is close to bin 0)
diff <= tolerance || (100 - diff) <= tolerance
})
}
```
### Where to Apply Filter
In `compute.rs`, when adding outputs to histogram:
```rust
for sats in values {
if let Some(bin) = Histogram::sats_to_bin(sats) {
// Only include outputs in round-USD phase bins
if is_round_usd_bin(bin, 2) { // ±2 bin tolerance
block_sparse.push((bin as u16, 1.0));
// ... rest of processing
}
}
}
```
### Expected Impact
- Reduces histogram noise by ~60-70% (only ~35% of accurate outputs are round USD)
- Remaining outputs are 2-3x more likely to be accurate signals
- Stencil matching should be more reliable with cleaner signal
- Decade selection via anchors remains unchanged
### Alternative: Weighted Approach
Instead of hard filtering, weight round-USD bins higher:
```rust
let weight = if is_round_usd_bin(bin, 2) { 3.0 } else { 1.0 };
block_sparse.push((bin as u16, weight));
```
This preserves some signal from non-round outputs while emphasizing round USD.
## Bin Resolution: 100 vs 200
UTXOracle uses **200 bins per decade**. Current phase oracle uses 100.
| Resolution | Precision | Round USD cluster |
|------------|-----------|-------------------|
| 100 bins | 1% per bin | Wider, more overlap |
| 200 bins | 0.5% per bin | Tighter, cleaner separation |
**Round USD bins at 200 resolution:**
| USD Amount | Phase = frac(log10) | Bin (×200) |
|------------|---------------------|------------|
| $1, $10, $100 | 0.000 | 0 |
| $1.50, $15, $150 | 0.176 | 35 |
| $2, $20, $200 | 0.301 | 60 |
| $2.50, $25, $250 | 0.398 | 80 |
| $3, $30, $300 | 0.477 | 95 |
| $4, $40, $400 | 0.602 | 120 |
| $5, $50, $500 | 0.699 | 140 |
| $6, $60, $600 | 0.778 | 156 |
| $7, $70, $700 | 0.845 | 169 |
| $8, $80, $800 | 0.903 | 181 |
| $9, $90, $900 | 0.954 | 191 |
**Recommendation**: Use 200 bins for:
1. Compatibility with UTXOracle stencil
2. Tighter round-USD detection
3. Better separation of signal from noise
## Questions to Resolve
1. **Tolerance**: ±2 bins (at 200) = ±1% vs ±4 bins = ±2%
2. **Hard filter vs weight**: Filter completely or just weight higher?
3. **Minimum count threshold**: What if too few outputs pass filter?
4. **Interaction with existing smooth_round_btc()**: Still needed?
5. **Migration**: Update PHASE_BINS constant from 100 to 200
## Validation Plan
1. Implement phase-based filtering
2. Run on 2017-2018 data
3. Compare accuracy vs current approach
4. Tune tolerance parameter

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,282 @@
#!/usr/bin/env python3
"""
Test price phase detection from outputs alone.
The idea: Round USD outputs create a fingerprint pattern that reveals the price phase.
"""
import math
import http.client
import json
import time
from collections import defaultdict
API_HOST = "localhost"
API_PORT = 3110
# Round USD phases (fixed fingerprint)
# These are frac(log10(usd_cents)) for round USD values
ROUND_USD_PHASES = [
0.00, # $1, $10, $100, $1000
0.18, # $1.50, $15, $150
0.30, # $2, $20, $200
0.40, # $2.50, $25, $250
0.48, # $3, $30, $300
0.60, # $4, $40, $400
0.70, # $5, $50, $500
0.78, # $6, $60, $600
0.85, # $7, $70, $700
0.90, # $8, $80, $800
0.95, # $9, $90, $900
]
_conn = None
def get_conn():
global _conn
if _conn is None:
_conn = http.client.HTTPConnection(API_HOST, API_PORT, timeout=300)
return _conn
def reset_conn():
global _conn
if _conn:
try:
_conn.close()
except:
pass
_conn = None
def fetch(path: str, retries: int = 3):
for attempt in range(retries):
try:
conn = get_conn()
conn.request("GET", path)
resp = conn.getresponse()
data = resp.read().decode('utf-8')
return json.loads(data)
except Exception as e:
reset_conn()
if attempt < retries - 1:
time.sleep(2)
else:
raise
def fetch_chunked(path_template: str, start: int, end: int, chunk_size: int = 25000) -> list:
result = []
for chunk_start in range(start, end, chunk_size):
chunk_end = min(chunk_start + chunk_size, end)
path = path_template.format(start=chunk_start, end=chunk_end)
data = fetch(path)["data"]
result.extend(data)
return result
def get_sats_phase(sats: int) -> float:
"""Get the phase (fractional part of log10) for a sats value."""
if sats <= 0:
return 0.0
return math.log10(sats) % 1.0
def count_round_usd_matches(outputs: list, price_phase: float, tolerance: float = 0.02) -> int:
"""
Count how many outputs match round USD bins at the given price phase.
At price_phase P, round USD outputs should appear at sats_phase = (usd_phase - P) mod 1
"""
# Compute expected sats phases for round USD at this price phase
expected_phases = [(usd_phase - price_phase) % 1.0 for usd_phase in ROUND_USD_PHASES]
count = 0
for sats in outputs:
if sats is None or sats < 1000:
continue
sats_phase = get_sats_phase(sats)
# Check if sats_phase matches any expected phase
for exp_phase in expected_phases:
diff = abs(sats_phase - exp_phase)
# Handle wraparound (0.99 is close to 0.01)
if diff < tolerance or diff > (1.0 - tolerance):
count += 1
break
return count
def find_best_price_phase(outputs: list, tolerance: float = 0.02, resolution: int = 100) -> tuple:
"""
Find the price phase that maximizes round USD matches.
Returns (best_phase, best_count, all_counts).
"""
counts = []
best_phase = 0.0
best_count = 0
for i in range(resolution):
price_phase = i / resolution
count = count_round_usd_matches(outputs, price_phase, tolerance)
counts.append(count)
if count > best_count:
best_count = count
best_phase = price_phase
return best_phase, best_count, counts
def actual_price_phase(price: float) -> float:
"""Get the actual price phase from a price."""
return math.log10(price) % 1.0
def analyze_day(date_str: str, start_height: int, end_height: int, actual_price: float):
"""Analyze a single day's outputs."""
# Get transaction range for these heights
first_tx = fetch(f"/api/metric/first_txindex/height?start={start_height}&end={end_height}")
first_txs = first_tx["data"]
if not first_txs or len(first_txs) < 2:
return None
tx_start = first_txs[0]
tx_end = first_txs[-1]
# Get output range
tx_first_out = fetch_chunked("/api/metric/first_txoutindex/txindex?start={start}&end={end}", tx_start, tx_end)
if not tx_first_out:
return None
out_start = tx_first_out[0]
out_end = tx_first_out[-1] + 10 # estimate
# Fetch output values
out_values = fetch_chunked("/api/metric/value/txoutindex?start={start}&end={end}", out_start, out_end)
# Filter to reasonable range (1000 sats to 100 BTC)
outputs = [v for v in out_values if v and 1000 <= v <= 10_000_000_000]
if len(outputs) < 1000:
return None
# Find best price phase
detected_phase, match_count, _ = find_best_price_phase(outputs, tolerance=0.02)
# Compare with actual
actual_phase = actual_price_phase(actual_price)
# Phase error (handle wraparound)
phase_error = abs(detected_phase - actual_phase)
if phase_error > 0.5:
phase_error = 1.0 - phase_error
return {
'date': date_str,
'actual_price': actual_price,
'actual_phase': actual_phase,
'detected_phase': detected_phase,
'phase_error': phase_error,
'match_count': match_count,
'total_outputs': len(outputs),
'match_pct': 100 * match_count / len(outputs),
}
def main():
print("=" * 60)
print("PRICE PHASE DETECTION TEST")
print("=" * 60)
print("\nIdea: Round USD outputs form a fingerprint pattern.")
print("Sliding this pattern across the histogram reveals the price phase.\n")
# Fetch dates
print("Fetching date index...")
dates = fetch("/api/metric/date/dateindex?start=0&end=4000")["data"]
# Fetch daily OHLC
print("Fetching daily prices...")
ohlc_data = fetch("/api/metric/price_ohlc/dateindex?start=2800&end=3600")["data"]
# Fetch heights
print("Fetching heights...")
heights = fetch("/api/metric/first_height/dateindex?start=2800&end=3600")["data"]
results = []
# Test on 2017-2018 (roughly dateindex 2900-3600)
# Sample every 7 days to speed up
for di in range(2900, 3550, 7):
if di - 2800 >= len(ohlc_data) or di - 2800 >= len(heights):
continue
ohlc = ohlc_data[di - 2800]
if not ohlc or len(ohlc) < 4:
continue
# Use close price as "actual"
actual_price = ohlc[3]
if not actual_price or actual_price <= 0:
continue
date_str = dates[di] if di < len(dates) else f"di={di}"
start_height = heights[di - 2800]
end_height = heights[di - 2800 + 1] if di - 2800 + 1 < len(heights) else start_height + 144
if not start_height:
continue
print(f"\nAnalyzing {date_str} (${actual_price:.0f})...")
try:
result = analyze_day(date_str, start_height, end_height, actual_price)
if result:
results.append(result)
print(f" Actual phase: {result['actual_phase']:.3f}")
print(f" Detected phase: {result['detected_phase']:.3f}")
print(f" Phase error: {result['phase_error']:.3f} ({result['phase_error']*100:.1f}%)")
print(f" Matches: {result['match_count']:,} / {result['total_outputs']:,} ({result['match_pct']:.1f}%)")
except Exception as e:
print(f" Error: {e}")
continue
# Summary
if results:
print("\n" + "=" * 60)
print("SUMMARY")
print("=" * 60)
errors = [r['phase_error'] for r in results]
avg_error = sum(errors) / len(errors)
# Count how many are within various thresholds
within_01 = sum(1 for e in errors if e <= 0.01)
within_02 = sum(1 for e in errors if e <= 0.02)
within_05 = sum(1 for e in errors if e <= 0.05)
within_10 = sum(1 for e in errors if e <= 0.10)
print(f"\nTotal days analyzed: {len(results)}")
print(f"Average phase error: {avg_error:.3f} ({avg_error*100:.1f}%)")
print(f"\nPhase error distribution:")
print(f" ≤1%: {within_01:3d} / {len(results)} ({100*within_01/len(results):.0f}%)")
print(f" ≤2%: {within_02:3d} / {len(results)} ({100*within_02/len(results):.0f}%)")
print(f" ≤5%: {within_05:3d} / {len(results)} ({100*within_05/len(results):.0f}%)")
print(f" ≤10%: {within_10:3d} / {len(results)} ({100*within_10/len(results):.0f}%)")
# Show worst cases
print(f"\nWorst cases:")
worst = sorted(results, key=lambda r: -r['phase_error'])[:5]
for r in worst:
print(f" {r['date']}: detected {r['detected_phase']:.2f} vs actual {r['actual_phase']:.2f} "
f"(error {r['phase_error']:.2f}, ${r['actual_price']:.0f})")
# Show best cases
print(f"\nBest cases:")
best = sorted(results, key=lambda r: r['phase_error'])[:5]
for r in best:
print(f" {r['date']}: detected {r['detected_phase']:.2f} vs actual {r['actual_phase']:.2f} "
f"(error {r['phase_error']:.3f}, ${r['actual_price']:.0f})")
if __name__ == "__main__":
main()

View File

@@ -37,24 +37,30 @@ export function createMarketSection(ctx) {
title: "Bitcoin Price",
...(localhost && {
top: [
candlestick({
metric: price.oracle.ohlcDollars,
name: "Oracle",
unit: Unit.usd,
colors: [colors.cyan, colors.purple],
}),
// candlestick({
// metric: price.oracle.ohlcDollars,
// name: "Oracle base",
// unit: Unit.usd,
// colors: [colors.cyan, colors.purple],
// }),
line({
metric: price.oracle.phaseDailyDollars.median,
name: "Oracle2 median",
unit: Unit.usd,
color: colors.blue,
}),
line({
metric: price.oracle.phaseDailyDollars.average,
name: "Oracle2 average",
name: "o. p50",
unit: Unit.usd,
color: colors.yellow,
}),
line({
metric: price.oracle.phaseDailyDollars.max,
name: "o. max",
unit: Unit.usd,
color: colors.lime,
}),
line({
metric: price.oracle.phaseDailyDollars.min,
name: "o. min",
unit: Unit.usd,
color: colors.rose,
}),
],
}),
},