mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-10 18:48:13 -07:00
global: cost basis -> urpd
This commit is contained in:
@@ -1,98 +0,0 @@
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_types::{
|
||||
CostBasisBucket, CostBasisDistribution, CostBasisFormatted, CostBasisValue, Date, Day1,
|
||||
};
|
||||
use vecdb::ReadableOptionVec;
|
||||
|
||||
use crate::Query;
|
||||
|
||||
impl Query {
|
||||
/// List available cohorts for cost basis distribution.
|
||||
pub fn cost_basis_cohorts(&self) -> Result<Vec<String>> {
|
||||
let states_path = &self.computer().distribution.states_path;
|
||||
|
||||
let mut cohorts: Vec<String> = fs::read_dir(states_path)?
|
||||
.filter_map(|entry| {
|
||||
let name = entry.ok()?.file_name().into_string().ok()?;
|
||||
let cohort = name.strip_prefix("utxo_")?.strip_suffix("_cost_basis")?;
|
||||
states_path
|
||||
.join(&name)
|
||||
.join("by_date")
|
||||
.exists()
|
||||
.then(|| cohort.to_string())
|
||||
})
|
||||
.collect();
|
||||
|
||||
cohorts.sort();
|
||||
Ok(cohorts)
|
||||
}
|
||||
|
||||
fn cost_basis_dir(&self, cohort: &str) -> Result<PathBuf> {
|
||||
let dir = self
|
||||
.computer()
|
||||
.distribution
|
||||
.states_path
|
||||
.join(format!("utxo_{cohort}_cost_basis/by_date"));
|
||||
|
||||
if !dir.exists() {
|
||||
let valid = self.cost_basis_cohorts().unwrap_or_default().join(", ");
|
||||
return Err(Error::NotFound(format!(
|
||||
"Unknown cohort '{cohort}'. Available: {valid}"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// Get the cost basis distribution for a cohort on a specific date.
|
||||
pub fn cost_basis_distribution(
|
||||
&self,
|
||||
cohort: &str,
|
||||
date: Date,
|
||||
) -> Result<CostBasisDistribution> {
|
||||
let path = self.cost_basis_dir(cohort)?.join(date.to_string());
|
||||
|
||||
if !path.exists() {
|
||||
return Err(Error::NotFound(format!(
|
||||
"No data for cohort '{cohort}' on {date}"
|
||||
)));
|
||||
}
|
||||
|
||||
CostBasisDistribution::deserialize(&fs::read(&path)?)
|
||||
}
|
||||
|
||||
/// List available dates for a cohort's cost basis distribution.
|
||||
pub fn cost_basis_dates(&self, cohort: &str) -> Result<Vec<Date>> {
|
||||
let dir = self.cost_basis_dir(cohort)?;
|
||||
|
||||
let mut dates: Vec<Date> = fs::read_dir(&dir)?
|
||||
.filter_map(|entry| entry.ok()?.file_name().to_str()?.parse().ok())
|
||||
.collect();
|
||||
|
||||
dates.sort();
|
||||
Ok(dates)
|
||||
}
|
||||
|
||||
/// Get the formatted cost basis distribution.
|
||||
pub fn cost_basis_formatted(
|
||||
&self,
|
||||
cohort: &str,
|
||||
date: Date,
|
||||
bucket: CostBasisBucket,
|
||||
value: CostBasisValue,
|
||||
) -> Result<CostBasisFormatted> {
|
||||
let distribution = self.cost_basis_distribution(cohort, date)?;
|
||||
let day1 = Day1::try_from(date).map_err(|e| Error::Parse(e.to_string()))?;
|
||||
let price = &self.computer().prices;
|
||||
let spot = price
|
||||
.split
|
||||
.close
|
||||
.cents
|
||||
.day1
|
||||
.collect_one_flat(day1)
|
||||
.ok_or_else(|| Error::NotFound(format!("No price data for {date}")))?;
|
||||
Ok(distribution.format(bucket, value, spot))
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
mod addr;
|
||||
mod block;
|
||||
mod cost_basis;
|
||||
mod mempool;
|
||||
mod mining;
|
||||
mod price;
|
||||
mod series;
|
||||
mod tx;
|
||||
mod urpd;
|
||||
|
||||
pub use block::BLOCK_TXS_PAGE_SIZE;
|
||||
pub use series::ResolvedQuery;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
use brk_error::{Error, Result};
|
||||
use brk_types::{Cohort, Date, Day1, Urpd, UrpdAggregation, UrpdRaw};
|
||||
use vecdb::ReadableOptionVec;
|
||||
|
||||
use crate::Query;
|
||||
|
||||
impl Query {
|
||||
/// Available cohorts for URPD.
|
||||
pub fn urpd_cohorts(&self) -> Result<Vec<Cohort>> {
|
||||
let states_path = &self.computer().distribution.states_path;
|
||||
|
||||
let mut cohorts: Vec<Cohort> = fs::read_dir(states_path)?
|
||||
.filter_map(|entry| {
|
||||
let name = entry.ok()?.file_name().into_string().ok()?;
|
||||
states_path
|
||||
.join(&name)
|
||||
.join("urpd")
|
||||
.exists()
|
||||
.then(|| Cohort::from(name))
|
||||
})
|
||||
.collect();
|
||||
|
||||
cohorts.sort_by(|a, b| a.to_string().cmp(&b.to_string()));
|
||||
Ok(cohorts)
|
||||
}
|
||||
|
||||
pub(crate) fn urpd_dir(&self, cohort: &str) -> Result<PathBuf> {
|
||||
let dir = self
|
||||
.computer()
|
||||
.distribution
|
||||
.states_path
|
||||
.join(cohort)
|
||||
.join("urpd");
|
||||
|
||||
if !dir.exists() {
|
||||
let valid = self
|
||||
.urpd_cohorts()
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|c| c.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
return Err(Error::NotFound(format!(
|
||||
"Unknown cohort '{cohort}'. Available: {valid}"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// Available dates for a cohort.
|
||||
pub fn urpd_dates(&self, cohort: &Cohort) -> Result<Vec<Date>> {
|
||||
let dir = self.urpd_dir(cohort)?;
|
||||
|
||||
let mut dates: Vec<Date> = fs::read_dir(&dir)?
|
||||
.filter_map(|entry| entry.ok()?.file_name().to_str()?.parse().ok())
|
||||
.collect();
|
||||
|
||||
dates.sort();
|
||||
Ok(dates)
|
||||
}
|
||||
|
||||
/// Raw URPD data for a cohort on a specific date.
|
||||
pub fn urpd_raw(&self, cohort: &Cohort, date: Date) -> Result<UrpdRaw> {
|
||||
let path = self.urpd_dir(cohort)?.join(date.to_string());
|
||||
|
||||
if !path.exists() {
|
||||
return Err(Error::NotFound(format!(
|
||||
"No URPD for cohort '{cohort}' on {date}"
|
||||
)));
|
||||
}
|
||||
|
||||
UrpdRaw::deserialize(&fs::read(&path)?)
|
||||
}
|
||||
|
||||
/// URPD for a cohort on a specific date.
|
||||
pub fn urpd_at(
|
||||
&self,
|
||||
cohort: &Cohort,
|
||||
date: Date,
|
||||
agg: UrpdAggregation,
|
||||
) -> Result<Urpd> {
|
||||
let raw = self.urpd_raw(cohort, date)?;
|
||||
let day1 = Day1::try_from(date).map_err(|e| Error::Parse(e.to_string()))?;
|
||||
let close = self
|
||||
.computer()
|
||||
.prices
|
||||
.split
|
||||
.close
|
||||
.cents
|
||||
.day1
|
||||
.collect_one_flat(day1)
|
||||
.ok_or_else(|| Error::NotFound(format!("No price data for {date}")))?;
|
||||
Ok(Urpd::build(cohort.clone(), date, close, &raw, agg))
|
||||
}
|
||||
|
||||
/// URPD for the most recently available date in a cohort.
|
||||
pub fn urpd_latest(&self, cohort: &Cohort, agg: UrpdAggregation) -> Result<Urpd> {
|
||||
let dates = self.urpd_dates(cohort)?;
|
||||
let date = *dates.last().ok_or_else(|| {
|
||||
Error::NotFound(format!("No URPD available for cohort '{cohort}'"))
|
||||
})?;
|
||||
self.urpd_at(cohort, date, agg)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user