investing: more data + charts

This commit is contained in:
nym21
2026-01-26 10:28:26 +01:00
parent 5c824e50b8
commit 371fb2cb17
30 changed files with 1922 additions and 1010 deletions

View File

@@ -1,9 +1,11 @@
use brk_error::Result;
use brk_types::{Close, Dollars, StoredF32, StoredU32};
use vecdb::Exit;
use super::Vecs;
use super::{ByDcaClass, ByDcaPeriod, Vecs};
use crate::{
ComputeIndexes,
internal::{ComputedFromDateLast, LazyBinaryFromDateLast},
market::lookback,
price,
traits::{ComputeDCAAveragePriceViaLen, ComputeDCAStackViaLen, ComputeLumpSumStackViaLen},
@@ -61,6 +63,17 @@ impl Vecs {
})?;
}
// DCA by period - profitability
compute_period_profitability(
&mut self.period_days_in_profit,
&mut self.period_days_in_loss,
&mut self.period_max_drawdown,
&mut self.period_max_return,
&self.period_returns,
starting_indexes,
exit,
)?;
// Lump sum by period - stack (for comparison with DCA)
let lookback_dca = lookback.price_ago.as_dca_period();
for (stack, lookback_price, days) in
@@ -78,6 +91,17 @@ impl Vecs {
})?;
}
// Lump sum by period - profitability
compute_period_profitability(
&mut self.period_lump_sum_days_in_profit,
&mut self.period_lump_sum_days_in_loss,
&mut self.period_lump_sum_max_drawdown,
&mut self.period_lump_sum_max_return,
&self.period_lump_sum_returns,
starting_indexes,
exit,
)?;
// DCA by year class - stack and average_price
let dateindexes = super::ByDcaClass::<()>::dateindexes();
for ((stack, average_price), dateindex) in self
@@ -102,6 +126,134 @@ impl Vecs {
})?;
}
// DCA by year class - profitability
compute_class_profitability(
&mut self.class_days_in_profit,
&mut self.class_days_in_loss,
&mut self.class_max_drawdown,
&mut self.class_max_return,
&self.class_returns,
starting_indexes,
exit,
)?;
Ok(())
}
}
fn compute_period_profitability(
days_in_profit: &mut ByDcaPeriod<ComputedFromDateLast<StoredU32>>,
days_in_loss: &mut ByDcaPeriod<ComputedFromDateLast<StoredU32>>,
max_drawdown: &mut ByDcaPeriod<ComputedFromDateLast<StoredF32>>,
max_return: &mut ByDcaPeriod<ComputedFromDateLast<StoredF32>>,
returns: &ByDcaPeriod<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
for ((((dip, dil), md), mr), (ret, days)) in days_in_profit
.iter_mut()
.zip(days_in_loss.iter_mut())
.zip(max_drawdown.iter_mut())
.zip(max_return.iter_mut())
.zip(returns.iter_with_days())
{
dip.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_rolling_count(
starting_indexes.dateindex,
&ret.dateindex,
days as usize,
|r| f32::from(*r) > 0.0,
exit,
)?)
})?;
dil.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_rolling_count(
starting_indexes.dateindex,
&ret.dateindex,
days as usize,
|r| f32::from(*r) < 0.0,
exit,
)?)
})?;
md.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_min(
starting_indexes.dateindex,
&ret.dateindex,
days as usize,
exit,
)?)
})?;
mr.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_max(
starting_indexes.dateindex,
&ret.dateindex,
days as usize,
exit,
)?)
})?;
}
Ok(())
}
fn compute_class_profitability(
days_in_profit: &mut ByDcaClass<ComputedFromDateLast<StoredU32>>,
days_in_loss: &mut ByDcaClass<ComputedFromDateLast<StoredU32>>,
max_drawdown: &mut ByDcaClass<ComputedFromDateLast<StoredF32>>,
max_return: &mut ByDcaClass<ComputedFromDateLast<StoredF32>>,
returns: &ByDcaClass<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
starting_indexes: &ComputeIndexes,
exit: &Exit,
) -> Result<()> {
let dateindexes = ByDcaClass::<()>::dateindexes();
for (((((dip, dil), md), mr), ret), from) in days_in_profit
.iter_mut()
.zip(days_in_loss.iter_mut())
.zip(max_drawdown.iter_mut())
.zip(max_return.iter_mut())
.zip(returns.iter())
.zip(dateindexes)
{
dip.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_cumulative_count_from(
starting_indexes.dateindex,
&ret.dateindex,
from,
|r| f32::from(*r) > 0.0,
exit,
)?)
})?;
dil.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_cumulative_count_from(
starting_indexes.dateindex,
&ret.dateindex,
from,
|r| f32::from(*r) < 0.0,
exit,
)?)
})?;
md.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_all_time_low_from(
starting_indexes.dateindex,
&ret.dateindex,
from,
exit,
)?)
})?;
mr.compute_all(starting_indexes, exit, |v| {
Ok(v.compute_all_time_high_from(
starting_indexes.dateindex,
&ret.dateindex,
from,
exit,
)?)
})?;
}
Ok(())
}

View File

@@ -1,11 +1,15 @@
use brk_error::Result;
use brk_types::Version;
use vecdb::Database;
use vecdb::{Database, IterableCloneableVec};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, DCA_CLASS_NAMES, DCA_PERIOD_NAMES, Vecs};
use crate::{
indexes,
internal::{ComputedFromDateLast, LazyBinaryFromDateLast, PercentageDiffCloseDollars, ValueFromDateLast},
internal::{
ComputedFromDateLast, LazyBinaryFromDateLast, PercentageDiffCloseDollars,
ValueFromDateLast,
},
market::lookback,
price,
};
@@ -15,6 +19,7 @@ impl Vecs {
version: Version,
indexes: &indexes::Vecs,
price: &price::Vecs,
lookback: &lookback::Vecs,
) -> Result<Self> {
// DCA by period - stack (KISS)
let period_stack = ByDcaPeriod::try_new(|name, _days| {
@@ -48,6 +53,43 @@ impl Vecs {
ComputedFromDateLast::forced_import(db, &format!("{name}_dca_cagr"), version, indexes)
})?;
// DCA by period - profitability
let period_days_in_profit = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_dca_days_in_profit"),
version + Version::ONE,
indexes,
)
})?;
let period_days_in_loss = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_dca_days_in_loss"),
version + Version::ONE,
indexes,
)
})?;
let period_max_drawdown = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_dca_max_drawdown"),
version,
indexes,
)
})?;
let period_max_return = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_dca_max_return"),
version,
indexes,
)
})?;
// Lump sum by period - stack (KISS)
let period_lump_sum_stack = ByDcaPeriod::try_new(|name, _days| {
ValueFromDateLast::forced_import(
@@ -59,6 +101,58 @@ impl Vecs {
)
})?;
// Lump sum by period - returns
let period_lump_sum_returns = DCA_PERIOD_NAMES
.zip_ref(&lookback.price_ago.as_dca_period())
.map(|(name, lookback_price)| {
LazyBinaryFromDateLast::from_derived_last_and_computed_last::<
PercentageDiffCloseDollars,
>(
&format!("{name}_lump_sum_returns"),
version,
price.usd.split.close.dateindex.boxed_clone(),
&price.usd.split.close.rest,
lookback_price,
)
});
// Lump sum by period - profitability
let period_lump_sum_days_in_profit = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_lump_sum_days_in_profit"),
version + Version::ONE,
indexes,
)
})?;
let period_lump_sum_days_in_loss = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_lump_sum_days_in_loss"),
version + Version::ONE,
indexes,
)
})?;
let period_lump_sum_max_drawdown = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_lump_sum_max_drawdown"),
version,
indexes,
)
})?;
let period_lump_sum_max_return = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_lump_sum_max_return"),
version,
indexes,
)
})?;
// DCA by year class - stack (KISS)
let class_stack = ByDcaClass::try_new(|name, _year, _dateindex| {
ValueFromDateLast::forced_import(db, &format!("{name}_stack"), version, true, indexes)
@@ -81,15 +175,65 @@ impl Vecs {
)
});
// DCA by year class - profitability
let class_days_in_profit = ByDcaClass::try_new(|name, _year, _dateindex| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_days_in_profit"),
version,
indexes,
)
})?;
let class_days_in_loss = ByDcaClass::try_new(|name, _year, _dateindex| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_days_in_loss"),
version,
indexes,
)
})?;
let class_max_drawdown = ByDcaClass::try_new(|name, _year, _dateindex| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_max_drawdown"),
version,
indexes,
)
})?;
let class_max_return = ByDcaClass::try_new(|name, _year, _dateindex| {
ComputedFromDateLast::forced_import(
db,
&format!("{name}_max_return"),
version,
indexes,
)
})?;
Ok(Self {
period_stack,
period_average_price,
period_returns,
period_cagr,
period_days_in_profit,
period_days_in_loss,
period_max_drawdown,
period_max_return,
period_lump_sum_stack,
period_lump_sum_returns,
period_lump_sum_days_in_profit,
period_lump_sum_days_in_loss,
period_lump_sum_max_drawdown,
period_lump_sum_max_return,
class_stack,
class_average_price,
class_returns,
class_days_in_profit,
class_days_in_loss,
class_max_drawdown,
class_max_return,
})
}
}

View File

@@ -1,5 +1,5 @@
use brk_traversable::Traversable;
use brk_types::{Close, Dollars, StoredF32};
use brk_types::{Close, Dollars, StoredF32, StoredU32};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
use crate::internal::{ComputedFromDateLast, LazyBinaryFromDateLast, ValueFromDateLast};
@@ -13,11 +13,30 @@ pub struct Vecs {
pub period_returns: ByDcaPeriod<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
pub period_cagr: ByDcaCagr<ComputedFromDateLast<StoredF32>>,
// DCA by period - profitability
pub period_days_in_profit: ByDcaPeriod<ComputedFromDateLast<StoredU32>>,
pub period_days_in_loss: ByDcaPeriod<ComputedFromDateLast<StoredU32>>,
pub period_max_drawdown: ByDcaPeriod<ComputedFromDateLast<StoredF32>>,
pub period_max_return: ByDcaPeriod<ComputedFromDateLast<StoredF32>>,
// Lump sum by period (for comparison with DCA) - KISS types
pub period_lump_sum_stack: ByDcaPeriod<ValueFromDateLast>,
pub period_lump_sum_returns: ByDcaPeriod<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
// Lump sum by period - profitability
pub period_lump_sum_days_in_profit: ByDcaPeriod<ComputedFromDateLast<StoredU32>>,
pub period_lump_sum_days_in_loss: ByDcaPeriod<ComputedFromDateLast<StoredU32>>,
pub period_lump_sum_max_drawdown: ByDcaPeriod<ComputedFromDateLast<StoredF32>>,
pub period_lump_sum_max_return: ByDcaPeriod<ComputedFromDateLast<StoredF32>>,
// DCA by year class - KISS types
pub class_stack: ByDcaClass<ValueFromDateLast>,
pub class_average_price: ByDcaClass<ComputedFromDateLast<Dollars>>,
pub class_returns: ByDcaClass<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
// DCA by year class - profitability
pub class_days_in_profit: ByDcaClass<ComputedFromDateLast<StoredU32>>,
pub class_days_in_loss: ByDcaClass<ComputedFromDateLast<StoredU32>>,
pub class_max_drawdown: ByDcaClass<ComputedFromDateLast<StoredF32>>,
pub class_max_return: ByDcaClass<ComputedFromDateLast<StoredF32>>,
}

View File

@@ -34,7 +34,7 @@ impl Vecs {
let volatility = VolatilityVecs::forced_import(version, &returns);
let range = RangeVecs::forced_import(&db, version, indexes)?;
let moving_average = MovingAverageVecs::forced_import(&db, version, indexes, Some(price))?;
let dca = DcaVecs::forced_import(&db, version, indexes, price)?;
let dca = DcaVecs::forced_import(&db, version, indexes, price, &lookback)?;
let indicators = IndicatorsVecs::forced_import(
&db,
version,