global: snapshot

This commit is contained in:
k
2024-10-08 21:47:46 +02:00
parent 068bb07d6e
commit 79ffbf3d1d
25 changed files with 2696 additions and 1480 deletions

View File

@@ -12,10 +12,16 @@
## Datasets
- Added `Sell Side Risk Ratio` to all entities
- Added `Adjusted Value Created`, `Adjusted Value Destroyed` and `Adjusted Spent Output Profit Ratio` to all entities
- Added `Open`, `High` and `Low` datasets
- Added `Satoshis Per Dollar`
- Added `All Time High`
- Added `All Time High Date`
- Added `Days Since All Time High`
- Added `Max Days Between All Time Highs`
- Added `Max Years Between All Time Highs`
- Added `Drawdown`
- Added `Adjusted Value Created`, `Adjusted Value Destroyed` and `Adjusted Spent Output Profit Ratio` to all entities
- Added `Realized Profit To Loss Ratio` to all entities
- Removed datasets split by liquidity for all datasets already split by any address kind, while fun to have, they took time to compute, ram, and space to store and no one was actually checking them
- Fixed a lot of values in split by liquidity datasets

2
biter/Cargo.lock generated
View File

@@ -110,7 +110,7 @@ dependencies = [
[[package]]
name = "biter"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"bitcoin",
"bitcoincore-rpc",

View File

@@ -10,7 +10,6 @@ allocative = "0.3.3"
bincode = { git = "https://github.com/bincode-org/bincode.git", features = [
"serde",
] }
# biter = "0.1.0"
biter = { path = "../biter" }
bitcoin_hashes = { version = "0.14.0" }
chrono = { version = "0.4.38", features = ["serde"] }

View File

@@ -27,12 +27,10 @@ pub fn iter_blocks(
log("Starting...");
let mut datasets = AllDatasets::import()?;
// RAM: 200MB at this point
log("Imported datasets");
let mut databases = Databases::import();
// RAM: 200MB too
log("Imported databases");

View File

@@ -23,7 +23,7 @@ type Database = _Database<Key, Value>;
pub struct AddressIndexToAddressData {
pub metadata: Metadata,
map: BTreeMap<usize, Database>,
pub map: BTreeMap<usize, Database>,
}
impl Deref for AddressIndexToAddressData {
@@ -99,7 +99,7 @@ impl AddressIndexToAddressData {
})
}
fn open_all(&mut self) {
pub fn open_all(&mut self) {
let path = Self::full_path();
fs::create_dir_all(&path).unwrap();

View File

@@ -19,7 +19,7 @@ type Database = _Database<Key, Value>;
pub struct TxoutIndexToAmount {
pub metadata: Metadata,
pub map: BTreeMap<usize, Database>,
map: BTreeMap<usize, Database>,
}
impl Deref for TxoutIndexToAmount {

View File

@@ -186,7 +186,7 @@ impl CointimeDataset {
.multi_insert_simple_transform(
heights,
&mut circulating_supply.height,
|circulating_supply| circulating_supply as f32,
|circulating_supply, _| circulating_supply as f32,
);
self.coinblocks_created
.multi_date_insert_sum_range(dates, first_height, last_height);
@@ -414,7 +414,7 @@ impl CointimeDataset {
.multi_insert_complex_transform(
dates,
&mut self.active_cap.date,
|(active_cap, date, _)| {
|(active_cap, date, _, _)| {
let investor_cap = self.investor_cap.date.get(date).unwrap();
(active_cap - investor_cap) / active_cap
},

View File

@@ -215,14 +215,14 @@ impl AllDatasets {
self.address.compute(
&compute_data,
&mut self.price.closes,
&mut self.price.close,
&mut self.mining.cumulative_subsidy,
&mut self.price.market_cap,
);
self.utxo.compute(
&compute_data,
&mut self.price.closes,
&mut self.price.close,
&mut self.mining.cumulative_subsidy,
&mut self.price.market_cap,
);
@@ -255,7 +255,7 @@ impl AllDatasets {
&compute_data,
&mut self.date_metadata.first_height,
&mut self.date_metadata.last_height,
&mut self.price.closes,
&mut self.price.close,
&mut self.mining.cumulative_subsidy,
&mut self.address.cohorts.all.subs.capitalization.realized_cap,
&mut self.address.cohorts.all.subs.capitalization.realized_price,

View File

@@ -32,10 +32,13 @@ pub struct PriceDatasets {
kibo_by_date: BTreeMap<DateMapChunkId, BTreeMap<Date, OHLC>>,
// Inserted
pub ohlcs: BiMap<OHLC>,
pub ohlc: BiMap<OHLC>,
// Computed
pub closes: BiMap<f32>,
pub open: BiMap<f32>,
pub high: BiMap<f32>,
pub low: BiMap<f32>,
pub close: BiMap<f32>,
pub market_cap: BiMap<f32>,
pub price_1w_sma: BiMap<f32>,
pub price_1w_sma_ratio: RatioDataset,
@@ -76,6 +79,10 @@ pub struct PriceDatasets {
pub price_4y_compound_return: DateMap<f32>,
// projection via lowest 4y compound value
pub all_time_high: BiMap<f32>,
pub all_time_high_date: DateMap<Date>,
pub days_since_all_time_high: DateMap<u32>,
pub max_days_between_all_time_highs: DateMap<u32>,
pub max_years_between_all_time_highs: DateMap<f32>,
pub market_price_to_all_time_high_ratio: BiMap<f32>,
pub drawdown: BiMap<f32>,
pub sats_per_dollar: BiMap<f32>,
@@ -99,8 +106,11 @@ impl PriceDatasets {
kibo_by_height: BTreeMap::default(),
kibo_by_date: BTreeMap::default(),
ohlcs: BiMap::new_json(1, price_path),
closes: BiMap::new_bin(1, &f("close")),
ohlc: BiMap::new_json(1, price_path),
open: BiMap::new_bin(1, &f("open")),
high: BiMap::new_bin(1, &f("high")),
low: BiMap::new_bin(1, &f("low")),
close: BiMap::new_bin(1, &f("close")),
market_cap: BiMap::new_bin(1, &f("market_cap")),
price_1w_sma: BiMap::new_bin(1, &f("price_1w_sma")),
price_1w_sma_ratio: RatioDataset::import(datasets_path, "price_1w_sma")?,
@@ -140,6 +150,16 @@ impl PriceDatasets {
price_10y_total_return: DateMap::new_bin(1, &f("price_10y_total_return")),
price_4y_compound_return: DateMap::new_bin(1, &f("price_4y_compound_return")),
all_time_high: BiMap::new_bin(1, &f("all_time_high")),
all_time_high_date: DateMap::new_bin(1, &f("all_time_high_date")),
days_since_all_time_high: DateMap::new_bin(1, &f("days_since_all_time_high")),
max_days_between_all_time_highs: DateMap::new_bin(
1,
&f("max_days_between_all_time_highs"),
),
max_years_between_all_time_highs: DateMap::new_bin(
2,
&f("max_years_between_all_time_highs"),
),
market_price_to_all_time_high_ratio: BiMap::new_bin(
1,
&f("market_price_to_all_time_high_ratio"),
@@ -157,128 +177,137 @@ impl PriceDatasets {
pub fn compute(&mut self, compute_data: &ComputeData, circulating_supply: &mut BiMap<f64>) {
let &ComputeData { dates, heights, .. } = compute_data;
self.closes
.multi_insert_simple_transform(heights, dates, &mut self.ohlcs, &|ohlc| ohlc.close);
self.open
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.open);
self.high
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.high);
self.low
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.low);
self.close
.multi_insert_simple_transform(heights, dates, &mut self.ohlc, &|ohlc| ohlc.close);
self.market_cap
.multi_insert_multiply(heights, dates, &mut self.closes, circulating_supply);
.multi_insert_multiply(heights, dates, &mut self.close, circulating_supply);
self.price_1w_sma.multi_insert_simple_average(
heights,
dates,
&mut self.closes,
&mut self.close,
ONE_WEEK_IN_DAYS,
);
self.price_1m_sma.multi_insert_simple_average(
heights,
dates,
&mut self.closes,
&mut self.close,
ONE_MONTH_IN_DAYS,
);
self.price_1y_sma.multi_insert_simple_average(
heights,
dates,
&mut self.closes,
&mut self.close,
ONE_YEAR_IN_DAYS,
);
self.price_2y_sma.multi_insert_simple_average(
heights,
dates,
&mut self.closes,
&mut self.close,
2 * ONE_YEAR_IN_DAYS,
);
self.price_4y_sma.multi_insert_simple_average(
heights,
dates,
&mut self.closes,
&mut self.close,
4 * ONE_YEAR_IN_DAYS,
);
self.price_8d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 8);
.multi_insert_simple_average(heights, dates, &mut self.close, 8);
self.price_13d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 13);
.multi_insert_simple_average(heights, dates, &mut self.close, 13);
self.price_21d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 21);
.multi_insert_simple_average(heights, dates, &mut self.close, 21);
self.price_34d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 34);
.multi_insert_simple_average(heights, dates, &mut self.close, 34);
self.price_55d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 55);
.multi_insert_simple_average(heights, dates, &mut self.close, 55);
self.price_89d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 89);
.multi_insert_simple_average(heights, dates, &mut self.close, 89);
self.price_144d_sma
.multi_insert_simple_average(heights, dates, &mut self.closes, 144);
.multi_insert_simple_average(heights, dates, &mut self.close, 144);
self.price_200w_sma.multi_insert_simple_average(
heights,
dates,
&mut self.closes,
&mut self.close,
200 * ONE_WEEK_IN_DAYS,
);
self.price_1d_total_return
.multi_insert_percentage_change(dates, &mut self.closes.date, 1);
.multi_insert_percentage_change(dates, &mut self.close.date, 1);
self.price_1m_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
ONE_MONTH_IN_DAYS,
);
self.price_6m_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
6 * ONE_MONTH_IN_DAYS,
);
self.price_1y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
ONE_YEAR_IN_DAYS,
);
self.price_2y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
2 * ONE_YEAR_IN_DAYS,
);
self.price_3y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
3 * ONE_YEAR_IN_DAYS,
);
self.price_4y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
4 * ONE_YEAR_IN_DAYS,
);
self.price_6y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
6 * ONE_YEAR_IN_DAYS,
);
self.price_8y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
8 * ONE_YEAR_IN_DAYS,
);
self.price_10y_total_return.multi_insert_percentage_change(
dates,
&mut self.closes.date,
&mut self.close.date,
10 * ONE_YEAR_IN_DAYS,
);
self.price_4y_compound_return
.multi_insert_complex_transform(
dates,
&mut self.closes.date,
|(last_value, date, closes)| {
&mut self.close.date,
|(last_value, date, closes, _)| {
let previous_value = date
.checked_sub_days(Days::new(4 * ONE_YEAR_IN_DAYS as u64))
.and_then(|date| closes.get_or_import(&Date::wrap(date)))
@@ -289,37 +318,69 @@ impl PriceDatasets {
);
self.price_1w_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_1w_sma);
.compute(compute_data, &mut self.close, &mut self.price_1w_sma);
self.price_1m_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_1m_sma);
.compute(compute_data, &mut self.close, &mut self.price_1m_sma);
self.price_1y_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_1y_sma);
.compute(compute_data, &mut self.close, &mut self.price_1y_sma);
self.price_2y_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_2y_sma);
.compute(compute_data, &mut self.close, &mut self.price_2y_sma);
self.price_4y_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_4y_sma);
.compute(compute_data, &mut self.close, &mut self.price_4y_sma);
self.price_8d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_8d_sma);
.compute(compute_data, &mut self.close, &mut self.price_8d_sma);
self.price_13d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_13d_sma);
.compute(compute_data, &mut self.close, &mut self.price_13d_sma);
self.price_21d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_21d_sma);
.compute(compute_data, &mut self.close, &mut self.price_21d_sma);
self.price_34d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_34d_sma);
.compute(compute_data, &mut self.close, &mut self.price_34d_sma);
self.price_55d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_55d_sma);
.compute(compute_data, &mut self.close, &mut self.price_55d_sma);
self.price_89d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_89d_sma);
.compute(compute_data, &mut self.close, &mut self.price_89d_sma);
self.price_144d_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_144d_sma);
.compute(compute_data, &mut self.close, &mut self.price_144d_sma);
self.price_200w_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_200w_sma);
.compute(compute_data, &mut self.close, &mut self.price_200w_sma);
self.all_time_high
.multi_insert_max(heights, dates, &mut self.closes);
.multi_insert_max(heights, dates, &mut self.high);
self.market_price_to_all_time_high_ratio
.multi_insert_percentage(heights, dates, &mut self.closes, &mut self.all_time_high);
.multi_insert_percentage(heights, dates, &mut self.close, &mut self.all_time_high);
self.all_time_high_date.multi_insert_complex_transform(
dates,
&mut self.all_time_high.date,
|(value, date, _, map)| {
let high = self.high.date.get(date).unwrap();
let is_ath = high == value;
if is_ath {
*date
} else {
let previous_date = date.checked_sub(1).unwrap();
*map.get(&previous_date).as_ref().unwrap_or(date)
}
},
);
self.days_since_all_time_high.multi_insert_simple_transform(
dates,
&mut self.all_time_high_date,
|value, key| key.difference_in_days_between(value),
);
self.max_days_between_all_time_highs
.multi_insert_max(dates, &mut self.days_since_all_time_high);
self.max_years_between_all_time_highs
.multi_insert_simple_transform(
dates,
&mut self.max_days_between_all_time_highs,
|days, _| (days as f64 / ONE_YEAR_IN_DAYS as f64) as f32,
);
self.drawdown.multi_insert_simple_transform(
heights,
@@ -331,21 +392,21 @@ impl PriceDatasets {
self.sats_per_dollar.multi_insert_simple_transform(
heights,
dates,
&mut self.closes,
&mut self.close,
&|price| Amount::ONE_BTC_F32 / price,
);
}
pub fn get_date_ohlc(&mut self, date: Date) -> color_eyre::Result<OHLC> {
if self.ohlcs.date.is_key_safe(date) {
Ok(self.ohlcs.date.get(&date).unwrap().to_owned())
if self.ohlc.date.is_key_safe(date) {
Ok(self.ohlc.date.get(&date).unwrap().to_owned())
} else {
let ohlc = self
.get_from_daily_kraken(&date)
.or_else(|_| self.get_from_daily_binance(&date))
.or_else(|_| self.get_from_date_kibo(&date))?;
self.ohlcs.date.insert(date, ohlc);
self.ohlc.date.insert(date, ohlc);
Ok(ohlc)
}
@@ -427,7 +488,7 @@ impl PriceDatasets {
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
) -> color_eyre::Result<OHLC> {
if let Some(ohlc) = self.ohlcs.height.get(&height) {
if let Some(ohlc) = self.ohlc.height.get(&height) {
return Ok(ohlc);
}
@@ -469,7 +530,7 @@ How to fix this:
})
});
self.ohlcs.height.insert(height, ohlc);
self.ohlc.height.insert(height, ohlc);
Ok(ohlc)
}
@@ -617,16 +678,19 @@ impl AnyDataset for PriceDatasets {
}
fn to_inserted_bi_map_vec(&self) -> Vec<&(dyn AnyBiMap + Send + Sync)> {
vec![&self.ohlcs]
vec![&self.ohlc]
}
fn to_inserted_mut_bi_map_vec(&mut self) -> Vec<&mut dyn AnyBiMap> {
vec![&mut self.ohlcs]
vec![&mut self.ohlc]
}
fn to_computed_bi_map_vec(&self) -> Vec<&(dyn AnyBiMap + Send + Sync)> {
let mut v = vec![
&self.closes as &(dyn AnyBiMap + Send + Sync),
&self.open as &(dyn AnyBiMap + Send + Sync),
&self.high,
&self.low,
&self.close,
&self.market_cap,
&self.price_1w_sma,
&self.price_1m_sma,
@@ -666,7 +730,10 @@ impl AnyDataset for PriceDatasets {
fn to_computed_mut_bi_map_vec(&mut self) -> Vec<&mut dyn AnyBiMap> {
let mut v = vec![
&mut self.closes as &mut dyn AnyBiMap,
&mut self.open as &mut dyn AnyBiMap,
&mut self.high,
&mut self.low,
&mut self.close,
&mut self.market_cap,
&mut self.price_1w_sma,
&mut self.price_1m_sma,
@@ -717,6 +784,10 @@ impl AnyDataset for PriceDatasets {
&self.price_8y_total_return,
&self.price_10y_total_return,
&self.price_4y_compound_return,
&self.all_time_high_date,
&self.days_since_all_time_high,
&self.max_days_between_all_time_highs,
&self.max_years_between_all_time_highs,
]
}
@@ -733,6 +804,10 @@ impl AnyDataset for PriceDatasets {
&mut self.price_8y_total_return,
&mut self.price_10y_total_return,
&mut self.price_4y_compound_return,
&mut self.all_time_high_date,
&mut self.days_since_all_time_high,
&mut self.max_days_between_all_time_highs,
&mut self.max_years_between_all_time_highs,
]
}
}

View File

@@ -122,7 +122,7 @@ impl RatioDataset {
self.ratio_1y_sma_momentum_oscillator
.date
.multi_insert_complex_transform(dates, &mut self.ratio.date, |(ratio, date, _)| {
.multi_insert_complex_transform(dates, &mut self.ratio.date, |(ratio, date, _, _)| {
(ratio / self.ratio_1y_sma.date.get_or_import(date).unwrap()) - 1.0
});

View File

@@ -32,6 +32,7 @@ pub struct RealizedSubDataset {
cumulative_net_realized_profit_and_loss_1m_net_change: BiMap<f32>,
realized_value: BiMap<f32>,
sell_side_risk_ratio: DateMap<f32>,
realized_profit_to_loss_ratio: BiMap<f32>,
}
impl RealizedSubDataset {
@@ -77,6 +78,7 @@ impl RealizedSubDataset {
),
realized_value: BiMap::new_bin(1, &f("realized_value")),
sell_side_risk_ratio: DateMap::new_bin(1, &f("sell_side_risk_ratio")),
realized_profit_to_loss_ratio: BiMap::new_bin(1, &f("realized_profit_to_loss_ratio")),
};
s.min_initial_states
@@ -243,6 +245,13 @@ impl RealizedSubDataset {
&mut self.realized_value.date,
&mut market_cap.date,
);
self.realized_profit_to_loss_ratio.multi_insert_divide(
heights,
dates,
&mut self.realized_profit,
&mut self.realized_loss,
);
}
}
@@ -287,6 +296,7 @@ impl AnyDataset for RealizedSubDataset {
&self.cumulative_net_realized_profit_and_loss,
&self.cumulative_net_realized_profit_and_loss_1m_net_change,
&self.realized_value,
&self.realized_profit_to_loss_ratio,
]
}
@@ -304,6 +314,7 @@ impl AnyDataset for RealizedSubDataset {
&mut self.cumulative_net_realized_profit_and_loss,
&mut self.cumulative_net_realized_profit_and_loss_1m_net_change,
&mut self.realized_value,
&mut self.realized_profit_to_loss_ratio,
]
}

View File

@@ -182,7 +182,7 @@ impl TransactionDataset {
self.transactions_per_second
.date
.multi_insert_simple_transform(dates, &mut self.count.date, |count| {
.multi_insert_simple_transform(dates, &mut self.count.date, |count, _| {
count as f32 / ONE_DAY_IN_S as f32
});

View File

@@ -67,21 +67,21 @@ where
self.date.multi_insert_const(dates, constant);
}
pub fn multi_insert_simple_transform<F, K>(
pub fn multi_insert_simple_transform<F, V>(
&mut self,
heights: &[Height],
dates: &[Date],
source: &mut BiMap<K>,
source: &mut BiMap<V>,
transform: &F,
) where
Value: Div<Output = Value>,
F: Fn(K) -> Value,
K: MapValue,
F: Fn(V) -> Value,
V: MapValue,
{
self.height
.multi_insert_simple_transform(heights, &mut source.height, transform);
.multi_insert_simple_transform(heights, &mut source.height, |v, _| transform(v));
self.date
.multi_insert_simple_transform(dates, &mut source.date, transform);
.multi_insert_simple_transform(dates, &mut source.date, |v, _| transform(v));
}
#[allow(unused)]

View File

@@ -45,6 +45,10 @@ impl Date {
pub fn yesterday() -> Self {
Self(Self::today().checked_sub_days(Days::new(1)).unwrap())
}
pub fn difference_in_days_between(&self, older: Self) -> u32 {
(**self - *older).num_days() as u32
}
}
impl MapKey<DateMapChunkId> for Date {

View File

@@ -21,8 +21,8 @@ impl MapChunkId for DateMapChunkId {
self.0.to_string()
}
fn from_path(path: &Path) -> Self {
Self(
fn from_path(path: &Path) -> color_eyre::Result<Self> {
Ok(Self(
path.file_name()
.unwrap()
.to_str()
@@ -30,9 +30,8 @@ impl MapChunkId for DateMapChunkId {
.split(".")
.next()
.unwrap()
.parse::<i32>()
.unwrap(),
)
.parse::<i32>()?,
))
}
fn to_usize(self) -> usize {

View File

@@ -63,7 +63,7 @@ where
Self: Ord + Debug + Copy + Clone,
{
fn to_name(&self) -> String;
fn from_path(path: &Path) -> Self;
fn from_path(path: &Path) -> color_eyre::Result<Self>;
fn to_usize(self) -> usize;
fn from_usize(id: usize) -> Self;
}
@@ -189,9 +189,12 @@ where
.unwrap()
.map(|entry| entry.unwrap().path())
.filter(|path| serialization.is_serializable(path))
.map(|path| {
let chunk_id = ChunkId::from_path(&path);
(chunk_id, path)
.flat_map(|path| {
if let Ok(chunk_id) = ChunkId::from_path(&path) {
Some((chunk_id, path))
} else {
None
}
})
.collect()
}
@@ -414,10 +417,10 @@ where
) where
SourceValue: MapValue,
SourceSerialized: MapSerialized<Key, SourceValue, ChunkId>,
F: Fn(SourceValue) -> Value,
F: Fn(SourceValue, &Key) -> Value,
{
keys.iter().for_each(|key| {
self.insert(*key, transform(source.get_or_import(key).unwrap()));
self.insert(*key, transform(source.get_or_import(key).unwrap(), key));
});
}
@@ -434,13 +437,14 @@ where
SourceValue,
&Key,
&mut GenericMap<Key, SourceValue, ChunkId, SourceSerialized>,
&Self,
),
) -> Value,
{
keys.iter().for_each(|key| {
self.insert(
*key,
transform((source.get_or_import(key).unwrap(), key, source)),
transform((source.get_or_import(key).unwrap(), key, source, self)),
);
});
}
@@ -823,31 +827,27 @@ where
where
Value: Default + PartialOrd,
{
let mut max = None;
let mut previous_max = None;
keys.iter().for_each(|key| {
let previous_max = max.unwrap_or_else(|| {
if previous_max.is_none() {
key.checked_sub(1)
.and_then(|previous_max_key| self.get(&previous_max_key))
.unwrap_or_default()
});
.and_then(|v| previous_max.replace(v));
}
let last_value = source.get_or_import(key).unwrap_or_else(|| {
dbg!(key);
panic!()
});
if max.is_none() || last_value > previous_max {
max.replace(last_value);
if previous_max.is_none()
|| previous_max.is_some_and(|previous_max| previous_max < last_value)
{
previous_max.replace(last_value);
}
self.insert(
*key,
max.unwrap_or_else(|| {
dbg!(previous_max, last_value, max);
panic!();
}),
);
self.insert(*key, previous_max.unwrap());
});
}
}

View File

@@ -28,8 +28,8 @@ impl MapChunkId for HeightMapChunkId {
format!("{start}..{end}")
}
fn from_path(path: &Path) -> Self {
Self(Height::new(
fn from_path(path: &Path) -> color_eyre::Result<Self> {
Ok(Self(Height::new(
path.file_name()
.unwrap()
.to_str()
@@ -37,12 +37,8 @@ impl MapChunkId for HeightMapChunkId {
.split("..")
.next()
.unwrap()
.parse::<u32>()
.unwrap_or_else(|_| {
dbg!(path);
panic!()
}),
))
.parse::<u32>()?,
)))
}
fn to_usize(self) -> usize {

View File

@@ -15,11 +15,12 @@
<meta name="mobile-web-app-capable" content="yes" />
<script type="module" crossorigin src="./script.js"></script>
<!-- ------ -->
<!-- Styles -->
<!-- ------ -->
<!-- Tailwind Base -->
<style>
/* Tailwind base */
*,
::after,
::before,
@@ -217,6 +218,7 @@
}
</style>
<!-- Custom -->
<style>
:root {
--default-font-family: "Satoshi";
@@ -803,6 +805,23 @@
color: var(--orange);
}
summary {
list-style: none;
display: flex;
align-items: center;
cursor: pointer;
position: relative;
&::marker,
&::-webkit-details-marker {
display: none;
}
&:hover {
color: var(--orange);
}
}
svg {
width: 20px;
height: 20px;
@@ -888,21 +907,8 @@
}
> details > summary {
list-style: none;
display: flex;
align-items: center;
cursor: pointer;
position: relative;
&::marker,
&::-webkit-details-marker {
display: none;
}
&:hover {
color: var(--orange);
}
details[open] > & {
&::after {
content: "";
@@ -1024,49 +1030,56 @@
}
> #dashboards {
display: flex;
flex-direction: column;
z-index: 50;
margin: -0.5px calc(-1.5rem - 1px);
display: flex;
> table {
flex: 1;
table-layout: auto;
border-collapse: separate;
border-spacing: 0px 1px;
border: 1px;
border-top: 0;
padding: 0.25rem 0;
margin: -0.5px;
/* font-size: var(--font-size-xs);
line-height: var(--line-height-xs); */
> details {
/* border: 1px; */
caption {
border-bottom-style: dashed !important;
border-width: 1px;
padding: 0.375rem 0.625rem;
> summary {
margin: -0.5px;
display: block;
padding: 0.5rem 1.5rem;
text-align: left;
}
tr {
border: 1px;
&:hover,
&:hover * {
color: var(--orange) !important;
details[open] > & {
border-bottom-style: dashed !important;
border-bottom-width: 1px;
}
}
td {
padding: 0.125rem 0.625rem;
text-align: right;
> table {
border: 1px;
border-top-width: 0px;
margin: -0.5px;
width: calc(100% + 1px);
border-collapse: separate;
&:first-child {
text-align: left;
color: var(--off-color);
table-layout: auto;
padding: 0.375rem 1.5rem;
tr {
/* border: 1px; */
&:hover,
&:hover * {
color: var(--orange) !important;
}
> i {
color: var(--off-color);
td {
padding: 0.125rem 0;
text-align: right;
&:first-child {
text-align: left;
color: var(--off-color);
}
> i {
color: var(--off-color);
}
}
}
}
@@ -1373,7 +1386,184 @@
}
</style>
<!-- Flex Masonry -->
<style>
.flexmasonry {
display: flex;
flex-flow: column wrap;
align-content: space-between;
}
.flexmasonry-item {
width: 100%;
.flexmasonry-cols-2 & {
width: 50%;
&:nth-child(2n + 1) {
order: 1;
}
&:nth-child(2n) {
order: 2;
}
}
.flexmasonry-cols-3 & {
width: 33.333%;
&:nth-child(3n + 1) {
order: 1;
}
&:nth-child(3n + 2) {
order: 2;
}
&:nth-child(3n) {
order: 3;
}
}
.flexmasonry-cols-4 & {
width: 25%;
&:nth-child(4n + 1) {
order: 1;
}
&:nth-child(4n + 2) {
order: 2;
}
&:nth-child(4n + 3) {
order: 3;
}
&:nth-child(4n) {
order: 4;
}
}
.flexmasonry-cols-5 & {
width: 20%;
&:nth-child(5n + 1) {
order: 1;
}
&:nth-child(5n + 2) {
order: 2;
}
&:nth-child(5n + 3) {
order: 3;
}
&:nth-child(5n + 4) {
order: 4;
}
&:nth-child(5n) {
order: 5;
}
}
.flexmasonry-cols-6 & {
width: 16.666%;
&:nth-child(6n + 1) {
order: 1;
}
&:nth-child(6n + 2) {
order: 2;
}
&:nth-child(6n + 3) {
order: 3;
}
&:nth-child(6n + 4) {
order: 4;
}
&:nth-child(6n + 5) {
order: 5;
}
&:nth-child(6n) {
order: 6;
}
}
.flexmasonry-cols-7 & {
width: 14.285%;
&:nth-child(7n + 1) {
order: 1;
}
&:nth-child(7n + 2) {
order: 2;
}
&:nth-child(7n + 3) {
order: 3;
}
&:nth-child(7n + 4) {
order: 4;
}
&:nth-child(7n + 5) {
order: 5;
}
&:nth-child(7n + 6) {
order: 6;
}
&:nth-child(7n) {
order: 7;
}
}
.flexmasonry-cols-8 & {
width: 12.5%;
&:nth-child(8n + 1) {
order: 1;
}
&:nth-child(8n + 2) {
order: 2;
}
&:nth-child(8n + 3) {
order: 3;
}
&:nth-child(8n + 4) {
order: 4;
}
&:nth-child(8n + 5) {
order: 5;
}
&:nth-child(8n + 6) {
order: 6;
}
&:nth-child(8n + 7) {
order: 7;
}
&:nth-child(8n) {
order: 8;
}
}
}
.flexmasonry-break {
content: "";
flex-basis: 100%;
width: 0 !important;
margin: 0;
}
.flexmasonry-break-1 {
order: 1;
}
.flexmasonry-break-2 {
order: 2;
}
.flexmasonry-break-3 {
order: 3;
}
.flexmasonry-break-4 {
order: 4;
}
.flexmasonry-break-5 {
order: 5;
}
.flexmasonry-break-6 {
order: 6;
}
.flexmasonry-break-7 {
order: 7;
}
</style>
<!-- ------- -->
<!-- Scripts -->
<!-- ------- -->
<script>
// @ts-check

View File

@@ -0,0 +1,3 @@
URL + Version:
https://unpkg.com/browse/flexmasonry@latest/

View File

@@ -0,0 +1,198 @@
const defaultOptions = {
/*
* If `responsive` is `true`, `breakpointCols` will be used to determine
* how many columns a grid should have at a given responsive breakpoint.
*/
responsive: true,
/*
* A list of how many columns should be shown at different responsive
* breakpoints, defined by media queries.
*/
breakpointCols: {
1500: 5,
1200: 4,
992: 3,
768: 2,
576: 1,
},
/*
* If `responsive` is `false`, this number of columns will always be shown,
* no matter the width of the screen.
*/
numCols: 4,
};
let _resizeId = null;
let _options = {};
let _targets = [];
function init(targets, options = {}) {
if (typeof targets === "string") {
_targets = document.querySelectorAll(targets);
} else {
_targets = targets;
}
_options = Object.assign(defaultOptions, options);
_targets.forEach(function (target) {
setUp(target);
// setHeight(target);
});
addEventListeners();
return this;
}
function setUp(target) {
target.classList.add("flexmasonry");
if (_options.responsive) {
target.classList.add("flexmasonry-responsive");
}
setColsClass(target);
Array.from(target.children).forEach(function (item) {
item.classList.add("flexmasonry-item");
});
addBreakElements(target);
}
// function onLoad() {
// _targets.forEach(function (target) {
// setHeight(target);
// });
// }
function onResize() {
if (_resizeId) {
window.cancelAnimationFrame(_resizeId);
}
_resizeId = window.requestAnimationFrame(function () {
refreshAll();
});
}
function addEventListeners() {
// window.addEventListener("load", onLoad);
window.addEventListener("resize", onResize);
}
function removeEventListeners() {
// window.removeEventListener("load", onLoad);
window.removeEventListener("resize", onResize);
}
// function setHeight(target) {
// if (getCurrentCols(target) < 2) {
// target.style.removeProperty("height");
// return;
// }
// let heights = [];
// Array.from(target.children).forEach(function (item) {
// if (item.classList.contains("flexmasonry-break")) {
// return;
// }
// const comp = window.getComputedStyle(item);
// const order = comp.getPropertyValue("order");
// const height = comp.getPropertyValue("height");
// if (!heights[order - 1]) {
// heights[order - 1] = 0;
// }
// heights[order - 1] += Math.ceil(parseFloat(height));
// });
// const maxHeight = Math.max(...heights);
// target.style.height = maxHeight + "px";
// }
function addBreakElements(target) {
const breakEls = target.querySelectorAll(".flexmasonry-break");
if (Array.from(breakEls).length === getCurrentCols(target) - 1) {
return;
}
for (let i = 1; i < getCurrentCols(target); i++) {
const breakDiv = document.createElement("div");
breakDiv.classList.add("flexmasonry-break");
breakDiv.classList.add("flexmasonry-break-" + i);
target.appendChild(breakDiv);
}
}
function removeBreakElements(target) {
const breakEls = target.querySelectorAll(".flexmasonry-break");
if (Array.from(breakEls).length === getCurrentCols(target) - 1) {
return;
}
Array.from(breakEls).forEach(function (breakEl) {
breakEl.parentNode.removeChild(breakEl);
});
}
function setColsClass(target) {
if (target.classList.contains("flexmasonry-cols-" + getCurrentCols(target))) {
return;
}
target.className = target.className.replace(/(flexmasonry-cols-\d+)/, "");
target.classList.add("flexmasonry-cols-" + getCurrentCols(target));
}
function getCurrentCols(target) {
if (!_options.responsive) {
return _options.numCols;
}
const descendingMinWidths = Object.keys(_options.breakpointCols)
.map((key) => Number(key))
.sort()
.reverse();
for (const minWidth of descendingMinWidths) {
if (target.clientWidth >= minWidth) {
return _options.breakpointCols[minWidth];
}
}
return 1;
}
function refresh(target, options = {}) {
_options = Object.assign(defaultOptions, options);
setColsClass(target);
removeBreakElements(target);
addBreakElements(target);
// setHeight(target);
return this;
}
function refreshAll(options = {}) {
_targets.forEach(function (target) {
refresh(target, options);
});
return this;
}
function destroyAll() {
removeEventListeners();
}
export default {
init,
refresh,
refreshAll,
destroyAll,
};

View File

@@ -0,0 +1,170 @@
.flexmasonry {
display: flex;
flex-flow: column wrap;
align-content: space-between;
}
.flexmasonry-item {
width: 100%;
}
.flexmasonry-cols-2 .flexmasonry-item {
width: 50%;
}
.flexmasonry-cols-3 .flexmasonry-item {
width: 33.333%;
}
.flexmasonry-cols-4 .flexmasonry-item {
width: 25%;
}
.flexmasonry-cols-5 .flexmasonry-item {
width: 20%;
}
.flexmasonry-cols-6 .flexmasonry-item {
width: 16.666%;
}
.flexmasonry-cols-7 .flexmasonry-item {
width: 14.285%;
}
.flexmasonry-cols-8 .flexmasonry-item {
width: 12.5%;
}
.flexmasonry-cols-2 .flexmasonry-item:nth-child(2n + 1) {
order: 1;
}
.flexmasonry-cols-2 .flexmasonry-item:nth-child(2n) {
order: 2;
}
.flexmasonry-cols-3 .flexmasonry-item:nth-child(3n + 1) {
order: 1;
}
.flexmasonry-cols-3 .flexmasonry-item:nth-child(3n + 2) {
order: 2;
}
.flexmasonry-cols-3 .flexmasonry-item:nth-child(3n) {
order: 3;
}
.flexmasonry-cols-4 .flexmasonry-item:nth-child(4n + 1) {
order: 1;
}
.flexmasonry-cols-4 .flexmasonry-item:nth-child(4n + 2) {
order: 2;
}
.flexmasonry-cols-4 .flexmasonry-item:nth-child(4n + 3) {
order: 3;
}
.flexmasonry-cols-4 .flexmasonry-item:nth-child(4n) {
order: 4;
}
.flexmasonry-cols-5 .flexmasonry-item:nth-child(5n + 1) {
order: 1;
}
.flexmasonry-cols-5 .flexmasonry-item:nth-child(5n + 2) {
order: 2;
}
.flexmasonry-cols-5 .flexmasonry-item:nth-child(5n + 3) {
order: 3;
}
.flexmasonry-cols-5 .flexmasonry-item:nth-child(5n + 4) {
order: 4;
}
.flexmasonry-cols-5 .flexmasonry-item:nth-child(5n) {
order: 5;
}
.flexmasonry-cols-6 .flexmasonry-item:nth-child(6n + 1) {
order: 1;
}
.flexmasonry-cols-6 .flexmasonry-item:nth-child(6n + 2) {
order: 2;
}
.flexmasonry-cols-6 .flexmasonry-item:nth-child(6n + 3) {
order: 3;
}
.flexmasonry-cols-6 .flexmasonry-item:nth-child(6n + 4) {
order: 4;
}
.flexmasonry-cols-6 .flexmasonry-item:nth-child(6n + 5) {
order: 5;
}
.flexmasonry-cols-6 .flexmasonry-item:nth-child(6n) {
order: 6;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n + 1) {
order: 1;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n + 2) {
order: 2;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n + 3) {
order: 3;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n + 4) {
order: 4;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n + 5) {
order: 5;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n + 6) {
order: 6;
}
.flexmasonry-cols-7 .flexmasonry-item:nth-child(7n) {
order: 7;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 1) {
order: 1;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 2) {
order: 2;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 3) {
order: 3;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 4) {
order: 4;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 5) {
order: 5;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 6) {
order: 6;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n + 7) {
order: 7;
}
.flexmasonry-cols-8 .flexmasonry-item:nth-child(8n) {
order: 8;
}
.flexmasonry-break {
content: "";
flex-basis: 100%;
width: 0 !important;
margin: 0;
}
.flexmasonry-break-1 {
order: 1;
}
.flexmasonry-break-2 {
order: 2;
}
.flexmasonry-break-3 {
order: 3;
}
.flexmasonry-break-4 {
order: 4;
}
.flexmasonry-break-5 {
order: 5;
}
.flexmasonry-break-6 {
order: 6;
}
.flexmasonry-break-7 {
order: 7;
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
const version = "v1";
self.addEventListener("install", (_event) => {
console.log("service-worker: install");
const event = /** @type {any} */ (_event);
event.waitUntil(
@@ -17,66 +19,66 @@ self.addEventListener("install", (_event) => {
"/packages/lightweight-charts/v4.2.0/script.js",
"/assets/fonts/satoshi/2024-09/font.var.woff2",
]);
})
}),
);
// @ts-ignore
self.skipWaiting();
});
/**
* @param {Response | undefined} cachedResponse
* @param {Response | undefined} badResponse
*/
function pickCorrectResponse(cachedResponse, badResponse) {
if (cachedResponse) {
return cachedResponse;
} else {
return caches
.match("/")
.then((response) => {
return response ?? badResponse;
})
.catch(() => {
return badResponse;
});
}
}
self.addEventListener("fetch", (_event) => {
const event = /** @type {any} */ (_event);
/** @type {Request} */
const request = event.request;
const { url, method } = request;
let request = event.request;
const method = request.method;
let url = request.url;
const { pathname, origin } = new URL(url);
const slashMatches = url.match(/\//g);
const dotMatches = pathname.split("/").at(-1)?.match(/./g);
const endsWithDotHtml = pathname.endsWith(".html");
const slashApiSlashMatches = url.match(/\/api\//g);
if (
slashMatches &&
slashMatches.length <= 3 &&
!slashApiSlashMatches &&
(!dotMatches || endsWithDotHtml)
) {
url = `${origin}/`;
}
request = new Request(url, request.mode !== "navigate" ? request : undefined);
console.log(`service-worker: fetching: ${url}`);
event.respondWith(
caches.match(request).then((cachedResponse) => {
caches.match(request).then(async (cachedResponse) => {
return fetch(request)
.then((response) => {
const { status } = response;
// @ts-ignore
if (method !== "GET" || url.includes("/api/")) {
if (method !== "GET" || slashApiSlashMatches) {
// API calls are cached in script.js
return response;
}
return caches.open(version).then((cache) => {
if (status === 200 || status === 304) {
if (status === 200) {
cache.put(request, response.clone());
}
return response;
} else {
return pickCorrectResponse(cachedResponse, response);
} else if (status === 200 || status === 304) {
if (status === 200) {
const clonedResponse = response.clone();
caches.open(version).then((cache) => {
cache.put(request, clonedResponse);
});
}
});
return response;
} else {
return cachedResponse || response;
}
})
.catch(() => {
return pickCorrectResponse(cachedResponse, undefined);
console.log("service-worker: offline");
return cachedResponse;
});
})
}),
);
});

File diff suppressed because one or more lines are too long

View File

@@ -77,21 +77,23 @@ type SeriesBlueprint = {
} & AnySpecificSeriesBlueprint;
type Unit =
| "US Dollars"
| ""
| "Bitcoin"
| "Percentage"
| "Height"
| "Count"
| "Megabytes"
| "Transactions"
| "Weight"
| "Ratio"
| "Virtual Bytes"
| "Seconds"
| "Coinblocks"
| "ExaHash / Second"
| "Count"
| "Date"
| "Dollars / (PetaHash / Second)"
| "";
| "ExaHash / Second"
| "Height"
| "Megabytes"
| "Percentage"
| "Ratio"
| "Satoshis"
| "Seconds"
| "Transactions"
| "US Dollars"
| "Virtual Bytes"
| "Weight";
interface PartialOption {
icon: string;
@@ -107,6 +109,7 @@ interface PartialHomeOption extends PartialOption {
interface PartialDashboardOption extends PartialOption {
title: string;
description: string;
defaultOpen?: false;
groups: {
name: string;
unit?: Unit;
@@ -114,6 +117,7 @@ interface PartialDashboardOption extends PartialOption {
name: string;
path?: LastPath;
unit?: Unit;
long?: true;
}[];
}[];
}
@@ -194,7 +198,7 @@ interface OHLC {
interface ResourceDataset<
Scale extends TimeScale,
Type extends OHLC | number = number
Type extends OHLC | number = number,
> {
scale: Scale;
url: string;
@@ -212,7 +216,7 @@ interface FetchedResult<
SingleValueData | ValuedCandlestickData
> = DatasetValue<
Type extends number ? SingleValueData : ValuedCandlestickData
>
>,
> {
at: Date | null;
json: Signal<FetchedJSON<Scale, Type> | null>;
@@ -242,7 +246,7 @@ interface FetchedChunk {
type FetchedDataset<
Scale extends TimeScale,
Type extends number | OHLC
Type extends number | OHLC,
> = Scale extends "date"
? FetchedDateDataset<Type>
: FetchedHeightDataset<Type>;