use brk_traversable::Traversable;
use brk_types::Cents;
use rayon::prelude::*;
use serde::Serialize;
use super::CohortName;
pub const PROFITABILITY_BOUNDARY_COUNT: usize = 24;
pub fn compute_profitability_boundaries(spot: Cents) -> [Cents; PROFITABILITY_BOUNDARY_COUNT] {
let s = spot.as_u128();
let divisors: [u128; PROFITABILITY_BOUNDARY_COUNT] = [
1100, 600, 400, 300, 200, 190, 180, 170, 160, 150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, ];
let mut boundaries = [Cents::ZERO; PROFITABILITY_BOUNDARY_COUNT];
for (i, &d) in divisors.iter().enumerate() {
boundaries[i] = Cents::from(s * 100 / d);
}
boundaries
}
pub const PROFITABILITY_RANGE_NAMES: ProfitabilityRange<CohortName> = ProfitabilityRange {
over_1000pct_in_profit: CohortName::new(
"utxos_over_1000pct_in_profit",
"+>1000%",
"Over 1000% in Profit",
),
_500pct_to_1000pct_in_profit: CohortName::new(
"utxos_500pct_to_1000pct_in_profit",
"+500-1000%",
"500-1000% in Profit",
),
_300pct_to_500pct_in_profit: CohortName::new(
"utxos_300pct_to_500pct_in_profit",
"+300-500%",
"300-500% in Profit",
),
_200pct_to_300pct_in_profit: CohortName::new(
"utxos_200pct_to_300pct_in_profit",
"+200-300%",
"200-300% in Profit",
),
_100pct_to_200pct_in_profit: CohortName::new(
"utxos_100pct_to_200pct_in_profit",
"+100-200%",
"100-200% in Profit",
),
_90pct_to_100pct_in_profit: CohortName::new(
"utxos_90pct_to_100pct_in_profit",
"+90-100%",
"90-100% in Profit",
),
_80pct_to_90pct_in_profit: CohortName::new(
"utxos_80pct_to_90pct_in_profit",
"+80-90%",
"80-90% in Profit",
),
_70pct_to_80pct_in_profit: CohortName::new(
"utxos_70pct_to_80pct_in_profit",
"+70-80%",
"70-80% in Profit",
),
_60pct_to_70pct_in_profit: CohortName::new(
"utxos_60pct_to_70pct_in_profit",
"+60-70%",
"60-70% in Profit",
),
_50pct_to_60pct_in_profit: CohortName::new(
"utxos_50pct_to_60pct_in_profit",
"+50-60%",
"50-60% in Profit",
),
_40pct_to_50pct_in_profit: CohortName::new(
"utxos_40pct_to_50pct_in_profit",
"+40-50%",
"40-50% in Profit",
),
_30pct_to_40pct_in_profit: CohortName::new(
"utxos_30pct_to_40pct_in_profit",
"+30-40%",
"30-40% in Profit",
),
_20pct_to_30pct_in_profit: CohortName::new(
"utxos_20pct_to_30pct_in_profit",
"+20-30%",
"20-30% in Profit",
),
_10pct_to_20pct_in_profit: CohortName::new(
"utxos_10pct_to_20pct_in_profit",
"+10-20%",
"10-20% in Profit",
),
_0pct_to_10pct_in_profit: CohortName::new(
"utxos_0pct_to_10pct_in_profit",
"+0-10%",
"0-10% in Profit",
),
_0pct_to_10pct_in_loss: CohortName::new(
"utxos_0pct_to_10pct_in_loss",
"-0-10%",
"0-10% in Loss",
),
_10pct_to_20pct_in_loss: CohortName::new(
"utxos_10pct_to_20pct_in_loss",
"-10-20%",
"10-20% in Loss",
),
_20pct_to_30pct_in_loss: CohortName::new(
"utxos_20pct_to_30pct_in_loss",
"-20-30%",
"20-30% in Loss",
),
_30pct_to_40pct_in_loss: CohortName::new(
"utxos_30pct_to_40pct_in_loss",
"-30-40%",
"30-40% in Loss",
),
_40pct_to_50pct_in_loss: CohortName::new(
"utxos_40pct_to_50pct_in_loss",
"-40-50%",
"40-50% in Loss",
),
_50pct_to_60pct_in_loss: CohortName::new(
"utxos_50pct_to_60pct_in_loss",
"-50-60%",
"50-60% in Loss",
),
_60pct_to_70pct_in_loss: CohortName::new(
"utxos_60pct_to_70pct_in_loss",
"-60-70%",
"60-70% in Loss",
),
_70pct_to_80pct_in_loss: CohortName::new(
"utxos_70pct_to_80pct_in_loss",
"-70-80%",
"70-80% in Loss",
),
_80pct_to_90pct_in_loss: CohortName::new(
"utxos_80pct_to_90pct_in_loss",
"-80-90%",
"80-90% in Loss",
),
_90pct_to_100pct_in_loss: CohortName::new(
"utxos_90pct_to_100pct_in_loss",
"-90-100%",
"90-100% in Loss",
),
};
impl ProfitabilityRange<CohortName> {
pub const fn names() -> &'static Self {
&PROFITABILITY_RANGE_NAMES
}
}
#[derive(Default, Clone, Traversable, Serialize)]
pub struct ProfitabilityRange<T> {
pub over_1000pct_in_profit: T,
pub _500pct_to_1000pct_in_profit: T,
pub _300pct_to_500pct_in_profit: T,
pub _200pct_to_300pct_in_profit: T,
pub _100pct_to_200pct_in_profit: T,
pub _90pct_to_100pct_in_profit: T,
pub _80pct_to_90pct_in_profit: T,
pub _70pct_to_80pct_in_profit: T,
pub _60pct_to_70pct_in_profit: T,
pub _50pct_to_60pct_in_profit: T,
pub _40pct_to_50pct_in_profit: T,
pub _30pct_to_40pct_in_profit: T,
pub _20pct_to_30pct_in_profit: T,
pub _10pct_to_20pct_in_profit: T,
pub _0pct_to_10pct_in_profit: T,
pub _0pct_to_10pct_in_loss: T,
pub _10pct_to_20pct_in_loss: T,
pub _20pct_to_30pct_in_loss: T,
pub _30pct_to_40pct_in_loss: T,
pub _40pct_to_50pct_in_loss: T,
pub _50pct_to_60pct_in_loss: T,
pub _60pct_to_70pct_in_loss: T,
pub _70pct_to_80pct_in_loss: T,
pub _80pct_to_90pct_in_loss: T,
pub _90pct_to_100pct_in_loss: T,
}
pub const PROFITABILITY_RANGE_COUNT: usize = 25;
impl<T> ProfitabilityRange<T> {
pub fn new<F>(mut create: F) -> Self
where
F: FnMut(&'static str) -> T,
{
let n = &PROFITABILITY_RANGE_NAMES;
Self {
over_1000pct_in_profit: create(n.over_1000pct_in_profit.id),
_500pct_to_1000pct_in_profit: create(n._500pct_to_1000pct_in_profit.id),
_300pct_to_500pct_in_profit: create(n._300pct_to_500pct_in_profit.id),
_200pct_to_300pct_in_profit: create(n._200pct_to_300pct_in_profit.id),
_100pct_to_200pct_in_profit: create(n._100pct_to_200pct_in_profit.id),
_90pct_to_100pct_in_profit: create(n._90pct_to_100pct_in_profit.id),
_80pct_to_90pct_in_profit: create(n._80pct_to_90pct_in_profit.id),
_70pct_to_80pct_in_profit: create(n._70pct_to_80pct_in_profit.id),
_60pct_to_70pct_in_profit: create(n._60pct_to_70pct_in_profit.id),
_50pct_to_60pct_in_profit: create(n._50pct_to_60pct_in_profit.id),
_40pct_to_50pct_in_profit: create(n._40pct_to_50pct_in_profit.id),
_30pct_to_40pct_in_profit: create(n._30pct_to_40pct_in_profit.id),
_20pct_to_30pct_in_profit: create(n._20pct_to_30pct_in_profit.id),
_10pct_to_20pct_in_profit: create(n._10pct_to_20pct_in_profit.id),
_0pct_to_10pct_in_profit: create(n._0pct_to_10pct_in_profit.id),
_0pct_to_10pct_in_loss: create(n._0pct_to_10pct_in_loss.id),
_10pct_to_20pct_in_loss: create(n._10pct_to_20pct_in_loss.id),
_20pct_to_30pct_in_loss: create(n._20pct_to_30pct_in_loss.id),
_30pct_to_40pct_in_loss: create(n._30pct_to_40pct_in_loss.id),
_40pct_to_50pct_in_loss: create(n._40pct_to_50pct_in_loss.id),
_50pct_to_60pct_in_loss: create(n._50pct_to_60pct_in_loss.id),
_60pct_to_70pct_in_loss: create(n._60pct_to_70pct_in_loss.id),
_70pct_to_80pct_in_loss: create(n._70pct_to_80pct_in_loss.id),
_80pct_to_90pct_in_loss: create(n._80pct_to_90pct_in_loss.id),
_90pct_to_100pct_in_loss: create(n._90pct_to_100pct_in_loss.id),
}
}
pub fn try_new<F, E>(mut create: F) -> Result<Self, E>
where
F: FnMut(&'static str) -> Result<T, E>,
{
let n = &PROFITABILITY_RANGE_NAMES;
Ok(Self {
over_1000pct_in_profit: create(n.over_1000pct_in_profit.id)?,
_500pct_to_1000pct_in_profit: create(n._500pct_to_1000pct_in_profit.id)?,
_300pct_to_500pct_in_profit: create(n._300pct_to_500pct_in_profit.id)?,
_200pct_to_300pct_in_profit: create(n._200pct_to_300pct_in_profit.id)?,
_100pct_to_200pct_in_profit: create(n._100pct_to_200pct_in_profit.id)?,
_90pct_to_100pct_in_profit: create(n._90pct_to_100pct_in_profit.id)?,
_80pct_to_90pct_in_profit: create(n._80pct_to_90pct_in_profit.id)?,
_70pct_to_80pct_in_profit: create(n._70pct_to_80pct_in_profit.id)?,
_60pct_to_70pct_in_profit: create(n._60pct_to_70pct_in_profit.id)?,
_50pct_to_60pct_in_profit: create(n._50pct_to_60pct_in_profit.id)?,
_40pct_to_50pct_in_profit: create(n._40pct_to_50pct_in_profit.id)?,
_30pct_to_40pct_in_profit: create(n._30pct_to_40pct_in_profit.id)?,
_20pct_to_30pct_in_profit: create(n._20pct_to_30pct_in_profit.id)?,
_10pct_to_20pct_in_profit: create(n._10pct_to_20pct_in_profit.id)?,
_0pct_to_10pct_in_profit: create(n._0pct_to_10pct_in_profit.id)?,
_0pct_to_10pct_in_loss: create(n._0pct_to_10pct_in_loss.id)?,
_10pct_to_20pct_in_loss: create(n._10pct_to_20pct_in_loss.id)?,
_20pct_to_30pct_in_loss: create(n._20pct_to_30pct_in_loss.id)?,
_30pct_to_40pct_in_loss: create(n._30pct_to_40pct_in_loss.id)?,
_40pct_to_50pct_in_loss: create(n._40pct_to_50pct_in_loss.id)?,
_50pct_to_60pct_in_loss: create(n._50pct_to_60pct_in_loss.id)?,
_60pct_to_70pct_in_loss: create(n._60pct_to_70pct_in_loss.id)?,
_70pct_to_80pct_in_loss: create(n._70pct_to_80pct_in_loss.id)?,
_80pct_to_90pct_in_loss: create(n._80pct_to_90pct_in_loss.id)?,
_90pct_to_100pct_in_loss: create(n._90pct_to_100pct_in_loss.id)?,
})
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
[
&self.over_1000pct_in_profit,
&self._500pct_to_1000pct_in_profit,
&self._300pct_to_500pct_in_profit,
&self._200pct_to_300pct_in_profit,
&self._100pct_to_200pct_in_profit,
&self._90pct_to_100pct_in_profit,
&self._80pct_to_90pct_in_profit,
&self._70pct_to_80pct_in_profit,
&self._60pct_to_70pct_in_profit,
&self._50pct_to_60pct_in_profit,
&self._40pct_to_50pct_in_profit,
&self._30pct_to_40pct_in_profit,
&self._20pct_to_30pct_in_profit,
&self._10pct_to_20pct_in_profit,
&self._0pct_to_10pct_in_profit,
&self._0pct_to_10pct_in_loss,
&self._10pct_to_20pct_in_loss,
&self._20pct_to_30pct_in_loss,
&self._30pct_to_40pct_in_loss,
&self._40pct_to_50pct_in_loss,
&self._50pct_to_60pct_in_loss,
&self._60pct_to_70pct_in_loss,
&self._70pct_to_80pct_in_loss,
&self._80pct_to_90pct_in_loss,
&self._90pct_to_100pct_in_loss,
]
.into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.iter_mut_with_is_profit().map(|(_, v)| v)
}
pub fn iter_mut_with_is_profit(&mut self) -> impl Iterator<Item = (bool, &mut T)> {
[
(true, &mut self.over_1000pct_in_profit),
(true, &mut self._500pct_to_1000pct_in_profit),
(true, &mut self._300pct_to_500pct_in_profit),
(true, &mut self._200pct_to_300pct_in_profit),
(true, &mut self._100pct_to_200pct_in_profit),
(true, &mut self._90pct_to_100pct_in_profit),
(true, &mut self._80pct_to_90pct_in_profit),
(true, &mut self._70pct_to_80pct_in_profit),
(true, &mut self._60pct_to_70pct_in_profit),
(true, &mut self._50pct_to_60pct_in_profit),
(true, &mut self._40pct_to_50pct_in_profit),
(true, &mut self._30pct_to_40pct_in_profit),
(true, &mut self._20pct_to_30pct_in_profit),
(true, &mut self._10pct_to_20pct_in_profit),
(true, &mut self._0pct_to_10pct_in_profit),
(false, &mut self._0pct_to_10pct_in_loss),
(false, &mut self._10pct_to_20pct_in_loss),
(false, &mut self._20pct_to_30pct_in_loss),
(false, &mut self._30pct_to_40pct_in_loss),
(false, &mut self._40pct_to_50pct_in_loss),
(false, &mut self._50pct_to_60pct_in_loss),
(false, &mut self._60pct_to_70pct_in_loss),
(false, &mut self._70pct_to_80pct_in_loss),
(false, &mut self._80pct_to_90pct_in_loss),
(false, &mut self._90pct_to_100pct_in_loss),
]
.into_iter()
}
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut T>
where
T: Send + Sync,
{
[
&mut self.over_1000pct_in_profit,
&mut self._500pct_to_1000pct_in_profit,
&mut self._300pct_to_500pct_in_profit,
&mut self._200pct_to_300pct_in_profit,
&mut self._100pct_to_200pct_in_profit,
&mut self._90pct_to_100pct_in_profit,
&mut self._80pct_to_90pct_in_profit,
&mut self._70pct_to_80pct_in_profit,
&mut self._60pct_to_70pct_in_profit,
&mut self._50pct_to_60pct_in_profit,
&mut self._40pct_to_50pct_in_profit,
&mut self._30pct_to_40pct_in_profit,
&mut self._20pct_to_30pct_in_profit,
&mut self._10pct_to_20pct_in_profit,
&mut self._0pct_to_10pct_in_profit,
&mut self._0pct_to_10pct_in_loss,
&mut self._10pct_to_20pct_in_loss,
&mut self._20pct_to_30pct_in_loss,
&mut self._30pct_to_40pct_in_loss,
&mut self._40pct_to_50pct_in_loss,
&mut self._50pct_to_60pct_in_loss,
&mut self._60pct_to_70pct_in_loss,
&mut self._70pct_to_80pct_in_loss,
&mut self._80pct_to_90pct_in_loss,
&mut self._90pct_to_100pct_in_loss,
]
.into_par_iter()
}
pub fn as_array(&self) -> [&T; PROFITABILITY_RANGE_COUNT] {
[
&self.over_1000pct_in_profit,
&self._500pct_to_1000pct_in_profit,
&self._300pct_to_500pct_in_profit,
&self._200pct_to_300pct_in_profit,
&self._100pct_to_200pct_in_profit,
&self._90pct_to_100pct_in_profit,
&self._80pct_to_90pct_in_profit,
&self._70pct_to_80pct_in_profit,
&self._60pct_to_70pct_in_profit,
&self._50pct_to_60pct_in_profit,
&self._40pct_to_50pct_in_profit,
&self._30pct_to_40pct_in_profit,
&self._20pct_to_30pct_in_profit,
&self._10pct_to_20pct_in_profit,
&self._0pct_to_10pct_in_profit,
&self._0pct_to_10pct_in_loss,
&self._10pct_to_20pct_in_loss,
&self._20pct_to_30pct_in_loss,
&self._30pct_to_40pct_in_loss,
&self._40pct_to_50pct_in_loss,
&self._50pct_to_60pct_in_loss,
&self._60pct_to_70pct_in_loss,
&self._70pct_to_80pct_in_loss,
&self._80pct_to_90pct_in_loss,
&self._90pct_to_100pct_in_loss,
]
}
pub fn as_array_mut(&mut self) -> [&mut T; PROFITABILITY_RANGE_COUNT] {
[
&mut self.over_1000pct_in_profit,
&mut self._500pct_to_1000pct_in_profit,
&mut self._300pct_to_500pct_in_profit,
&mut self._200pct_to_300pct_in_profit,
&mut self._100pct_to_200pct_in_profit,
&mut self._90pct_to_100pct_in_profit,
&mut self._80pct_to_90pct_in_profit,
&mut self._70pct_to_80pct_in_profit,
&mut self._60pct_to_70pct_in_profit,
&mut self._50pct_to_60pct_in_profit,
&mut self._40pct_to_50pct_in_profit,
&mut self._30pct_to_40pct_in_profit,
&mut self._20pct_to_30pct_in_profit,
&mut self._10pct_to_20pct_in_profit,
&mut self._0pct_to_10pct_in_profit,
&mut self._0pct_to_10pct_in_loss,
&mut self._10pct_to_20pct_in_loss,
&mut self._20pct_to_30pct_in_loss,
&mut self._30pct_to_40pct_in_loss,
&mut self._40pct_to_50pct_in_loss,
&mut self._50pct_to_60pct_in_loss,
&mut self._60pct_to_70pct_in_loss,
&mut self._70pct_to_80pct_in_loss,
&mut self._80pct_to_90pct_in_loss,
&mut self._90pct_to_100pct_in_loss,
]
}
}