Derive live bond burden from owned slots

This commit is contained in:
Jan Petykiewicz 2026-04-18 01:46:08 -07:00
commit ec734c5d92
5 changed files with 49 additions and 1 deletions

View file

@ -136,6 +136,8 @@ pub struct RuntimeCompanyAnnualFinanceState {
pub largest_live_bond_principal: Option<u32>,
#[serde(default)]
pub highest_coupon_live_bond_principal: Option<u32>,
#[serde(default)]
pub live_bond_coupon_burden_total: Option<i64>,
pub assigned_share_pool: u32,
pub unassigned_share_pool: u32,
#[serde(default)]
@ -331,6 +333,8 @@ pub struct RuntimeCompanyAnnualBondPolicyState {
#[serde(default)]
pub next_live_bond_maturity_year: Option<u32>,
#[serde(default)]
pub live_bond_coupon_burden_total: Option<i64>,
#[serde(default)]
pub current_cash: Option<i64>,
#[serde(default)]
pub cash_after_full_repayment: Option<i64>,
@ -3104,6 +3108,24 @@ pub fn runtime_company_average_live_bond_coupon(
Some(weighted_coupon_sum / total_principal as f64)
}
pub fn runtime_company_live_bond_coupon_burden_total(
state: &RuntimeState,
company_id: u32,
) -> Option<i64> {
let market_state = state.service_state.company_market_state.get(&company_id)?;
let mut total = 0i64;
for slot in &market_state.live_bond_slots {
let coupon_rate = f32::from_bits(slot.coupon_rate_raw_u32) as f64;
if !coupon_rate.is_finite() {
continue;
}
let coupon_burden =
runtime_round_f64_to_i64((slot.principal as f64) * coupon_rate).unwrap_or(0);
total = total.checked_add(coupon_burden)?;
}
Some(total)
}
pub fn runtime_company_bond_interest_rate_quote_f64(
state: &RuntimeState,
company_id: u32,
@ -3287,6 +3309,7 @@ pub fn runtime_company_annual_bond_policy_state(
matured_live_bond_count,
matured_live_bond_principal_total,
next_live_bond_maturity_year,
live_bond_coupon_burden_total: annual_finance_state.live_bond_coupon_burden_total,
current_cash,
cash_after_full_repayment,
issue_cash_floor,
@ -4037,6 +4060,9 @@ pub fn runtime_company_annual_finance_state(
bond_count: market_state.bond_count,
largest_live_bond_principal: market_state.largest_live_bond_principal,
highest_coupon_live_bond_principal: market_state.highest_coupon_live_bond_principal,
live_bond_coupon_burden_total: runtime_company_live_bond_coupon_burden_total(
state, company_id,
),
assigned_share_pool,
unassigned_share_pool,
cached_share_price: rounded_cached_share_price_i64(market_state.cached_share_price_raw_u32),
@ -7421,6 +7447,10 @@ mod tests {
let average_live_bond_coupon =
runtime_company_average_live_bond_coupon(&state, 7).expect("average coupon");
assert!((average_live_bond_coupon - 0.05).abs() < 1e-6);
assert_eq!(
annual_finance_state.live_bond_coupon_burden_total,
Some(5_000)
);
assert_eq!(runtime_world_prime_rate_baseline(&state), Some(5.0));
assert_eq!(
runtime_world_issue_opinion_term_sum_raw(
@ -7801,6 +7831,7 @@ mod tests {
bond_count: 3,
largest_live_bond_principal: Some(650_000),
highest_coupon_live_bond_principal: Some(500_000),
live_bond_coupon_burden_total: Some(0),
assigned_share_pool: 15_500,
unassigned_share_pool: 4_500,
cached_share_price: Some(40),
@ -8308,6 +8339,7 @@ mod tests {
assert_eq!(bond_state.matured_live_bond_count, Some(0));
assert_eq!(bond_state.matured_live_bond_principal_total, Some(0));
assert_eq!(bond_state.next_live_bond_maturity_year, None);
assert_eq!(bond_state.live_bond_coupon_burden_total, Some(30_000));
assert_eq!(bond_state.current_cash, Some(-400_000));
assert_eq!(bond_state.cash_after_full_repayment, Some(-750_000));
assert_eq!(bond_state.issue_cash_floor, Some(-30_000));

View file

@ -122,6 +122,7 @@ pub struct RuntimeSummary {
pub selected_company_annual_bond_matured_live_bond_count: Option<u32>,
pub selected_company_annual_bond_matured_live_bond_principal_total: Option<u32>,
pub selected_company_annual_bond_next_live_bond_maturity_year: Option<u32>,
pub selected_company_annual_bond_live_bond_coupon_burden_total: Option<i64>,
pub selected_company_annual_bond_current_cash: Option<i64>,
pub selected_company_annual_bond_cash_after_full_repayment: Option<i64>,
pub selected_company_annual_bond_issue_cash_floor: Option<i64>,
@ -592,6 +593,10 @@ impl RuntimeSummary {
selected_company_annual_bond_state
.as_ref()
.and_then(|bond_state| bond_state.next_live_bond_maturity_year),
selected_company_annual_bond_live_bond_coupon_burden_total:
selected_company_annual_bond_state
.as_ref()
.and_then(|bond_state| bond_state.live_bond_coupon_burden_total),
selected_company_annual_bond_current_cash: selected_company_annual_bond_state
.as_ref()
.and_then(|bond_state| bond_state.current_cash),
@ -3215,6 +3220,10 @@ mod tests {
summary.selected_company_annual_bond_next_live_bond_maturity_year,
None
);
assert_eq!(
summary.selected_company_annual_bond_live_bond_coupon_burden_total,
Some(30_000)
);
assert_eq!(
summary.selected_company_annual_bond_current_cash,
Some(-400_000)