global: snapshot

This commit is contained in:
k
2024-10-04 19:09:09 +02:00
parent 1c9d118ba2
commit 068bb07d6e
57 changed files with 1799 additions and 295 deletions

View File

@@ -13,7 +13,7 @@ use crate::{
datasets::{AllDatasets, ComputeData},
io::OUTPUTS_FOLDER_PATH,
states::{AddressCohortsDurableStates, States, UTXOCohortsDurableStates},
structs::{Date, DateData, MapKey},
structs::{DateData, MapKey, Timestamp},
utils::{generate_allocation_files, log, time},
Config, Exit, Height,
};
@@ -81,9 +81,9 @@ pub fn iter_blocks(
if let Some((_current_block_height, current_block, _current_block_hash)) =
current_block_opt
{
let timestamp = current_block.header.time;
let timestamp = Timestamp::wrap(current_block.header.time);
let current_block_date = Date::from_timestamp(timestamp);
let current_block_date = timestamp.to_date();
let current_block_height: Height = height + blocks_loop_i;
if current_block_height.to_usize() != _current_block_height {
@@ -91,9 +91,9 @@ pub fn iter_blocks(
panic!()
}
let next_block_date = next_block_opt
.as_ref()
.map(|(_, next_block, _)| Date::from_timestamp(next_block.header.time));
let next_block_date = next_block_opt.as_ref().map(|(_, next_block, _)| {
Timestamp::wrap(next_block.header.time).to_date()
});
// Always run for the first block of the loop
if blocks_loop_date.is_none() {
@@ -168,7 +168,6 @@ pub fn iter_blocks(
height: current_block_height,
is_date_last_block,
states: &mut states,
timestamp,
});
}
@@ -213,7 +212,7 @@ pub fn iter_blocks(
});
}
if !config.dry_run {
if !config.dry_run() {
let is_safe = height.is_safe(approx_block_count);
export(ExportedData {
@@ -225,7 +224,7 @@ pub fn iter_blocks(
exit: exit.clone(),
})?;
if config.record_ram_usage {
if config.record_ram_usage() {
time("Exporing allocation files", || {
generate_allocation_files(&datasets, &databases, &states, last_height)
})?;

View File

@@ -20,7 +20,7 @@ use crate::{
},
structs::{
Address, AddressData, AddressRealizedData, Amount, BlockData, BlockPath, Counter, Date,
EmptyAddressData, Height, PartialTxoutData, Price, SentData, TxData, TxoutIndex,
EmptyAddressData, Height, PartialTxoutData, Price, SentData, Timestamp, TxData, TxoutIndex,
},
};
@@ -37,7 +37,6 @@ pub struct ParseData<'a> {
pub is_date_last_block: bool,
pub rpc: &'a biter::bitcoincore_rpc::Client,
pub states: &'a mut States,
pub timestamp: u32,
}
pub fn parse(
@@ -53,11 +52,12 @@ pub fn parse(
is_date_last_block,
rpc,
states,
timestamp,
}: ParseData,
) {
// log(&format!("{height}"));
let timestamp = Timestamp::wrap(block.header.time);
// If false, expect that the code is flawless
// or create a 0 value txid database
let enable_check_if_txout_value_is_zero_in_db: bool = true;
@@ -89,9 +89,9 @@ pub fn parse(
let block_size = block.total_size();
let block_weight = block.weight().to_wu();
let block_vbytes = block.weight().to_vbytes_floor();
let block_interval = previous_timestamp.map_or(0, |previous_timestamp| {
let block_interval = previous_timestamp.map_or(Timestamp::ZERO, |previous_timestamp| {
if previous_timestamp >= timestamp {
0
Timestamp::ZERO
} else {
timestamp - previous_timestamp
}
@@ -519,6 +519,8 @@ pub fn parse(
input_amount,
block_price,
previous_price,
timestamp,
input_block_data.timestamp,
);
};
@@ -645,6 +647,7 @@ pub fn parse(
&states.date_data_vec,
&block_path_to_sent_data,
block_price,
timestamp,
);
});
}

View File

@@ -2,7 +2,7 @@ use allocative::Allocative;
use crate::{
datasets::AnyDataset,
structs::{AnyHeightMap, Date, HeightMap},
structs::{AnyHeightMap, Date, HeightMap, Timestamp},
};
use super::{InsertData, MinInitialStates};
@@ -13,7 +13,7 @@ pub struct BlockMetadataDataset {
// Inserted
pub date: HeightMap<Date>,
pub timestamp: HeightMap<u32>,
pub timestamp: HeightMap<Timestamp>,
}
impl BlockMetadataDataset {
@@ -41,7 +41,7 @@ impl BlockMetadataDataset {
) {
self.timestamp.insert(height, timestamp);
self.date.insert(height, Date::from_timestamp(timestamp));
self.date.insert(height, timestamp.to_date());
}
}

View File

@@ -302,7 +302,7 @@ impl MiningDataset {
self.block_weight
.insert(height, block_weight as f32 / BYTES_IN_MB as f32);
self.block_vbytes.insert(height, block_vbytes);
self.block_interval.insert(height, block_interval);
self.block_interval.insert(height, *block_interval);
if is_date_last_block {
self.coinbase.date_insert_sum_range(date, date_blocks_range);

View File

@@ -45,7 +45,7 @@ use crate::{
// UTXOCohortsReceivedStates,
UTXOCohortsSentStates,
},
structs::{Amount, Date, Height, Price},
structs::{Amount, Date, Height, Price, Timestamp},
};
pub struct InsertData<'a> {
@@ -53,7 +53,7 @@ pub struct InsertData<'a> {
pub address_cohorts_one_shot_states: &'a Option<AddressCohortsOneShotStates>,
pub address_cohorts_realized_states: &'a Option<AddressCohortsRealizedStates>,
pub amount_sent: Amount,
pub block_interval: u32,
pub block_interval: Timestamp,
pub block_price: Price,
pub block_size: usize,
pub block_vbytes: u64,
@@ -71,7 +71,7 @@ pub struct InsertData<'a> {
pub satblocks_destroyed: Amount,
pub satdays_destroyed: Amount,
pub states: &'a States,
pub timestamp: u32,
pub timestamp: Timestamp,
pub transaction_count: usize,
pub utxo_cohorts_one_shot_states: &'a UTXOCohortsOneShotStates,
// pub utxo_cohorts_received_states: &'a UTXOCohortsReceivedStates,

View File

@@ -3,17 +3,16 @@ mod ohlc;
use std::collections::BTreeMap;
use allocative::Allocative;
use chrono::{Days, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc};
use chrono::Days;
use color_eyre::eyre::Error;
pub use ohlc::*;
use crate::{
log,
price::{Binance, Kibo, Kraken},
structs::{
AnyBiMap, AnyDateMap, BiMap, Date, DateMap, DateMapChunkId, Height, HeightMapChunkId,
MapKey,
Amount, AnyBiMap, AnyDateMap, BiMap, Date, DateMap, DateMapChunkId, Height,
HeightMapChunkId, MapKey, Timestamp,
},
utils::{ONE_MONTH_IN_DAYS, ONE_WEEK_IN_DAYS, ONE_YEAR_IN_DAYS},
};
@@ -76,9 +75,11 @@ pub struct PriceDatasets {
pub price_10y_total_return: DateMap<f32>,
pub price_4y_compound_return: DateMap<f32>,
// projection via lowest 4y compound value
pub all_time_high: BiMap<f32>,
pub market_price_to_all_time_high_ratio: BiMap<f32>,
pub drawdown: BiMap<f32>,
pub sats_per_dollar: BiMap<f32>,
// volatility
// drawdown
// sats per dollar
}
impl PriceDatasets {
@@ -138,6 +139,13 @@ impl PriceDatasets {
price_8y_total_return: DateMap::new_bin(1, &f("price_8y_total_return")),
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")),
market_price_to_all_time_high_ratio: BiMap::new_bin(
1,
&f("market_price_to_all_time_high_ratio"),
),
drawdown: BiMap::new_bin(1, &f("drawdown")),
sats_per_dollar: BiMap::new_bin(1, &f("sats_per_dollar")),
};
s.min_initial_states
@@ -306,6 +314,26 @@ impl PriceDatasets {
.compute(compute_data, &mut self.closes, &mut self.price_144d_sma);
self.price_200w_sma_ratio
.compute(compute_data, &mut self.closes, &mut self.price_200w_sma);
self.all_time_high
.multi_insert_max(heights, dates, &mut self.closes);
self.market_price_to_all_time_high_ratio
.multi_insert_percentage(heights, dates, &mut self.closes, &mut self.all_time_high);
self.drawdown.multi_insert_simple_transform(
heights,
dates,
&mut self.market_price_to_all_time_high_ratio,
&|v| -(100.0 - v),
);
self.sats_per_dollar.multi_insert_simple_transform(
heights,
dates,
&mut self.closes,
&|price| Amount::ONE_BTC_F32 / price,
);
}
pub fn get_date_ohlc(&mut self, date: Date) -> color_eyre::Result<OHLC> {
@@ -396,31 +424,20 @@ impl PriceDatasets {
pub fn get_height_ohlc(
&mut self,
height: Height,
timestamp: u32,
previous_timestamp: Option<u32>,
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
) -> color_eyre::Result<OHLC> {
if let Some(ohlc) = self.ohlcs.height.get(&height) {
return Ok(ohlc);
}
let clean_timestamp = |timestamp| {
let date_time = Utc.timestamp_opt(i64::from(timestamp), 0).unwrap();
NaiveDateTime::new(
date_time.date_naive(),
NaiveTime::from_hms_opt(date_time.hour(), date_time.minute(), 0).unwrap(),
)
.and_utc()
.timestamp() as u32
};
let timestamp = clean_timestamp(timestamp);
let timestamp = timestamp.to_floored_seconds();
if previous_timestamp.is_none() && !height.is_first() {
panic!("Shouldn't be possible");
}
let previous_timestamp = previous_timestamp.map(clean_timestamp);
let previous_timestamp = previous_timestamp.map(|t| t.to_floored_seconds());
let ohlc = self
.get_from_1mn_kraken(timestamp, previous_timestamp)
@@ -430,7 +447,7 @@ impl PriceDatasets {
self.get_from_har_binance(timestamp, previous_timestamp)
.unwrap_or_else(|_| {
self.get_from_height_kibo(&height).unwrap_or_else(|_| {
let date = Date::from_timestamp(timestamp);
let date = timestamp.to_date();
panic!(
"Can't find the price for: height: {height} - date: {date}
@@ -479,8 +496,8 @@ How to fix this:
fn get_from_1mn_kraken(
&mut self,
timestamp: u32,
previous_timestamp: Option<u32>,
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
) -> color_eyre::Result<OHLC> {
if self.kraken_1mn.is_none()
|| self
@@ -500,8 +517,8 @@ How to fix this:
fn get_from_1mn_binance(
&mut self,
timestamp: u32,
previous_timestamp: Option<u32>,
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
) -> color_eyre::Result<OHLC> {
if self.binance_1mn.is_none()
|| self
@@ -526,8 +543,8 @@ How to fix this:
fn get_from_har_binance(
&mut self,
timestamp: u32,
previous_timestamp: Option<u32>,
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
) -> color_eyre::Result<OHLC> {
if self.binance_har.is_none() {
self.binance_har
@@ -544,8 +561,8 @@ How to fix this:
fn find_height_ohlc(
tree: &Option<BTreeMap<u32, OHLC>>,
timestamp: u32,
previous_timestamp: Option<u32>,
timestamp: Timestamp,
previous_timestamp: Option<Timestamp>,
name: &str,
) -> color_eyre::Result<OHLC> {
let tree = tree.as_ref().unwrap();
@@ -572,12 +589,12 @@ How to fix this:
close: previous_ohlc.close,
};
let start = previous_timestamp.unwrap_or(0);
let start = previous_timestamp.unwrap_or_default();
let end = timestamp;
// Otherwise it's a re-org
if start < end {
tree.range(&start..=&end).skip(1).for_each(|(_, ohlc)| {
tree.range(&*start..=&*end).skip(1).for_each(|(_, ohlc)| {
if ohlc.high > final_ohlc.high {
final_ohlc.high = ohlc.high
}
@@ -624,6 +641,10 @@ impl AnyDataset for PriceDatasets {
&self.price_89d_sma,
&self.price_144d_sma,
&self.price_200w_sma,
&self.all_time_high,
&self.market_price_to_all_time_high_ratio,
&self.drawdown,
&self.sats_per_dollar,
];
v.append(&mut self.price_1w_sma_ratio.to_computed_bi_map_vec());
@@ -660,6 +681,10 @@ impl AnyDataset for PriceDatasets {
&mut self.price_89d_sma,
&mut self.price_144d_sma,
&mut self.price_200w_sma,
&mut self.all_time_high,
&mut self.market_price_to_all_time_high_ratio,
&mut self.drawdown,
&mut self.sats_per_dollar,
];
v.append(&mut self.price_1w_sma_ratio.to_computed_mut_bi_map_vec());

View File

@@ -66,7 +66,7 @@ impl CapitalizationDataset {
let realized_cap = self
.realized_cap
.height
.insert(height, state.realized_cap.to_dollar() as f32);
.insert(height, state.realized_cap().to_dollar() as f32);
if is_date_last_block {
self.realized_cap.date.insert(date, realized_cap);

View File

@@ -51,9 +51,12 @@ impl InputSubDataset {
}: &InsertData,
state: &InputState,
) {
let count = self.count.height.insert(height, state.count.round() as u64);
let count = self
.count
.height
.insert(height, state.count().round() as u64);
self.volume.height.insert(height, state.volume.to_btc());
self.volume.height.insert(height, state.volume().to_btc());
if is_date_last_block {
self.count.date.insert(date, count);

View File

@@ -83,28 +83,25 @@ impl PricePaidSubDataset {
}: &InsertData,
state: &PricePaidState,
) {
let PricePaidState {
pp_05p,
pp_10p,
pp_15p,
pp_20p,
pp_25p,
pp_30p,
pp_35p,
pp_40p,
pp_45p,
pp_median,
pp_55p,
pp_60p,
pp_65p,
pp_70p,
pp_75p,
pp_80p,
pp_85p,
pp_90p,
pp_95p,
..
} = state;
let pp_05p = state.pp_05p();
let pp_10p = state.pp_10p();
let pp_15p = state.pp_15p();
let pp_20p = state.pp_20p();
let pp_25p = state.pp_25p();
let pp_30p = state.pp_30p();
let pp_35p = state.pp_35p();
let pp_40p = state.pp_40p();
let pp_45p = state.pp_45p();
let pp_median = state.pp_median();
let pp_55p = state.pp_55p();
let pp_60p = state.pp_60p();
let pp_65p = state.pp_65p();
let pp_70p = state.pp_70p();
let pp_75p = state.pp_75p();
let pp_80p = state.pp_80p();
let pp_85p = state.pp_85p();
let pp_90p = state.pp_90p();
let pp_95p = state.pp_95p();
// Check if iter was empty
if pp_05p.is_none() {

View File

@@ -15,9 +15,12 @@ pub struct RealizedSubDataset {
// Inserted
realized_profit: BiMap<f32>,
realized_loss: BiMap<f32>,
value_destroyed: BiMap<f32>,
value_created: BiMap<f32>,
adjusted_value_created: BiMap<f32>,
value_destroyed: BiMap<f32>,
adjusted_value_destroyed: BiMap<f32>,
spent_output_profit_ratio: BiMap<f32>,
adjusted_spent_output_profit_ratio: BiMap<f32>,
// Computed
negative_realized_loss: BiMap<f32>,
@@ -47,8 +50,14 @@ impl RealizedSubDataset {
realized_profit: BiMap::new_bin(1, &f("realized_profit")),
realized_loss: BiMap::new_bin(1, &f("realized_loss")),
value_created: BiMap::new_bin(1, &f("value_created")),
adjusted_value_created: BiMap::new_bin(1, &f("adjusted_value_created")),
value_destroyed: BiMap::new_bin(1, &f("value_destroyed")),
adjusted_value_destroyed: BiMap::new_bin(1, &f("adjusted_value_destroyed")),
spent_output_profit_ratio: BiMap::new_bin(2, &f("spent_output_profit_ratio")),
adjusted_spent_output_profit_ratio: BiMap::new_bin(
2,
&f("adjusted_spent_output_profit_ratio"),
),
negative_realized_loss: BiMap::new_bin(2, &f("negative_realized_loss")),
net_realized_profit_and_loss: BiMap::new_bin(1, &f("net_realized_profit_and_loss")),
@@ -89,29 +98,51 @@ impl RealizedSubDataset {
) {
self.realized_profit
.height
.insert(height, height_state.realized_profit.to_dollar() as f32);
.insert(height, height_state.realized_profit().to_dollar() as f32);
self.realized_loss
.height
.insert(height, height_state.realized_loss.to_dollar() as f32);
.insert(height, height_state.realized_loss().to_dollar() as f32);
self.value_created
.height
.insert(height, height_state.value_created.to_dollar() as f32);
.insert(height, height_state.value_created().to_dollar() as f32);
self.adjusted_value_created.height.insert(
height,
height_state.adjusted_value_created().to_dollar() as f32,
);
self.value_destroyed
.height
.insert(height, height_state.value_destroyed.to_dollar() as f32);
.insert(height, height_state.value_destroyed().to_dollar() as f32);
self.adjusted_value_destroyed.height.insert(
height,
height_state.adjusted_value_destroyed().to_dollar() as f32,
);
self.spent_output_profit_ratio.height.insert(height, {
if height_state.value_destroyed > Price::ZERO {
(height_state.value_created.to_cent() as f64
/ height_state.value_destroyed.to_cent() as f64) as f32
if height_state.value_destroyed() > Price::ZERO {
(height_state.value_created().to_cent() as f64
/ height_state.value_destroyed().to_cent() as f64) as f32
} else {
1.0
}
});
self.adjusted_spent_output_profit_ratio
.height
.insert(height, {
if height_state.adjusted_value_destroyed() > Price::ZERO {
(height_state.adjusted_value_created().to_cent() as f64
/ height_state.adjusted_value_destroyed().to_cent() as f64)
as f32
} else {
1.0
}
});
if is_date_last_block {
self.realized_profit
.date_insert_sum_range(date, date_blocks_range);
@@ -122,14 +153,31 @@ impl RealizedSubDataset {
self.value_created
.date_insert_sum_range(date, date_blocks_range);
self.adjusted_value_created
.date_insert_sum_range(date, date_blocks_range);
self.value_destroyed
.date_insert_sum_range(date, date_blocks_range);
self.adjusted_value_destroyed
.date_insert_sum_range(date, date_blocks_range);
self.spent_output_profit_ratio.date.insert(
date,
self.value_created.height.sum_range(date_blocks_range)
/ self.value_destroyed.height.sum_range(date_blocks_range),
);
self.adjusted_spent_output_profit_ratio.date.insert(
date,
self.adjusted_value_created
.height
.sum_range(date_blocks_range)
/ self
.adjusted_value_destroyed
.height
.sum_range(date_blocks_range),
);
}
}
@@ -208,8 +256,11 @@ impl AnyDataset for RealizedSubDataset {
&self.realized_loss,
&self.realized_profit,
&self.value_created,
&self.adjusted_value_created,
&self.value_destroyed,
&self.adjusted_value_destroyed,
&self.spent_output_profit_ratio,
&self.adjusted_spent_output_profit_ratio,
]
}
@@ -218,8 +269,11 @@ impl AnyDataset for RealizedSubDataset {
&mut self.realized_loss,
&mut self.realized_profit,
&mut self.value_created,
&mut self.adjusted_value_created,
&mut self.value_destroyed,
&mut self.adjusted_value_destroyed,
&mut self.spent_output_profit_ratio,
&mut self.adjusted_spent_output_profit_ratio,
]
}

View File

@@ -254,99 +254,3 @@ where
v
}
}
// impl<Key, Value, ChunkId, Serialized> AnyDataset for RecapDataset<Key, Value, ChunkId, Serialized>
// where
// Value: MapValue,
// ChunkId: MapChunkId,
// Key: MapKey<ChunkId>,
// Serialized: MapSerialized<Key, Value, ChunkId>,
// {
// fn get_min_initial_states(&self) -> &MinInitialStates {
// &self.min_initial_states
// }
// fn to_computed_date_map_vec(&self) -> Vec<&(dyn AnyDateMap + Send + Sync)> {
// let mut v: Vec<&(dyn AnyDateMap + Send + Sync)> = vec![];
// if let Some(min) = self.min.as_ref() {
// v.push(min);
// }
// if let Some(max) = self.max.as_ref() {
// v.push(max);
// }
// if let Some(median) = self.median.as_ref() {
// v.push(median);
// }
// if let Some(average) = self.average.as_ref() {
// v.push(average);
// }
// if let Some(sum) = self.sum.as_ref() {
// v.push(sum);
// }
// if let Some(_90p) = self._90p.as_ref() {
// v.push(_90p);
// }
// if let Some(_75p) = self._75p.as_ref() {
// v.push(_75p);
// }
// if let Some(_25p) = self._25p.as_ref() {
// v.push(_25p);
// }
// if let Some(_10p) = self._10p.as_ref() {
// v.push(_10p);
// }
// v
// }
// fn to_computed_mut_date_map_vec(&mut self) -> Vec<&mut dyn AnyDateMap> {
// let mut v: Vec<&mut dyn AnyDateMap> = vec![];
// if let Some(min) = self.min.as_mut() {
// v.push(min);
// }
// if let Some(max) = self.max.as_mut() {
// v.push(max);
// }
// if let Some(median) = self.median.as_mut() {
// v.push(median);
// }
// if let Some(average) = self.average.as_mut() {
// v.push(average);
// }
// if let Some(sum) = self.sum.as_mut() {
// v.push(sum);
// }
// if let Some(_90p) = self._90p.as_mut() {
// v.push(_90p);
// }
// if let Some(_75p) = self._75p.as_mut() {
// v.push(_75p);
// }
// if let Some(_25p) = self._25p.as_mut() {
// v.push(_25p);
// }
// if let Some(_10p) = self._10p.as_mut() {
// v.push(_10p);
// }
// v
// }
// }

View File

@@ -60,7 +60,7 @@ impl SupplySubDataset {
}: &InsertData,
state: &SupplyState,
) {
let total_supply = self.supply.height.insert(height, state.supply.to_btc());
let total_supply = self.supply.height.insert(height, state.supply().to_btc());
if is_date_last_block {
self.supply.date.insert(date, total_supply);

View File

@@ -86,30 +86,30 @@ impl UnrealizedSubDataset {
) {
self.supply_in_profit
.height
.insert(height, block_state.supply_in_profit.to_btc());
.insert(height, block_state.supply_in_profit().to_btc());
self.unrealized_profit
.height
.insert(height, block_state.unrealized_profit.to_dollar() as f32);
.insert(height, block_state.unrealized_profit().to_dollar() as f32);
self.unrealized_loss
.height
.insert(height, block_state.unrealized_loss.to_dollar() as f32);
.insert(height, block_state.unrealized_loss().to_dollar() as f32);
if is_date_last_block {
let date_state = date_state.as_ref().unwrap();
self.supply_in_profit
.date
.insert(date, date_state.supply_in_profit.to_btc());
.insert(date, date_state.supply_in_profit().to_btc());
self.unrealized_profit
.date
.insert(date, date_state.unrealized_profit.to_dollar() as f32);
.insert(date, date_state.unrealized_profit().to_dollar() as f32);
self.unrealized_loss
.date
.insert(date, date_state.unrealized_loss.to_dollar() as f32);
.insert(date, date_state.unrealized_loss().to_dollar() as f32);
}
}

View File

@@ -46,7 +46,7 @@ impl UTXOSubDataset {
}: &InsertData,
state: &UTXOState,
) {
let count = self.count.height.insert(height, state.count);
let count = self.count.height.insert(height, state.count());
if is_date_last_block {
self.count.date.insert(date, count);

View File

@@ -9,7 +9,7 @@ use serde_json::Value;
use crate::{
datasets::OHLC,
io::{Json, INPUTS_FOLDER_PATH},
structs::Date,
structs::{Date, Timestamp},
utils::{log, retry},
};
@@ -169,9 +169,10 @@ impl Binance {
// [timestamp, open, high, low, close, volume, ...]
let array = value.as_array().unwrap();
let date = Date::from_timestamp(
let date = Timestamp::wrap(
(array.first().unwrap().as_u64().unwrap() / 1_000) as u32,
);
)
.to_date();
let get_f32 = |index: usize| {
array

View File

@@ -5,7 +5,7 @@ use serde_json::Value;
use crate::{
datasets::OHLC,
structs::Date,
structs::{Date, Timestamp},
utils::{log, retry},
};
@@ -91,8 +91,8 @@ impl Kraken {
.map(|value| {
let array = value.as_array().unwrap();
let date =
Date::from_timestamp(array.first().unwrap().as_u64().unwrap() as u32);
let date = Timestamp::wrap(array.first().unwrap().as_u64().unwrap() as u32)
.to_date();
let get_f32 = |index: usize| {
array

View File

@@ -93,7 +93,7 @@ impl AddressCohortDurableStates {
let one_shot_states_ref = &mut one_shot_states;
let supply = self.durable_states.supply_state.supply;
let supply = self.durable_states.supply_state.supply();
self.price_to_amount.iterate(supply, |price_paid, amount| {
one_shot_states_ref

View File

@@ -19,14 +19,18 @@ impl AddressCohortsRealizedStates {
let realized_profit = realized_data.profit;
let realized_loss = realized_data.loss;
let value_created = realized_data.value_created;
let adjusted_value_created = realized_data.adjusted_value_created;
let value_destroyed = realized_data.value_destroyed;
let adjusted_value_destroyed = realized_data.adjusted_value_destroyed;
let normal_iteration = move |state: &mut RealizedState| -> color_eyre::Result<()> {
state.iterate(
realized_profit,
realized_loss,
value_created,
adjusted_value_created,
value_destroyed,
adjusted_value_destroyed,
);
Ok(())
};
@@ -35,8 +39,12 @@ impl AddressCohortsRealizedStates {
liquidity_classification.split(realized_profit.to_cent() as f64);
let split_realized_loss = liquidity_classification.split(realized_loss.to_cent() as f64);
let split_value_created = liquidity_classification.split(value_created.to_cent() as f64);
let split_adjusted_value_created =
liquidity_classification.split(adjusted_value_created.to_cent() as f64);
let split_value_destroyed =
liquidity_classification.split(value_destroyed.to_cent() as f64);
let split_adjusted_value_destroyed =
liquidity_classification.split(adjusted_value_destroyed.to_cent() as f64);
let liquified_iteration =
move |liquidity, state: &mut RealizedState| -> color_eyre::Result<()> {
@@ -44,7 +52,9 @@ impl AddressCohortsRealizedStates {
Price::from_cent(split_realized_profit.from(liquidity) as u64),
Price::from_cent(split_realized_loss.from(liquidity) as u64),
Price::from_cent(split_value_created.from(liquidity) as u64),
Price::from_cent(split_adjusted_value_created.from(liquidity) as u64),
Price::from_cent(split_value_destroyed.from(liquidity) as u64),
Price::from_cent(split_adjusted_value_destroyed.from(liquidity) as u64),
);
Ok(())
};

View File

@@ -6,10 +6,14 @@ use crate::structs::Price;
#[derive(Debug, Default, Allocative)]
pub struct CapitalizationState {
pub realized_cap: Price,
realized_cap: Price,
}
impl CapitalizationState {
pub fn realized_cap(&self) -> Price {
self.realized_cap
}
pub fn increment(&mut self, realized_cap: Price) {
self.realized_cap += realized_cap;
}

View File

@@ -2,11 +2,19 @@ use crate::structs::Amount;
#[derive(Debug, Default)]
pub struct InputState {
pub count: f64,
pub volume: Amount,
count: f64,
volume: Amount,
}
impl InputState {
pub fn count(&self) -> f64 {
self.count
}
pub fn volume(&self) -> Amount {
self.volume
}
pub fn iterate(&mut self, count: f64, volume: Amount) {
self.count += count;
self.volume += volume;

View File

@@ -2,11 +2,19 @@ use crate::structs::Amount;
#[derive(Debug, Default)]
pub struct OutputState {
pub count: f64,
pub volume: Amount,
count: f64,
volume: Amount,
}
impl OutputState {
// pub fn count(&self) -> f64 {
// self.count
// }
// pub fn volume(&self) -> Amount {
// self.volume
// }
pub fn iterate(&mut self, count: f64, volume: Amount) {
self.count += count;
self.volume += volume;

View File

@@ -2,30 +2,106 @@ use crate::structs::{Amount, Price};
#[derive(Default, Debug)]
pub struct PricePaidState {
pub pp_05p: Option<Price>,
pub pp_10p: Option<Price>,
pub pp_15p: Option<Price>,
pub pp_20p: Option<Price>,
pub pp_25p: Option<Price>,
pub pp_30p: Option<Price>,
pub pp_35p: Option<Price>,
pub pp_40p: Option<Price>,
pub pp_45p: Option<Price>,
pub pp_median: Option<Price>,
pub pp_55p: Option<Price>,
pub pp_60p: Option<Price>,
pub pp_65p: Option<Price>,
pub pp_70p: Option<Price>,
pub pp_75p: Option<Price>,
pub pp_80p: Option<Price>,
pub pp_85p: Option<Price>,
pub pp_90p: Option<Price>,
pub pp_95p: Option<Price>,
pp_05p: Option<Price>,
pp_10p: Option<Price>,
pp_15p: Option<Price>,
pp_20p: Option<Price>,
pp_25p: Option<Price>,
pp_30p: Option<Price>,
pp_35p: Option<Price>,
pp_40p: Option<Price>,
pp_45p: Option<Price>,
pp_median: Option<Price>,
pp_55p: Option<Price>,
pp_60p: Option<Price>,
pp_65p: Option<Price>,
pp_70p: Option<Price>,
pp_75p: Option<Price>,
pp_80p: Option<Price>,
pp_85p: Option<Price>,
pp_90p: Option<Price>,
pp_95p: Option<Price>,
pub processed_amount: Amount,
processed_amount: Amount,
}
impl PricePaidState {
pub fn pp_05p(&self) -> Option<Price> {
self.pp_05p
}
pub fn pp_10p(&self) -> Option<Price> {
self.pp_10p
}
pub fn pp_15p(&self) -> Option<Price> {
self.pp_15p
}
pub fn pp_20p(&self) -> Option<Price> {
self.pp_20p
}
pub fn pp_25p(&self) -> Option<Price> {
self.pp_25p
}
pub fn pp_30p(&self) -> Option<Price> {
self.pp_30p
}
pub fn pp_35p(&self) -> Option<Price> {
self.pp_35p
}
pub fn pp_40p(&self) -> Option<Price> {
self.pp_40p
}
pub fn pp_45p(&self) -> Option<Price> {
self.pp_45p
}
pub fn pp_median(&self) -> Option<Price> {
self.pp_median
}
pub fn pp_55p(&self) -> Option<Price> {
self.pp_55p
}
pub fn pp_60p(&self) -> Option<Price> {
self.pp_60p
}
pub fn pp_65p(&self) -> Option<Price> {
self.pp_65p
}
pub fn pp_70p(&self) -> Option<Price> {
self.pp_70p
}
pub fn pp_75p(&self) -> Option<Price> {
self.pp_75p
}
pub fn pp_80p(&self) -> Option<Price> {
self.pp_80p
}
pub fn pp_85p(&self) -> Option<Price> {
self.pp_85p
}
pub fn pp_90p(&self) -> Option<Price> {
self.pp_90p
}
pub fn pp_95p(&self) -> Option<Price> {
self.pp_95p
}
pub fn iterate(&mut self, price: Price, amount: Amount, supply: Amount) {
let PricePaidState {
processed_amount: processed_supply,

View File

@@ -2,23 +2,53 @@ use crate::structs::Price;
#[derive(Debug, Default)]
pub struct RealizedState {
pub realized_profit: Price,
pub realized_loss: Price,
pub value_created: Price,
pub value_destroyed: Price,
realized_profit: Price,
realized_loss: Price,
value_created: Price,
adjusted_value_created: Price,
value_destroyed: Price,
adjusted_value_destroyed: Price,
}
impl RealizedState {
pub fn realized_profit(&self) -> Price {
self.realized_profit
}
pub fn realized_loss(&self) -> Price {
self.realized_loss
}
pub fn value_created(&self) -> Price {
self.value_created
}
pub fn adjusted_value_created(&self) -> Price {
self.adjusted_value_created
}
pub fn value_destroyed(&self) -> Price {
self.value_destroyed
}
pub fn adjusted_value_destroyed(&self) -> Price {
self.adjusted_value_destroyed
}
pub fn iterate(
&mut self,
realized_profit: Price,
realized_loss: Price,
value_created: Price,
adjusted_value_created: Price,
value_destroyed: Price,
adjusted_value_destroyed: Price,
) {
self.realized_profit += realized_profit;
self.realized_loss += realized_loss;
self.value_created += value_created;
self.adjusted_value_created += adjusted_value_created;
self.value_destroyed += value_destroyed;
self.adjusted_value_destroyed += adjusted_value_destroyed;
}
}

View File

@@ -7,10 +7,14 @@ use crate::structs::Amount;
#[derive(Debug, Default, Allocative)]
pub struct SupplyState {
pub supply: Amount,
supply: Amount,
}
impl SupplyState {
pub fn supply(&self) -> Amount {
self.supply
}
pub fn increment(&mut self, amount: Amount) {
self.supply += amount;
}

View File

@@ -4,12 +4,24 @@ use crate::structs::{Amount, Price};
#[derive(Debug, Default)]
pub struct UnrealizedState {
pub supply_in_profit: Amount,
pub unrealized_profit: Price,
pub unrealized_loss: Price,
supply_in_profit: Amount,
unrealized_profit: Price,
unrealized_loss: Price,
}
impl UnrealizedState {
pub fn supply_in_profit(&self) -> Amount {
self.supply_in_profit
}
pub fn unrealized_profit(&self) -> Price {
self.unrealized_profit
}
pub fn unrealized_loss(&self) -> Price {
self.unrealized_loss
}
#[inline]
pub fn iterate(&mut self, price_then: Price, price_now: Price, amount: Amount) {
match price_then.cmp(&price_now) {

View File

@@ -5,10 +5,14 @@ use color_eyre::eyre::eyre;
#[derive(Debug, Default, Allocative)]
pub struct UTXOState {
pub count: f64,
count: f64,
}
impl UTXOState {
pub fn count(&self) -> f64 {
self.count
}
pub fn increment(&mut self, utxo_count: f64) {
self.count += utxo_count;
}

View File

@@ -85,7 +85,7 @@ impl UTXOCohortDurableStates {
.replace(UnrealizedState::default());
}
let supply = self.durable_states.supply_state.supply;
let supply = self.durable_states.supply_state.supply();
let one_shot_states_ref = &mut one_shot_states;

View File

@@ -5,9 +5,7 @@ use rayon::prelude::*;
use crate::{
states::DateDataVec,
structs::{Amount, BlockData, Price, SentData},
utils::difference_in_days_between_timestamps,
Date,
structs::{Amount, BlockData, Price, SentData, Timestamp},
};
use super::{SplitByUTXOCohort, UTXOCohortDurableStates, UTXOCohortsOneShotStates};
@@ -32,7 +30,7 @@ impl UTXOCohortsDurableStates {
return;
}
let increment_days_old = difference_in_days_between_timestamps(
let increment_days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
last_block_data.timestamp,
);
@@ -65,18 +63,18 @@ impl UTXOCohortsDurableStates {
}
if block_data.height == last_block_data.height {
let year = Date::from_timestamp(block_data.timestamp).year() as u32;
let year = block_data.timestamp.to_year();
self.initial_filtered_apply(&0, &year, |state| {
state.increment(amount, utxo_count, price).unwrap();
})
} else {
let increment_days_old = difference_in_days_between_timestamps(
let increment_days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
last_block_data.timestamp,
);
let decrement_days_old = difference_in_days_between_timestamps(
let decrement_days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
previous_last_block_data
.unwrap_or_else(|| {
@@ -117,12 +115,12 @@ impl UTXOCohortsDurableStates {
return;
}
let days_old = difference_in_days_between_timestamps(
let days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
previous_last_block_data.timestamp,
);
let year = Date::from_timestamp(block_data.timestamp).year() as u32;
let year = block_data.timestamp.to_year();
self.initial_filtered_apply(&days_old, &year, |state| {
state

View File

@@ -5,8 +5,7 @@ use derive_deref::{Deref, DerefMut};
use crate::{
states::{DateDataVec, InputState, RealizedState},
structs::{BlockPath, Price, SentData},
utils::difference_in_days_between_timestamps,
structs::{BlockPath, Price, SentData, Timestamp},
};
use super::SplitByUTXOCohort;
@@ -26,6 +25,7 @@ impl UTXOCohortsSentStates {
date_data_vec: &DateDataVec,
block_path_to_sent_data: &BTreeMap<BlockPath, SentData>,
current_price: Price,
current_timestamp: Timestamp,
) {
if let Some(last_block_data) = date_data_vec.last_block() {
block_path_to_sent_data
@@ -37,11 +37,12 @@ impl UTXOCohortsSentStates {
let block_data = date_data.get_block_data(block_path).unwrap();
let days_old = difference_in_days_between_timestamps(
let days_old = Timestamp::difference_in_days_between(
block_data.timestamp,
last_block_data.timestamp,
);
let previous_timestamp = block_data.timestamp;
let previous_price = block_data.price;
let amount_sent = sent_data.volume;
@@ -52,18 +53,34 @@ impl UTXOCohortsSentStates {
let previous_value = previous_price * amount_sent;
let current_value = current_price * amount_sent;
state.realized.value_destroyed += previous_value;
state.realized.value_created += current_value;
let mut realized_profit = Price::ZERO;
let mut realized_loss = Price::ZERO;
let value_created = current_value;
let mut adjusted_value_created = Price::ZERO;
let value_destroyed = previous_value;
let mut adjusted_value_destroyed = Price::ZERO;
match previous_value.cmp(&current_value) {
Ordering::Less => {
state.realized.realized_profit += current_value - previous_value;
}
Ordering::Less => realized_profit = current_value - previous_value,
Ordering::Greater => {
state.realized.realized_loss += previous_value - current_value;
realized_loss = previous_value - current_value;
}
Ordering::Equal => {}
}
if previous_timestamp.older_by_1h_plus_than(current_timestamp) {
adjusted_value_created = value_created;
adjusted_value_destroyed = value_destroyed;
}
state.realized.iterate(
realized_profit,
realized_loss,
value_created,
adjusted_value_created,
value_destroyed,
adjusted_value_destroyed,
);
})
})
}

View File

@@ -1,4 +1,4 @@
use super::{AddressData, Amount, Price};
use super::{AddressData, Amount, Price, Timestamp};
#[derive(Debug)]
pub struct AddressRealizedData {
@@ -8,7 +8,9 @@ pub struct AddressRealizedData {
pub profit: Price,
pub loss: Price,
pub value_created: Price,
pub adjusted_value_created: Price,
pub value_destroyed: Price,
pub adjusted_value_destroyed: Price,
pub utxos_created: u32,
pub utxos_destroyed: u32,
}
@@ -23,7 +25,9 @@ impl AddressRealizedData {
utxos_created: 0,
utxos_destroyed: 0,
value_created: Price::ZERO,
adjusted_value_created: Price::ZERO,
value_destroyed: Price::ZERO,
adjusted_value_destroyed: Price::ZERO,
initial_address_data: *initial_address_data,
}
}
@@ -33,7 +37,14 @@ impl AddressRealizedData {
self.utxos_created += 1;
}
pub fn send(&mut self, amount: Amount, current_price: Price, previous_price: Price) {
pub fn send(
&mut self,
amount: Amount,
current_price: Price,
previous_price: Price,
current_timestamp: Timestamp,
previous_timestamp: Timestamp,
) {
self.sent += amount;
self.utxos_destroyed += 1;
@@ -44,6 +55,11 @@ impl AddressRealizedData {
self.value_created += current_value;
self.value_destroyed += previous_value;
if previous_timestamp.older_by_1h_plus_than(current_timestamp) {
self.adjusted_value_created += current_value;
self.adjusted_value_destroyed += previous_value;
}
if current_value >= previous_value {
self.profit += current_value - previous_value;
} else {

View File

@@ -36,6 +36,7 @@ direct_repr!(Amount);
impl Amount {
pub const ZERO: Self = Self(BitcoinAmount::ZERO);
pub const ONE_BTC_F32: f32 = 100_000_000.0;
pub const ONE_BTC_F64: f64 = 100_000_000.0;
#[inline(always)]

View File

@@ -299,6 +299,18 @@ where
self.date
.multi_insert_percentile(dates, date_map_and_percentiles, days);
}
pub fn multi_insert_max(
&mut self,
heights: &[Height],
dates: &[Date],
source: &mut BiMap<Value>,
) where
Value: PartialOrd,
{
self.height.multi_insert_max(heights, &mut source.height);
self.date.multi_insert_max(dates, &mut source.date);
}
}
pub trait AnyBiMap {

View File

@@ -2,19 +2,19 @@ use allocative::Allocative;
use bincode::{Decode, Encode};
use serde::{Deserialize, Serialize};
use super::{Amount, Height, Price};
use super::{Amount, Height, Price, Timestamp};
#[derive(Debug, Serialize, Deserialize, Encode, Decode, Allocative)]
pub struct BlockData {
pub height: Height,
pub price: Price,
pub timestamp: u32,
pub timestamp: Timestamp,
pub amount: Amount,
pub utxos: u32,
}
impl BlockData {
pub fn new(height: Height, price: Price, timestamp: u32) -> Self {
pub fn new(height: Height, price: Price, timestamp: Timestamp) -> Self {
Self {
height,
price,

View File

@@ -33,20 +33,21 @@ pub struct Config {
pub delay: Option<u64>,
/// Start a dry run, default: false, not saved
#[arg(long, default_value_t = false)]
pub dry_run: bool,
#[arg(long, value_name = "BOOL")]
dry_run: Option<bool>,
/// Record ram usage, default: false, not saved
#[arg(long, default_value_t = false)]
pub record_ram_usage: bool,
#[arg(long, value_name = "BOOL")]
record_ram_usage: Option<bool>,
}
impl Config {
const PATH: &'static str = "./config.toml";
pub fn import() -> color_eyre::Result<Self> {
let mut config_saved = fs::read_to_string(Self::PATH)
.map_or(Config::default(), |contents| {
let mut config_saved =
fs::read_to_string(Self::PATH).map_or(Config::default(), |contents| {
dbg!(&contents);
toml::from_str(&contents).unwrap_or_default()
});
@@ -92,8 +93,8 @@ impl Config {
log(&format!("rpcuser: {:?}", config.rpcuser));
log(&format!("rpcpassword: {:?}", config.rpcpassword));
log(&format!("delay: {:?}", config.delay));
log(&format!("dry_run: {}", config.dry_run));
log(&format!("record_ram_usage: {}", config.record_ram_usage));
log(&format!("dry_run: {:?}", config.dry_run));
log(&format!("record_ram_usage: {:?}", config.record_ram_usage));
log("---");
Ok(config)
@@ -124,4 +125,12 @@ impl Config {
fn write(&self) -> std::io::Result<()> {
fs::write(Self::PATH, toml::to_string(self).unwrap())
}
pub fn dry_run(&self) -> bool {
self.dry_run.is_some_and(|b| b)
}
pub fn record_ram_usage(&self) -> bool {
self.record_ram_usage.is_some_and(|b| b)
}
}

View File

@@ -7,7 +7,7 @@ use bincode::{
error::{DecodeError, EncodeError},
BorrowDecode, Decode, Encode,
};
use chrono::{Datelike, Days, NaiveDate, TimeZone, Utc};
use chrono::{Datelike, Days, NaiveDate};
use derive_deref::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
@@ -38,14 +38,6 @@ impl Date {
Self(date)
}
pub fn from_timestamp(timestamp: u32) -> Self {
Self(
Utc.timestamp_opt(i64::from(timestamp), 0)
.unwrap()
.date_naive(),
)
}
pub fn today() -> Self {
Self(chrono::offset::Utc::now().date_naive())
}

View File

@@ -818,4 +818,36 @@ where
ControlFlow::Continue(())
});
}
pub fn multi_insert_max(&mut self, keys: &[Key], source: &mut Self)
where
Value: Default + PartialOrd,
{
let mut max = None;
keys.iter().for_each(|key| {
let previous_max = max.unwrap_or_else(|| {
key.checked_sub(1)
.and_then(|previous_max_key| self.get(&previous_max_key))
.unwrap_or_default()
});
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);
}
self.insert(
*key,
max.unwrap_or_else(|| {
dbg!(previous_max, last_value, max);
panic!();
}),
);
});
}
}

View File

@@ -6,7 +6,7 @@ use serde::{de::DeserializeOwned, Serialize};
use crate::datasets::OHLC;
use super::{Date, Height};
use super::{Date, Height, Timestamp};
pub trait MapValue:
Clone
@@ -32,3 +32,4 @@ impl MapValue for f64 {}
impl MapValue for Date {}
impl MapValue for OHLC {}
impl MapValue for Height {}
impl MapValue for Timestamp {}

View File

@@ -29,6 +29,7 @@ mod price;
mod sent_data;
mod serialized_btreemap;
mod serialized_vec;
mod timestamp;
mod tx_data;
mod txout_index;
@@ -63,5 +64,6 @@ pub use price::*;
pub use sent_data::*;
pub use serialized_btreemap::*;
pub use serialized_vec::*;
pub use timestamp::*;
pub use tx_data::*;
pub use txout_index::*;

View File

@@ -0,0 +1,83 @@
use std::ops::Sub;
use allocative::Allocative;
use bincode::{Decode, Encode};
use chrono::{Datelike, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc};
use derive_deref::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use crate::utils::{ONE_DAY_IN_S, ONE_HOUR_IN_S};
use super::Date;
#[derive(
Debug,
Default,
Clone,
Copy,
Allocative,
Serialize,
Deserialize,
Deref,
DerefMut,
PartialEq,
Eq,
PartialOrd,
Ord,
Encode,
Decode,
)]
pub struct Timestamp(u32);
impl Timestamp {
pub const ZERO: Self = Self(0);
pub fn wrap(timestamp: u32) -> Self {
Self(timestamp)
}
pub fn to_date(self) -> Date {
Date::wrap(
Utc.timestamp_opt(i64::from(self.0), 0)
.unwrap()
.date_naive(),
)
}
pub fn to_year(self) -> u32 {
self.to_date().year() as u32
}
pub fn to_floored_seconds(self) -> Self {
let date_time = Utc.timestamp_opt(i64::from(self.0), 0).unwrap();
Self::wrap(
NaiveDateTime::new(
date_time.date_naive(),
NaiveTime::from_hms_opt(date_time.hour(), date_time.minute(), 0).unwrap(),
)
.and_utc()
.timestamp() as u32,
)
}
pub fn difference_in_days_between(older: Self, younger: Self) -> u32 {
if younger <= older {
0
} else {
*(younger - older) / ONE_DAY_IN_S as u32
}
}
pub fn older_by_1h_plus_than(&self, younger: Self) -> bool {
younger.checked_sub(**self).unwrap_or_default() > ONE_HOUR_IN_S as u32
}
}
impl Sub for Timestamp {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::wrap(self.0 - rhs.0)
}
}

View File

@@ -2,8 +2,6 @@ use std::time::Instant;
use crate::utils::log;
use super::ONE_DAY_IN_S;
pub fn time<F, T>(name: &str, function: F) -> T
where
F: FnOnce() -> T,
@@ -16,11 +14,3 @@ where
returned
}
pub fn difference_in_days_between_timestamps(older: u32, younger: u32) -> u32 {
if younger <= older {
0
} else {
(younger - older) / ONE_DAY_IN_S as u32
}
}