Derive live bond burden from owned slots
This commit is contained in:
parent
3d31b0b65e
commit
ec734c5d92
5 changed files with 49 additions and 1 deletions
|
|
@ -112,6 +112,9 @@ of stopping at reader-only diagnostics.
|
|||
The same save-native live bond-slot surface now also carries per-slot maturity years all the way
|
||||
through runtime summaries and annual bond policy state, which is the next owner seam needed for
|
||||
shellless repayment and bond-burden simulation instead of another round of raw-slot guessing.
|
||||
That same seam now also derives the current live coupon burden directly from owned bond slots, so
|
||||
later finance service work can consume a runtime reader instead of recomputing from scattered raw
|
||||
fields.
|
||||
The same seam now also carries the fixed-world building-density growth setting plus the linked
|
||||
chairman personality byte, which is enough to run the annual stock-repurchase gate as another
|
||||
pure reader over owned save-native state instead of a guessed finance-side approximation.
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -152,7 +152,8 @@ The highest-value next passes are now:
|
|||
creditor-pressure-bankruptcy, deep-distress-bankruptcy, dividend-adjustment, stock-repurchase,
|
||||
stock-issue, and bond-issue branches against owned runtime state; the same live bond-slot owner
|
||||
surface now also carries save-native maturity years into annual bond policy summaries as the
|
||||
next seam for shellless repayment work
|
||||
next seam for shellless repayment work, and now also derives the current live coupon burden
|
||||
directly from owned bond slots
|
||||
- the project rule on the remaining closure work is now explicit too: when one runtime-facing field
|
||||
is still ambiguous, prefer rehosting the owning source state or real reader/setter family first
|
||||
instead of guessing another derived leaf field from neighboring raw offsets
|
||||
|
|
|
|||
|
|
@ -250,6 +250,9 @@ outstanding-share, issue-calendar, live bond-slot, and company activity state.
|
|||
The same owned live bond-slot surface now also carries maturity years through save import,
|
||||
runtime state, and annual bond summaries, which is the right next base for shellless repayment and
|
||||
bond-service simulation.
|
||||
That same owner seam now also derives live coupon burden totals directly from saved bond slots,
|
||||
which gives later finance service work a bounded runtime reader instead of another synthetic
|
||||
finance leaf.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue