website: snapshot

This commit is contained in:
nym21
2026-01-23 22:03:01 +01:00
parent f7bfe5ecaa
commit 9b706dfaee
49 changed files with 631818 additions and 1298 deletions
+1
View File
@@ -23,6 +23,7 @@ brk_rpc = { workspace = true }
brk_server = { workspace = true }
brk_types = { workspace = true }
lexopt = "0.3"
owo-colors = { workspace = true }
tracing = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
+44 -43
View File
@@ -4,12 +4,14 @@ use std::{
};
use brk_error::{Error, Result};
use owo_colors::OwoColorize;
use brk_fetcher::Fetcher;
use brk_rpc::{Auth, Client};
use brk_server::Website;
use brk_types::Port;
use serde::{Deserialize, Deserializer, Serialize};
use crate::{default_brk_path, dot_brk_path, fix_user_path, website::WebsiteArg};
use crate::{default_brk_path, dot_brk_path, fix_user_path};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct Config {
@@ -20,7 +22,7 @@ pub struct Config {
brkport: Option<Port>,
#[serde(default, deserialize_with = "default_on_error")]
website: Option<WebsiteArg>,
website: Option<Website>,
#[serde(default, deserialize_with = "default_on_error")]
fetch: Option<bool>,
@@ -45,9 +47,6 @@ pub struct Config {
#[serde(default, deserialize_with = "default_on_error")]
rpcpassword: Option<String>,
#[serde(default, deserialize_with = "default_on_error")]
check_collisions: Option<bool>,
}
impl Config {
@@ -95,9 +94,6 @@ impl Config {
if let Some(v) = config_args.rpcpassword {
config.rpcpassword = Some(v);
}
if let Some(v) = config_args.check_collisions {
config.check_collisions = Some(v);
}
config.check();
@@ -126,14 +122,23 @@ impl Config {
Long("brkport") => config.brkport = Some(parser.value().unwrap().parse().unwrap()),
Long("website") => config.website = Some(parser.value().unwrap().parse().unwrap()),
Long("fetch") => config.fetch = Some(parser.value().unwrap().parse().unwrap()),
Long("bitcoindir") => config.bitcoindir = Some(parser.value().unwrap().parse().unwrap()),
Long("blocksdir") => config.blocksdir = Some(parser.value().unwrap().parse().unwrap()),
Long("rpcconnect") => config.rpcconnect = Some(parser.value().unwrap().parse().unwrap()),
Long("bitcoindir") => {
config.bitcoindir = Some(parser.value().unwrap().parse().unwrap())
}
Long("blocksdir") => {
config.blocksdir = Some(parser.value().unwrap().parse().unwrap())
}
Long("rpcconnect") => {
config.rpcconnect = Some(parser.value().unwrap().parse().unwrap())
}
Long("rpcport") => config.rpcport = Some(parser.value().unwrap().parse().unwrap()),
Long("rpccookiefile") => config.rpccookiefile = Some(parser.value().unwrap().parse().unwrap()),
Long("rpccookiefile") => {
config.rpccookiefile = Some(parser.value().unwrap().parse().unwrap())
}
Long("rpcuser") => config.rpcuser = Some(parser.value().unwrap().parse().unwrap()),
Long("rpcpassword") => config.rpcpassword = Some(parser.value().unwrap().parse().unwrap()),
Long("check-collisions") => config.check_collisions = Some(parser.value().unwrap().parse().unwrap()),
Long("rpcpassword") => {
config.rpcpassword = Some(parser.value().unwrap().parse().unwrap())
}
_ => {
eprintln!("{}", arg.unexpected());
std::process::exit(1);
@@ -145,32 +150,31 @@ impl Config {
}
fn print_help() {
println!(
"brk {}
Bitcoin Research Kit
let v = env!("CARGO_PKG_VERSION");
USAGE:
brk [OPTIONS]
OPTIONS:
-h, --help Print help
-V, --version Print version
--brkdir <PATH> Output directory [~/.brk]
--brkport <PORT> Server port [3110]
--website <BOOL|PATH> Website: true, false, or path [true]
--fetch <BOOL> Fetch prices [true]
--bitcoindir <PATH> Bitcoin directory [~/.bitcoin, ~/Library/Application Support/Bitcoin]
--blocksdir <PATH> Blocks directory [<bitcoindir>/blocks]
--rpcconnect <IP> RPC host [localhost]
--rpcport <PORT> RPC port [8332]
--rpccookiefile <PATH> RPC cookie file [<bitcoindir>/.cookie]
--rpcuser <USERNAME> RPC username
--rpcpassword <PASSWORD> RPC password",
env!("CARGO_PKG_VERSION")
);
println!("{} {}", "brk".bold(), v.bright_black());
println!("Bitcoin Research Kit");
println!();
println!("{}", "USAGE:".bold());
println!(" brk {}", "[OPTIONS]".bright_black());
println!();
println!("{}", "OPTIONS:".bold());
println!(" -h, --help Print help");
println!(" -V, --version Print version");
println!();
println!(" --brkdir {} Output directory {}", "<PATH>".bright_black(), "[~/.brk]".bright_black());
println!(" --brkport {} Server port {}", "<PORT>".bright_black(), "[3110]".bright_black());
println!(" --website {} Website: true, false, or path {}", "<BOOL|PATH>".bright_black(), "[true]".bright_black());
println!(" --fetch {} Fetch prices {}", "<BOOL>".bright_black(), "[true]".bright_black());
println!();
println!(" --bitcoindir {} Bitcoin directory {}", "<PATH>".bright_black(), "[~/.bitcoin, ~/Library/...]".bright_black());
println!(" --blocksdir {} Blocks directory {}", "<PATH>".bright_black(), "[<bitcoindir>/blocks]".bright_black());
println!();
println!(" --rpcconnect {} RPC host {}", "<IP>".bright_black(), "[localhost]".bright_black());
println!(" --rpcport {} RPC port {}", "<PORT>".bright_black(), "[8332]".bright_black());
println!(" --rpccookiefile {} RPC cookie file {}", "<PATH>".bright_black(), "[<bitcoindir>/.cookie]".bright_black());
println!(" --rpcuser {} RPC username", "<USERNAME>".bright_black());
println!(" --rpcpassword {} RPC password", "<PASSWORD>".bright_black());
}
fn check(&self) {
@@ -280,7 +284,7 @@ Finally, you can run the program with '-h' for help."
)
}
pub fn website(&self) -> WebsiteArg {
pub fn website(&self) -> Website {
self.website.clone().unwrap_or_default()
}
@@ -297,9 +301,6 @@ Finally, you can run the program with '-h' for help."
.then(|| Fetcher::import(Some(self.harsdir().as_path())).unwrap())
}
pub fn check_collisions(&self) -> bool {
self.check_collisions.is_some_and(|b| b)
}
}
fn default_on_error<'de, D, T>(deserializer: D) -> Result<T, D::Error>
+4 -9
View File
@@ -14,15 +14,14 @@ use brk_iterator::Blocks;
use brk_mempool::Mempool;
use brk_query::AsyncQuery;
use brk_reader::Reader;
use brk_server::{Server, Website};
use brk_server::Server;
use tracing::info;
use vecdb::Exit;
mod config;
mod paths;
mod website;
use crate::{config::Config, paths::*, website::WebsiteArg};
use crate::{config::Config, paths::*};
pub fn main() -> anyhow::Result<()> {
// Can't increase main thread's stack size, thus we need to use another thread
@@ -80,11 +79,7 @@ pub fn run() -> anyhow::Result<()> {
let data_path = config.brkdir();
let website = match config.website() {
WebsiteArg::Enabled(false) => Website::Disabled,
WebsiteArg::Enabled(true) => Website::Default,
WebsiteArg::Path(p) => Website::Filesystem(p),
};
let website = config.website();
let port = config.brkport();
@@ -111,7 +106,7 @@ pub fn run() -> anyhow::Result<()> {
info!("{} blocks found.", u32::from(last_height) + 1);
let starting_indexes = if config.check_collisions() {
let starting_indexes = if cfg!(debug_assertions) {
indexer.checked_index(&blocks, &client, &exit)?
} else {
indexer.index(&blocks, &client, &exit)?
-34
View File
@@ -1,34 +0,0 @@
use std::{path::PathBuf, str::FromStr};
use serde::{Deserialize, Serialize};
use crate::paths::fix_user_path;
/// Website configuration:
/// - `true` or omitted: serve embedded website
/// - `false`: disable website serving
/// - `"/path/to/website"`: serve custom website from path
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
#[serde(untagged)]
pub enum WebsiteArg {
Enabled(bool),
Path(PathBuf),
}
impl Default for WebsiteArg {
fn default() -> Self {
Self::Enabled(true)
}
}
impl FromStr for WebsiteArg {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"true" | "1" | "yes" | "on" => Self::Enabled(true),
"false" | "0" | "no" | "off" => Self::Enabled(false),
_ => Self::Path(fix_user_path(s)),
})
}
}
@@ -0,0 +1,51 @@
use std::fs::File;
use std::io::{BufWriter, Write};
use brk_client::{BrkClient, BrkClientOptions, Result};
use brk_types::Dollars;
const CHUNK_SIZE: usize = 10_000;
const END_HEIGHT: usize = 630_000;
const OUTPUT_FILE: &str = "prices_avg.txt";
fn main() -> Result<()> {
let client = BrkClient::with_options(BrkClientOptions {
base_url: "https://next.bitview.space".to_string(),
timeout_secs: 60,
});
let file = File::create(OUTPUT_FILE).map_err(|e| brk_client::BrkError {
message: e.to_string(),
})?;
let mut writer = BufWriter::new(file);
for start in (0..END_HEIGHT).step_by(CHUNK_SIZE) {
let end = (start + CHUNK_SIZE).min(END_HEIGHT);
eprintln!("Fetching {start} to {end}...");
let ohlcs = client
.metrics()
.price
.cents
.ohlc
.by
.height()
.range(start..end)
.fetch()?;
for ohlc in ohlcs.data {
let avg = (*ohlc.open + *ohlc.close) / 2;
let avg = Dollars::from(avg);
writeln!(writer, "{avg}").map_err(|e| brk_client::BrkError {
message: e.to_string(),
})?;
}
}
writer.flush().map_err(|e| brk_client::BrkError {
message: e.to_string(),
})?;
eprintln!("Done. Output in {OUTPUT_FILE}");
Ok(())
}
+247 -231
View File
@@ -1268,56 +1268,6 @@ impl Price111dSmaPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct PercentilesPattern {
pub pct05: MetricPattern4<Dollars>,
pub pct10: MetricPattern4<Dollars>,
pub pct15: MetricPattern4<Dollars>,
pub pct20: MetricPattern4<Dollars>,
pub pct25: MetricPattern4<Dollars>,
pub pct30: MetricPattern4<Dollars>,
pub pct35: MetricPattern4<Dollars>,
pub pct40: MetricPattern4<Dollars>,
pub pct45: MetricPattern4<Dollars>,
pub pct50: MetricPattern4<Dollars>,
pub pct55: MetricPattern4<Dollars>,
pub pct60: MetricPattern4<Dollars>,
pub pct65: MetricPattern4<Dollars>,
pub pct70: MetricPattern4<Dollars>,
pub pct75: MetricPattern4<Dollars>,
pub pct80: MetricPattern4<Dollars>,
pub pct85: MetricPattern4<Dollars>,
pub pct90: MetricPattern4<Dollars>,
pub pct95: MetricPattern4<Dollars>,
}
impl PercentilesPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
pct05: MetricPattern4::new(client.clone(), _m(&acc, "pct05")),
pct10: MetricPattern4::new(client.clone(), _m(&acc, "pct10")),
pct15: MetricPattern4::new(client.clone(), _m(&acc, "pct15")),
pct20: MetricPattern4::new(client.clone(), _m(&acc, "pct20")),
pct25: MetricPattern4::new(client.clone(), _m(&acc, "pct25")),
pct30: MetricPattern4::new(client.clone(), _m(&acc, "pct30")),
pct35: MetricPattern4::new(client.clone(), _m(&acc, "pct35")),
pct40: MetricPattern4::new(client.clone(), _m(&acc, "pct40")),
pct45: MetricPattern4::new(client.clone(), _m(&acc, "pct45")),
pct50: MetricPattern4::new(client.clone(), _m(&acc, "pct50")),
pct55: MetricPattern4::new(client.clone(), _m(&acc, "pct55")),
pct60: MetricPattern4::new(client.clone(), _m(&acc, "pct60")),
pct65: MetricPattern4::new(client.clone(), _m(&acc, "pct65")),
pct70: MetricPattern4::new(client.clone(), _m(&acc, "pct70")),
pct75: MetricPattern4::new(client.clone(), _m(&acc, "pct75")),
pct80: MetricPattern4::new(client.clone(), _m(&acc, "pct80")),
pct85: MetricPattern4::new(client.clone(), _m(&acc, "pct85")),
pct90: MetricPattern4::new(client.clone(), _m(&acc, "pct90")),
pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct ActivePriceRatioPattern {
pub ratio: MetricPattern4<StoredF32>,
@@ -1368,6 +1318,56 @@ impl ActivePriceRatioPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct PercentilesPattern {
pub pct05: MetricPattern4<Dollars>,
pub pct10: MetricPattern4<Dollars>,
pub pct15: MetricPattern4<Dollars>,
pub pct20: MetricPattern4<Dollars>,
pub pct25: MetricPattern4<Dollars>,
pub pct30: MetricPattern4<Dollars>,
pub pct35: MetricPattern4<Dollars>,
pub pct40: MetricPattern4<Dollars>,
pub pct45: MetricPattern4<Dollars>,
pub pct50: MetricPattern4<Dollars>,
pub pct55: MetricPattern4<Dollars>,
pub pct60: MetricPattern4<Dollars>,
pub pct65: MetricPattern4<Dollars>,
pub pct70: MetricPattern4<Dollars>,
pub pct75: MetricPattern4<Dollars>,
pub pct80: MetricPattern4<Dollars>,
pub pct85: MetricPattern4<Dollars>,
pub pct90: MetricPattern4<Dollars>,
pub pct95: MetricPattern4<Dollars>,
}
impl PercentilesPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
pct05: MetricPattern4::new(client.clone(), _m(&acc, "pct05")),
pct10: MetricPattern4::new(client.clone(), _m(&acc, "pct10")),
pct15: MetricPattern4::new(client.clone(), _m(&acc, "pct15")),
pct20: MetricPattern4::new(client.clone(), _m(&acc, "pct20")),
pct25: MetricPattern4::new(client.clone(), _m(&acc, "pct25")),
pct30: MetricPattern4::new(client.clone(), _m(&acc, "pct30")),
pct35: MetricPattern4::new(client.clone(), _m(&acc, "pct35")),
pct40: MetricPattern4::new(client.clone(), _m(&acc, "pct40")),
pct45: MetricPattern4::new(client.clone(), _m(&acc, "pct45")),
pct50: MetricPattern4::new(client.clone(), _m(&acc, "pct50")),
pct55: MetricPattern4::new(client.clone(), _m(&acc, "pct55")),
pct60: MetricPattern4::new(client.clone(), _m(&acc, "pct60")),
pct65: MetricPattern4::new(client.clone(), _m(&acc, "pct65")),
pct70: MetricPattern4::new(client.clone(), _m(&acc, "pct70")),
pct75: MetricPattern4::new(client.clone(), _m(&acc, "pct75")),
pct80: MetricPattern4::new(client.clone(), _m(&acc, "pct80")),
pct85: MetricPattern4::new(client.clone(), _m(&acc, "pct85")),
pct90: MetricPattern4::new(client.clone(), _m(&acc, "pct90")),
pct95: MetricPattern4::new(client.clone(), _m(&acc, "pct95")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct RelativePattern5 {
pub neg_unrealized_loss_rel_to_market_cap: MetricPattern1<StoredF32>,
@@ -1566,6 +1566,42 @@ impl<T: DeserializeOwned> PeriodAveragePricePattern<T> {
}
}
/// Pattern struct for repeated tree structure.
pub struct ClassAveragePricePattern<T> {
pub _2015: MetricPattern4<T>,
pub _2016: MetricPattern4<T>,
pub _2017: MetricPattern4<T>,
pub _2018: MetricPattern4<T>,
pub _2019: MetricPattern4<T>,
pub _2020: MetricPattern4<T>,
pub _2021: MetricPattern4<T>,
pub _2022: MetricPattern4<T>,
pub _2023: MetricPattern4<T>,
pub _2024: MetricPattern4<T>,
pub _2025: MetricPattern4<T>,
pub _2026: MetricPattern4<T>,
}
impl<T: DeserializeOwned> ClassAveragePricePattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
_2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_returns")),
_2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_returns")),
_2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_returns")),
_2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_returns")),
_2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_returns")),
_2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_returns")),
_2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_returns")),
_2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_returns")),
_2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_returns")),
_2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_returns")),
_2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_returns")),
_2026: MetricPattern4::new(client.clone(), _m(&acc, "2026_returns")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct BitcoinPattern {
pub average: MetricPattern2<Bitcoin>,
@@ -1600,40 +1636,6 @@ impl BitcoinPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct ClassAveragePricePattern<T> {
pub _2015: MetricPattern4<T>,
pub _2016: MetricPattern4<T>,
pub _2017: MetricPattern4<T>,
pub _2018: MetricPattern4<T>,
pub _2019: MetricPattern4<T>,
pub _2020: MetricPattern4<T>,
pub _2021: MetricPattern4<T>,
pub _2022: MetricPattern4<T>,
pub _2023: MetricPattern4<T>,
pub _2024: MetricPattern4<T>,
pub _2025: MetricPattern4<T>,
}
impl<T: DeserializeOwned> ClassAveragePricePattern<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
_2015: MetricPattern4::new(client.clone(), _m(&acc, "2015_returns")),
_2016: MetricPattern4::new(client.clone(), _m(&acc, "2016_returns")),
_2017: MetricPattern4::new(client.clone(), _m(&acc, "2017_returns")),
_2018: MetricPattern4::new(client.clone(), _m(&acc, "2018_returns")),
_2019: MetricPattern4::new(client.clone(), _m(&acc, "2019_returns")),
_2020: MetricPattern4::new(client.clone(), _m(&acc, "2020_returns")),
_2021: MetricPattern4::new(client.clone(), _m(&acc, "2021_returns")),
_2022: MetricPattern4::new(client.clone(), _m(&acc, "2022_returns")),
_2023: MetricPattern4::new(client.clone(), _m(&acc, "2023_returns")),
_2024: MetricPattern4::new(client.clone(), _m(&acc, "2024_returns")),
_2025: MetricPattern4::new(client.clone(), _m(&acc, "2025_returns")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct DollarsPattern<T> {
pub average: MetricPattern2<T>,
@@ -1937,24 +1939,76 @@ impl _10yTo12yPattern {
}
/// Pattern struct for repeated tree structure.
pub struct _10yPattern {
pub struct UnrealizedPattern {
pub neg_unrealized_loss: MetricPattern1<Dollars>,
pub net_unrealized_pnl: MetricPattern1<Dollars>,
pub supply_in_loss: ActiveSupplyPattern,
pub supply_in_profit: ActiveSupplyPattern,
pub total_unrealized_pnl: MetricPattern1<Dollars>,
pub unrealized_loss: MetricPattern1<Dollars>,
pub unrealized_profit: MetricPattern1<Dollars>,
}
impl UnrealizedPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")),
net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")),
supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")),
total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")),
unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")),
unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _0satsPattern2 {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern4,
pub relative: RelativePattern,
pub realized: RealizedPattern,
pub relative: RelativePattern4,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _10yPattern {
impl _0satsPattern2 {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern4::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()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _100btcPattern {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern,
pub relative: RelativePattern,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _100btcPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern::new(client.clone(), acc.clone()),
relative: RelativePattern::new(client.clone(), acc.clone()),
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
@@ -1989,76 +2043,24 @@ impl PeriodCagrPattern {
}
/// Pattern struct for repeated tree structure.
pub struct _0satsPattern2 {
pub struct _10yPattern {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern,
pub relative: RelativePattern4,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _0satsPattern2 {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
activity: ActivityPattern2::new(client.clone(), acc.clone()),
cost_basis: CostBasisPattern::new(client.clone(), acc.clone()),
outputs: OutputsPattern::new(client.clone(), _m(&acc, "utxo_count")),
realized: RealizedPattern::new(client.clone(), acc.clone()),
relative: RelativePattern4::new(client.clone(), _m(&acc, "supply_in")),
supply: SupplyPattern2::new(client.clone(), _m(&acc, "supply")),
unrealized: UnrealizedPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct UnrealizedPattern {
pub neg_unrealized_loss: MetricPattern1<Dollars>,
pub net_unrealized_pnl: MetricPattern1<Dollars>,
pub supply_in_loss: ActiveSupplyPattern,
pub supply_in_profit: ActiveSupplyPattern,
pub total_unrealized_pnl: MetricPattern1<Dollars>,
pub unrealized_loss: MetricPattern1<Dollars>,
pub unrealized_profit: MetricPattern1<Dollars>,
}
impl UnrealizedPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
neg_unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "neg_unrealized_loss")),
net_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "net_unrealized_pnl")),
supply_in_loss: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_loss")),
supply_in_profit: ActiveSupplyPattern::new(client.clone(), _m(&acc, "supply_in_profit")),
total_unrealized_pnl: MetricPattern1::new(client.clone(), _m(&acc, "total_unrealized_pnl")),
unrealized_loss: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_loss")),
unrealized_profit: MetricPattern1::new(client.clone(), _m(&acc, "unrealized_profit")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _100btcPattern {
pub activity: ActivityPattern2,
pub cost_basis: CostBasisPattern,
pub outputs: OutputsPattern,
pub realized: RealizedPattern,
pub realized: RealizedPattern4,
pub relative: RelativePattern,
pub supply: SupplyPattern2,
pub unrealized: UnrealizedPattern,
}
impl _100btcPattern {
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: RealizedPattern::new(client.clone(), acc.clone()),
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()),
@@ -2108,6 +2110,24 @@ impl<T: DeserializeOwned> SplitPattern2<T> {
}
}
/// Pattern struct for repeated tree structure.
pub struct SegwitAdoptionPattern {
pub base: MetricPattern11<StoredF32>,
pub cumulative: MetricPattern2<StoredF32>,
pub sum: MetricPattern2<StoredF32>,
}
impl SegwitAdoptionPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
base: MetricPattern11::new(client.clone(), acc.clone()),
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct UnclaimedRewardsPattern {
pub bitcoin: BitcoinPattern2<Bitcoin>,
@@ -2144,24 +2164,6 @@ impl ActiveSupplyPattern {
}
}
/// 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>,
@@ -2180,42 +2182,6 @@ impl CoinbasePattern2 {
}
}
/// Pattern struct for repeated tree structure.
pub struct SegwitAdoptionPattern {
pub base: MetricPattern11<StoredF32>,
pub cumulative: MetricPattern2<StoredF32>,
pub sum: MetricPattern2<StoredF32>,
}
impl SegwitAdoptionPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
base: MetricPattern11::new(client.clone(), acc.clone()),
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
sum: MetricPattern2::new(client.clone(), _m(&acc, "sum")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CoinbasePattern {
pub bitcoin: BitcoinPattern,
pub dollars: DollarsPattern<Dollars>,
pub sats: DollarsPattern<Sats>,
}
impl CoinbasePattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: BitcoinPattern::new(client.clone(), _m(&acc, "btc")),
dollars: DollarsPattern::new(client.clone(), _m(&acc, "usd")),
sats: DollarsPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct CostBasisPattern2 {
pub max: MetricPattern1<Dollars>,
@@ -2234,6 +2200,42 @@ impl CostBasisPattern2 {
}
}
/// 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 CoinbasePattern {
pub bitcoin: BitcoinPattern,
pub dollars: DollarsPattern<Dollars>,
pub sats: DollarsPattern<Sats>,
}
impl CoinbasePattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
bitcoin: BitcoinPattern::new(client.clone(), _m(&acc, "btc")),
dollars: DollarsPattern::new(client.clone(), _m(&acc, "usd")),
sats: DollarsPattern::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct RelativePattern4 {
pub supply_in_loss_rel_to_own_supply: MetricPattern1<StoredF64>,
@@ -2250,22 +2252,6 @@ impl RelativePattern4 {
}
}
/// Pattern struct for repeated tree structure.
pub struct CostBasisPattern {
pub max: MetricPattern1<Dollars>,
pub min: MetricPattern1<Dollars>,
}
impl CostBasisPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")),
min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct _1dReturns1mSdPattern {
pub sd: MetricPattern4<StoredF32>,
@@ -2282,6 +2268,22 @@ impl _1dReturns1mSdPattern {
}
}
/// Pattern struct for repeated tree structure.
pub struct CostBasisPattern {
pub max: MetricPattern1<Dollars>,
pub min: MetricPattern1<Dollars>,
}
impl CostBasisPattern {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
max: MetricPattern1::new(client.clone(), _m(&acc, "max_cost_basis")),
min: MetricPattern1::new(client.clone(), _m(&acc, "min_cost_basis")),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct SupplyPattern2 {
pub halved: ActiveSupplyPattern,
@@ -2298,22 +2300,6 @@ impl SupplyPattern2 {
}
}
/// Pattern struct for repeated tree structure.
pub struct BitcoinPattern2<T> {
pub cumulative: MetricPattern2<T>,
pub sum: MetricPattern1<T>,
}
impl<T: DeserializeOwned> BitcoinPattern2<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
sum: MetricPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct SatsPattern<T> {
pub ohlc: MetricPattern1<T>,
@@ -2330,6 +2316,22 @@ impl<T: DeserializeOwned> SatsPattern<T> {
}
}
/// Pattern struct for repeated tree structure.
pub struct BitcoinPattern2<T> {
pub cumulative: MetricPattern2<T>,
pub sum: MetricPattern1<T>,
}
impl<T: DeserializeOwned> BitcoinPattern2<T> {
/// Create a new pattern node with accumulated metric name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
cumulative: MetricPattern2::new(client.clone(), _m(&acc, "cumulative")),
sum: MetricPattern1::new(client.clone(), acc.clone()),
}
}
}
/// Pattern struct for repeated tree structure.
pub struct BlockCountPattern<T> {
pub cumulative: MetricPattern1<T>,
@@ -4238,6 +4240,7 @@ pub struct MetricsTree_Market_Dca_ClassAveragePrice {
pub _2023: MetricPattern4<Dollars>,
pub _2024: MetricPattern4<Dollars>,
pub _2025: MetricPattern4<Dollars>,
pub _2026: MetricPattern4<Dollars>,
}
impl MetricsTree_Market_Dca_ClassAveragePrice {
@@ -4254,6 +4257,7 @@ impl MetricsTree_Market_Dca_ClassAveragePrice {
_2023: MetricPattern4::new(client.clone(), "dca_class_2023_average_price".to_string()),
_2024: MetricPattern4::new(client.clone(), "dca_class_2024_average_price".to_string()),
_2025: MetricPattern4::new(client.clone(), "dca_class_2025_average_price".to_string()),
_2026: MetricPattern4::new(client.clone(), "dca_class_2026_average_price".to_string()),
}
}
}
@@ -4271,6 +4275,7 @@ pub struct MetricsTree_Market_Dca_ClassStack {
pub _2023: _2015Pattern,
pub _2024: _2015Pattern,
pub _2025: _2015Pattern,
pub _2026: _2015Pattern,
}
impl MetricsTree_Market_Dca_ClassStack {
@@ -4287,6 +4292,7 @@ impl MetricsTree_Market_Dca_ClassStack {
_2023: _2015Pattern::new(client.clone(), "dca_class_2023_stack".to_string()),
_2024: _2015Pattern::new(client.clone(), "dca_class_2024_stack".to_string()),
_2025: _2015Pattern::new(client.clone(), "dca_class_2025_stack".to_string()),
_2026: _2015Pattern::new(client.clone(), "dca_class_2026_stack".to_string()),
}
}
}
@@ -5014,7 +5020,11 @@ impl MetricsTree_Price_Cents_Split {
/// Metrics tree node.
pub struct MetricsTree_Price_Oracle {
pub close_ohlc_cents: MetricPattern6<OHLCCents>,
pub close_ohlc_dollars: MetricPattern6<OHLCDollars>,
pub height_to_first_pairoutputindex: MetricPattern11<PairOutputIndex>,
pub mid_ohlc_cents: MetricPattern6<OHLCCents>,
pub mid_ohlc_dollars: MetricPattern6<OHLCDollars>,
pub ohlc_cents: MetricPattern6<OHLCCents>,
pub ohlc_dollars: MetricPattern6<OHLCDollars>,
pub output0_value: MetricPattern33<Sats>,
@@ -5045,7 +5055,11 @@ pub struct MetricsTree_Price_Oracle {
impl MetricsTree_Price_Oracle {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self {
close_ohlc_cents: MetricPattern6::new(client.clone(), "close_ohlc_cents".to_string()),
close_ohlc_dollars: MetricPattern6::new(client.clone(), "close_ohlc_dollars".to_string()),
height_to_first_pairoutputindex: MetricPattern11::new(client.clone(), "height_to_first_pairoutputindex".to_string()),
mid_ohlc_cents: MetricPattern6::new(client.clone(), "mid_ohlc_cents".to_string()),
mid_ohlc_dollars: MetricPattern6::new(client.clone(), "mid_ohlc_dollars".to_string()),
ohlc_cents: MetricPattern6::new(client.clone(), "oracle_ohlc_cents".to_string()),
ohlc_dollars: MetricPattern6::new(client.clone(), "oracle_ohlc".to_string()),
output0_value: MetricPattern33::new(client.clone(), "pair_output0_value".to_string()),
@@ -5407,6 +5421,7 @@ pub struct MetricsTree_Transactions_Volume {
pub annualized_volume: _2015Pattern,
pub inputs_per_sec: MetricPattern4<StoredF32>,
pub outputs_per_sec: MetricPattern4<StoredF32>,
pub received_sum: ActiveSupplyPattern,
pub sent_sum: ActiveSupplyPattern,
pub tx_per_sec: MetricPattern4<StoredF32>,
}
@@ -5417,6 +5432,7 @@ impl MetricsTree_Transactions_Volume {
annualized_volume: _2015Pattern::new(client.clone(), "annualized_volume".to_string()),
inputs_per_sec: MetricPattern4::new(client.clone(), "inputs_per_sec".to_string()),
outputs_per_sec: MetricPattern4::new(client.clone(), "outputs_per_sec".to_string()),
received_sum: ActiveSupplyPattern::new(client.clone(), "received_sum".to_string()),
sent_sum: ActiveSupplyPattern::new(client.clone(), "sent_sum".to_string()),
tx_per_sec: MetricPattern4::new(client.clone(), "tx_per_sec".to_string()),
}
@@ -30,7 +30,7 @@ where
pub dates: LazyDateDerivedFull<T>,
}
const VERSION: Version = Version::ZERO;
const VERSION: Version = Version::ONE;
impl<T> TxDerivedFull<T>
where
@@ -111,7 +111,7 @@ where
self.dateindex.compute(
starting_indexes.dateindex,
&self.height.average().0,
&self.height.sum().0,
&indexes.dateindex.first_height,
&indexes.dateindex.height_count,
exit,
+13 -1
View File
@@ -14,6 +14,7 @@ pub const DCA_CLASS_YEARS: ByDcaClass<u16> = ByDcaClass {
_2023: 2023,
_2024: 2024,
_2025: 2025,
_2026: 2026,
};
/// DCA class names
@@ -29,6 +30,7 @@ pub const DCA_CLASS_NAMES: ByDcaClass<&'static str> = ByDcaClass {
_2023: "dca_class_2023",
_2024: "dca_class_2024",
_2025: "dca_class_2025",
_2026: "dca_class_2026",
};
/// Generic wrapper for DCA year class data
@@ -45,6 +47,7 @@ pub struct ByDcaClass<T> {
pub _2023: T,
pub _2024: T,
pub _2025: T,
pub _2026: T,
}
impl<T> ByDcaClass<T> {
@@ -66,6 +69,7 @@ impl<T> ByDcaClass<T> {
_2023: create(n._2023, y._2023, Self::dateindex(y._2023)),
_2024: create(n._2024, y._2024, Self::dateindex(y._2024)),
_2025: create(n._2025, y._2025, Self::dateindex(y._2025)),
_2026: create(n._2026, y._2026, Self::dateindex(y._2026)),
}
}
@@ -87,6 +91,7 @@ impl<T> ByDcaClass<T> {
_2023: create(n._2023, y._2023, Self::dateindex(y._2023))?,
_2024: create(n._2024, y._2024, Self::dateindex(y._2024))?,
_2025: create(n._2025, y._2025, Self::dateindex(y._2025))?,
_2026: create(n._2026, y._2026, Self::dateindex(y._2026))?,
})
}
@@ -107,6 +112,7 @@ impl<T> ByDcaClass<T> {
&self._2023,
&self._2024,
&self._2025,
&self._2026,
]
.into_iter()
}
@@ -124,6 +130,7 @@ impl<T> ByDcaClass<T> {
&mut self._2023,
&mut self._2024,
&mut self._2025,
&mut self._2026,
]
.into_iter()
}
@@ -142,11 +149,12 @@ impl<T> ByDcaClass<T> {
(&mut self._2023, Self::dateindex(y._2023)),
(&mut self._2024, Self::dateindex(y._2024)),
(&mut self._2025, Self::dateindex(y._2025)),
(&mut self._2026, Self::dateindex(y._2026)),
]
.into_iter()
}
pub fn dateindexes() -> [DateIndex; 11] {
pub fn dateindexes() -> [DateIndex; 12] {
let y = DCA_CLASS_YEARS;
[
Self::dateindex(y._2015),
@@ -160,6 +168,7 @@ impl<T> ByDcaClass<T> {
Self::dateindex(y._2023),
Self::dateindex(y._2024),
Self::dateindex(y._2025),
Self::dateindex(y._2026),
]
}
@@ -176,6 +185,7 @@ impl<T> ByDcaClass<T> {
_2023: (self._2023, other._2023),
_2024: (self._2024, other._2024),
_2025: (self._2025, other._2025),
_2026: (self._2026, other._2026),
}
}
@@ -192,6 +202,7 @@ impl<T> ByDcaClass<T> {
_2023: (&self._2023, &other._2023),
_2024: (&self._2024, &other._2024),
_2025: (&self._2025, &other._2025),
_2026: (&self._2026, &other._2026),
}
}
@@ -208,6 +219,7 @@ impl<T> ByDcaClass<T> {
_2023: f(self._2023),
_2024: f(self._2024),
_2025: f(self._2025),
_2026: f(self._2026),
}
}
}
@@ -102,6 +102,9 @@ impl Vecs {
// Step 7: Aggregate to daily OHLC
self.compute_daily_ohlc(indexes, starting_indexes, exit)?;
// Step 7b: Compute close-only and mid-price daily OHLC
self.compute_close_and_mid_ohlc(indexes, price_cents, starting_indexes, exit)?;
// Step 8: Compute Phase Oracle V2 (round USD template matching)
// 8a: Per-block 200-bin histograms (uses ALL outputs, not pair-filtered)
self.compute_phase_v2_histograms(indexer, indexes, starting_indexes, exit)?;
@@ -1120,6 +1123,143 @@ impl Vecs {
Ok(())
}
/// Compute daily OHLC from height close only and mid price ((open+close)/2)
fn compute_close_and_mid_ohlc(
&mut self,
indexes: &indexes::Vecs,
price_cents: &cents::Vecs,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let last_dateindex = DateIndex::from(indexes.dateindex.date.len());
let start_dateindex = starting_indexes
.dateindex
.min(DateIndex::from(self.close_ohlc_cents.len()))
.min(DateIndex::from(self.mid_ohlc_cents.len()));
if start_dateindex >= last_dateindex {
return Ok(());
}
let last_height = Height::from(price_cents.ohlc.height.len());
let mut close_iter = price_cents.split.height.close.iter();
let mut open_iter = price_cents.split.height.open.iter();
let mut dateindex_to_first_height_iter = indexes.dateindex.first_height.iter();
let mut height_count_iter = indexes.dateindex.height_count.iter();
for dateindex in start_dateindex.to_usize()..last_dateindex.to_usize() {
let dateindex = DateIndex::from(dateindex);
let first_height = dateindex_to_first_height_iter.get_unwrap(dateindex);
let count = height_count_iter.get_unwrap(dateindex);
if *count == 0 || first_height >= last_height {
continue;
}
let count = *count as usize;
// Close-only OHLC
let mut close_open = None;
let mut close_high = Cents::from(0i64);
let mut close_low = Cents::from(i64::MAX);
let mut close_close = Cents::from(0i64);
// Mid-price OHLC
let mut mid_open = None;
let mut mid_high = Cents::from(0i64);
let mut mid_low = Cents::from(i64::MAX);
let mut mid_close = Cents::from(0i64);
for i in 0..count {
let height = first_height + Height::from(i);
if height >= last_height {
break;
}
// Get close price for this height
if let Some(close_price) = close_iter.get(height) {
let close_cents = Cents::from(*close_price);
// Close-only OHLC
if close_open.is_none() {
close_open = Some(close_cents);
}
if close_cents > close_high {
close_high = close_cents;
}
if close_cents < close_low {
close_low = close_cents;
}
close_close = close_cents;
// Mid-price OHLC
if let Some(open_price) = open_iter.get(height) {
let open_cents = Cents::from(*open_price);
let mid_cents =
Cents::from((i64::from(open_cents) + i64::from(close_cents)) / 2);
if mid_open.is_none() {
mid_open = Some(mid_cents);
}
if mid_cents > mid_high {
mid_high = mid_cents;
}
if mid_cents < mid_low {
mid_low = mid_cents;
}
mid_close = mid_cents;
}
}
}
// Build close-only OHLC
let close_ohlc = if let Some(open_price) = close_open {
OHLCCents {
open: Open::new(open_price),
high: High::new(close_high),
low: Low::new(close_low),
close: Close::new(close_close),
}
} else if dateindex > DateIndex::from(0usize) {
self.close_ohlc_cents
.iter()?
.get(dateindex.decremented().unwrap())
.unwrap_or_default()
} else {
OHLCCents::default()
};
// Build mid-price OHLC
let mid_ohlc = if let Some(open_price) = mid_open {
OHLCCents {
open: Open::new(open_price),
high: High::new(mid_high),
low: Low::new(mid_low),
close: Close::new(mid_close),
}
} else if dateindex > DateIndex::from(0usize) {
self.mid_ohlc_cents
.iter()?
.get(dateindex.decremented().unwrap())
.unwrap_or_default()
} else {
OHLCCents::default()
};
self.close_ohlc_cents.truncate_push(dateindex, close_ohlc)?;
self.mid_ohlc_cents.truncate_push(dateindex, mid_ohlc)?;
}
// Write daily data
{
let _lock = exit.lock();
self.close_ohlc_cents.write()?;
self.mid_ohlc_cents.write()?;
}
Ok(())
}
/// Compute Phase Oracle V2 - Step 1: Per-block 200-bin phase histograms
///
/// Uses ALL outputs (like Python test), filtered only by sats range (1k-100k BTC).
@@ -46,6 +46,24 @@ impl Vecs {
|di: DateIndex, iter| iter.get(di).map(|o: OHLCCents| OHLCDollars::from(o)),
);
// Daily OHLC from height close only
let close_ohlc_cents = BytesVec::forced_import(db, "close_ohlc_cents", version)?;
let close_ohlc_dollars = LazyVecFrom1::init(
"close_ohlc_dollars",
version,
close_ohlc_cents.boxed_clone(),
|di: DateIndex, iter| iter.get(di).map(|o: OHLCCents| OHLCDollars::from(o)),
);
// Daily OHLC from height mid price ((open+close)/2)
let mid_ohlc_cents = BytesVec::forced_import(db, "mid_ohlc_cents", version)?;
let mid_ohlc_dollars = LazyVecFrom1::init(
"mid_ohlc_dollars",
version,
mid_ohlc_cents.boxed_clone(),
|di: DateIndex, iter| iter.get(di).map(|o: OHLCCents| OHLCDollars::from(o)),
);
// Phase Oracle V2 (round USD template matching)
// v3: Peak prices use 100 bins (downsampled from 200)
let phase_v2_version = version + Version::new(3);
@@ -111,6 +129,10 @@ impl Vecs {
ohlc_cents,
ohlc_dollars,
tx_count,
close_ohlc_cents,
close_ohlc_dollars,
mid_ohlc_cents,
mid_ohlc_dollars,
phase_v2_histogram,
phase_v2_price_cents,
phase_v2_peak_price_cents,
@@ -56,6 +56,20 @@ pub struct Vecs {
/// Number of qualifying transactions per day (for confidence)
pub tx_count: PcoVec<DateIndex, StoredU32>,
// ========== Daily OHLC from height close only ==========
/// Daily OHLC computed from height close prices only
pub close_ohlc_cents: BytesVec<DateIndex, OHLCCents>,
/// Daily OHLC from close in dollars (lazy conversion)
pub close_ohlc_dollars: LazyVecFrom1<DateIndex, OHLCDollars, DateIndex, OHLCCents>,
// ========== Daily OHLC from height mid price (open+close)/2 ==========
/// Daily OHLC computed from height mid prices ((open+close)/2)
pub mid_ohlc_cents: BytesVec<DateIndex, OHLCCents>,
/// Daily OHLC from mid in dollars (lazy conversion)
pub mid_ohlc_dollars: LazyVecFrom1<DateIndex, OHLCDollars, DateIndex, OHLCCents>,
// ========== Phase Oracle V2 (round USD template matching) ==========
/// Per-block 200-bin phase histogram
pub phase_v2_histogram: BytesVec<Height, OracleBinsV2>,
File diff suppressed because it is too large Load Diff
@@ -33,6 +33,18 @@ impl Vecs {
Ok(())
})?;
self.received_sum
.compute_all(indexes, starting_indexes, exit, |v| {
v.compute_sum_from_indexes(
starting_indexes.height,
&indexer.vecs.transactions.first_txindex,
&indexes.height.txindex_count,
&fees_vecs.output_value,
exit,
)?;
Ok(())
})?;
self.annualized_volume.compute_sats(|v| {
v.compute_sum(
starting_indexes.dateindex,
@@ -23,6 +23,13 @@ impl Vecs {
indexes,
price,
)?,
received_sum: ValueFromHeightSum::forced_import(
db,
"received_sum",
version,
indexes,
price,
)?,
annualized_volume: ValueFromDateLast::forced_import(
db,
"annualized_volume",
@@ -7,6 +7,7 @@ use crate::internal::{ComputedFromDateLast, ValueFromHeightSum, ValueFromDateLas
#[derive(Clone, Traversable)]
pub struct Vecs {
pub sent_sum: ValueFromHeightSum,
pub received_sum: ValueFromHeightSum,
pub annualized_volume: ValueFromDateLast,
pub tx_per_sec: ComputedFromDateLast<StoredF32>,
pub outputs_per_sec: ComputedFromDateLast<StoredF32>,
+1 -1
View File
@@ -9,7 +9,7 @@ repository.workspace = true
[dependencies]
jiff = { workspace = true }
owo-colors = "4.2.3"
owo-colors = { workspace = true }
tracing = { workspace = true }
tracing-log = "0.2"
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "env-filter", "std"] }
+13
View File
@@ -15,6 +15,7 @@ brk_fetcher = { workspace = true }
brk_indexer = { workspace = true }
brk_logger = { workspace = true }
brk_types = { workspace = true }
jiff = { workspace = true }
memmap2 = "0.9"
plotters = "0.3"
tracing = { workspace = true }
@@ -27,3 +28,15 @@ path = "examples/heatmap.rs"
[[example]]
name = "oracle"
path = "examples/oracle.rs"
[[example]]
name = "plot_oracle"
path = "examples/plot_oracle.rs"
[[example]]
name = "tune_smooth"
path = "examples/tune_smooth.rs"
[[example]]
name = "debug_oracle"
path = "examples/debug_oracle.rs"
+1 -1
View File
@@ -16,7 +16,7 @@ pub mod signal;
pub use anchors::{Ohlc, get_anchor_ohlc, get_anchor_range};
pub use conditions::{MappedOutputConditions, out_bits, tx_bits};
pub use constants::{NUM_BINS, OutputFilter, ROUND_USD_AMOUNTS};
pub use constants::{NUM_BINS, OutputFilter, ROUND_USD_AMOUNTS, START_HEIGHT};
pub use filters::FILTERS;
pub use histogram::load_or_compute_output_conditions;
pub use oracle::{
+1
View File
@@ -13,6 +13,7 @@ axum = { workspace = true }
include_dir = "0.7"
# importmap = { path = "../../../importmap", features = ["embedded"] }
importmap = { version = "0.4.0", features = ["embedded"] }
serde = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
+63 -2
View File
@@ -1,11 +1,13 @@
use std::{
fs,
path::{Path, PathBuf},
str::FromStr,
sync::OnceLock,
};
use importmap::ImportMap;
use include_dir::{Dir, include_dir};
use serde::{Deserialize, Serialize};
use tracing::{error, info};
use crate::{Error, Result};
@@ -16,8 +18,11 @@ pub static EMBEDDED_WEBSITE: Dir = include_dir!("$CARGO_MANIFEST_DIR/website");
/// Cached index.html with importmap injected
static INDEX_HTML: OnceLock<String> = OnceLock::new();
/// Source for serving the website
#[derive(Debug, Clone, Default)]
/// Website configuration:
/// - `true` or omitted: serve embedded website
/// - `false`: disable website serving
/// - `"/path/to/website"`: serve custom website from path
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Website {
Disabled,
#[default]
@@ -181,3 +186,59 @@ impl Website {
})
}
}
impl FromStr for Website {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"true" | "1" | "yes" | "on" => Self::Default,
"false" | "0" | "no" | "off" => Self::Disabled,
_ => Self::Filesystem(PathBuf::from(s)),
})
}
}
impl Serialize for Website {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
Self::Disabled => serializer.serialize_bool(false),
Self::Default => serializer.serialize_bool(true),
Self::Filesystem(p) => p.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for Website {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
use serde::de::{self, Visitor};
struct WebsiteVisitor;
impl<'de> Visitor<'de> for WebsiteVisitor {
type Value = Website;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a boolean or a path string")
}
fn visit_bool<E: de::Error>(self, v: bool) -> std::result::Result<Self::Value, E> {
Ok(if v {
Website::Default
} else {
Website::Disabled
})
}
fn visit_str<E: de::Error>(self, v: &str) -> std::result::Result<Self::Value, E> {
Ok(Website::Filesystem(PathBuf::from(v)))
}
fn visit_string<E: de::Error>(self, v: String) -> std::result::Result<Self::Value, E> {
Ok(Website::Filesystem(PathBuf::from(v)))
}
}
deserializer.deserialize_any(WebsiteVisitor)
}
}