use chrono::NaiveDate;use fev::market::{CleanPrice, CreditSpread, DirtyPrice};use fev::nav::{DailyNav, GpResponse, Valuation};use fev::pipeline::CleanStore;pub struct DailyNavEngine<'a> { sources: &'a SourceLake, cleaner: &'a Normalizer, peers: &'a PeerMatcher, spreads: &'a CreditSpreadCurve, gp_response: &'a GpResponseModel, cash_flows: &'a CashFlowLedger, store: &'a CleanStore,}impl<'a> DailyNavEngine<'a> { pub fn collect_sources( &self, as_of: NaiveDate, ) -> CleanStore { let raw = self.sources.collect(as_of); let cleaned = self.cleaner.normalize(raw); self.store.replace(cleaned) } pub fn peer_proxy<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> ProxyMove<'h> { let peers = self .peers .match_size_and_industry(holding, as_of); peers.weighted_move(holding.last_gp_period(), as_of) } pub fn gp_response<'h>( &self, holding: &'h Holding, proxy: &ProxyMove<'h>, as_of: NaiveDate, ) -> Valuation<'h> { let gp_nav = holding .gp_navs .iter() .filter(|mark| mark.received <= as_of) .max_by_key(|mark| mark.period_end) .unwrap_or(&holding.entry_nav);
}impl<'a> DailyNavEngine<'a> { pub fn collect_sources( &self, as_of: NaiveDate, ) -> CleanStore { let raw = self.sources.collect(as_of); let cleaned = self.cleaner.normalize(raw); self.store.replace(cleaned) } pub fn peer_proxy<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> ProxyMove<'h> { let peers = self .peers .match_size_and_industry(holding, as_of); peers.weighted_move(holding.last_gp_period(), as_of) } pub fn gp_response<'h>( &self, holding: &'h Holding, proxy: &ProxyMove<'h>, as_of: NaiveDate, ) -> Valuation<'h> { let gp_nav = holding .gp_navs .iter() .filter(|mark| mark.received <= as_of) .max_by_key(|mark| mark.period_end) .unwrap_or(&holding.entry_nav); self.gp_response.roll_forward(gp_nav, proxy, as_of) } pub fn private_credit_price( &self, loan: &LoanPosition, as_of: NaiveDate, ) -> (CleanPrice, DirtyPrice) { let spread: CreditSpread = self .spreads .for_rating(loan.rating, as_of); let clean = loan.cash_flows.discount(spread, as_of); let dirty = clean.with_accrued_interest(loan.accrued(as_of));
&self, holding: &'h Holding, as_of: NaiveDate, ) -> ProxyMove<'h> { let peers = self .peers .match_size_and_industry(holding, as_of); peers.weighted_move(holding.last_gp_period(), as_of) } pub fn gp_response<'h>( &self, holding: &'h Holding, proxy: &ProxyMove<'h>, as_of: NaiveDate, ) -> Valuation<'h> { let gp_nav = holding .gp_navs .iter() .filter(|mark| mark.received <= as_of) .max_by_key(|mark| mark.period_end) .unwrap_or(&holding.entry_nav); self.gp_response.roll_forward(gp_nav, proxy, as_of) } pub fn private_credit_price( &self, loan: &LoanPosition, as_of: NaiveDate, ) -> (CleanPrice, DirtyPrice) { let spread: CreditSpread = self .spreads .for_rating(loan.rating, as_of); let clean = loan.cash_flows.discount(spread, as_of); let dirty = clean.with_accrued_interest(loan.accrued(as_of)); (clean, dirty) } pub fn roll_forward<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> Valuation<'h> { let proxy = self.peer_proxy(holding, as_of); let mut value = self.gp_response(holding, &proxy, as_of); for flow in self.cash_flows.for_holding(holding, as_of) { value = value.apply_cash_flow(flow); }
proxy: &ProxyMove<'h>, as_of: NaiveDate, ) -> Valuation<'h> { let gp_nav = holding .gp_navs .iter() .filter(|mark| mark.received <= as_of) .max_by_key(|mark| mark.period_end) .unwrap_or(&holding.entry_nav); self.gp_response.roll_forward(gp_nav, proxy, as_of) } pub fn private_credit_price( &self, loan: &LoanPosition, as_of: NaiveDate, ) -> (CleanPrice, DirtyPrice) { let spread: CreditSpread = self .spreads .for_rating(loan.rating, as_of); let clean = loan.cash_flows.discount(spread, as_of); let dirty = clean.with_accrued_interest(loan.accrued(as_of)); (clean, dirty) } pub fn roll_forward<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> Valuation<'h> { let proxy = self.peer_proxy(holding, as_of); let mut value = self.gp_response(holding, &proxy, as_of); for flow in self.cash_flows.for_holding(holding, as_of) { value = value.apply_cash_flow(flow); } if let HoldingKind::PrivateCredit(loan) = &holding.kind { let (clean, dirty) = self.private_credit_price(loan, as_of); value = value.with_credit_prices(clean, dirty); } value } pub fn daily_nav( &self, fund: &Fund, as_of: NaiveDate, ) -> DailyNav { let valuations = fund
&self, loan: &LoanPosition, as_of: NaiveDate, ) -> (CleanPrice, DirtyPrice) { let spread: CreditSpread = self .spreads .for_rating(loan.rating, as_of); let clean = loan.cash_flows.discount(spread, as_of); let dirty = clean.with_accrued_interest(loan.accrued(as_of)); (clean, dirty) } pub fn roll_forward<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> Valuation<'h> { let proxy = self.peer_proxy(holding, as_of); let mut value = self.gp_response(holding, &proxy, as_of); for flow in self.cash_flows.for_holding(holding, as_of) { value = value.apply_cash_flow(flow); } if let HoldingKind::PrivateCredit(loan) = &holding.kind { let (clean, dirty) = self.private_credit_price(loan, as_of); value = value.with_credit_prices(clean, dirty); } value } pub fn daily_nav( &self, fund: &Fund, as_of: NaiveDate, ) -> DailyNav { let valuations = fund .holdings .iter() .map(|holding| self.roll_forward(holding, as_of)) .collect::<Vec<_>>(); DailyNav::from_holdings(fund, valuations, as_of) }}use chrono::NaiveDate;use fev::market::{CleanPrice, CreditSpread, DirtyPrice};use fev::nav::{DailyNav, GpResponse, Valuation};use fev::pipeline::CleanStore;
&self, holding: &'h Holding, as_of: NaiveDate, ) -> Valuation<'h> { let proxy = self.peer_proxy(holding, as_of); let mut value = self.gp_response(holding, &proxy, as_of); for flow in self.cash_flows.for_holding(holding, as_of) { value = value.apply_cash_flow(flow); } if let HoldingKind::PrivateCredit(loan) = &holding.kind { let (clean, dirty) = self.private_credit_price(loan, as_of); value = value.with_credit_prices(clean, dirty); } value } pub fn daily_nav( &self, fund: &Fund, as_of: NaiveDate, ) -> DailyNav { let valuations = fund .holdings .iter() .map(|holding| self.roll_forward(holding, as_of)) .collect::<Vec<_>>(); DailyNav::from_holdings(fund, valuations, as_of) }}use chrono::NaiveDate;use fev::market::{CleanPrice, CreditSpread, DirtyPrice};use fev::nav::{DailyNav, GpResponse, Valuation};use fev::pipeline::CleanStore;pub struct DailyNavEngine<'a> { sources: &'a SourceLake, cleaner: &'a Normalizer, peers: &'a PeerMatcher, spreads: &'a CreditSpreadCurve, gp_response: &'a GpResponseModel, cash_flows: &'a CashFlowLedger, store: &'a CleanStore,}impl<'a> DailyNavEngine<'a> { pub fn collect_sources( &self,
value } pub fn daily_nav( &self, fund: &Fund, as_of: NaiveDate, ) -> DailyNav { let valuations = fund .holdings .iter() .map(|holding| self.roll_forward(holding, as_of)) .collect::<Vec<_>>(); DailyNav::from_holdings(fund, valuations, as_of) }}use chrono::NaiveDate;use fev::market::{CleanPrice, CreditSpread, DirtyPrice};use fev::nav::{DailyNav, GpResponse, Valuation};use fev::pipeline::CleanStore;pub struct DailyNavEngine<'a> { sources: &'a SourceLake, cleaner: &'a Normalizer, peers: &'a PeerMatcher, spreads: &'a CreditSpreadCurve, gp_response: &'a GpResponseModel, cash_flows: &'a CashFlowLedger, store: &'a CleanStore,}impl<'a> DailyNavEngine<'a> { pub fn collect_sources( &self, as_of: NaiveDate, ) -> CleanStore { let raw = self.sources.collect(as_of); let cleaned = self.cleaner.normalize(raw); self.store.replace(cleaned) } pub fn peer_proxy<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> ProxyMove<'h> { let peers = self
DailyNav::from_holdings(fund, valuations, as_of) }}use chrono::NaiveDate;use fev::market::{CleanPrice, CreditSpread, DirtyPrice};use fev::nav::{DailyNav, GpResponse, Valuation};use fev::pipeline::CleanStore;pub struct DailyNavEngine<'a> { sources: &'a SourceLake, cleaner: &'a Normalizer, peers: &'a PeerMatcher, spreads: &'a CreditSpreadCurve, gp_response: &'a GpResponseModel, cash_flows: &'a CashFlowLedger, store: &'a CleanStore,}impl<'a> DailyNavEngine<'a> { pub fn collect_sources( &self, as_of: NaiveDate, ) -> CleanStore { let raw = self.sources.collect(as_of); let cleaned = self.cleaner.normalize(raw); self.store.replace(cleaned) } pub fn peer_proxy<'h>( &self, holding: &'h Holding, as_of: NaiveDate, ) -> ProxyMove<'h> { let peers = self .peers .match_size_and_industry(holding, as_of); peers.weighted_move(holding.last_gp_period(), as_of) } pub fn gp_response<'h>( &self, holding: &'h Holding, proxy: &ProxyMove<'h>, as_of: NaiveDate, ) -> Valuation<'h> { let gp_nav = holding .gp_navs