mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-28 08:39:59 -07:00
git: reset
This commit is contained in:
47
parser/src/states/_trait.rs
Normal file
47
parser/src/states/_trait.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::{fmt::Debug, fs, io};
|
||||
|
||||
use bincode::{Decode, Encode};
|
||||
|
||||
use crate::io::{Binary, OUTPUTS_FOLDER_PATH};
|
||||
|
||||
// https://github.com/djkoloski/rust_serialization_benchmark
|
||||
pub trait AnyState
|
||||
where
|
||||
Self: Debug + Encode + Decode,
|
||||
{
|
||||
fn name<'a>() -> &'a str;
|
||||
|
||||
fn create_dir_all() -> color_eyre::Result<(), io::Error> {
|
||||
fs::create_dir_all(Self::folder_path())
|
||||
}
|
||||
|
||||
fn folder_path() -> String {
|
||||
format!("{OUTPUTS_FOLDER_PATH}/states")
|
||||
}
|
||||
|
||||
fn full_path() -> String {
|
||||
let name = Self::name();
|
||||
|
||||
let folder_path = Self::folder_path();
|
||||
|
||||
format!("{folder_path}/{name}.bin")
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> color_eyre::Result<(), io::Error> {
|
||||
self.clear();
|
||||
|
||||
fs::remove_file(Self::full_path())
|
||||
}
|
||||
|
||||
fn import() -> color_eyre::Result<Self> {
|
||||
Self::create_dir_all()?;
|
||||
|
||||
Binary::import(&Self::full_path())
|
||||
}
|
||||
|
||||
fn export(&self) -> color_eyre::Result<()> {
|
||||
Binary::export(&Self::full_path(), self)
|
||||
}
|
||||
|
||||
fn clear(&mut self);
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
use allocative::Allocative;
|
||||
|
||||
use crate::{
|
||||
states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState},
|
||||
structs::{LiquiditySplitResult, Price, SplitByLiquidity, WAmount},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Allocative)]
|
||||
pub struct AddressCohortDurableStates {
|
||||
pub address_count: usize,
|
||||
pub split_durable_states: SplitByLiquidity<DurableStates>,
|
||||
pub price_to_split_amount: PriceToValue<SplitByLiquidity<WAmount>>,
|
||||
}
|
||||
|
||||
const ONE_THIRD: f64 = 0.33333333333;
|
||||
|
||||
// TODO: Clean that mess, move to a generic liquidity split and somehow support rest for non floats
|
||||
impl AddressCohortDurableStates {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn increment(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
realized_cap: Price,
|
||||
mean_price_paid: Price,
|
||||
split_sat_amount_result: &LiquiditySplitResult,
|
||||
split_utxo_count_result: &LiquiditySplitResult,
|
||||
split_realized_cap_result: &LiquiditySplitResult,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.address_count += 1;
|
||||
|
||||
self._crement(
|
||||
amount,
|
||||
utxo_count,
|
||||
realized_cap,
|
||||
mean_price_paid,
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result,
|
||||
split_realized_cap_result,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn decrement(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
realized_cap: Price,
|
||||
mean_price_paid: Price,
|
||||
split_sat_amount_result: &LiquiditySplitResult,
|
||||
split_utxo_count_result: &LiquiditySplitResult,
|
||||
split_realized_cap_result: &LiquiditySplitResult,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.address_count -= 1;
|
||||
|
||||
self._crement(
|
||||
amount,
|
||||
utxo_count,
|
||||
realized_cap,
|
||||
mean_price_paid,
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result,
|
||||
split_realized_cap_result,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn _crement(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
realized_cap: Price,
|
||||
mean_price_paid: Price,
|
||||
split_sat_amount_result: &LiquiditySplitResult,
|
||||
split_utxo_count_result: &LiquiditySplitResult,
|
||||
split_realized_cap_result: &LiquiditySplitResult,
|
||||
increment: bool,
|
||||
) -> color_eyre::Result<()> {
|
||||
if increment {
|
||||
self.split_durable_states
|
||||
.all
|
||||
.increment(amount, utxo_count, realized_cap)
|
||||
} else {
|
||||
self.split_durable_states
|
||||
.all
|
||||
.decrement(amount, utxo_count, realized_cap)
|
||||
}
|
||||
.inspect_err(|report| {
|
||||
dbg!(
|
||||
report,
|
||||
"split all failed",
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result
|
||||
);
|
||||
})?;
|
||||
|
||||
let illiquid_amount = split_sat_amount_result.illiquid.trunc();
|
||||
let illiquid_amount_rest = split_sat_amount_result.illiquid - illiquid_amount;
|
||||
let mut illiquid_amount = WAmount::from_sat(illiquid_amount as u64);
|
||||
let mut illiquid_utxo_count = split_utxo_count_result.illiquid.trunc() as usize;
|
||||
let illiquid_utxo_count_rest = split_utxo_count_result.illiquid.fract();
|
||||
let mut illiquid_realized_cap =
|
||||
Price::from_cent(split_realized_cap_result.illiquid.trunc() as u64);
|
||||
let illiquid_realized_cap_rest = split_realized_cap_result.illiquid.fract();
|
||||
|
||||
let liquid_amount = split_sat_amount_result.liquid.trunc();
|
||||
let liquid_amount_rest = split_sat_amount_result.liquid - liquid_amount;
|
||||
let mut liquid_amount = WAmount::from_sat(liquid_amount as u64);
|
||||
let mut liquid_utxo_count = split_utxo_count_result.liquid.trunc() as usize;
|
||||
let liquid_utxo_count_rest = split_utxo_count_result.liquid.fract();
|
||||
let mut liquid_realized_cap =
|
||||
Price::from_cent(split_realized_cap_result.liquid.trunc() as u64);
|
||||
let liquid_realized_cap_rest = split_realized_cap_result.liquid.fract();
|
||||
|
||||
let mut highly_liquid_amount = amount - illiquid_amount - liquid_amount;
|
||||
let mut highly_liquid_utxo_count = utxo_count - illiquid_utxo_count - liquid_utxo_count;
|
||||
let mut highly_liquid_realized_cap =
|
||||
realized_cap - illiquid_realized_cap - liquid_realized_cap;
|
||||
|
||||
let amount_diff = amount - illiquid_amount - liquid_amount - highly_liquid_amount;
|
||||
if amount_diff > WAmount::ZERO {
|
||||
if illiquid_amount_rest >= ONE_THIRD && illiquid_amount_rest > liquid_amount_rest {
|
||||
illiquid_amount += amount_diff;
|
||||
} else if illiquid_amount_rest >= ONE_THIRD {
|
||||
liquid_amount += amount_diff;
|
||||
} else {
|
||||
highly_liquid_amount += amount_diff;
|
||||
}
|
||||
}
|
||||
|
||||
let utxo_count_diff =
|
||||
utxo_count - illiquid_utxo_count - liquid_utxo_count - highly_liquid_utxo_count;
|
||||
if utxo_count_diff > 0 {
|
||||
if illiquid_utxo_count_rest >= ONE_THIRD
|
||||
&& illiquid_utxo_count_rest > liquid_utxo_count_rest
|
||||
{
|
||||
illiquid_utxo_count += utxo_count_diff;
|
||||
} else if illiquid_utxo_count_rest >= ONE_THIRD {
|
||||
liquid_utxo_count += utxo_count_diff;
|
||||
} else {
|
||||
highly_liquid_utxo_count += utxo_count_diff;
|
||||
}
|
||||
}
|
||||
|
||||
let realized_cap_diff =
|
||||
realized_cap - illiquid_realized_cap - liquid_realized_cap - highly_liquid_realized_cap;
|
||||
if realized_cap_diff > Price::ZERO {
|
||||
if illiquid_realized_cap_rest >= ONE_THIRD
|
||||
&& illiquid_realized_cap_rest > liquid_realized_cap_rest
|
||||
{
|
||||
illiquid_realized_cap += realized_cap_diff;
|
||||
} else if illiquid_realized_cap_rest >= ONE_THIRD {
|
||||
liquid_realized_cap += realized_cap_diff;
|
||||
} else {
|
||||
highly_liquid_realized_cap += realized_cap_diff;
|
||||
}
|
||||
}
|
||||
|
||||
let split_amount = SplitByLiquidity {
|
||||
all: amount,
|
||||
illiquid: illiquid_amount,
|
||||
liquid: liquid_amount,
|
||||
highly_liquid: highly_liquid_amount,
|
||||
};
|
||||
|
||||
let split_utxo_count = SplitByLiquidity {
|
||||
all: utxo_count,
|
||||
illiquid: illiquid_utxo_count,
|
||||
liquid: liquid_utxo_count,
|
||||
highly_liquid: highly_liquid_utxo_count,
|
||||
};
|
||||
|
||||
let split_realized_cap = SplitByLiquidity {
|
||||
all: realized_cap,
|
||||
illiquid: illiquid_realized_cap,
|
||||
liquid: liquid_realized_cap,
|
||||
highly_liquid: highly_liquid_realized_cap,
|
||||
};
|
||||
|
||||
if increment {
|
||||
self.price_to_split_amount
|
||||
.increment(mean_price_paid, split_amount);
|
||||
} else {
|
||||
self.price_to_split_amount
|
||||
.decrement(mean_price_paid, split_amount)
|
||||
.inspect_err(|report| {
|
||||
dbg!(
|
||||
report,
|
||||
"cents_to_split_amount decrement",
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result,
|
||||
split_amount,
|
||||
split_utxo_count,
|
||||
split_realized_cap,
|
||||
);
|
||||
})?;
|
||||
}
|
||||
|
||||
if increment {
|
||||
self.split_durable_states.illiquid.increment(
|
||||
illiquid_amount,
|
||||
illiquid_utxo_count,
|
||||
illiquid_realized_cap,
|
||||
)
|
||||
} else {
|
||||
self.split_durable_states.illiquid.decrement(
|
||||
illiquid_amount,
|
||||
illiquid_utxo_count,
|
||||
illiquid_realized_cap,
|
||||
)
|
||||
}
|
||||
.inspect_err(|report| {
|
||||
dbg!(
|
||||
report,
|
||||
"split illiquid failed",
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result,
|
||||
split_amount,
|
||||
split_utxo_count,
|
||||
split_realized_cap,
|
||||
);
|
||||
})?;
|
||||
|
||||
if increment {
|
||||
self.split_durable_states.liquid.increment(
|
||||
liquid_amount,
|
||||
liquid_utxo_count,
|
||||
liquid_realized_cap,
|
||||
)
|
||||
} else {
|
||||
self.split_durable_states.liquid.decrement(
|
||||
liquid_amount,
|
||||
liquid_utxo_count,
|
||||
liquid_realized_cap,
|
||||
)
|
||||
}
|
||||
.inspect_err(|report| {
|
||||
dbg!(
|
||||
report,
|
||||
"split liquid failed",
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result,
|
||||
split_amount,
|
||||
split_utxo_count,
|
||||
split_realized_cap,
|
||||
);
|
||||
})?;
|
||||
|
||||
if increment {
|
||||
self.split_durable_states.highly_liquid.increment(
|
||||
highly_liquid_amount,
|
||||
highly_liquid_utxo_count,
|
||||
highly_liquid_realized_cap,
|
||||
)
|
||||
} else {
|
||||
self.split_durable_states.highly_liquid.decrement(
|
||||
highly_liquid_amount,
|
||||
highly_liquid_utxo_count,
|
||||
highly_liquid_realized_cap,
|
||||
)
|
||||
}
|
||||
.inspect_err(|report| {
|
||||
dbg!(
|
||||
report,
|
||||
"split highly liquid failed",
|
||||
split_sat_amount_result,
|
||||
split_utxo_count_result,
|
||||
split_amount,
|
||||
split_utxo_count,
|
||||
split_realized_cap,
|
||||
);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_one_shot_states(
|
||||
&self,
|
||||
block_price: Price,
|
||||
date_price: Option<Price>,
|
||||
) -> SplitByLiquidity<OneShotStates> {
|
||||
let mut one_shot_states: SplitByLiquidity<OneShotStates> = SplitByLiquidity::default();
|
||||
|
||||
if date_price.is_some() {
|
||||
one_shot_states
|
||||
.all
|
||||
.unrealized_date_state
|
||||
.replace(UnrealizedState::default());
|
||||
one_shot_states
|
||||
.illiquid
|
||||
.unrealized_date_state
|
||||
.replace(UnrealizedState::default());
|
||||
one_shot_states
|
||||
.liquid
|
||||
.unrealized_date_state
|
||||
.replace(UnrealizedState::default());
|
||||
one_shot_states
|
||||
.highly_liquid
|
||||
.unrealized_date_state
|
||||
.replace(UnrealizedState::default());
|
||||
}
|
||||
|
||||
let all_supply = self.split_durable_states.all.supply_state.supply;
|
||||
let illiquid_supply = self.split_durable_states.illiquid.supply_state.supply;
|
||||
let liquid_supply = self.split_durable_states.liquid.supply_state.supply;
|
||||
let highly_liquid_supply = self.split_durable_states.highly_liquid.supply_state.supply;
|
||||
|
||||
let one_shot_states_ref = &mut one_shot_states;
|
||||
|
||||
self.price_to_split_amount.iterate(
|
||||
SplitByLiquidity {
|
||||
all: all_supply,
|
||||
illiquid: illiquid_supply,
|
||||
liquid: liquid_supply,
|
||||
highly_liquid: highly_liquid_supply,
|
||||
},
|
||||
|price_paid, split_amount| {
|
||||
one_shot_states_ref.all.price_paid_state.iterate(
|
||||
price_paid,
|
||||
split_amount.all,
|
||||
all_supply,
|
||||
);
|
||||
one_shot_states_ref.all.unrealized_block_state.iterate(
|
||||
price_paid,
|
||||
block_price,
|
||||
split_amount.all,
|
||||
);
|
||||
if let Some(unrealized_date_state) =
|
||||
one_shot_states_ref.all.unrealized_date_state.as_mut()
|
||||
{
|
||||
unrealized_date_state.iterate(
|
||||
price_paid,
|
||||
date_price.unwrap(),
|
||||
split_amount.all,
|
||||
);
|
||||
}
|
||||
|
||||
if split_amount.illiquid > WAmount::ZERO {
|
||||
one_shot_states_ref.illiquid.price_paid_state.iterate(
|
||||
price_paid,
|
||||
split_amount.illiquid,
|
||||
illiquid_supply,
|
||||
);
|
||||
one_shot_states_ref.illiquid.unrealized_block_state.iterate(
|
||||
price_paid,
|
||||
block_price,
|
||||
split_amount.illiquid,
|
||||
);
|
||||
if let Some(unrealized_date_state) =
|
||||
one_shot_states_ref.illiquid.unrealized_date_state.as_mut()
|
||||
{
|
||||
unrealized_date_state.iterate(
|
||||
price_paid,
|
||||
date_price.unwrap(),
|
||||
split_amount.illiquid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if split_amount.liquid > WAmount::ZERO {
|
||||
one_shot_states_ref.liquid.price_paid_state.iterate(
|
||||
price_paid,
|
||||
split_amount.liquid,
|
||||
liquid_supply,
|
||||
);
|
||||
one_shot_states_ref.liquid.unrealized_block_state.iterate(
|
||||
price_paid,
|
||||
block_price,
|
||||
split_amount.liquid,
|
||||
);
|
||||
if let Some(unrealized_date_state) =
|
||||
one_shot_states_ref.liquid.unrealized_date_state.as_mut()
|
||||
{
|
||||
unrealized_date_state.iterate(
|
||||
price_paid,
|
||||
date_price.unwrap(),
|
||||
split_amount.liquid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if split_amount.highly_liquid > WAmount::ZERO {
|
||||
one_shot_states_ref.highly_liquid.price_paid_state.iterate(
|
||||
price_paid,
|
||||
split_amount.highly_liquid,
|
||||
highly_liquid_supply,
|
||||
);
|
||||
one_shot_states_ref
|
||||
.highly_liquid
|
||||
.unrealized_block_state
|
||||
.iterate(price_paid, block_price, split_amount.highly_liquid);
|
||||
if let Some(unrealized_date_state) = one_shot_states_ref
|
||||
.highly_liquid
|
||||
.unrealized_date_state
|
||||
.as_mut()
|
||||
{
|
||||
unrealized_date_state.iterate(
|
||||
price_paid,
|
||||
date_price.unwrap(),
|
||||
split_amount.highly_liquid,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
one_shot_states
|
||||
}
|
||||
}
|
||||
68
parser/src/states/cohorts_states/address/cohort_id.rs
Normal file
68
parser/src/states/cohorts_states/address/cohort_id.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use crate::structs::{AddressSize, AddressSplit, AddressType};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum AddressCohortId {
|
||||
All,
|
||||
|
||||
Plankton,
|
||||
Shrimp,
|
||||
Crab,
|
||||
Fish,
|
||||
Shark,
|
||||
Whale,
|
||||
Humpback,
|
||||
Megalodon,
|
||||
|
||||
P2PK,
|
||||
P2PKH,
|
||||
P2SH,
|
||||
P2WPKH,
|
||||
P2WSH,
|
||||
P2TR,
|
||||
}
|
||||
|
||||
impl AddressCohortId {
|
||||
pub fn as_name(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::All => None,
|
||||
|
||||
Self::Plankton => Some("plankton"),
|
||||
Self::Shrimp => Some("shrimp"),
|
||||
Self::Crab => Some("crab"),
|
||||
Self::Fish => Some("fish"),
|
||||
Self::Shark => Some("shark"),
|
||||
Self::Whale => Some("whale"),
|
||||
Self::Humpback => Some("humpback"),
|
||||
Self::Megalodon => Some("megalodon"),
|
||||
|
||||
Self::P2PK => Some("p2pk"),
|
||||
Self::P2PKH => Some("p2pkh"),
|
||||
Self::P2SH => Some("p2sh"),
|
||||
Self::P2WPKH => Some("p2wpkh"),
|
||||
Self::P2WSH => Some("p2wsh"),
|
||||
Self::P2TR => Some("p2tr"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_split(&self) -> AddressSplit {
|
||||
match self {
|
||||
Self::All => AddressSplit::All,
|
||||
|
||||
Self::Plankton => AddressSplit::Size(AddressSize::Plankton),
|
||||
Self::Shrimp => AddressSplit::Size(AddressSize::Shrimp),
|
||||
Self::Crab => AddressSplit::Size(AddressSize::Crab),
|
||||
Self::Fish => AddressSplit::Size(AddressSize::Fish),
|
||||
Self::Shark => AddressSplit::Size(AddressSize::Shark),
|
||||
Self::Whale => AddressSplit::Size(AddressSize::Whale),
|
||||
Self::Humpback => AddressSplit::Size(AddressSize::Humpback),
|
||||
Self::Megalodon => AddressSplit::Size(AddressSize::Megalodon),
|
||||
|
||||
Self::P2PK => AddressSplit::Type(AddressType::P2PK),
|
||||
Self::P2PKH => AddressSplit::Type(AddressType::P2PKH),
|
||||
Self::P2SH => AddressSplit::Type(AddressType::P2SH),
|
||||
Self::P2WPKH => AddressSplit::Type(AddressType::P2WPKH),
|
||||
Self::P2WSH => AddressSplit::Type(AddressType::P2WSH),
|
||||
Self::P2TR => AddressSplit::Type(AddressType::P2TR),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
use allocative::Allocative;
|
||||
use color_eyre::eyre::eyre;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::{
|
||||
databases::AddressIndexToAddressData,
|
||||
structs::{AddressData, AddressRealizedData, Price},
|
||||
};
|
||||
|
||||
use super::{AddressCohortDurableStates, AddressCohortsOneShotStates, SplitByAddressCohort};
|
||||
|
||||
#[derive(Default, Deref, DerefMut, Allocative)]
|
||||
pub struct AddressCohortsDurableStates(SplitByAddressCohort<AddressCohortDurableStates>);
|
||||
|
||||
impl AddressCohortsDurableStates {
|
||||
pub fn init(address_index_to_address_data: &mut AddressIndexToAddressData) -> Self {
|
||||
let mut s = Self::default();
|
||||
|
||||
// Paralize that, different s could be added together
|
||||
address_index_to_address_data
|
||||
.iter(&mut |(_, address_data)| s.increment(address_data).unwrap());
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn iterate(
|
||||
&mut self,
|
||||
address_realized_data: &AddressRealizedData,
|
||||
current_address_data: &AddressData,
|
||||
) -> color_eyre::Result<()> {
|
||||
self.decrement(&address_realized_data.initial_address_data)
|
||||
.inspect_err(|report| {
|
||||
dbg!(report);
|
||||
dbg!(address_realized_data, current_address_data);
|
||||
dbg!("decrement initial address_data");
|
||||
})?;
|
||||
|
||||
self.increment(current_address_data).inspect_err(|report| {
|
||||
dbg!(report);
|
||||
dbg!(address_realized_data, current_address_data);
|
||||
dbg!("increment address_data");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Should always increment using current address data state
|
||||
fn increment(&mut self, address_data: &AddressData) -> color_eyre::Result<()> {
|
||||
self._crement(address_data, true)
|
||||
}
|
||||
|
||||
/// Should always decrement using initial address data state
|
||||
fn decrement(&mut self, address_data: &AddressData) -> color_eyre::Result<()> {
|
||||
self._crement(address_data, false)
|
||||
}
|
||||
|
||||
fn _crement(&mut self, address_data: &AddressData, increment: bool) -> color_eyre::Result<()> {
|
||||
// No need to either insert or remove if empty
|
||||
if address_data.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let amount = address_data.amount;
|
||||
let utxo_count = address_data.outputs_len as usize;
|
||||
let realized_cap = address_data.realized_cap;
|
||||
|
||||
let mean_price_paid = address_data.realized_cap / amount;
|
||||
|
||||
let liquidity_classification = address_data.compute_liquidity_classification();
|
||||
|
||||
let split_sat_amount = liquidity_classification.split(amount.to_sat() as f64);
|
||||
let split_utxo_count = liquidity_classification.split(utxo_count as f64);
|
||||
let split_realized_cap = liquidity_classification.split(utxo_count as f64);
|
||||
|
||||
self.0
|
||||
.iterate(address_data, |state: &mut AddressCohortDurableStates| {
|
||||
if increment {
|
||||
if let Err(report) = state.increment(
|
||||
amount,
|
||||
utxo_count,
|
||||
realized_cap,
|
||||
mean_price_paid,
|
||||
&split_sat_amount,
|
||||
&split_utxo_count,
|
||||
&split_realized_cap,
|
||||
) {
|
||||
dbg!(
|
||||
report.to_string(),
|
||||
&state,
|
||||
&address_data,
|
||||
&liquidity_classification
|
||||
);
|
||||
return Err(eyre!("increment error"));
|
||||
}
|
||||
} else if let Err(report) = state.decrement(
|
||||
amount,
|
||||
utxo_count,
|
||||
realized_cap,
|
||||
mean_price_paid,
|
||||
&split_sat_amount,
|
||||
&split_utxo_count,
|
||||
&split_realized_cap,
|
||||
) {
|
||||
dbg!(
|
||||
report.to_string(),
|
||||
&state,
|
||||
&address_data,
|
||||
&liquidity_classification
|
||||
);
|
||||
return Err(eyre!("decrement error"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_one_shot_states(
|
||||
&mut self,
|
||||
block_price: Price,
|
||||
date_price: Option<Price>,
|
||||
) -> AddressCohortsOneShotStates {
|
||||
let mut one_shot_states = AddressCohortsOneShotStates::default();
|
||||
|
||||
self.as_vec()
|
||||
.into_par_iter()
|
||||
.map(|(states, address_cohort_id)| {
|
||||
(
|
||||
address_cohort_id,
|
||||
states.compute_one_shot_states(block_price, date_price),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.for_each(|(address_cohort_id, states)| {
|
||||
*one_shot_states.get_mut_from_id(&address_cohort_id) = states;
|
||||
});
|
||||
|
||||
one_shot_states
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
states::InputState,
|
||||
structs::{AddressRealizedData, LiquidityClassification, SplitByLiquidity, WAmount},
|
||||
};
|
||||
|
||||
use super::SplitByAddressCohort;
|
||||
|
||||
#[derive(Deref, DerefMut, Default)]
|
||||
pub struct AddressCohortsInputStates(SplitByAddressCohort<SplitByLiquidity<InputState>>);
|
||||
|
||||
impl AddressCohortsInputStates {
|
||||
pub fn iterate_input(
|
||||
&mut self,
|
||||
realized_data: &AddressRealizedData,
|
||||
liquidity_classification: &LiquidityClassification,
|
||||
) -> color_eyre::Result<()> {
|
||||
let count = realized_data.utxos_destroyed as f64;
|
||||
let sent = realized_data.sent;
|
||||
|
||||
let split_count = liquidity_classification.split(count);
|
||||
let split_volume = liquidity_classification.split(sent.to_sat() as f64);
|
||||
|
||||
let iterate = move |state: &mut SplitByLiquidity<InputState>| -> color_eyre::Result<()> {
|
||||
state.all.iterate(count, sent);
|
||||
|
||||
state.illiquid.iterate(
|
||||
split_count.illiquid,
|
||||
WAmount::from_sat(split_volume.illiquid.round() as u64),
|
||||
);
|
||||
|
||||
state.liquid.iterate(
|
||||
split_count.liquid,
|
||||
WAmount::from_sat(split_volume.liquid.round() as u64),
|
||||
);
|
||||
|
||||
state.highly_liquid.iterate(
|
||||
split_count.highly_liquid,
|
||||
WAmount::from_sat(split_volume.highly_liquid.round() as u64),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
self.iterate(&realized_data.initial_address_data, iterate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::{states::OneShotStates, structs::SplitByLiquidity};
|
||||
|
||||
use super::SplitByAddressCohort;
|
||||
|
||||
#[derive(Deref, DerefMut, Default)]
|
||||
pub struct AddressCohortsOneShotStates(pub SplitByAddressCohort<SplitByLiquidity<OneShotStates>>);
|
||||
@@ -0,0 +1,48 @@
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
states::OutputState,
|
||||
structs::{AddressRealizedData, LiquidityClassification, SplitByLiquidity, WAmount},
|
||||
};
|
||||
|
||||
use super::SplitByAddressCohort;
|
||||
|
||||
#[derive(Deref, DerefMut, Default)]
|
||||
pub struct AddressCohortsOutputStates(SplitByAddressCohort<SplitByLiquidity<OutputState>>);
|
||||
|
||||
impl AddressCohortsOutputStates {
|
||||
pub fn iterate_output(
|
||||
&mut self,
|
||||
realized_data: &AddressRealizedData,
|
||||
liquidity_classification: &LiquidityClassification,
|
||||
) -> color_eyre::Result<()> {
|
||||
let count = realized_data.utxos_created as f64;
|
||||
let volume = realized_data.received;
|
||||
|
||||
let split_count = liquidity_classification.split(count);
|
||||
let split_volume = liquidity_classification.split(volume.to_sat() as f64);
|
||||
|
||||
let iterate = move |state: &mut SplitByLiquidity<OutputState>| -> color_eyre::Result<()> {
|
||||
state.all.iterate(count, volume);
|
||||
|
||||
state.illiquid.iterate(
|
||||
split_count.illiquid,
|
||||
WAmount::from_sat(split_volume.illiquid.round() as u64),
|
||||
);
|
||||
|
||||
state.liquid.iterate(
|
||||
split_count.liquid,
|
||||
WAmount::from_sat(split_volume.liquid.round() as u64),
|
||||
);
|
||||
|
||||
state.highly_liquid.iterate(
|
||||
split_count.highly_liquid,
|
||||
WAmount::from_sat(split_volume.highly_liquid.round() as u64),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
self.iterate(&realized_data.initial_address_data, iterate)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
states::RealizedState,
|
||||
structs::{AddressRealizedData, LiquidityClassification, Price, SplitByLiquidity},
|
||||
};
|
||||
|
||||
use super::SplitByAddressCohort;
|
||||
|
||||
#[derive(Deref, DerefMut, Default)]
|
||||
pub struct AddressCohortsRealizedStates(SplitByAddressCohort<SplitByLiquidity<RealizedState>>);
|
||||
|
||||
impl AddressCohortsRealizedStates {
|
||||
pub fn iterate_realized(
|
||||
&mut self,
|
||||
realized_data: &AddressRealizedData,
|
||||
liquidity_classification: &LiquidityClassification,
|
||||
) -> color_eyre::Result<()> {
|
||||
let profit = realized_data.profit;
|
||||
let loss = realized_data.loss;
|
||||
|
||||
let split_profit = liquidity_classification.split(profit.to_cent() as f64);
|
||||
let split_loss = liquidity_classification.split(loss.to_cent() as f64);
|
||||
|
||||
let iterate = move |state: &mut SplitByLiquidity<RealizedState>| -> color_eyre::Result<()> {
|
||||
state.all.iterate(profit, loss);
|
||||
|
||||
state.illiquid.iterate(
|
||||
Price::from_cent(split_profit.illiquid as u64),
|
||||
Price::from_cent(split_loss.illiquid as u64),
|
||||
);
|
||||
|
||||
state.liquid.iterate(
|
||||
Price::from_cent(split_profit.liquid as u64),
|
||||
Price::from_cent(split_loss.liquid as u64),
|
||||
);
|
||||
|
||||
state.highly_liquid.iterate(
|
||||
Price::from_cent(split_profit.highly_liquid as u64),
|
||||
Price::from_cent(split_loss.highly_liquid as u64),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
self.iterate(&realized_data.initial_address_data, iterate)
|
||||
}
|
||||
}
|
||||
17
parser/src/states/cohorts_states/address/mod.rs
Normal file
17
parser/src/states/cohorts_states/address/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
mod cohort_durable_states;
|
||||
mod cohort_id;
|
||||
mod cohorts_durable_states;
|
||||
mod cohorts_input_states;
|
||||
mod cohorts_one_shot_states;
|
||||
mod cohorts_output_states;
|
||||
mod cohorts_realized_states;
|
||||
mod split_by_address_cohort;
|
||||
|
||||
pub use cohort_durable_states::*;
|
||||
pub use cohort_id::*;
|
||||
pub use cohorts_durable_states::*;
|
||||
pub use cohorts_input_states::*;
|
||||
pub use cohorts_one_shot_states::*;
|
||||
pub use cohorts_output_states::*;
|
||||
pub use cohorts_realized_states::*;
|
||||
pub use split_by_address_cohort::*;
|
||||
@@ -0,0 +1,177 @@
|
||||
use allocative::Allocative;
|
||||
|
||||
use crate::structs::{AddressData, AddressSize, AddressSplit, AddressType};
|
||||
|
||||
use super::AddressCohortId;
|
||||
|
||||
#[derive(Default, Allocative)]
|
||||
pub struct SplitByAddressCohort<T> {
|
||||
pub all: T,
|
||||
|
||||
pub plankton: T,
|
||||
pub shrimp: T,
|
||||
pub crab: T,
|
||||
pub fish: T,
|
||||
pub shark: T,
|
||||
pub whale: T,
|
||||
pub humpback: T,
|
||||
pub megalodon: T,
|
||||
|
||||
pub p2pk: T,
|
||||
pub p2pkh: T,
|
||||
pub p2sh: T,
|
||||
pub p2wpkh: T,
|
||||
pub p2wsh: T,
|
||||
pub p2tr: T,
|
||||
}
|
||||
|
||||
impl<T> SplitByAddressCohort<T> {
|
||||
pub fn get(&self, split: &AddressSplit) -> Option<&T> {
|
||||
match &split {
|
||||
AddressSplit::All => Some(&self.all),
|
||||
|
||||
AddressSplit::Type(address_type) => match address_type {
|
||||
AddressType::P2PK => Some(&self.p2pk),
|
||||
AddressType::P2PKH => Some(&self.p2pkh),
|
||||
AddressType::P2SH => Some(&self.p2sh),
|
||||
AddressType::P2WPKH => Some(&self.p2wpkh),
|
||||
AddressType::P2WSH => Some(&self.p2wsh),
|
||||
AddressType::P2TR => Some(&self.p2tr),
|
||||
AddressType::MultiSig => None,
|
||||
AddressType::Unknown => None,
|
||||
AddressType::OpReturn => None,
|
||||
AddressType::PushOnly => None,
|
||||
AddressType::Empty => None,
|
||||
},
|
||||
|
||||
AddressSplit::Size(address_size) => match address_size {
|
||||
AddressSize::Plankton => Some(&self.plankton),
|
||||
AddressSize::Shrimp => Some(&self.shrimp),
|
||||
AddressSize::Crab => Some(&self.crab),
|
||||
AddressSize::Fish => Some(&self.fish),
|
||||
AddressSize::Shark => Some(&self.shark),
|
||||
AddressSize::Whale => Some(&self.whale),
|
||||
AddressSize::Humpback => Some(&self.humpback),
|
||||
AddressSize::Megalodon => Some(&self.megalodon),
|
||||
AddressSize::Empty => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iterate(
|
||||
&mut self,
|
||||
address_data: &AddressData,
|
||||
iterate: impl Fn(&mut T) -> color_eyre::Result<()>,
|
||||
) -> color_eyre::Result<()> {
|
||||
if let Some(state) = self.get_mut_from_split(&AddressSplit::All) {
|
||||
iterate(state)?;
|
||||
}
|
||||
|
||||
if let Some(state) = self.get_mut_from_split(&AddressSplit::Type(address_data.address_type))
|
||||
{
|
||||
iterate(state)?;
|
||||
}
|
||||
|
||||
if let Some(state) = self.get_mut_from_split(&AddressSplit::Size(AddressSize::from_amount(
|
||||
address_data.amount,
|
||||
))) {
|
||||
iterate(state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_mut_from_split(&mut self, split: &AddressSplit) -> Option<&mut T> {
|
||||
match &split {
|
||||
AddressSplit::All => Some(&mut self.all),
|
||||
|
||||
AddressSplit::Type(address_type) => match address_type {
|
||||
AddressType::P2PK => Some(&mut self.p2pk),
|
||||
AddressType::P2PKH => Some(&mut self.p2pkh),
|
||||
AddressType::P2SH => Some(&mut self.p2sh),
|
||||
AddressType::P2WPKH => Some(&mut self.p2wpkh),
|
||||
AddressType::P2WSH => Some(&mut self.p2wsh),
|
||||
AddressType::P2TR => Some(&mut self.p2tr),
|
||||
AddressType::MultiSig => None,
|
||||
AddressType::Unknown => None,
|
||||
AddressType::OpReturn => None,
|
||||
AddressType::PushOnly => None,
|
||||
AddressType::Empty => None,
|
||||
},
|
||||
|
||||
AddressSplit::Size(address_size) => match address_size {
|
||||
AddressSize::Plankton => Some(&mut self.plankton),
|
||||
AddressSize::Shrimp => Some(&mut self.shrimp),
|
||||
AddressSize::Crab => Some(&mut self.crab),
|
||||
AddressSize::Fish => Some(&mut self.fish),
|
||||
AddressSize::Shark => Some(&mut self.shark),
|
||||
AddressSize::Whale => Some(&mut self.whale),
|
||||
AddressSize::Humpback => Some(&mut self.humpback),
|
||||
AddressSize::Megalodon => Some(&mut self.megalodon),
|
||||
AddressSize::Empty => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut_from_id(&mut self, id: &AddressCohortId) -> &mut T {
|
||||
match id {
|
||||
AddressCohortId::All => &mut self.all,
|
||||
|
||||
AddressCohortId::Plankton => &mut self.plankton,
|
||||
AddressCohortId::Shrimp => &mut self.shrimp,
|
||||
AddressCohortId::Crab => &mut self.crab,
|
||||
AddressCohortId::Fish => &mut self.fish,
|
||||
AddressCohortId::Shark => &mut self.shark,
|
||||
AddressCohortId::Whale => &mut self.whale,
|
||||
AddressCohortId::Humpback => &mut self.humpback,
|
||||
AddressCohortId::Megalodon => &mut self.megalodon,
|
||||
|
||||
AddressCohortId::P2PK => &mut self.p2pk,
|
||||
AddressCohortId::P2PKH => &mut self.p2pkh,
|
||||
AddressCohortId::P2SH => &mut self.p2sh,
|
||||
AddressCohortId::P2WPKH => &mut self.p2wpkh,
|
||||
AddressCohortId::P2WSH => &mut self.p2wsh,
|
||||
AddressCohortId::P2TR => &mut self.p2tr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec(&self) -> Vec<(&T, AddressCohortId)> {
|
||||
vec![
|
||||
(&self.all, AddressCohortId::All),
|
||||
(&self.plankton, AddressCohortId::Plankton),
|
||||
(&self.shrimp, AddressCohortId::Shrimp),
|
||||
(&self.crab, AddressCohortId::Crab),
|
||||
(&self.fish, AddressCohortId::Fish),
|
||||
(&self.shark, AddressCohortId::Shark),
|
||||
(&self.whale, AddressCohortId::Whale),
|
||||
(&self.humpback, AddressCohortId::Humpback),
|
||||
(&self.megalodon, AddressCohortId::Megalodon),
|
||||
(&self.p2pk, AddressCohortId::P2PK),
|
||||
(&self.p2pkh, AddressCohortId::P2PKH),
|
||||
(&self.p2sh, AddressCohortId::P2SH),
|
||||
(&self.p2wpkh, AddressCohortId::P2WPKH),
|
||||
(&self.p2wsh, AddressCohortId::P2WSH),
|
||||
(&self.p2tr, AddressCohortId::P2TR),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn as_mut_vec(&mut self) -> Vec<(&mut T, AddressCohortId)> {
|
||||
vec![
|
||||
(&mut self.all, AddressCohortId::All),
|
||||
(&mut self.plankton, AddressCohortId::Plankton),
|
||||
(&mut self.shrimp, AddressCohortId::Shrimp),
|
||||
(&mut self.crab, AddressCohortId::Crab),
|
||||
(&mut self.fish, AddressCohortId::Fish),
|
||||
(&mut self.shark, AddressCohortId::Shark),
|
||||
(&mut self.whale, AddressCohortId::Whale),
|
||||
(&mut self.humpback, AddressCohortId::Humpback),
|
||||
(&mut self.megalodon, AddressCohortId::Megalodon),
|
||||
(&mut self.p2pk, AddressCohortId::P2PK),
|
||||
(&mut self.p2pkh, AddressCohortId::P2PKH),
|
||||
(&mut self.p2sh, AddressCohortId::P2SH),
|
||||
(&mut self.p2wpkh, AddressCohortId::P2WPKH),
|
||||
(&mut self.p2wsh, AddressCohortId::P2WSH),
|
||||
(&mut self.p2tr, AddressCohortId::P2TR),
|
||||
]
|
||||
}
|
||||
}
|
||||
18
parser/src/states/cohorts_states/any/capitalization_state.rs
Normal file
18
parser/src/states/cohorts_states/any/capitalization_state.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use allocative::Allocative;
|
||||
|
||||
use crate::structs::Price;
|
||||
|
||||
#[derive(Debug, Default, Allocative)]
|
||||
pub struct CapitalizationState {
|
||||
pub realized_cap: Price,
|
||||
}
|
||||
|
||||
impl CapitalizationState {
|
||||
pub fn increment(&mut self, realized_cap: Price) {
|
||||
self.realized_cap += realized_cap;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, realized_cap: Price) {
|
||||
self.realized_cap -= realized_cap;
|
||||
}
|
||||
}
|
||||
55
parser/src/states/cohorts_states/any/durable_states.rs
Normal file
55
parser/src/states/cohorts_states/any/durable_states.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use allocative::Allocative;
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use crate::structs::{Price, WAmount};
|
||||
|
||||
use super::{CapitalizationState, SupplyState, UTXOState};
|
||||
|
||||
#[derive(Default, Debug, Allocative)]
|
||||
pub struct DurableStates {
|
||||
pub capitalization_state: CapitalizationState,
|
||||
pub supply_state: SupplyState,
|
||||
pub utxo_state: UTXOState,
|
||||
}
|
||||
|
||||
impl DurableStates {
|
||||
pub fn increment(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
realized_cap: Price,
|
||||
) -> color_eyre::Result<()> {
|
||||
if amount == WAmount::ZERO {
|
||||
if utxo_count != 0 {
|
||||
dbg!(amount, utxo_count);
|
||||
return Err(eyre!("Shouldn't be possible"));
|
||||
}
|
||||
} else {
|
||||
self.capitalization_state.increment(realized_cap);
|
||||
self.supply_state.increment(amount);
|
||||
self.utxo_state.increment(utxo_count);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decrement(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
realized_cap: Price,
|
||||
) -> color_eyre::Result<()> {
|
||||
if amount == WAmount::ZERO {
|
||||
if utxo_count != 0 {
|
||||
dbg!(amount, utxo_count);
|
||||
unreachable!("Shouldn't be possible")
|
||||
}
|
||||
} else {
|
||||
self.capitalization_state.decrement(realized_cap);
|
||||
self.supply_state.decrement(amount)?;
|
||||
self.utxo_state.decrement(utxo_count)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
14
parser/src/states/cohorts_states/any/input_state.rs
Normal file
14
parser/src/states/cohorts_states/any/input_state.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::structs::WAmount;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InputState {
|
||||
pub count: f64,
|
||||
pub volume: WAmount,
|
||||
}
|
||||
|
||||
impl InputState {
|
||||
pub fn iterate(&mut self, count: f64, volume: WAmount) {
|
||||
self.count += count;
|
||||
self.volume += volume;
|
||||
}
|
||||
}
|
||||
23
parser/src/states/cohorts_states/any/mod.rs
Normal file
23
parser/src/states/cohorts_states/any/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
mod capitalization_state;
|
||||
mod durable_states;
|
||||
mod input_state;
|
||||
mod one_shot_states;
|
||||
mod output_state;
|
||||
mod price_paid_state;
|
||||
mod price_to_value;
|
||||
mod realized_state;
|
||||
mod supply_state;
|
||||
mod unrealized_state;
|
||||
mod utxo_state;
|
||||
|
||||
pub use capitalization_state::*;
|
||||
pub use durable_states::*;
|
||||
pub use input_state::*;
|
||||
pub use one_shot_states::*;
|
||||
pub use output_state::*;
|
||||
pub use price_paid_state::*;
|
||||
pub use price_to_value::*;
|
||||
pub use realized_state::*;
|
||||
pub use supply_state::*;
|
||||
pub use unrealized_state::*;
|
||||
pub use utxo_state::*;
|
||||
9
parser/src/states/cohorts_states/any/one_shot_states.rs
Normal file
9
parser/src/states/cohorts_states/any/one_shot_states.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use super::{PricePaidState, UnrealizedState};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OneShotStates {
|
||||
pub price_paid_state: PricePaidState,
|
||||
|
||||
pub unrealized_block_state: UnrealizedState,
|
||||
pub unrealized_date_state: Option<UnrealizedState>,
|
||||
}
|
||||
14
parser/src/states/cohorts_states/any/output_state.rs
Normal file
14
parser/src/states/cohorts_states/any/output_state.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::structs::WAmount;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct OutputState {
|
||||
pub count: f64,
|
||||
pub volume: WAmount,
|
||||
}
|
||||
|
||||
impl OutputState {
|
||||
pub fn iterate(&mut self, count: f64, volume: WAmount) {
|
||||
self.count += count;
|
||||
self.volume += volume;
|
||||
}
|
||||
}
|
||||
210
parser/src/states/cohorts_states/any/price_paid_state.rs
Normal file
210
parser/src/states/cohorts_states/any/price_paid_state.rs
Normal file
@@ -0,0 +1,210 @@
|
||||
use crate::structs::{Price, WAmount};
|
||||
|
||||
#[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>,
|
||||
|
||||
pub processed_amount: WAmount,
|
||||
}
|
||||
|
||||
impl PricePaidState {
|
||||
pub fn iterate(&mut self, price: Price, amount: WAmount, total_supply: WAmount) {
|
||||
let PricePaidState {
|
||||
processed_amount,
|
||||
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,
|
||||
} = self;
|
||||
|
||||
*processed_amount += amount;
|
||||
|
||||
if pp_95p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let processed_sat_amount = processed_amount.to_sat();
|
||||
let total_sat_supply = total_supply.to_sat();
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 95 / 100 {
|
||||
pp_95p.replace(price);
|
||||
}
|
||||
|
||||
if pp_90p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 90 / 100 {
|
||||
pp_90p.replace(price);
|
||||
}
|
||||
|
||||
if pp_85p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 85 / 100 {
|
||||
pp_85p.replace(price);
|
||||
}
|
||||
|
||||
if pp_80p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 80 / 100 {
|
||||
pp_80p.replace(price);
|
||||
}
|
||||
|
||||
if pp_75p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 75 / 100 {
|
||||
pp_75p.replace(price);
|
||||
}
|
||||
|
||||
if pp_70p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 70 / 100 {
|
||||
pp_70p.replace(price);
|
||||
}
|
||||
|
||||
if pp_65p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 65 / 100 {
|
||||
pp_65p.replace(price);
|
||||
}
|
||||
|
||||
if pp_60p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 60 / 100 {
|
||||
pp_60p.replace(price);
|
||||
}
|
||||
|
||||
if pp_55p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 55 / 100 {
|
||||
pp_55p.replace(price);
|
||||
}
|
||||
|
||||
if pp_median.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply / 2 {
|
||||
pp_median.replace(price);
|
||||
}
|
||||
|
||||
if pp_45p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 45 / 100 {
|
||||
pp_45p.replace(price);
|
||||
}
|
||||
|
||||
if pp_40p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 40 / 100 {
|
||||
pp_40p.replace(price);
|
||||
}
|
||||
|
||||
if pp_35p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 35 / 100 {
|
||||
pp_35p.replace(price);
|
||||
}
|
||||
|
||||
if pp_30p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 30 / 100 {
|
||||
pp_30p.replace(price);
|
||||
}
|
||||
|
||||
if pp_25p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply / 4 {
|
||||
pp_25p.replace(price);
|
||||
}
|
||||
|
||||
if pp_20p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply / 5 {
|
||||
pp_20p.replace(price);
|
||||
}
|
||||
|
||||
if pp_15p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply * 15 / 100 {
|
||||
pp_15p.replace(price);
|
||||
}
|
||||
|
||||
if pp_10p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply / 10 {
|
||||
pp_10p.replace(price);
|
||||
}
|
||||
|
||||
if pp_05p.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
if processed_sat_amount >= total_sat_supply / 20 {
|
||||
pp_05p.replace(price);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
parser/src/states/cohorts_states/any/price_to_value.rs
Normal file
123
parser/src/states/cohorts_states/any/price_to_value.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::Debug,
|
||||
ops::{AddAssign, SubAssign},
|
||||
};
|
||||
|
||||
use allocative::Allocative;
|
||||
use color_eyre::eyre::eyre;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::structs::{Price, SplitByLiquidity, WAmount};
|
||||
|
||||
#[derive(Deref, DerefMut, Default, Debug, Allocative)]
|
||||
pub struct PriceToValue<T>(BTreeMap<u32, T>);
|
||||
|
||||
impl<T> PriceToValue<T>
|
||||
where
|
||||
T: Default
|
||||
+ Debug
|
||||
+ AddAssign
|
||||
+ SubAssign
|
||||
+ CanSubtract
|
||||
+ Default
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ PartialEq
|
||||
+ IsZero,
|
||||
{
|
||||
pub fn increment(&mut self, price: Price, value: T) {
|
||||
*self.entry(price.to_cent() as u32).or_default() += value;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, price: Price, value: T) -> color_eyre::Result<()> {
|
||||
let cent = price.to_cent() as u32;
|
||||
|
||||
let delete = {
|
||||
let self_value = self.get_mut(¢);
|
||||
|
||||
if self_value.is_none() {
|
||||
dbg!(&self.0, price, value);
|
||||
return Err(eyre!("self_value is none"));
|
||||
}
|
||||
|
||||
let self_value = self_value.unwrap();
|
||||
|
||||
if !self_value.can_subtract(&value) {
|
||||
dbg!(*self_value, &self.0, price, value);
|
||||
return Err(eyre!("self value < value"));
|
||||
}
|
||||
|
||||
*self_value -= value;
|
||||
|
||||
self_value.is_zero()?
|
||||
};
|
||||
|
||||
if delete {
|
||||
self.remove(¢).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn iterate(&self, supply: T, mut iterate: impl FnMut(Price, T)) {
|
||||
let mut processed = T::default();
|
||||
|
||||
self.iter().for_each(|(cent, value)| {
|
||||
let value = *value;
|
||||
|
||||
processed += value;
|
||||
|
||||
iterate(Price::from_cent(*cent as u64), value)
|
||||
});
|
||||
|
||||
if processed != supply {
|
||||
dbg!(processed, supply);
|
||||
panic!("processed_amount isn't equal to supply")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CanSubtract {
|
||||
fn can_subtract(&self, other: &Self) -> bool;
|
||||
}
|
||||
|
||||
impl CanSubtract for WAmount {
|
||||
fn can_subtract(&self, other: &Self) -> bool {
|
||||
self >= other
|
||||
}
|
||||
}
|
||||
|
||||
impl CanSubtract for SplitByLiquidity<WAmount> {
|
||||
fn can_subtract(&self, other: &Self) -> bool {
|
||||
self.all >= other.all
|
||||
&& self.illiquid >= other.illiquid
|
||||
&& self.liquid >= other.liquid
|
||||
&& self.highly_liquid >= other.highly_liquid
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IsZero {
|
||||
fn is_zero(&self) -> color_eyre::Result<bool>;
|
||||
}
|
||||
|
||||
impl IsZero for WAmount {
|
||||
fn is_zero(&self) -> color_eyre::Result<bool> {
|
||||
Ok(*self == WAmount::ZERO)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsZero for SplitByLiquidity<WAmount> {
|
||||
fn is_zero(&self) -> color_eyre::Result<bool> {
|
||||
if self.all == WAmount::ZERO
|
||||
&& (self.illiquid != WAmount::ZERO
|
||||
|| self.liquid != WAmount::ZERO
|
||||
|| self.highly_liquid != WAmount::ZERO)
|
||||
{
|
||||
dbg!(&self);
|
||||
Err(eyre!("Bad split"))
|
||||
} else {
|
||||
Ok(self.all == WAmount::ZERO)
|
||||
}
|
||||
}
|
||||
}
|
||||
14
parser/src/states/cohorts_states/any/realized_state.rs
Normal file
14
parser/src/states/cohorts_states/any/realized_state.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::structs::Price;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RealizedState {
|
||||
pub realized_profit: Price,
|
||||
pub realized_loss: Price,
|
||||
}
|
||||
|
||||
impl RealizedState {
|
||||
pub fn iterate(&mut self, realized_profit: Price, realized_loss: Price) {
|
||||
self.realized_profit += realized_profit;
|
||||
self.realized_loss += realized_loss;
|
||||
}
|
||||
}
|
||||
27
parser/src/states/cohorts_states/any/supply_state.rs
Normal file
27
parser/src/states/cohorts_states/any/supply_state.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use allocative::Allocative;
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
use crate::structs::WAmount;
|
||||
|
||||
#[derive(Debug, Default, Allocative)]
|
||||
pub struct SupplyState {
|
||||
pub supply: WAmount,
|
||||
}
|
||||
|
||||
impl SupplyState {
|
||||
pub fn increment(&mut self, amount: WAmount) {
|
||||
self.supply += amount;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, amount: WAmount) -> color_eyre::Result<()> {
|
||||
if self.supply < amount {
|
||||
dbg!(self.supply, amount);
|
||||
|
||||
return Err(eyre!("supply smaller than supply"));
|
||||
}
|
||||
|
||||
self.supply -= amount;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
38
parser/src/states/cohorts_states/any/unrealized_state.rs
Normal file
38
parser/src/states/cohorts_states/any/unrealized_state.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::{cmp::Ordering, ops::Add};
|
||||
|
||||
use crate::structs::{Price, WAmount};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct UnrealizedState {
|
||||
pub supply_in_profit: WAmount,
|
||||
pub unrealized_profit: Price,
|
||||
pub unrealized_loss: Price,
|
||||
}
|
||||
|
||||
impl UnrealizedState {
|
||||
#[inline]
|
||||
pub fn iterate(&mut self, price_then: Price, price_now: Price, amount: WAmount) {
|
||||
match price_then.cmp(&price_now) {
|
||||
Ordering::Less => {
|
||||
self.unrealized_profit += (price_now - price_then) * amount;
|
||||
self.supply_in_profit += amount;
|
||||
}
|
||||
Ordering::Greater => {
|
||||
self.unrealized_loss += (price_then - price_now) * amount;
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<UnrealizedState> for UnrealizedState {
|
||||
type Output = UnrealizedState;
|
||||
|
||||
fn add(self, other: UnrealizedState) -> UnrealizedState {
|
||||
UnrealizedState {
|
||||
supply_in_profit: self.supply_in_profit + other.supply_in_profit,
|
||||
unrealized_profit: self.unrealized_profit + other.unrealized_profit,
|
||||
unrealized_loss: self.unrealized_loss + other.unrealized_loss,
|
||||
}
|
||||
}
|
||||
}
|
||||
25
parser/src/states/cohorts_states/any/utxo_state.rs
Normal file
25
parser/src/states/cohorts_states/any/utxo_state.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use allocative::Allocative;
|
||||
use color_eyre::eyre::eyre;
|
||||
|
||||
#[derive(Debug, Default, Allocative)]
|
||||
pub struct UTXOState {
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
impl UTXOState {
|
||||
pub fn increment(&mut self, utxo_count: usize) {
|
||||
self.count += utxo_count;
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self, utxo_count: usize) -> color_eyre::Result<()> {
|
||||
if self.count < utxo_count {
|
||||
dbg!(self.count, utxo_count);
|
||||
|
||||
return Err(eyre!("self.count smaller than utxo_count"));
|
||||
}
|
||||
|
||||
self.count -= utxo_count;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
7
parser/src/states/cohorts_states/mod.rs
Normal file
7
parser/src/states/cohorts_states/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod address;
|
||||
mod any;
|
||||
mod utxo;
|
||||
|
||||
pub use address::*;
|
||||
pub use any::*;
|
||||
pub use utxo::*;
|
||||
107
parser/src/states/cohorts_states/utxo/cohort_durable_states.rs
Normal file
107
parser/src/states/cohorts_states/utxo/cohort_durable_states.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use allocative::Allocative;
|
||||
|
||||
use crate::{
|
||||
states::{DurableStates, OneShotStates, PriceToValue, UnrealizedState},
|
||||
structs::{Price, WAmount},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Allocative)]
|
||||
pub struct UTXOCohortDurableStates {
|
||||
pub durable_states: DurableStates,
|
||||
pub price_to_amount: PriceToValue<WAmount>,
|
||||
}
|
||||
|
||||
impl UTXOCohortDurableStates {
|
||||
pub fn increment(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
price: Price,
|
||||
) -> color_eyre::Result<()> {
|
||||
self._crement(amount, utxo_count, price, true)
|
||||
}
|
||||
|
||||
pub fn decrement(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
price: Price,
|
||||
) -> color_eyre::Result<()> {
|
||||
self._crement(amount, utxo_count, price, false)
|
||||
}
|
||||
|
||||
pub fn _crement(
|
||||
&mut self,
|
||||
amount: WAmount,
|
||||
utxo_count: usize,
|
||||
price: Price,
|
||||
increment: bool,
|
||||
) -> color_eyre::Result<()> {
|
||||
let realized_cap = price * amount;
|
||||
|
||||
if increment {
|
||||
self.durable_states
|
||||
.increment(amount, utxo_count, realized_cap)
|
||||
} else {
|
||||
self.durable_states
|
||||
.decrement(amount, utxo_count, realized_cap)
|
||||
}
|
||||
.inspect_err(|report| {
|
||||
dbg!(report, "split all failed", amount, utxo_count);
|
||||
})?;
|
||||
|
||||
let rounded_price = price.to_significant();
|
||||
|
||||
if increment {
|
||||
self.price_to_amount.increment(rounded_price, amount);
|
||||
} else {
|
||||
self.price_to_amount
|
||||
.decrement(rounded_price, amount)
|
||||
.inspect_err(|report| {
|
||||
dbg!(
|
||||
report,
|
||||
"cents_to_amount decrement failed",
|
||||
amount,
|
||||
utxo_count
|
||||
);
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_one_shot_states(
|
||||
&self,
|
||||
block_price: Price,
|
||||
date_price: Option<Price>,
|
||||
) -> OneShotStates {
|
||||
let mut one_shot_states = OneShotStates::default();
|
||||
|
||||
if date_price.is_some() {
|
||||
one_shot_states
|
||||
.unrealized_date_state
|
||||
.replace(UnrealizedState::default());
|
||||
}
|
||||
|
||||
let supply = self.durable_states.supply_state.supply;
|
||||
|
||||
let one_shot_states_ref = &mut one_shot_states;
|
||||
|
||||
self.price_to_amount.iterate(supply, |price_paid, amount| {
|
||||
one_shot_states_ref
|
||||
.price_paid_state
|
||||
.iterate(price_paid, amount, supply);
|
||||
|
||||
one_shot_states_ref
|
||||
.unrealized_block_state
|
||||
.iterate(price_paid, block_price, amount);
|
||||
|
||||
if let Some(unrealized_date_state) = one_shot_states_ref.unrealized_date_state.as_mut()
|
||||
{
|
||||
unrealized_date_state.iterate(price_paid, date_price.unwrap(), amount);
|
||||
}
|
||||
});
|
||||
|
||||
one_shot_states
|
||||
}
|
||||
}
|
||||
32
parser/src/states/cohorts_states/utxo/cohort_filter.rs
Normal file
32
parser/src/states/cohorts_states/utxo/cohort_filter.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
pub enum UTXOFilter {
|
||||
To(u32),
|
||||
FromTo { from: u32, to: u32 },
|
||||
From(u32),
|
||||
Year(u32),
|
||||
}
|
||||
|
||||
impl UTXOCheck for UTXOFilter {
|
||||
fn check(&self, days_old: &u32, year: &u32) -> bool {
|
||||
match self {
|
||||
UTXOFilter::From(from) => from <= days_old,
|
||||
UTXOFilter::To(to) => to > days_old,
|
||||
UTXOFilter::FromTo { from, to } => from <= days_old && to > days_old,
|
||||
UTXOFilter::Year(_year) => _year == year,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_days_old(&self, days_old: &u32) -> bool {
|
||||
match self {
|
||||
UTXOFilter::From(from) => from <= days_old,
|
||||
UTXOFilter::To(to) => to > days_old,
|
||||
UTXOFilter::FromTo { from, to } => from <= days_old && to > days_old,
|
||||
UTXOFilter::Year(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UTXOCheck {
|
||||
fn check(&self, days_old: &u32, year: &u32) -> bool;
|
||||
|
||||
fn check_days_old(&self, days_old: &u32) -> bool;
|
||||
}
|
||||
84
parser/src/states/cohorts_states/utxo/cohort_filters.rs
Normal file
84
parser/src/states/cohorts_states/utxo/cohort_filters.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use super::{SplitByUTXOCohort, UTXOFilter};
|
||||
|
||||
pub const UTXO_FILTERS: SplitByUTXOCohort<UTXOFilter> = SplitByUTXOCohort {
|
||||
up_to_1d: UTXOFilter::To(1),
|
||||
up_to_1w: UTXOFilter::To(7),
|
||||
up_to_1m: UTXOFilter::To(30),
|
||||
up_to_2m: UTXOFilter::To(2 * 30),
|
||||
up_to_3m: UTXOFilter::To(3 * 30),
|
||||
up_to_4m: UTXOFilter::To(4 * 30),
|
||||
up_to_5m: UTXOFilter::To(5 * 30),
|
||||
up_to_6m: UTXOFilter::To(6 * 30),
|
||||
up_to_1y: UTXOFilter::To(365),
|
||||
up_to_2y: UTXOFilter::To(2 * 365),
|
||||
up_to_3y: UTXOFilter::To(3 * 365),
|
||||
up_to_5y: UTXOFilter::To(5 * 365),
|
||||
up_to_7y: UTXOFilter::To(7 * 365),
|
||||
up_to_10y: UTXOFilter::To(10 * 365),
|
||||
up_to_15y: UTXOFilter::To(15 * 365),
|
||||
|
||||
from_1d_to_1w: UTXOFilter::FromTo { from: 1, to: 7 },
|
||||
from_1w_to_1m: UTXOFilter::FromTo { from: 7, to: 30 },
|
||||
from_1m_to_3m: UTXOFilter::FromTo {
|
||||
from: 30,
|
||||
to: 3 * 30,
|
||||
},
|
||||
from_3m_to_6m: UTXOFilter::FromTo {
|
||||
from: 3 * 30,
|
||||
to: 6 * 30,
|
||||
},
|
||||
from_6m_to_1y: UTXOFilter::FromTo {
|
||||
from: 6 * 30,
|
||||
to: 365,
|
||||
},
|
||||
from_1y_to_2y: UTXOFilter::FromTo {
|
||||
from: 365,
|
||||
to: 2 * 365,
|
||||
},
|
||||
from_2y_to_3y: UTXOFilter::FromTo {
|
||||
from: 2 * 365,
|
||||
to: 3 * 365,
|
||||
},
|
||||
from_3y_to_5y: UTXOFilter::FromTo {
|
||||
from: 3 * 365,
|
||||
to: 5 * 365,
|
||||
},
|
||||
from_5y_to_7y: UTXOFilter::FromTo {
|
||||
from: 5 * 365,
|
||||
to: 7 * 365,
|
||||
},
|
||||
from_7y_to_10y: UTXOFilter::FromTo {
|
||||
from: 7 * 365,
|
||||
to: 10 * 365,
|
||||
},
|
||||
from_10y_to_15y: UTXOFilter::FromTo {
|
||||
from: 10 * 365,
|
||||
to: 15 * 365,
|
||||
},
|
||||
|
||||
from_1y: UTXOFilter::From(365),
|
||||
from_2y: UTXOFilter::From(2 * 365),
|
||||
from_4y: UTXOFilter::From(4 * 365),
|
||||
from_10y: UTXOFilter::From(10 * 365),
|
||||
from_15y: UTXOFilter::From(15 * 365),
|
||||
|
||||
year_2009: UTXOFilter::Year(2009),
|
||||
year_2010: UTXOFilter::Year(2010),
|
||||
year_2011: UTXOFilter::Year(2011),
|
||||
year_2012: UTXOFilter::Year(2012),
|
||||
year_2013: UTXOFilter::Year(2013),
|
||||
year_2014: UTXOFilter::Year(2014),
|
||||
year_2015: UTXOFilter::Year(2015),
|
||||
year_2016: UTXOFilter::Year(2016),
|
||||
year_2017: UTXOFilter::Year(2017),
|
||||
year_2018: UTXOFilter::Year(2018),
|
||||
year_2019: UTXOFilter::Year(2019),
|
||||
year_2020: UTXOFilter::Year(2020),
|
||||
year_2021: UTXOFilter::Year(2021),
|
||||
year_2022: UTXOFilter::Year(2022),
|
||||
year_2023: UTXOFilter::Year(2023),
|
||||
year_2024: UTXOFilter::Year(2024),
|
||||
|
||||
sth: UTXOFilter::To(155),
|
||||
lth: UTXOFilter::From(155),
|
||||
};
|
||||
119
parser/src/states/cohorts_states/utxo/cohort_id.rs
Normal file
119
parser/src/states/cohorts_states/utxo/cohort_id.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use allocative::Allocative;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Allocative)]
|
||||
pub enum UTXOCohortId {
|
||||
#[default]
|
||||
UpTo1d,
|
||||
UpTo1w,
|
||||
UpTo1m,
|
||||
UpTo2m,
|
||||
UpTo3m,
|
||||
UpTo4m,
|
||||
UpTo5m,
|
||||
UpTo6m,
|
||||
UpTo1y,
|
||||
UpTo2y,
|
||||
UpTo3y,
|
||||
UpTo5y,
|
||||
UpTo7y,
|
||||
UpTo10y,
|
||||
UpTo15y,
|
||||
|
||||
From1dTo1w,
|
||||
From1wTo1m,
|
||||
From1mTo3m,
|
||||
From3mTo6m,
|
||||
From6mTo1y,
|
||||
From1yTo2y,
|
||||
From2yTo3y,
|
||||
From3yTo5y,
|
||||
From5yTo7y,
|
||||
From7yTo10y,
|
||||
From10yTo15y,
|
||||
|
||||
From1y,
|
||||
From2y,
|
||||
From4y,
|
||||
From10y,
|
||||
From15y,
|
||||
|
||||
Year2009,
|
||||
Year2010,
|
||||
Year2011,
|
||||
Year2012,
|
||||
Year2013,
|
||||
Year2014,
|
||||
Year2015,
|
||||
Year2016,
|
||||
Year2017,
|
||||
Year2018,
|
||||
Year2019,
|
||||
Year2020,
|
||||
Year2021,
|
||||
Year2022,
|
||||
Year2023,
|
||||
Year2024,
|
||||
|
||||
ShortTermHolders,
|
||||
LongTermHolders,
|
||||
}
|
||||
|
||||
impl UTXOCohortId {
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::UpTo1d => "up_to_1d",
|
||||
Self::UpTo1w => "up_to_1w",
|
||||
Self::UpTo1m => "up_to_1m",
|
||||
Self::UpTo2m => "up_to_2m",
|
||||
Self::UpTo3m => "up_to_3m",
|
||||
Self::UpTo4m => "up_to_4m",
|
||||
Self::UpTo5m => "up_to_5m",
|
||||
Self::UpTo6m => "up_to_6m",
|
||||
Self::UpTo1y => "up_to_1y",
|
||||
Self::UpTo2y => "up_to_2y",
|
||||
Self::UpTo3y => "up_to_3y",
|
||||
Self::UpTo5y => "up_to_5y",
|
||||
Self::UpTo7y => "up_to_7y",
|
||||
Self::UpTo10y => "up_to_10y",
|
||||
Self::UpTo15y => "up_to_15y",
|
||||
|
||||
Self::From1dTo1w => "from_1d_to_1w",
|
||||
Self::From1wTo1m => "from_1w_to_1m",
|
||||
Self::From1mTo3m => "from_1m_to_3m",
|
||||
Self::From3mTo6m => "from_3m_to_6m",
|
||||
Self::From6mTo1y => "from_6m_to_1y",
|
||||
Self::From1yTo2y => "from_1y_to_2y",
|
||||
Self::From2yTo3y => "from_2y_to_3y",
|
||||
Self::From3yTo5y => "from_3y_to_5y",
|
||||
Self::From5yTo7y => "from_5y_to_7y",
|
||||
Self::From7yTo10y => "from_7y_to_10y",
|
||||
Self::From10yTo15y => "from_10y_to_15y",
|
||||
|
||||
Self::From1y => "from_1y",
|
||||
Self::From2y => "from_2y",
|
||||
Self::From4y => "from_4y",
|
||||
Self::From10y => "from_10y",
|
||||
Self::From15y => "from_15y",
|
||||
|
||||
Self::Year2009 => "year_2009",
|
||||
Self::Year2010 => "year_2010",
|
||||
Self::Year2011 => "year_2011",
|
||||
Self::Year2012 => "year_2012",
|
||||
Self::Year2013 => "year_2013",
|
||||
Self::Year2014 => "year_2014",
|
||||
Self::Year2015 => "year_2015",
|
||||
Self::Year2016 => "year_2016",
|
||||
Self::Year2017 => "year_2017",
|
||||
Self::Year2018 => "year_2018",
|
||||
Self::Year2019 => "year_2019",
|
||||
Self::Year2020 => "year_2020",
|
||||
Self::Year2021 => "year_2021",
|
||||
Self::Year2022 => "year_2022",
|
||||
Self::Year2023 => "year_2023",
|
||||
Self::Year2024 => "year_2024",
|
||||
|
||||
Self::ShortTermHolders => "sth",
|
||||
Self::LongTermHolders => "lth",
|
||||
}
|
||||
}
|
||||
}
|
||||
154
parser/src/states/cohorts_states/utxo/cohorts_durable_states.rs
Normal file
154
parser/src/states/cohorts_states/utxo/cohorts_durable_states.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use allocative::Allocative;
|
||||
use chrono::Datelike;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
|
||||
use crate::{
|
||||
states::DateDataVec,
|
||||
structs::{BlockData, Price, SentData, WAmount},
|
||||
utils::difference_in_days_between_timestamps,
|
||||
WNaiveDate,
|
||||
};
|
||||
|
||||
use super::{SplitByUTXOCohort, UTXOCohortDurableStates, UTXOCohortsOneShotStates};
|
||||
|
||||
#[derive(Default, Deref, DerefMut, Allocative)]
|
||||
pub struct UTXOCohortsDurableStates(SplitByUTXOCohort<UTXOCohortDurableStates>);
|
||||
|
||||
impl UTXOCohortsDurableStates {
|
||||
pub fn init(date_data_vec: &DateDataVec) -> Self {
|
||||
let mut s = Self::default();
|
||||
|
||||
if let Some(last_date_data) = date_data_vec.last() {
|
||||
let last_block_data = last_date_data.blocks.last().unwrap();
|
||||
|
||||
date_data_vec.iter().for_each(|date_data| {
|
||||
let year = date_data.date.year() as u32;
|
||||
|
||||
date_data.blocks.iter().for_each(|block_data| {
|
||||
let amount = block_data.amount;
|
||||
let utxo_count = block_data.utxos as usize;
|
||||
|
||||
// No need to either insert or remove if 0
|
||||
if amount == WAmount::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
let increment_days_old = difference_in_days_between_timestamps(
|
||||
block_data.timestamp,
|
||||
last_block_data.timestamp,
|
||||
);
|
||||
|
||||
s.initial_filtered_apply(&increment_days_old, &year, |state| {
|
||||
state
|
||||
.increment(amount, utxo_count, block_data.price)
|
||||
.unwrap();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
pub fn udpate_age_if_needed(
|
||||
&mut self,
|
||||
block_data: &BlockData,
|
||||
last_block_data: &BlockData,
|
||||
previous_last_block_data: Option<&BlockData>,
|
||||
) {
|
||||
let amount = block_data.amount;
|
||||
let utxo_count = block_data.utxos as usize;
|
||||
let price = block_data.price;
|
||||
|
||||
// No need to either insert or remove if 0
|
||||
if amount == WAmount::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
if block_data.height == last_block_data.height {
|
||||
let year = WNaiveDate::from_timestamp(block_data.timestamp).year() as u32;
|
||||
|
||||
self.initial_filtered_apply(&0, &year, |state| {
|
||||
state.increment(amount, utxo_count, price).unwrap();
|
||||
})
|
||||
} else {
|
||||
let increment_days_old = difference_in_days_between_timestamps(
|
||||
block_data.timestamp,
|
||||
last_block_data.timestamp,
|
||||
);
|
||||
|
||||
let decrement_days_old = difference_in_days_between_timestamps(
|
||||
block_data.timestamp,
|
||||
previous_last_block_data
|
||||
.unwrap_or_else(|| {
|
||||
dbg!(block_data, last_block_data, previous_last_block_data);
|
||||
panic!()
|
||||
})
|
||||
.timestamp,
|
||||
);
|
||||
|
||||
if increment_days_old == decrement_days_old {
|
||||
return;
|
||||
}
|
||||
|
||||
self.duo_filtered_apply(
|
||||
&increment_days_old,
|
||||
&decrement_days_old,
|
||||
|state| {
|
||||
state.increment(amount, utxo_count, price).unwrap();
|
||||
},
|
||||
|state| {
|
||||
state.decrement(amount, utxo_count, price).unwrap();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subtract_moved(
|
||||
&mut self,
|
||||
block_data: &BlockData,
|
||||
sent_data: &SentData,
|
||||
previous_last_block_data: &BlockData,
|
||||
) {
|
||||
let amount = sent_data.volume;
|
||||
let utxo_count = sent_data.count as usize;
|
||||
|
||||
// No need to either insert or remove if 0
|
||||
if amount == WAmount::ZERO {
|
||||
return;
|
||||
}
|
||||
|
||||
let days_old = difference_in_days_between_timestamps(
|
||||
block_data.timestamp,
|
||||
previous_last_block_data.timestamp,
|
||||
);
|
||||
|
||||
let year = WNaiveDate::from_timestamp(block_data.timestamp).year() as u32;
|
||||
|
||||
self.initial_filtered_apply(&days_old, &year, |state| {
|
||||
state
|
||||
.decrement(amount, utxo_count, block_data.price)
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compute_one_shot_states(
|
||||
&mut self,
|
||||
block_price: Price,
|
||||
date_price: Option<Price>,
|
||||
) -> UTXOCohortsOneShotStates {
|
||||
let mut one_shot_states = UTXOCohortsOneShotStates::default();
|
||||
|
||||
self.as_vec()
|
||||
.into_par_iter()
|
||||
.map(|(states, id)| (states.compute_one_shot_states(block_price, date_price), id))
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.for_each(|(states, id)| {
|
||||
*one_shot_states.get_mut(&id) = states;
|
||||
});
|
||||
|
||||
one_shot_states
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::states::OneShotStates;
|
||||
|
||||
use super::SplitByUTXOCohort;
|
||||
|
||||
#[derive(Deref, DerefMut, Default)]
|
||||
pub struct UTXOCohortsOneShotStates(pub SplitByUTXOCohort<OneShotStates>);
|
||||
68
parser/src/states/cohorts_states/utxo/cohorts_sent_states.rs
Normal file
68
parser/src/states/cohorts_states/utxo/cohorts_sent_states.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::{cmp::Ordering, collections::BTreeMap};
|
||||
|
||||
use chrono::Datelike;
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
states::{DateDataVec, InputState, RealizedState},
|
||||
structs::{BlockPath, Price, SentData},
|
||||
utils::difference_in_days_between_timestamps,
|
||||
};
|
||||
|
||||
use super::SplitByUTXOCohort;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct SentState {
|
||||
pub input: InputState,
|
||||
pub realized: RealizedState,
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Default)]
|
||||
pub struct UTXOCohortsSentStates(SplitByUTXOCohort<SentState>);
|
||||
|
||||
impl UTXOCohortsSentStates {
|
||||
pub fn compute(
|
||||
&mut self,
|
||||
date_data_vec: &DateDataVec,
|
||||
block_path_to_sent_data: &BTreeMap<BlockPath, SentData>,
|
||||
current_price: Price,
|
||||
) {
|
||||
if let Some(last_block_data) = date_data_vec.last_block() {
|
||||
block_path_to_sent_data
|
||||
.iter()
|
||||
.for_each(|(block_path, sent_data)| {
|
||||
let date_data = date_data_vec.get_date_data(block_path).unwrap();
|
||||
|
||||
let year = date_data.date.year() as u32;
|
||||
|
||||
let block_data = date_data.get_block_data(block_path).unwrap();
|
||||
|
||||
let days_old = difference_in_days_between_timestamps(
|
||||
block_data.timestamp,
|
||||
last_block_data.timestamp,
|
||||
);
|
||||
|
||||
let previous_price = block_data.price;
|
||||
|
||||
let amount_sent = sent_data.volume;
|
||||
|
||||
self.initial_filtered_apply(&days_old, &year, |state| {
|
||||
state.input.iterate(sent_data.count as f64, amount_sent);
|
||||
|
||||
let previous_value = previous_price * amount_sent;
|
||||
let current_value = current_price * amount_sent;
|
||||
|
||||
match previous_value.cmp(¤t_value) {
|
||||
Ordering::Less => {
|
||||
state.realized.realized_profit += current_value - previous_value;
|
||||
}
|
||||
Ordering::Greater => {
|
||||
state.realized.realized_loss += previous_value - current_value;
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
17
parser/src/states/cohorts_states/utxo/mod.rs
Normal file
17
parser/src/states/cohorts_states/utxo/mod.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
mod cohort_durable_states;
|
||||
mod cohort_filter;
|
||||
mod cohort_filters;
|
||||
mod cohort_id;
|
||||
mod cohorts_durable_states;
|
||||
mod cohorts_one_shot_states;
|
||||
mod cohorts_sent_states;
|
||||
mod split_by_utxo_cohort;
|
||||
|
||||
pub use cohort_durable_states::*;
|
||||
pub use cohort_filter::*;
|
||||
pub use cohort_filters::*;
|
||||
pub use cohort_id::*;
|
||||
pub use cohorts_durable_states::*;
|
||||
pub use cohorts_one_shot_states::*;
|
||||
pub use cohorts_sent_states::*;
|
||||
pub use split_by_utxo_cohort::*;
|
||||
740
parser/src/states/cohorts_states/utxo/split_by_utxo_cohort.rs
Normal file
740
parser/src/states/cohorts_states/utxo/split_by_utxo_cohort.rs
Normal file
@@ -0,0 +1,740 @@
|
||||
use allocative::Allocative;
|
||||
|
||||
use super::{UTXOCheck, UTXOCohortId, UTXO_FILTERS};
|
||||
|
||||
#[derive(Default, Allocative)]
|
||||
pub struct SplitByUTXOCohort<T> {
|
||||
pub sth: T,
|
||||
pub lth: T,
|
||||
|
||||
pub up_to_1d: T,
|
||||
pub up_to_1w: T,
|
||||
pub up_to_1m: T,
|
||||
pub up_to_2m: T,
|
||||
pub up_to_3m: T,
|
||||
pub up_to_4m: T,
|
||||
pub up_to_5m: T,
|
||||
pub up_to_6m: T,
|
||||
pub up_to_1y: T,
|
||||
pub up_to_2y: T,
|
||||
pub up_to_3y: T,
|
||||
pub up_to_5y: T,
|
||||
pub up_to_7y: T,
|
||||
pub up_to_10y: T,
|
||||
pub up_to_15y: T,
|
||||
|
||||
pub from_1d_to_1w: T,
|
||||
pub from_1w_to_1m: T,
|
||||
pub from_1m_to_3m: T,
|
||||
pub from_3m_to_6m: T,
|
||||
pub from_6m_to_1y: T,
|
||||
pub from_1y_to_2y: T,
|
||||
pub from_2y_to_3y: T,
|
||||
pub from_3y_to_5y: T,
|
||||
pub from_5y_to_7y: T,
|
||||
pub from_7y_to_10y: T,
|
||||
pub from_10y_to_15y: T,
|
||||
|
||||
pub from_1y: T,
|
||||
pub from_2y: T,
|
||||
pub from_4y: T,
|
||||
pub from_10y: T,
|
||||
pub from_15y: T,
|
||||
|
||||
pub year_2009: T,
|
||||
pub year_2010: T,
|
||||
pub year_2011: T,
|
||||
pub year_2012: T,
|
||||
pub year_2013: T,
|
||||
pub year_2014: T,
|
||||
pub year_2015: T,
|
||||
pub year_2016: T,
|
||||
pub year_2017: T,
|
||||
pub year_2018: T,
|
||||
pub year_2019: T,
|
||||
pub year_2020: T,
|
||||
pub year_2021: T,
|
||||
pub year_2022: T,
|
||||
pub year_2023: T,
|
||||
pub year_2024: T,
|
||||
}
|
||||
|
||||
impl<T> SplitByUTXOCohort<T> {
|
||||
pub fn get(&self, id: &UTXOCohortId) -> &T {
|
||||
match id {
|
||||
UTXOCohortId::UpTo1d => &self.up_to_1d,
|
||||
UTXOCohortId::UpTo1w => &self.up_to_1w,
|
||||
UTXOCohortId::UpTo1m => &self.up_to_1m,
|
||||
UTXOCohortId::UpTo2m => &self.up_to_2m,
|
||||
UTXOCohortId::UpTo3m => &self.up_to_3m,
|
||||
UTXOCohortId::UpTo4m => &self.up_to_4m,
|
||||
UTXOCohortId::UpTo5m => &self.up_to_5m,
|
||||
UTXOCohortId::UpTo6m => &self.up_to_6m,
|
||||
UTXOCohortId::UpTo1y => &self.up_to_1y,
|
||||
UTXOCohortId::UpTo2y => &self.up_to_2y,
|
||||
UTXOCohortId::UpTo3y => &self.up_to_3y,
|
||||
UTXOCohortId::UpTo5y => &self.up_to_5y,
|
||||
UTXOCohortId::UpTo7y => &self.up_to_7y,
|
||||
UTXOCohortId::UpTo10y => &self.up_to_10y,
|
||||
UTXOCohortId::UpTo15y => &self.up_to_15y,
|
||||
UTXOCohortId::From1dTo1w => &self.from_1d_to_1w,
|
||||
UTXOCohortId::From1wTo1m => &self.from_1w_to_1m,
|
||||
UTXOCohortId::From1mTo3m => &self.from_1m_to_3m,
|
||||
UTXOCohortId::From3mTo6m => &self.from_3m_to_6m,
|
||||
UTXOCohortId::From6mTo1y => &self.from_6m_to_1y,
|
||||
UTXOCohortId::From1yTo2y => &self.from_1y_to_2y,
|
||||
UTXOCohortId::From2yTo3y => &self.from_2y_to_3y,
|
||||
UTXOCohortId::From3yTo5y => &self.from_3y_to_5y,
|
||||
UTXOCohortId::From5yTo7y => &self.from_5y_to_7y,
|
||||
UTXOCohortId::From7yTo10y => &self.from_7y_to_10y,
|
||||
UTXOCohortId::From10yTo15y => &self.from_10y_to_15y,
|
||||
UTXOCohortId::From1y => &self.from_1y,
|
||||
UTXOCohortId::From2y => &self.from_2y,
|
||||
UTXOCohortId::From4y => &self.from_4y,
|
||||
UTXOCohortId::From10y => &self.from_10y,
|
||||
UTXOCohortId::From15y => &self.from_15y,
|
||||
UTXOCohortId::Year2009 => &self.year_2009,
|
||||
UTXOCohortId::Year2010 => &self.year_2010,
|
||||
UTXOCohortId::Year2011 => &self.year_2011,
|
||||
UTXOCohortId::Year2012 => &self.year_2012,
|
||||
UTXOCohortId::Year2013 => &self.year_2013,
|
||||
UTXOCohortId::Year2014 => &self.year_2014,
|
||||
UTXOCohortId::Year2015 => &self.year_2015,
|
||||
UTXOCohortId::Year2016 => &self.year_2016,
|
||||
UTXOCohortId::Year2017 => &self.year_2017,
|
||||
UTXOCohortId::Year2018 => &self.year_2018,
|
||||
UTXOCohortId::Year2019 => &self.year_2019,
|
||||
UTXOCohortId::Year2020 => &self.year_2020,
|
||||
UTXOCohortId::Year2021 => &self.year_2021,
|
||||
UTXOCohortId::Year2022 => &self.year_2022,
|
||||
UTXOCohortId::Year2023 => &self.year_2023,
|
||||
UTXOCohortId::Year2024 => &self.year_2024,
|
||||
UTXOCohortId::ShortTermHolders => &self.sth,
|
||||
UTXOCohortId::LongTermHolders => &self.lth,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: &UTXOCohortId) -> &mut T {
|
||||
match id {
|
||||
UTXOCohortId::UpTo1d => &mut self.up_to_1d,
|
||||
UTXOCohortId::UpTo1w => &mut self.up_to_1w,
|
||||
UTXOCohortId::UpTo1m => &mut self.up_to_1m,
|
||||
UTXOCohortId::UpTo2m => &mut self.up_to_2m,
|
||||
UTXOCohortId::UpTo3m => &mut self.up_to_3m,
|
||||
UTXOCohortId::UpTo4m => &mut self.up_to_4m,
|
||||
UTXOCohortId::UpTo5m => &mut self.up_to_5m,
|
||||
UTXOCohortId::UpTo6m => &mut self.up_to_6m,
|
||||
UTXOCohortId::UpTo1y => &mut self.up_to_1y,
|
||||
UTXOCohortId::UpTo2y => &mut self.up_to_2y,
|
||||
UTXOCohortId::UpTo3y => &mut self.up_to_3y,
|
||||
UTXOCohortId::UpTo5y => &mut self.up_to_5y,
|
||||
UTXOCohortId::UpTo7y => &mut self.up_to_7y,
|
||||
UTXOCohortId::UpTo10y => &mut self.up_to_10y,
|
||||
UTXOCohortId::UpTo15y => &mut self.up_to_15y,
|
||||
UTXOCohortId::From1dTo1w => &mut self.from_1d_to_1w,
|
||||
UTXOCohortId::From1wTo1m => &mut self.from_1w_to_1m,
|
||||
UTXOCohortId::From1mTo3m => &mut self.from_1m_to_3m,
|
||||
UTXOCohortId::From3mTo6m => &mut self.from_3m_to_6m,
|
||||
UTXOCohortId::From6mTo1y => &mut self.from_6m_to_1y,
|
||||
UTXOCohortId::From1yTo2y => &mut self.from_1y_to_2y,
|
||||
UTXOCohortId::From2yTo3y => &mut self.from_2y_to_3y,
|
||||
UTXOCohortId::From3yTo5y => &mut self.from_3y_to_5y,
|
||||
UTXOCohortId::From5yTo7y => &mut self.from_5y_to_7y,
|
||||
UTXOCohortId::From7yTo10y => &mut self.from_7y_to_10y,
|
||||
UTXOCohortId::From10yTo15y => &mut self.from_10y_to_15y,
|
||||
UTXOCohortId::From1y => &mut self.from_1y,
|
||||
UTXOCohortId::From2y => &mut self.from_2y,
|
||||
UTXOCohortId::From4y => &mut self.from_4y,
|
||||
UTXOCohortId::From10y => &mut self.from_10y,
|
||||
UTXOCohortId::From15y => &mut self.from_15y,
|
||||
UTXOCohortId::Year2009 => &mut self.year_2009,
|
||||
UTXOCohortId::Year2010 => &mut self.year_2010,
|
||||
UTXOCohortId::Year2011 => &mut self.year_2011,
|
||||
UTXOCohortId::Year2012 => &mut self.year_2012,
|
||||
UTXOCohortId::Year2013 => &mut self.year_2013,
|
||||
UTXOCohortId::Year2014 => &mut self.year_2014,
|
||||
UTXOCohortId::Year2015 => &mut self.year_2015,
|
||||
UTXOCohortId::Year2016 => &mut self.year_2016,
|
||||
UTXOCohortId::Year2017 => &mut self.year_2017,
|
||||
UTXOCohortId::Year2018 => &mut self.year_2018,
|
||||
UTXOCohortId::Year2019 => &mut self.year_2019,
|
||||
UTXOCohortId::Year2020 => &mut self.year_2020,
|
||||
UTXOCohortId::Year2021 => &mut self.year_2021,
|
||||
UTXOCohortId::Year2022 => &mut self.year_2022,
|
||||
UTXOCohortId::Year2023 => &mut self.year_2023,
|
||||
UTXOCohortId::Year2024 => &mut self.year_2024,
|
||||
UTXOCohortId::ShortTermHolders => &mut self.sth,
|
||||
UTXOCohortId::LongTermHolders => &mut self.lth,
|
||||
}
|
||||
}
|
||||
|
||||
/// Excluding years since they're static
|
||||
pub fn duo_filtered_apply(
|
||||
&mut self,
|
||||
current_days_old: &u32,
|
||||
previous_days_old: &u32,
|
||||
apply_if_current_only: impl Fn(&mut T),
|
||||
apply_if_previous_only: impl Fn(&mut T),
|
||||
) {
|
||||
let is_up_to_1d = UTXO_FILTERS.up_to_1d.check_days_old(current_days_old);
|
||||
let was_up_to_1d = UTXO_FILTERS.up_to_1d.check_days_old(previous_days_old);
|
||||
if is_up_to_1d && !was_up_to_1d {
|
||||
apply_if_current_only(&mut self.up_to_1d);
|
||||
} else if was_up_to_1d && !is_up_to_1d {
|
||||
apply_if_previous_only(&mut self.up_to_1d);
|
||||
}
|
||||
|
||||
let is_up_to_1w = UTXO_FILTERS.up_to_1w.check_days_old(current_days_old);
|
||||
let was_up_to_1w = UTXO_FILTERS.up_to_1w.check_days_old(previous_days_old);
|
||||
if is_up_to_1w && !was_up_to_1w {
|
||||
apply_if_current_only(&mut self.up_to_1w);
|
||||
} else if was_up_to_1w && !is_up_to_1w {
|
||||
apply_if_previous_only(&mut self.up_to_1w);
|
||||
}
|
||||
|
||||
let is_up_to_1m = UTXO_FILTERS.up_to_1m.check_days_old(current_days_old);
|
||||
let was_up_to_1m = UTXO_FILTERS.up_to_1m.check_days_old(previous_days_old);
|
||||
if is_up_to_1m && !was_up_to_1m {
|
||||
apply_if_current_only(&mut self.up_to_1m);
|
||||
} else if was_up_to_1m && !is_up_to_1m {
|
||||
apply_if_previous_only(&mut self.up_to_1m);
|
||||
}
|
||||
|
||||
let is_up_to_2m = UTXO_FILTERS.up_to_2m.check_days_old(current_days_old);
|
||||
let was_up_to_2m = UTXO_FILTERS.up_to_2m.check_days_old(previous_days_old);
|
||||
if is_up_to_2m && !was_up_to_2m {
|
||||
apply_if_current_only(&mut self.up_to_2m);
|
||||
} else if was_up_to_2m && !is_up_to_2m {
|
||||
apply_if_previous_only(&mut self.up_to_2m);
|
||||
}
|
||||
|
||||
let is_up_to_3m = UTXO_FILTERS.up_to_3m.check_days_old(current_days_old);
|
||||
let was_up_to_3m = UTXO_FILTERS.up_to_3m.check_days_old(previous_days_old);
|
||||
if is_up_to_3m && !was_up_to_3m {
|
||||
apply_if_current_only(&mut self.up_to_3m);
|
||||
} else if was_up_to_3m && !is_up_to_3m {
|
||||
apply_if_previous_only(&mut self.up_to_3m);
|
||||
}
|
||||
|
||||
let is_up_to_4m = UTXO_FILTERS.up_to_4m.check_days_old(current_days_old);
|
||||
let was_up_to_4m = UTXO_FILTERS.up_to_4m.check_days_old(previous_days_old);
|
||||
if is_up_to_4m && !was_up_to_4m {
|
||||
apply_if_current_only(&mut self.up_to_4m);
|
||||
} else if was_up_to_4m && !is_up_to_4m {
|
||||
apply_if_previous_only(&mut self.up_to_4m);
|
||||
}
|
||||
|
||||
let is_up_to_5m = UTXO_FILTERS.up_to_5m.check_days_old(current_days_old);
|
||||
let was_up_to_5m = UTXO_FILTERS.up_to_5m.check_days_old(previous_days_old);
|
||||
if is_up_to_5m && !was_up_to_5m {
|
||||
apply_if_current_only(&mut self.up_to_5m);
|
||||
} else if was_up_to_5m && !is_up_to_5m {
|
||||
apply_if_previous_only(&mut self.up_to_5m);
|
||||
}
|
||||
|
||||
let is_up_to_6m = UTXO_FILTERS.up_to_6m.check_days_old(current_days_old);
|
||||
let was_up_to_6m = UTXO_FILTERS.up_to_6m.check_days_old(previous_days_old);
|
||||
if is_up_to_6m && !was_up_to_6m {
|
||||
apply_if_current_only(&mut self.up_to_6m);
|
||||
} else if was_up_to_6m && !is_up_to_6m {
|
||||
apply_if_previous_only(&mut self.up_to_6m);
|
||||
}
|
||||
|
||||
let is_up_to_1y = UTXO_FILTERS.up_to_1y.check_days_old(current_days_old);
|
||||
let was_up_to_1y = UTXO_FILTERS.up_to_1y.check_days_old(previous_days_old);
|
||||
if is_up_to_1y && !was_up_to_1y {
|
||||
apply_if_current_only(&mut self.up_to_1y);
|
||||
} else if was_up_to_1y && !is_up_to_1y {
|
||||
apply_if_previous_only(&mut self.up_to_1y);
|
||||
}
|
||||
|
||||
let is_up_to_2y = UTXO_FILTERS.up_to_2y.check_days_old(current_days_old);
|
||||
let was_up_to_2y = UTXO_FILTERS.up_to_2y.check_days_old(previous_days_old);
|
||||
if is_up_to_2y && !was_up_to_2y {
|
||||
apply_if_current_only(&mut self.up_to_2y);
|
||||
} else if was_up_to_2y && !is_up_to_2y {
|
||||
apply_if_previous_only(&mut self.up_to_2y);
|
||||
}
|
||||
|
||||
let is_up_to_3y = UTXO_FILTERS.up_to_3y.check_days_old(current_days_old);
|
||||
let was_up_to_3y = UTXO_FILTERS.up_to_3y.check_days_old(previous_days_old);
|
||||
if is_up_to_3y && !was_up_to_3y {
|
||||
apply_if_current_only(&mut self.up_to_3y);
|
||||
} else if was_up_to_3y && !is_up_to_3y {
|
||||
apply_if_previous_only(&mut self.up_to_3y);
|
||||
}
|
||||
|
||||
let is_up_to_5y = UTXO_FILTERS.up_to_5y.check_days_old(current_days_old);
|
||||
let was_up_to_5y = UTXO_FILTERS.up_to_5y.check_days_old(previous_days_old);
|
||||
if is_up_to_5y && !was_up_to_5y {
|
||||
apply_if_current_only(&mut self.up_to_5y);
|
||||
} else if was_up_to_5y && !is_up_to_5y {
|
||||
apply_if_previous_only(&mut self.up_to_5y);
|
||||
}
|
||||
|
||||
let is_up_to_7y = UTXO_FILTERS.up_to_7y.check_days_old(current_days_old);
|
||||
let was_up_to_7y = UTXO_FILTERS.up_to_7y.check_days_old(previous_days_old);
|
||||
if is_up_to_7y && !was_up_to_7y {
|
||||
apply_if_current_only(&mut self.up_to_7y);
|
||||
} else if was_up_to_7y && !is_up_to_7y {
|
||||
apply_if_previous_only(&mut self.up_to_7y);
|
||||
}
|
||||
|
||||
let is_up_to_10y = UTXO_FILTERS.up_to_10y.check_days_old(current_days_old);
|
||||
let was_up_to_10y = UTXO_FILTERS.up_to_10y.check_days_old(previous_days_old);
|
||||
if is_up_to_10y && !was_up_to_10y {
|
||||
apply_if_current_only(&mut self.up_to_10y);
|
||||
} else if was_up_to_10y && !is_up_to_10y {
|
||||
apply_if_previous_only(&mut self.up_to_10y);
|
||||
}
|
||||
|
||||
let is_up_to_15y = UTXO_FILTERS.up_to_15y.check_days_old(current_days_old);
|
||||
let was_up_to_15y = UTXO_FILTERS.up_to_15y.check_days_old(previous_days_old);
|
||||
if is_up_to_15y && !was_up_to_15y {
|
||||
apply_if_current_only(&mut self.up_to_15y);
|
||||
} else if was_up_to_15y && !is_up_to_15y {
|
||||
apply_if_previous_only(&mut self.up_to_15y);
|
||||
}
|
||||
|
||||
let is_from_1d_to_1w = UTXO_FILTERS.from_1d_to_1w.check_days_old(current_days_old);
|
||||
let was_from_1d_to_1w = UTXO_FILTERS.from_1d_to_1w.check_days_old(previous_days_old);
|
||||
if is_from_1d_to_1w && !was_from_1d_to_1w {
|
||||
apply_if_current_only(&mut self.from_1d_to_1w);
|
||||
} else if was_from_1d_to_1w && !is_from_1d_to_1w {
|
||||
apply_if_previous_only(&mut self.from_1d_to_1w);
|
||||
}
|
||||
|
||||
let is_from_1w_to_1m = UTXO_FILTERS.from_1w_to_1m.check_days_old(current_days_old);
|
||||
let was_from_1w_to_1m = UTXO_FILTERS.from_1w_to_1m.check_days_old(previous_days_old);
|
||||
if is_from_1w_to_1m && !was_from_1w_to_1m {
|
||||
apply_if_current_only(&mut self.from_1w_to_1m);
|
||||
} else if was_from_1w_to_1m && !is_from_1w_to_1m {
|
||||
apply_if_previous_only(&mut self.from_1w_to_1m);
|
||||
}
|
||||
|
||||
let is_from_1m_to_3m = UTXO_FILTERS.from_1m_to_3m.check_days_old(current_days_old);
|
||||
let was_from_1m_to_3m = UTXO_FILTERS.from_1m_to_3m.check_days_old(previous_days_old);
|
||||
if is_from_1m_to_3m && !was_from_1m_to_3m {
|
||||
apply_if_current_only(&mut self.from_1m_to_3m);
|
||||
} else if was_from_1m_to_3m && !is_from_1m_to_3m {
|
||||
apply_if_previous_only(&mut self.from_1m_to_3m);
|
||||
}
|
||||
|
||||
let is_from_3m_to_6m = UTXO_FILTERS.from_3m_to_6m.check_days_old(current_days_old);
|
||||
let was_from_3m_to_6m = UTXO_FILTERS.from_3m_to_6m.check_days_old(previous_days_old);
|
||||
if is_from_3m_to_6m && !was_from_3m_to_6m {
|
||||
apply_if_current_only(&mut self.from_3m_to_6m);
|
||||
} else if was_from_3m_to_6m && !is_from_3m_to_6m {
|
||||
apply_if_previous_only(&mut self.from_3m_to_6m);
|
||||
}
|
||||
|
||||
let is_from_6m_to_1y = UTXO_FILTERS.from_6m_to_1y.check_days_old(current_days_old);
|
||||
let was_from_6m_to_1y = UTXO_FILTERS.from_6m_to_1y.check_days_old(previous_days_old);
|
||||
if is_from_6m_to_1y && !was_from_6m_to_1y {
|
||||
apply_if_current_only(&mut self.from_6m_to_1y);
|
||||
} else if was_from_6m_to_1y && !is_from_6m_to_1y {
|
||||
apply_if_previous_only(&mut self.from_6m_to_1y);
|
||||
}
|
||||
|
||||
let is_from_1y_to_2y = UTXO_FILTERS.from_1y_to_2y.check_days_old(current_days_old);
|
||||
let was_from_1y_to_2y = UTXO_FILTERS.from_1y_to_2y.check_days_old(previous_days_old);
|
||||
if is_from_1y_to_2y && !was_from_1y_to_2y {
|
||||
apply_if_current_only(&mut self.from_1y_to_2y);
|
||||
} else if was_from_1y_to_2y && !is_from_1y_to_2y {
|
||||
apply_if_previous_only(&mut self.from_1y_to_2y);
|
||||
}
|
||||
|
||||
let is_from_2y_to_3y = UTXO_FILTERS.from_2y_to_3y.check_days_old(current_days_old);
|
||||
let was_from_2y_to_3y = UTXO_FILTERS.from_2y_to_3y.check_days_old(previous_days_old);
|
||||
if is_from_2y_to_3y && !was_from_2y_to_3y {
|
||||
apply_if_current_only(&mut self.from_2y_to_3y);
|
||||
} else if was_from_2y_to_3y && !is_from_2y_to_3y {
|
||||
apply_if_previous_only(&mut self.from_2y_to_3y);
|
||||
}
|
||||
|
||||
let is_from_3y_to_5y = UTXO_FILTERS.from_3y_to_5y.check_days_old(current_days_old);
|
||||
let was_from_3y_to_5y = UTXO_FILTERS.from_3y_to_5y.check_days_old(previous_days_old);
|
||||
if is_from_3y_to_5y && !was_from_3y_to_5y {
|
||||
apply_if_current_only(&mut self.from_3y_to_5y);
|
||||
} else if was_from_3y_to_5y && !is_from_3y_to_5y {
|
||||
apply_if_previous_only(&mut self.from_3y_to_5y);
|
||||
}
|
||||
|
||||
let is_from_5y_to_7y = UTXO_FILTERS.from_5y_to_7y.check_days_old(current_days_old);
|
||||
let was_from_5y_to_7y = UTXO_FILTERS.from_5y_to_7y.check_days_old(previous_days_old);
|
||||
if is_from_5y_to_7y && !was_from_5y_to_7y {
|
||||
apply_if_current_only(&mut self.from_5y_to_7y);
|
||||
} else if was_from_5y_to_7y && !is_from_5y_to_7y {
|
||||
apply_if_previous_only(&mut self.from_5y_to_7y);
|
||||
}
|
||||
|
||||
let is_from_7y_to_10y = UTXO_FILTERS.from_7y_to_10y.check_days_old(current_days_old);
|
||||
let was_from_7y_to_10y = UTXO_FILTERS
|
||||
.from_7y_to_10y
|
||||
.check_days_old(previous_days_old);
|
||||
if is_from_7y_to_10y && !was_from_7y_to_10y {
|
||||
apply_if_current_only(&mut self.from_7y_to_10y);
|
||||
} else if was_from_7y_to_10y && !is_from_7y_to_10y {
|
||||
apply_if_previous_only(&mut self.from_7y_to_10y);
|
||||
}
|
||||
|
||||
let is_from_10y_to_15y = UTXO_FILTERS
|
||||
.from_10y_to_15y
|
||||
.check_days_old(current_days_old);
|
||||
let was_from_10y_to_15y = UTXO_FILTERS
|
||||
.from_10y_to_15y
|
||||
.check_days_old(previous_days_old);
|
||||
if is_from_10y_to_15y && !was_from_10y_to_15y {
|
||||
apply_if_current_only(&mut self.from_10y_to_15y);
|
||||
} else if was_from_10y_to_15y && !is_from_10y_to_15y {
|
||||
apply_if_previous_only(&mut self.from_10y_to_15y);
|
||||
}
|
||||
|
||||
let is_from_1y = UTXO_FILTERS.from_1y.check_days_old(current_days_old);
|
||||
let was_from_1y = UTXO_FILTERS.from_1y.check_days_old(previous_days_old);
|
||||
if is_from_1y && !was_from_1y {
|
||||
apply_if_current_only(&mut self.from_1y);
|
||||
} else if was_from_1y && !is_from_1y {
|
||||
apply_if_previous_only(&mut self.from_1y);
|
||||
}
|
||||
|
||||
let is_from_2y = UTXO_FILTERS.from_2y.check_days_old(current_days_old);
|
||||
let was_from_2y = UTXO_FILTERS.from_2y.check_days_old(previous_days_old);
|
||||
if is_from_2y && !was_from_2y {
|
||||
apply_if_current_only(&mut self.from_2y);
|
||||
} else if was_from_2y && !is_from_2y {
|
||||
apply_if_previous_only(&mut self.from_2y);
|
||||
}
|
||||
|
||||
let is_from_4y = UTXO_FILTERS.from_4y.check_days_old(current_days_old);
|
||||
let was_from_4y = UTXO_FILTERS.from_4y.check_days_old(previous_days_old);
|
||||
if is_from_4y && !was_from_4y {
|
||||
apply_if_current_only(&mut self.from_4y);
|
||||
} else if was_from_4y && !is_from_4y {
|
||||
apply_if_previous_only(&mut self.from_4y);
|
||||
}
|
||||
|
||||
let is_from_10y = UTXO_FILTERS.from_10y.check_days_old(current_days_old);
|
||||
let was_from_10y = UTXO_FILTERS.from_10y.check_days_old(previous_days_old);
|
||||
if is_from_10y && !was_from_10y {
|
||||
apply_if_current_only(&mut self.from_10y);
|
||||
} else if was_from_10y && !is_from_10y {
|
||||
apply_if_previous_only(&mut self.from_10y);
|
||||
}
|
||||
|
||||
let is_from_15y = UTXO_FILTERS.from_15y.check_days_old(current_days_old);
|
||||
let was_from_15y = UTXO_FILTERS.from_15y.check_days_old(previous_days_old);
|
||||
if is_from_15y && !was_from_15y {
|
||||
apply_if_current_only(&mut self.from_15y);
|
||||
} else if was_from_15y && !is_from_15y {
|
||||
apply_if_previous_only(&mut self.from_15y);
|
||||
}
|
||||
|
||||
let is_sth = UTXO_FILTERS.sth.check_days_old(current_days_old);
|
||||
let was_sth = UTXO_FILTERS.sth.check_days_old(previous_days_old);
|
||||
if is_sth && !was_sth {
|
||||
apply_if_current_only(&mut self.sth);
|
||||
} else if was_sth && !is_sth {
|
||||
apply_if_previous_only(&mut self.sth);
|
||||
}
|
||||
|
||||
let is_lth = UTXO_FILTERS.lth.check_days_old(current_days_old);
|
||||
let was_lth = UTXO_FILTERS.lth.check_days_old(previous_days_old);
|
||||
if is_lth && !was_lth {
|
||||
if is_sth {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
apply_if_current_only(&mut self.lth);
|
||||
} else if was_lth && !is_lth {
|
||||
if was_sth {
|
||||
unreachable!()
|
||||
}
|
||||
// unreachable!();
|
||||
apply_if_previous_only(&mut self.lth);
|
||||
}
|
||||
}
|
||||
|
||||
/// Includes years since it's the initial apply
|
||||
pub fn initial_filtered_apply(&mut self, days_old: &u32, year: &u32, apply: impl Fn(&mut T)) {
|
||||
if UTXO_FILTERS.up_to_1d.check(days_old, year) {
|
||||
apply(&mut self.up_to_1d);
|
||||
} else if UTXO_FILTERS.from_1d_to_1w.check(days_old, year) {
|
||||
apply(&mut self.from_1d_to_1w);
|
||||
} else if UTXO_FILTERS.from_1w_to_1m.check(days_old, year) {
|
||||
apply(&mut self.from_1w_to_1m);
|
||||
} else if UTXO_FILTERS.from_1m_to_3m.check(days_old, year) {
|
||||
apply(&mut self.from_1m_to_3m);
|
||||
} else if UTXO_FILTERS.from_3m_to_6m.check(days_old, year) {
|
||||
apply(&mut self.from_3m_to_6m);
|
||||
} else if UTXO_FILTERS.from_6m_to_1y.check(days_old, year) {
|
||||
apply(&mut self.from_6m_to_1y);
|
||||
} else if UTXO_FILTERS.from_1y_to_2y.check(days_old, year) {
|
||||
apply(&mut self.from_1y_to_2y);
|
||||
} else if UTXO_FILTERS.from_2y_to_3y.check(days_old, year) {
|
||||
apply(&mut self.from_2y_to_3y);
|
||||
} else if UTXO_FILTERS.from_3y_to_5y.check(days_old, year) {
|
||||
apply(&mut self.from_3y_to_5y);
|
||||
} else if UTXO_FILTERS.from_5y_to_7y.check(days_old, year) {
|
||||
apply(&mut self.from_5y_to_7y);
|
||||
} else if UTXO_FILTERS.from_7y_to_10y.check(days_old, year) {
|
||||
apply(&mut self.from_7y_to_10y);
|
||||
} else if UTXO_FILTERS.from_10y_to_15y.check(days_old, year) {
|
||||
apply(&mut self.from_10y_to_15y);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.year_2009.check(days_old, year) {
|
||||
apply(&mut self.year_2009);
|
||||
} else if UTXO_FILTERS.year_2010.check(days_old, year) {
|
||||
apply(&mut self.year_2010);
|
||||
} else if UTXO_FILTERS.year_2011.check(days_old, year) {
|
||||
apply(&mut self.year_2011);
|
||||
} else if UTXO_FILTERS.year_2012.check(days_old, year) {
|
||||
apply(&mut self.year_2012);
|
||||
} else if UTXO_FILTERS.year_2013.check(days_old, year) {
|
||||
apply(&mut self.year_2013);
|
||||
} else if UTXO_FILTERS.year_2014.check(days_old, year) {
|
||||
apply(&mut self.year_2014);
|
||||
} else if UTXO_FILTERS.year_2015.check(days_old, year) {
|
||||
apply(&mut self.year_2015);
|
||||
} else if UTXO_FILTERS.year_2016.check(days_old, year) {
|
||||
apply(&mut self.year_2016);
|
||||
} else if UTXO_FILTERS.year_2017.check(days_old, year) {
|
||||
apply(&mut self.year_2017);
|
||||
} else if UTXO_FILTERS.year_2018.check(days_old, year) {
|
||||
apply(&mut self.year_2018);
|
||||
} else if UTXO_FILTERS.year_2019.check(days_old, year) {
|
||||
apply(&mut self.year_2019);
|
||||
} else if UTXO_FILTERS.year_2020.check(days_old, year) {
|
||||
apply(&mut self.year_2020);
|
||||
} else if UTXO_FILTERS.year_2021.check(days_old, year) {
|
||||
apply(&mut self.year_2021);
|
||||
} else if UTXO_FILTERS.year_2022.check(days_old, year) {
|
||||
apply(&mut self.year_2022);
|
||||
} else if UTXO_FILTERS.year_2023.check(days_old, year) {
|
||||
apply(&mut self.year_2023);
|
||||
} else if UTXO_FILTERS.year_2024.check(days_old, year) {
|
||||
apply(&mut self.year_2024);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.sth.check(days_old, year) {
|
||||
apply(&mut self.sth);
|
||||
} else if UTXO_FILTERS.lth.check(days_old, year) {
|
||||
apply(&mut self.lth);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.from_1y.check(days_old, year) {
|
||||
apply(&mut self.from_1y);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.from_2y.check(days_old, year) {
|
||||
apply(&mut self.from_2y);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.from_4y.check(days_old, year) {
|
||||
apply(&mut self.from_4y);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.from_10y.check(days_old, year) {
|
||||
apply(&mut self.from_10y);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.from_15y.check(days_old, year) {
|
||||
apply(&mut self.from_15y);
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_15y.check(days_old, year) {
|
||||
apply(&mut self.up_to_15y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_10y.check(days_old, year) {
|
||||
apply(&mut self.up_to_10y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_7y.check(days_old, year) {
|
||||
apply(&mut self.up_to_7y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_5y.check(days_old, year) {
|
||||
apply(&mut self.up_to_5y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_3y.check(days_old, year) {
|
||||
apply(&mut self.up_to_3y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_2y.check(days_old, year) {
|
||||
apply(&mut self.up_to_2y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_1y.check(days_old, year) {
|
||||
apply(&mut self.up_to_1y);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_6m.check(days_old, year) {
|
||||
apply(&mut self.up_to_6m);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_5m.check(days_old, year) {
|
||||
apply(&mut self.up_to_5m);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_4m.check(days_old, year) {
|
||||
apply(&mut self.up_to_4m);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_3m.check(days_old, year) {
|
||||
apply(&mut self.up_to_3m);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_2m.check(days_old, year) {
|
||||
apply(&mut self.up_to_2m);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_1m.check(days_old, year) {
|
||||
apply(&mut self.up_to_1m);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if UTXO_FILTERS.up_to_1w.check(days_old, year) {
|
||||
apply(&mut self.up_to_1w);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_vec(&self) -> Vec<(&T, UTXOCohortId)> {
|
||||
vec![
|
||||
(&self.up_to_1d, UTXOCohortId::UpTo1d),
|
||||
(&self.up_to_1w, UTXOCohortId::UpTo1w),
|
||||
(&self.up_to_1m, UTXOCohortId::UpTo1m),
|
||||
(&self.up_to_2m, UTXOCohortId::UpTo2m),
|
||||
(&self.up_to_3m, UTXOCohortId::UpTo3m),
|
||||
(&self.up_to_4m, UTXOCohortId::UpTo4m),
|
||||
(&self.up_to_5m, UTXOCohortId::UpTo5m),
|
||||
(&self.up_to_6m, UTXOCohortId::UpTo6m),
|
||||
(&self.up_to_1y, UTXOCohortId::UpTo1y),
|
||||
(&self.up_to_2y, UTXOCohortId::UpTo2y),
|
||||
(&self.up_to_3y, UTXOCohortId::UpTo3y),
|
||||
(&self.up_to_5y, UTXOCohortId::UpTo5y),
|
||||
(&self.up_to_7y, UTXOCohortId::UpTo7y),
|
||||
(&self.up_to_10y, UTXOCohortId::UpTo10y),
|
||||
(&self.up_to_15y, UTXOCohortId::UpTo15y),
|
||||
(&self.from_1d_to_1w, UTXOCohortId::From1dTo1w),
|
||||
(&self.from_1w_to_1m, UTXOCohortId::From1wTo1m),
|
||||
(&self.from_1m_to_3m, UTXOCohortId::From1mTo3m),
|
||||
(&self.from_3m_to_6m, UTXOCohortId::From3mTo6m),
|
||||
(&self.from_6m_to_1y, UTXOCohortId::From6mTo1y),
|
||||
(&self.from_1y_to_2y, UTXOCohortId::From1yTo2y),
|
||||
(&self.from_2y_to_3y, UTXOCohortId::From2yTo3y),
|
||||
(&self.from_3y_to_5y, UTXOCohortId::From3yTo5y),
|
||||
(&self.from_5y_to_7y, UTXOCohortId::From5yTo7y),
|
||||
(&self.from_7y_to_10y, UTXOCohortId::From7yTo10y),
|
||||
(&self.from_10y_to_15y, UTXOCohortId::From10yTo15y),
|
||||
(&self.from_1y, UTXOCohortId::From1y),
|
||||
(&self.from_2y, UTXOCohortId::From2y),
|
||||
(&self.from_4y, UTXOCohortId::From4y),
|
||||
(&self.from_10y, UTXOCohortId::From10y),
|
||||
(&self.from_15y, UTXOCohortId::From15y),
|
||||
(&self.year_2009, UTXOCohortId::Year2009),
|
||||
(&self.year_2010, UTXOCohortId::Year2010),
|
||||
(&self.year_2011, UTXOCohortId::Year2011),
|
||||
(&self.year_2012, UTXOCohortId::Year2012),
|
||||
(&self.year_2013, UTXOCohortId::Year2013),
|
||||
(&self.year_2014, UTXOCohortId::Year2014),
|
||||
(&self.year_2015, UTXOCohortId::Year2015),
|
||||
(&self.year_2016, UTXOCohortId::Year2016),
|
||||
(&self.year_2017, UTXOCohortId::Year2017),
|
||||
(&self.year_2018, UTXOCohortId::Year2018),
|
||||
(&self.year_2019, UTXOCohortId::Year2019),
|
||||
(&self.year_2020, UTXOCohortId::Year2020),
|
||||
(&self.year_2021, UTXOCohortId::Year2021),
|
||||
(&self.year_2022, UTXOCohortId::Year2022),
|
||||
(&self.year_2023, UTXOCohortId::Year2023),
|
||||
(&self.year_2024, UTXOCohortId::Year2024),
|
||||
(&self.sth, UTXOCohortId::ShortTermHolders),
|
||||
(&self.lth, UTXOCohortId::LongTermHolders),
|
||||
]
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_mut_vec(&mut self) -> Vec<(&mut T, UTXOCohortId)> {
|
||||
vec![
|
||||
(&mut self.up_to_1d, UTXOCohortId::UpTo1d),
|
||||
(&mut self.up_to_1w, UTXOCohortId::UpTo1w),
|
||||
(&mut self.up_to_1m, UTXOCohortId::UpTo1m),
|
||||
(&mut self.up_to_2m, UTXOCohortId::UpTo2m),
|
||||
(&mut self.up_to_3m, UTXOCohortId::UpTo3m),
|
||||
(&mut self.up_to_4m, UTXOCohortId::UpTo4m),
|
||||
(&mut self.up_to_5m, UTXOCohortId::UpTo5m),
|
||||
(&mut self.up_to_6m, UTXOCohortId::UpTo6m),
|
||||
(&mut self.up_to_1y, UTXOCohortId::UpTo1y),
|
||||
(&mut self.up_to_2y, UTXOCohortId::UpTo2y),
|
||||
(&mut self.up_to_3y, UTXOCohortId::UpTo3y),
|
||||
(&mut self.up_to_5y, UTXOCohortId::UpTo5y),
|
||||
(&mut self.up_to_7y, UTXOCohortId::UpTo7y),
|
||||
(&mut self.up_to_10y, UTXOCohortId::UpTo10y),
|
||||
(&mut self.up_to_15y, UTXOCohortId::UpTo15y),
|
||||
(&mut self.from_1d_to_1w, UTXOCohortId::From1dTo1w),
|
||||
(&mut self.from_1w_to_1m, UTXOCohortId::From1wTo1m),
|
||||
(&mut self.from_1m_to_3m, UTXOCohortId::From1mTo3m),
|
||||
(&mut self.from_3m_to_6m, UTXOCohortId::From3mTo6m),
|
||||
(&mut self.from_6m_to_1y, UTXOCohortId::From6mTo1y),
|
||||
(&mut self.from_1y_to_2y, UTXOCohortId::From1yTo2y),
|
||||
(&mut self.from_2y_to_3y, UTXOCohortId::From2yTo3y),
|
||||
(&mut self.from_3y_to_5y, UTXOCohortId::From3yTo5y),
|
||||
(&mut self.from_5y_to_7y, UTXOCohortId::From5yTo7y),
|
||||
(&mut self.from_7y_to_10y, UTXOCohortId::From7yTo10y),
|
||||
(&mut self.from_10y_to_15y, UTXOCohortId::From10yTo15y),
|
||||
(&mut self.from_1y, UTXOCohortId::From1y),
|
||||
(&mut self.from_2y, UTXOCohortId::From2y),
|
||||
(&mut self.from_4y, UTXOCohortId::From4y),
|
||||
(&mut self.from_10y, UTXOCohortId::From10y),
|
||||
(&mut self.from_15y, UTXOCohortId::From15y),
|
||||
(&mut self.year_2009, UTXOCohortId::Year2009),
|
||||
(&mut self.year_2010, UTXOCohortId::Year2010),
|
||||
(&mut self.year_2011, UTXOCohortId::Year2011),
|
||||
(&mut self.year_2012, UTXOCohortId::Year2012),
|
||||
(&mut self.year_2013, UTXOCohortId::Year2013),
|
||||
(&mut self.year_2014, UTXOCohortId::Year2014),
|
||||
(&mut self.year_2015, UTXOCohortId::Year2015),
|
||||
(&mut self.year_2016, UTXOCohortId::Year2016),
|
||||
(&mut self.year_2017, UTXOCohortId::Year2017),
|
||||
(&mut self.year_2018, UTXOCohortId::Year2018),
|
||||
(&mut self.year_2019, UTXOCohortId::Year2019),
|
||||
(&mut self.year_2020, UTXOCohortId::Year2020),
|
||||
(&mut self.year_2021, UTXOCohortId::Year2021),
|
||||
(&mut self.year_2022, UTXOCohortId::Year2022),
|
||||
(&mut self.year_2023, UTXOCohortId::Year2023),
|
||||
(&mut self.year_2024, UTXOCohortId::Year2024),
|
||||
(&mut self.sth, UTXOCohortId::ShortTermHolders),
|
||||
(&mut self.lth, UTXOCohortId::LongTermHolders),
|
||||
]
|
||||
}
|
||||
}
|
||||
29
parser/src/states/counters.rs
Normal file
29
parser/src/states/counters.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use allocative::Allocative;
|
||||
use bincode::{Decode, Encode};
|
||||
|
||||
use crate::structs::Counter;
|
||||
|
||||
use super::AnyState;
|
||||
|
||||
#[derive(Default, Debug, Encode, Decode, Allocative)]
|
||||
pub struct Counters {
|
||||
pub op_return_addresses: Counter,
|
||||
pub push_only_addresses: Counter,
|
||||
pub unknown_addresses: Counter,
|
||||
pub empty_addresses: Counter,
|
||||
}
|
||||
|
||||
impl Counters {}
|
||||
|
||||
impl AnyState for Counters {
|
||||
fn name<'a>() -> &'a str {
|
||||
"counters"
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.op_return_addresses.reset();
|
||||
self.push_only_addresses.reset();
|
||||
self.unknown_addresses.reset();
|
||||
self.empty_addresses.reset();
|
||||
}
|
||||
}
|
||||
48
parser/src/states/date_data_vec.rs
Normal file
48
parser/src/states/date_data_vec.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use allocative::Allocative;
|
||||
use bincode::{Decode, Encode};
|
||||
use derive_deref::{Deref, DerefMut};
|
||||
|
||||
use crate::structs::{BlockData, BlockPath, DateData};
|
||||
|
||||
use super::AnyState;
|
||||
|
||||
#[derive(Default, Deref, DerefMut, Debug, Encode, Decode, Allocative)]
|
||||
pub struct DateDataVec(Vec<DateData>);
|
||||
|
||||
impl DateDataVec {
|
||||
pub fn last_block(&self) -> Option<&BlockData> {
|
||||
self.last().and_then(|date_data| date_data.blocks.last())
|
||||
}
|
||||
|
||||
pub fn last_mut_block(&mut self) -> Option<&mut BlockData> {
|
||||
self.last_mut()
|
||||
.and_then(|date_data| date_data.blocks.last_mut())
|
||||
}
|
||||
|
||||
pub fn second_last_block(&self) -> Option<&BlockData> {
|
||||
self.iter()
|
||||
.flat_map(|date_data| &date_data.blocks)
|
||||
.rev()
|
||||
.nth(1)
|
||||
}
|
||||
|
||||
pub fn get_date_data(&self, block_path: &BlockPath) -> Option<&DateData> {
|
||||
self.0.get(block_path.date_index as usize)
|
||||
}
|
||||
|
||||
pub fn get_block_data(&self, block_path: &BlockPath) -> Option<&BlockData> {
|
||||
self.0
|
||||
.get(block_path.date_index as usize)
|
||||
.and_then(|date_data| date_data.blocks.get(block_path.block_index as usize))
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyState for DateDataVec {
|
||||
fn name<'a>() -> &'a str {
|
||||
"date_data_vec"
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
}
|
||||
95
parser/src/states/mod.rs
Normal file
95
parser/src/states/mod.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use std::thread;
|
||||
|
||||
mod _trait;
|
||||
mod cohorts_states;
|
||||
mod counters;
|
||||
mod date_data_vec;
|
||||
|
||||
pub use _trait::*;
|
||||
|
||||
use allocative::Allocative;
|
||||
pub use cohorts_states::*;
|
||||
use counters::*;
|
||||
use date_data_vec::*;
|
||||
|
||||
use crate::{databases::AddressIndexToAddressData, datasets::AllDatasets, utils::log};
|
||||
|
||||
#[derive(Default, Allocative)]
|
||||
pub struct States {
|
||||
pub address_counters: Counters,
|
||||
pub date_data_vec: DateDataVec,
|
||||
pub address_cohorts_durable_states: AddressCohortsDurableStates,
|
||||
pub utxo_cohorts_durable_states: UTXOCohortsDurableStates,
|
||||
}
|
||||
|
||||
impl States {
|
||||
pub fn import(
|
||||
address_index_to_address_data: &mut AddressIndexToAddressData,
|
||||
datasets: &AllDatasets,
|
||||
) -> color_eyre::Result<Self> {
|
||||
let date_data_vec_handle = thread::spawn(DateDataVec::import);
|
||||
|
||||
let address_counters = Counters::import()?;
|
||||
|
||||
let date_data_vec = date_data_vec_handle.join().unwrap()?;
|
||||
|
||||
// TODO:
|
||||
// For both address and utxo check if any of these datasets have a None min
|
||||
// If so use default state otherwise init
|
||||
// unrealized
|
||||
// price_paid
|
||||
// capitalization
|
||||
// supply
|
||||
// utxo
|
||||
|
||||
let mut address_cohorts_durable_states = AddressCohortsDurableStates::default();
|
||||
|
||||
let mut utxo_cohorts_durable_states = UTXOCohortsDurableStates::default();
|
||||
|
||||
if let Some(first_date_data) = date_data_vec.first() {
|
||||
if let Some(first_block_data) = first_date_data.blocks.first() {
|
||||
let first_height = first_block_data.height as usize;
|
||||
let first_date = first_date_data.date;
|
||||
|
||||
// TODO: Do the same for addresses
|
||||
address_cohorts_durable_states =
|
||||
AddressCohortsDurableStates::init(address_index_to_address_data);
|
||||
|
||||
if !datasets.utxo.needs_durable_states(first_height, first_date) {
|
||||
utxo_cohorts_durable_states = UTXOCohortsDurableStates::init(&date_data_vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
address_cohorts_durable_states,
|
||||
address_counters,
|
||||
date_data_vec,
|
||||
utxo_cohorts_durable_states,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, include_addresses: bool) {
|
||||
log("Reseting all states...");
|
||||
|
||||
let _ = self.date_data_vec.reset();
|
||||
|
||||
self.utxo_cohorts_durable_states = UTXOCohortsDurableStates::default();
|
||||
|
||||
// TODO: Check that they are ONLY computed in an `if include_addresses`
|
||||
if include_addresses {
|
||||
let _ = self.address_counters.reset();
|
||||
|
||||
self.address_cohorts_durable_states = AddressCohortsDurableStates::default();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn export(&self) -> color_eyre::Result<()> {
|
||||
thread::scope(|s| {
|
||||
s.spawn(|| self.address_counters.export().unwrap());
|
||||
s.spawn(|| self.date_data_vec.export().unwrap());
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user