use crate::{Asset, HashMap, Instrument, Position, PositionNum};
use alloc::{boxed::Box, fmt};
#[derive(Debug, Clone)]
pub struct PositionTree<'a, T> {
pub(crate) asset: &'a Asset,
pub(crate) value: T,
pub(crate) positions: HashMap<&'a Instrument, &'a Position<T>>,
pub(crate) children: HashMap<Instrument, PositionTree<'a, T>>,
}
impl<'a, T> PositionTree<'a, T>
where
T: PositionNum,
{
pub fn instruments(&self) -> impl Iterator<Item = &Instrument> {
let children: Box<dyn Iterator<Item = &Instrument>> =
Box::new(self.children.values().flat_map(|t| t.instruments()));
let pairs = self.children.keys();
let positions = self.positions.keys().copied();
children.chain(pairs).chain(positions)
}
pub fn eval(&self, prices: &HashMap<Instrument, T>) -> Option<T> {
let children = self
.children
.iter()
.map(|(inst, t)| {
let mut value = t.eval(prices)?;
value *= prices.get(inst)?;
Some(value)
})
.try_fold(T::zero(), |acc, x| Some(acc + x?))?;
let mut ans = self
.positions
.iter()
.map(|(inst, p)| Some(p.closed(prices.get(*inst)?)))
.try_fold(children, |acc, x| Some(acc + x?))?;
ans += &self.value;
Some(ans)
}
#[allow(clippy::type_complexity)]
pub fn eval_with<F>(&self, f: F) -> Option<T>
where
F: FnMut(&Position<T>) -> Option<T>,
{
let mut f: Box<dyn FnMut(&Position<T>) -> Option<T>> = Box::new(f);
let children = self
.children
.iter()
.map(|(inst, t)| {
let value = t.eval_with(&mut f)?;
let p = inst.position((T::zero(), value));
(f)(&p)
})
.try_fold(T::zero(), |acc, x| Some(acc + x?))?;
let mut ans = self
.positions
.values()
.map(|p| (f)(*p))
.try_fold(children, |acc, x| Some(acc + x?))?;
ans += &self.value;
Some(ans)
}
}
pub(super) fn write_position<T>(
f: &mut fmt::Formatter<'_>,
price: &T,
size: &T,
inst: &Instrument,
) -> fmt::Result
where
T: fmt::Display + PositionNum,
{
let asset = inst.base();
if inst.is_prefer_reversed() {
if price.is_zero() {
write!(f, "(Nan, {} {asset})*", -size.clone())
} else {
let mut real_price = T::one();
real_price /= price;
write!(f, "({}, {} {asset})*", real_price, -size.clone())
}
} else {
write!(f, "({price}, {size} {asset})")
}
}
impl<'a, T> fmt::Display for PositionTree<'a, T>
where
T: fmt::Display + PositionNum,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (idx, tree) in self.children.values().enumerate() {
if idx != 0 {
write!(f, " + {tree}")?;
} else {
write!(f, "{tree}")?;
}
}
let flag = !self.children.is_empty();
let mut value = self.value.clone();
for (idx, (inst, position)) in self.positions.iter().enumerate() {
if flag || idx != 0 {
write!(f, " + ")?;
}
write_position(
f,
&position.as_naive().price,
&position.as_naive().size,
inst,
)?;
value += position.value();
}
let flag = flag || !self.positions.is_empty();
if value.is_positive() && flag {
write!(f, " + {} {}", value, self.asset)
} else if value.is_negative() && flag {
write!(f, " - {} {}", value.abs(), self.asset)
} else if value.is_negative() {
write!(f, "- {} {}", value.abs(), self.asset)
} else {
write!(f, "{} {}", value, self.asset)
}
}
}