Rehost annual stock-capital issue branch
This commit is contained in:
parent
0658626a57
commit
d303ee994b
6 changed files with 789 additions and 39 deletions
|
|
@ -88,7 +88,9 @@ matches the stock-capital branch gate that requires at least two live bonds. The
|
||||||
bond table now also contributes both the largest live bond principal and the chosen
|
bond table now also contributes both the largest live bond principal and the chosen
|
||||||
highest-coupon live bond principal into owned company market and annual-finance state, so the
|
highest-coupon live bond principal into owned company market and annual-finance state, so the
|
||||||
stock-capital approval ladder can extend one rehosted owner-state surface instead of hunting
|
stock-capital approval ladder can extend one rehosted owner-state surface instead of hunting
|
||||||
another isolated finance leaf. A checked-in
|
another isolated finance leaf. The same bond-slot owner state now also exposes the highest live
|
||||||
|
coupon rate, which is enough to run the stock-capital price-to-book approval ladder as another
|
||||||
|
save-native runtime reader instead of a notes-only threshold table. A checked-in
|
||||||
fixed-world finance-policy seam now also carries the raw stock, bond, bankruptcy, and dividend
|
fixed-world finance-policy seam now also carries the raw stock, bond, bankruptcy, and dividend
|
||||||
policy bytes from the `0x32c8` save block, and the first annual creditor-pressure branch now runs
|
policy bytes from the `0x32c8` save block, and the first annual creditor-pressure branch now runs
|
||||||
headlessly as a pure runtime reader over owned annual-finance state, support-adjusted share price,
|
headlessly as a pure runtime reader over owned annual-finance state, support-adjusted share price,
|
||||||
|
|
|
||||||
|
|
@ -53,26 +53,27 @@ pub use runtime::{
|
||||||
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
|
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
|
||||||
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
||||||
RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualDeepDistressState,
|
RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualDeepDistressState,
|
||||||
RuntimeCompanyAnnualFinanceState, RuntimeCompanyAnnualStockRepurchaseState,
|
RuntimeCompanyAnnualFinanceState, RuntimeCompanyAnnualStockIssueState,
|
||||||
RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind,
|
RuntimeCompanyAnnualStockRepurchaseState, RuntimeCompanyBondSlot,
|
||||||
RuntimeCompanyMarketMetric, RuntimeCompanyMarketState, RuntimeCompanyMetric,
|
RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric,
|
||||||
RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget,
|
RuntimeCompanyMarketState, RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate,
|
||||||
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
|
RuntimeCompanyStatSelector, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess,
|
||||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator,
|
||||||
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry,
|
||||||
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
|
RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary,
|
||||||
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
|
RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary,
|
||||||
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
|
RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary,
|
||||||
RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState,
|
RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope,
|
||||||
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
|
RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState,
|
||||||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric,
|
||||||
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldFinanceNeighborhoodCandidate,
|
||||||
|
RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
||||||
runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state,
|
runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state,
|
||||||
runtime_company_annual_finance_state, runtime_company_annual_stock_repurchase_state,
|
runtime_company_annual_finance_state, runtime_company_annual_stock_issue_state,
|
||||||
runtime_company_assigned_share_pool, runtime_company_average_live_bond_coupon,
|
runtime_company_annual_stock_repurchase_state, runtime_company_assigned_share_pool,
|
||||||
runtime_company_book_value_per_share, runtime_company_credit_rating,
|
runtime_company_average_live_bond_coupon, runtime_company_book_value_per_share,
|
||||||
runtime_company_investor_confidence, runtime_company_management_attitude,
|
runtime_company_credit_rating, runtime_company_investor_confidence,
|
||||||
runtime_company_market_value, runtime_company_prime_rate,
|
runtime_company_management_attitude, runtime_company_market_value, runtime_company_prime_rate,
|
||||||
runtime_company_recent_per_share_subscore, runtime_company_stat_value,
|
runtime_company_recent_per_share_subscore, runtime_company_stat_value,
|
||||||
runtime_company_stat_value_f64, runtime_company_unassigned_share_pool,
|
runtime_company_stat_value_f64, runtime_company_unassigned_share_pool,
|
||||||
runtime_world_annual_finance_mode_active, runtime_world_bankruptcy_allowed,
|
runtime_world_annual_finance_mode_active, runtime_world_bankruptcy_allowed,
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,58 @@ pub struct RuntimeCompanyAnnualStockRepurchaseState {
|
||||||
pub eligible_for_single_batch_repurchase: bool,
|
pub eligible_for_single_batch_repurchase: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct RuntimeCompanyAnnualStockIssueState {
|
||||||
|
pub company_id: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub annual_mode_active: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub stock_issue_and_buyback_allowed: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub bond_issue_and_repayment_allowed: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub years_since_founding: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub live_bond_count: Option<u8>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub initial_issue_batch_size: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub trimmed_issue_batch_size: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub share_pressure_basis_points: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub pressured_support_adjusted_share_price_scalar: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub pressured_proceeds: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub book_value_per_share_floor_applied: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub price_to_book_ratio_basis_points: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub current_cash: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub highest_coupon_live_bond_principal: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub highest_coupon_live_bond_rate_basis_points: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub current_issue_age_absolute_counter_delta: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub current_issue_cooldown_floor: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub minimum_price_to_book_ratio_basis_points: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub passes_share_price_floor: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub passes_proceeds_floor: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub passes_cash_gate: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub passes_issue_cooldown_gate: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub passes_coupon_price_to_book_gate: Option<bool>,
|
||||||
|
pub eligible_for_double_tranche_issue: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
pub struct RuntimeTrackPieceCounts {
|
pub struct RuntimeTrackPieceCounts {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -2168,32 +2220,33 @@ fn runtime_decode_saved_f32_value_f64(raw_u32: u32) -> Option<f64> {
|
||||||
Some(value)
|
Some(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runtime_company_recent_per_share_subscore(
|
fn runtime_company_highest_live_bond_coupon_rate_f64(
|
||||||
state: &RuntimeState,
|
state: &RuntimeState,
|
||||||
company_id: u32,
|
company_id: u32,
|
||||||
) -> Option<f64> {
|
) -> Option<f64> {
|
||||||
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||||
if runtime_world_absolute_counter(state)
|
market_state
|
||||||
.is_some_and(|counter| counter == market_state.recent_per_share_cache_absolute_counter)
|
.live_bond_slots
|
||||||
{
|
.iter()
|
||||||
if let Some(cached_value) =
|
.filter_map(|slot| {
|
||||||
runtime_decode_saved_f64_bits(market_state.recent_per_share_cached_value_bits)
|
let value = f32::from_bits(slot.coupon_rate_raw_u32) as f64;
|
||||||
{
|
value.is_finite().then_some(value)
|
||||||
return Some(cached_value);
|
})
|
||||||
}
|
.max_by(|left, right| left.partial_cmp(right).unwrap_or(std::cmp::Ordering::Equal))
|
||||||
}
|
|
||||||
runtime_decode_saved_f32_value_f64(market_state.recent_per_share_subscore_raw_u32)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_company_support_adjusted_share_price_scalar_f64(
|
fn runtime_company_support_adjusted_share_price_scalar_with_pressure_f64(
|
||||||
state: &RuntimeState,
|
state: &RuntimeState,
|
||||||
company_id: u32,
|
company_id: u32,
|
||||||
|
share_pressure_shares: i64,
|
||||||
) -> Option<f64> {
|
) -> Option<f64> {
|
||||||
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||||
if let Some(cached_value) =
|
if let Some(cached_value) =
|
||||||
runtime_decode_saved_f32_value_f64(market_state.cached_share_price_raw_u32)
|
runtime_decode_saved_f32_value_f64(market_state.cached_share_price_raw_u32)
|
||||||
{
|
{
|
||||||
return Some(cached_value.max(0.0001));
|
if share_pressure_shares == 0 {
|
||||||
|
return Some(cached_value.max(0.0001));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if market_state.outstanding_shares == 0 {
|
if market_state.outstanding_shares == 0 {
|
||||||
|
|
@ -2224,9 +2277,12 @@ fn runtime_company_support_adjusted_share_price_scalar_f64(
|
||||||
|
|
||||||
let mutable_support =
|
let mutable_support =
|
||||||
runtime_decode_saved_f32_value_f64(market_state.mutable_support_scalar_raw_u32)?;
|
runtime_decode_saved_f32_value_f64(market_state.mutable_support_scalar_raw_u32)?;
|
||||||
|
let share_pressure =
|
||||||
|
(share_pressure_shares as f64 / market_state.outstanding_shares as f64).clamp(-0.2, 0.2);
|
||||||
|
let effective_mutable_support = mutable_support + share_pressure;
|
||||||
let share_count_growth_ratio = ((market_state.outstanding_shares as f64
|
let share_count_growth_ratio = ((market_state.outstanding_shares as f64
|
||||||
+ 1.4
|
+ 1.4
|
||||||
* mutable_support
|
* effective_mutable_support
|
||||||
* ((market_state.outstanding_shares as f64 / 20_000.0).powf(0.33)))
|
* ((market_state.outstanding_shares as f64 / 20_000.0).powf(0.33)))
|
||||||
/ market_state.outstanding_shares as f64)
|
/ market_state.outstanding_shares as f64)
|
||||||
.clamp(0.3, 6.0);
|
.clamp(0.3, 6.0);
|
||||||
|
|
@ -2242,6 +2298,30 @@ fn runtime_company_support_adjusted_share_price_scalar_f64(
|
||||||
Some(((recent_per_share * share_count_growth_ratio * investor_multiplier) + 1.0).max(0.0001))
|
Some(((recent_per_share * share_count_growth_ratio * investor_multiplier) + 1.0).max(0.0001))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn runtime_company_recent_per_share_subscore(
|
||||||
|
state: &RuntimeState,
|
||||||
|
company_id: u32,
|
||||||
|
) -> Option<f64> {
|
||||||
|
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||||
|
if runtime_world_absolute_counter(state)
|
||||||
|
.is_some_and(|counter| counter == market_state.recent_per_share_cache_absolute_counter)
|
||||||
|
{
|
||||||
|
if let Some(cached_value) =
|
||||||
|
runtime_decode_saved_f64_bits(market_state.recent_per_share_cached_value_bits)
|
||||||
|
{
|
||||||
|
return Some(cached_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runtime_decode_saved_f32_value_f64(market_state.recent_per_share_subscore_raw_u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_company_support_adjusted_share_price_scalar_f64(
|
||||||
|
state: &RuntimeState,
|
||||||
|
company_id: u32,
|
||||||
|
) -> Option<f64> {
|
||||||
|
runtime_company_support_adjusted_share_price_scalar_with_pressure_f64(state, company_id, 0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn runtime_company_investor_confidence(state: &RuntimeState, company_id: u32) -> Option<i64> {
|
pub fn runtime_company_investor_confidence(state: &RuntimeState, company_id: u32) -> Option<i64> {
|
||||||
let company = state
|
let company = state
|
||||||
.companies
|
.companies
|
||||||
|
|
@ -2936,6 +3016,175 @@ pub fn runtime_company_annual_stock_repurchase_state(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn runtime_company_stock_issue_price_to_book_ratio_f64(
|
||||||
|
pressured_support_adjusted_share_price_scalar: f64,
|
||||||
|
book_value_per_share: f64,
|
||||||
|
) -> Option<f64> {
|
||||||
|
let denominator = book_value_per_share.max(1.0);
|
||||||
|
if !pressured_support_adjusted_share_price_scalar.is_finite() || !denominator.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(pressured_support_adjusted_share_price_scalar / denominator)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runtime_company_stock_issue_minimum_price_to_book_ratio_f64(
|
||||||
|
highest_coupon_rate: f64,
|
||||||
|
) -> Option<f64> {
|
||||||
|
if !highest_coupon_rate.is_finite() || highest_coupon_rate <= 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(if highest_coupon_rate <= 0.07 {
|
||||||
|
1.30
|
||||||
|
} else if highest_coupon_rate <= 0.08 {
|
||||||
|
1.20
|
||||||
|
} else if highest_coupon_rate <= 0.09 {
|
||||||
|
1.10
|
||||||
|
} else if highest_coupon_rate <= 0.10 {
|
||||||
|
0.95
|
||||||
|
} else if highest_coupon_rate <= 0.11 {
|
||||||
|
0.80
|
||||||
|
} else if highest_coupon_rate <= 0.12 {
|
||||||
|
0.62
|
||||||
|
} else if highest_coupon_rate <= 0.13 {
|
||||||
|
0.50
|
||||||
|
} else if highest_coupon_rate <= 0.14 {
|
||||||
|
0.35
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime_company_annual_stock_issue_state(
|
||||||
|
state: &RuntimeState,
|
||||||
|
company_id: u32,
|
||||||
|
) -> Option<RuntimeCompanyAnnualStockIssueState> {
|
||||||
|
const ISSUE_PROCEEDS_CAP: i64 = 55_000;
|
||||||
|
const SHARE_PRICE_FLOOR: i64 = 22;
|
||||||
|
const ONE_YEAR_ABSOLUTE_COUNTER_SPAN: i64 = 12 * 28 * 24 * 60;
|
||||||
|
|
||||||
|
let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
|
||||||
|
let current_cash = runtime_company_control_transfer_stat_value_f64(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
)
|
||||||
|
.and_then(runtime_round_f64_to_i64);
|
||||||
|
let highest_coupon_live_bond_principal =
|
||||||
|
annual_finance_state.highest_coupon_live_bond_principal;
|
||||||
|
let highest_coupon_live_bond_rate =
|
||||||
|
runtime_company_highest_live_bond_coupon_rate_f64(state, company_id);
|
||||||
|
let highest_coupon_live_bond_rate_basis_points =
|
||||||
|
highest_coupon_live_bond_rate.and_then(|value| runtime_round_f64_to_i64(value * 10_000.0));
|
||||||
|
let mut initial_issue_batch_size =
|
||||||
|
(annual_finance_state.outstanding_shares / 10 / 1_000) * 1_000;
|
||||||
|
if initial_issue_batch_size < 2_000 {
|
||||||
|
initial_issue_batch_size = 2_000;
|
||||||
|
}
|
||||||
|
let initial_issue_batch_size = Some(initial_issue_batch_size);
|
||||||
|
let mut trimmed_issue_batch_size = initial_issue_batch_size?;
|
||||||
|
let mut pressured_support_adjusted_share_price_scalar =
|
||||||
|
runtime_company_support_adjusted_share_price_scalar_with_pressure_f64(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
-(trimmed_issue_batch_size as i64),
|
||||||
|
);
|
||||||
|
let mut pressured_proceeds = pressured_support_adjusted_share_price_scalar
|
||||||
|
.and_then(|value| runtime_round_f64_to_i64(value * trimmed_issue_batch_size as f64));
|
||||||
|
while trimmed_issue_batch_size > 2_000
|
||||||
|
&& pressured_proceeds.is_some_and(|value| value > ISSUE_PROCEEDS_CAP)
|
||||||
|
{
|
||||||
|
trimmed_issue_batch_size = trimmed_issue_batch_size.saturating_sub(1_000);
|
||||||
|
pressured_support_adjusted_share_price_scalar =
|
||||||
|
runtime_company_support_adjusted_share_price_scalar_with_pressure_f64(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
-(trimmed_issue_batch_size as i64),
|
||||||
|
);
|
||||||
|
pressured_proceeds = pressured_support_adjusted_share_price_scalar
|
||||||
|
.and_then(|value| runtime_round_f64_to_i64(value * trimmed_issue_batch_size as f64));
|
||||||
|
}
|
||||||
|
let pressured_support_adjusted_share_price_scalar_i64 =
|
||||||
|
pressured_support_adjusted_share_price_scalar.and_then(runtime_round_f64_to_i64);
|
||||||
|
let book_value_per_share_floor_applied =
|
||||||
|
runtime_company_book_value_per_share(state, company_id).map(|value| value.max(1));
|
||||||
|
let price_to_book_ratio = pressured_support_adjusted_share_price_scalar
|
||||||
|
.zip(book_value_per_share_floor_applied)
|
||||||
|
.and_then(|(share_price, book_value)| {
|
||||||
|
runtime_company_stock_issue_price_to_book_ratio_f64(share_price, book_value as f64)
|
||||||
|
});
|
||||||
|
let price_to_book_ratio_basis_points =
|
||||||
|
price_to_book_ratio.and_then(|value| runtime_round_f64_to_i64(value * 10_000.0));
|
||||||
|
let minimum_price_to_book_ratio = highest_coupon_live_bond_rate
|
||||||
|
.and_then(runtime_company_stock_issue_minimum_price_to_book_ratio_f64);
|
||||||
|
let minimum_price_to_book_ratio_basis_points =
|
||||||
|
minimum_price_to_book_ratio.and_then(|value| runtime_round_f64_to_i64(value * 10_000.0));
|
||||||
|
let passes_share_price_floor =
|
||||||
|
pressured_support_adjusted_share_price_scalar_i64.map(|value| value >= SHARE_PRICE_FLOOR);
|
||||||
|
let passes_proceeds_floor = pressured_proceeds.map(|value| value >= ISSUE_PROCEEDS_CAP);
|
||||||
|
let passes_cash_gate = current_cash
|
||||||
|
.zip(highest_coupon_live_bond_principal)
|
||||||
|
.map(|(cash, principal)| cash <= i64::from(principal) + 5_000);
|
||||||
|
let passes_issue_cooldown_gate = Some(
|
||||||
|
annual_finance_state
|
||||||
|
.current_issue_age_absolute_counter_delta
|
||||||
|
.is_none_or(|delta| delta >= ONE_YEAR_ABSOLUTE_COUNTER_SPAN),
|
||||||
|
);
|
||||||
|
let passes_coupon_price_to_book_gate = price_to_book_ratio_basis_points
|
||||||
|
.zip(minimum_price_to_book_ratio_basis_points)
|
||||||
|
.map(|(actual, minimum)| actual >= minimum);
|
||||||
|
let eligible_for_double_tranche_issue = runtime_world_annual_finance_mode_active(state)
|
||||||
|
== Some(true)
|
||||||
|
&& runtime_world_stock_issue_and_buyback_allowed(state) == Some(true)
|
||||||
|
&& runtime_world_bond_issue_and_repayment_allowed(state) == Some(true)
|
||||||
|
&& annual_finance_state.bond_count >= 2
|
||||||
|
&& annual_finance_state
|
||||||
|
.years_since_founding
|
||||||
|
.is_some_and(|years| years >= 1)
|
||||||
|
&& !runtime_company_annual_creditor_pressure_state(state, company_id)?
|
||||||
|
.eligible_for_bankruptcy_branch
|
||||||
|
&& !runtime_company_annual_deep_distress_state(state, company_id)?
|
||||||
|
.eligible_for_bankruptcy_fallback
|
||||||
|
&& !runtime_company_annual_stock_repurchase_state(state, company_id)?
|
||||||
|
.eligible_for_single_batch_repurchase
|
||||||
|
&& passes_share_price_floor == Some(true)
|
||||||
|
&& passes_proceeds_floor == Some(true)
|
||||||
|
&& passes_cash_gate == Some(true)
|
||||||
|
&& passes_issue_cooldown_gate == Some(true)
|
||||||
|
&& passes_coupon_price_to_book_gate == Some(true);
|
||||||
|
Some(RuntimeCompanyAnnualStockIssueState {
|
||||||
|
company_id,
|
||||||
|
annual_mode_active: runtime_world_annual_finance_mode_active(state),
|
||||||
|
stock_issue_and_buyback_allowed: runtime_world_stock_issue_and_buyback_allowed(state),
|
||||||
|
bond_issue_and_repayment_allowed: runtime_world_bond_issue_and_repayment_allowed(state),
|
||||||
|
years_since_founding: annual_finance_state.years_since_founding,
|
||||||
|
live_bond_count: Some(annual_finance_state.bond_count),
|
||||||
|
initial_issue_batch_size,
|
||||||
|
trimmed_issue_batch_size: Some(trimmed_issue_batch_size),
|
||||||
|
share_pressure_basis_points: runtime_round_f64_to_i64(
|
||||||
|
-(trimmed_issue_batch_size as f64) / annual_finance_state.outstanding_shares as f64
|
||||||
|
* 10_000.0,
|
||||||
|
),
|
||||||
|
pressured_support_adjusted_share_price_scalar:
|
||||||
|
pressured_support_adjusted_share_price_scalar_i64,
|
||||||
|
pressured_proceeds,
|
||||||
|
book_value_per_share_floor_applied,
|
||||||
|
price_to_book_ratio_basis_points,
|
||||||
|
current_cash,
|
||||||
|
highest_coupon_live_bond_principal,
|
||||||
|
highest_coupon_live_bond_rate_basis_points,
|
||||||
|
current_issue_age_absolute_counter_delta: annual_finance_state
|
||||||
|
.current_issue_age_absolute_counter_delta,
|
||||||
|
current_issue_cooldown_floor: Some(ONE_YEAR_ABSOLUTE_COUNTER_SPAN),
|
||||||
|
minimum_price_to_book_ratio_basis_points,
|
||||||
|
passes_share_price_floor,
|
||||||
|
passes_proceeds_floor,
|
||||||
|
passes_cash_gate,
|
||||||
|
passes_issue_cooldown_gate,
|
||||||
|
passes_coupon_price_to_book_gate,
|
||||||
|
eligible_for_double_tranche_issue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn runtime_company_annual_creditor_pressure_state(
|
pub fn runtime_company_annual_creditor_pressure_state(
|
||||||
state: &RuntimeState,
|
state: &RuntimeState,
|
||||||
company_id: u32,
|
company_id: u32,
|
||||||
|
|
@ -7372,6 +7621,188 @@ mod tests {
|
||||||
assert!(!repurchase_state.eligible_for_single_batch_repurchase);
|
assert!(!repurchase_state.eligible_for_single_batch_repurchase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derives_annual_stock_issue_state_from_rehosted_owner_state() {
|
||||||
|
let mut year_stat_family_qword_bits = vec![
|
||||||
|
0u64;
|
||||||
|
((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize
|
||||||
|
];
|
||||||
|
let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
|
||||||
|
let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
|
||||||
|
bits[index] = value.to_bits();
|
||||||
|
};
|
||||||
|
write_current_value(&mut year_stat_family_qword_bits, 0x0d, 250_000.0);
|
||||||
|
|
||||||
|
let state = RuntimeState {
|
||||||
|
calendar: CalendarPoint {
|
||||||
|
year: 1845,
|
||||||
|
month_slot: 0,
|
||||||
|
phase_slot: 0,
|
||||||
|
tick_slot: 0,
|
||||||
|
},
|
||||||
|
world_flags: BTreeMap::new(),
|
||||||
|
save_profile: RuntimeSaveProfileState::default(),
|
||||||
|
world_restore: RuntimeWorldRestoreState {
|
||||||
|
partial_year_progress_raw_u8: Some(0x0c),
|
||||||
|
stock_issue_and_buyback_policy_raw_u8: Some(0),
|
||||||
|
stock_issue_and_buyback_allowed: Some(true),
|
||||||
|
bond_issue_and_repayment_policy_raw_u8: Some(0),
|
||||||
|
bond_issue_and_repayment_allowed: Some(true),
|
||||||
|
issue_37_value: Some(2),
|
||||||
|
issue_37_multiplier_raw_u32: Some(1.0f32.to_bits()),
|
||||||
|
issue_37_multiplier_value_f32_text: Some("1.000000".to_string()),
|
||||||
|
absolute_counter_raw_u32: Some(885_911_040),
|
||||||
|
..RuntimeWorldRestoreState::default()
|
||||||
|
},
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
companies: vec![RuntimeCompany {
|
||||||
|
company_id: 14,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||||
|
linked_chairman_profile_id: Some(8),
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
}],
|
||||||
|
selected_company_id: None,
|
||||||
|
players: Vec::new(),
|
||||||
|
selected_player_id: None,
|
||||||
|
chairman_profiles: vec![RuntimeChairmanProfile {
|
||||||
|
profile_id: 8,
|
||||||
|
name: "Taylor".to_string(),
|
||||||
|
active: true,
|
||||||
|
current_cash: 200,
|
||||||
|
linked_company_id: Some(14),
|
||||||
|
company_holdings: BTreeMap::from([(14, 14_000)]),
|
||||||
|
holdings_value_total: 0,
|
||||||
|
net_worth_total: 0,
|
||||||
|
purchasing_power_total: 0,
|
||||||
|
}],
|
||||||
|
selected_chairman_profile_id: None,
|
||||||
|
trains: Vec::new(),
|
||||||
|
locomotive_catalog: Vec::new(),
|
||||||
|
cargo_catalog: Vec::new(),
|
||||||
|
territories: Vec::new(),
|
||||||
|
company_territory_track_piece_counts: Vec::new(),
|
||||||
|
company_territory_access: Vec::new(),
|
||||||
|
packed_event_collection: None,
|
||||||
|
event_runtime_records: Vec::new(),
|
||||||
|
candidate_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_cost: BTreeMap::new(),
|
||||||
|
all_cargo_price_override: None,
|
||||||
|
named_cargo_price_overrides: BTreeMap::new(),
|
||||||
|
all_cargo_production_override: None,
|
||||||
|
factory_cargo_production_override: None,
|
||||||
|
farm_mine_cargo_production_override: None,
|
||||||
|
named_cargo_production_overrides: BTreeMap::new(),
|
||||||
|
cargo_production_overrides: BTreeMap::new(),
|
||||||
|
world_runtime_variables: BTreeMap::new(),
|
||||||
|
company_runtime_variables: BTreeMap::new(),
|
||||||
|
player_runtime_variables: BTreeMap::new(),
|
||||||
|
territory_runtime_variables: BTreeMap::new(),
|
||||||
|
world_scalar_overrides: BTreeMap::new(),
|
||||||
|
special_conditions: BTreeMap::new(),
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
|
||||||
|
chairman_personality_raw_u8: BTreeMap::from([(8, 20)]),
|
||||||
|
company_market_state: BTreeMap::from([(
|
||||||
|
14,
|
||||||
|
RuntimeCompanyMarketState {
|
||||||
|
outstanding_shares: 20_000,
|
||||||
|
bond_count: 2,
|
||||||
|
highest_coupon_live_bond_principal: Some(300_000),
|
||||||
|
current_issue_calendar_word: 0x0101_0725,
|
||||||
|
current_issue_calendar_word_2: 0x0001_0001,
|
||||||
|
founding_year: 1840,
|
||||||
|
cached_share_price_raw_u32: 35.0f32.to_bits(),
|
||||||
|
recent_per_share_cache_absolute_counter: 885_911_040,
|
||||||
|
recent_per_share_cached_value_bits: 34.0f64.to_bits(),
|
||||||
|
city_connection_latch: false,
|
||||||
|
live_bond_slots: vec![
|
||||||
|
RuntimeCompanyBondSlot {
|
||||||
|
slot_index: 0,
|
||||||
|
principal: 300_000,
|
||||||
|
coupon_rate_raw_u32: 0.11f32.to_bits(),
|
||||||
|
},
|
||||||
|
RuntimeCompanyBondSlot {
|
||||||
|
slot_index: 1,
|
||||||
|
principal: 200_000,
|
||||||
|
coupon_rate_raw_u32: 0.07f32.to_bits(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direct_control_transfer_float_fields_raw_u32: BTreeMap::from([(
|
||||||
|
0x32f,
|
||||||
|
30.0f32.to_bits(),
|
||||||
|
)]),
|
||||||
|
year_stat_family_qword_bits,
|
||||||
|
..RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let stock_issue_state =
|
||||||
|
runtime_company_annual_stock_issue_state(&state, 14).expect("stock issue state");
|
||||||
|
assert_eq!(stock_issue_state.live_bond_count, Some(2));
|
||||||
|
assert_eq!(stock_issue_state.initial_issue_batch_size, Some(2_000));
|
||||||
|
assert_eq!(stock_issue_state.trimmed_issue_batch_size, Some(2_000));
|
||||||
|
assert_eq!(stock_issue_state.share_pressure_basis_points, Some(-1_000));
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.pressured_support_adjusted_share_price_scalar,
|
||||||
|
Some(35)
|
||||||
|
);
|
||||||
|
assert_eq!(stock_issue_state.pressured_proceeds, Some(70_000));
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.book_value_per_share_floor_applied,
|
||||||
|
Some(30)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.price_to_book_ratio_basis_points,
|
||||||
|
Some(11_667)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.highest_coupon_live_bond_rate_basis_points,
|
||||||
|
Some(1_100)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.minimum_price_to_book_ratio_basis_points,
|
||||||
|
Some(8_000)
|
||||||
|
);
|
||||||
|
assert_eq!(stock_issue_state.current_cash, Some(250_000));
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.highest_coupon_live_bond_principal,
|
||||||
|
Some(300_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.current_issue_age_absolute_counter_delta,
|
||||||
|
Some(967_680)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.current_issue_cooldown_floor,
|
||||||
|
Some(483_840)
|
||||||
|
);
|
||||||
|
assert_eq!(stock_issue_state.passes_share_price_floor, Some(true));
|
||||||
|
assert_eq!(stock_issue_state.passes_proceeds_floor, Some(true));
|
||||||
|
assert_eq!(stock_issue_state.passes_cash_gate, Some(true));
|
||||||
|
assert_eq!(stock_issue_state.passes_issue_cooldown_gate, Some(true));
|
||||||
|
assert_eq!(
|
||||||
|
stock_issue_state.passes_coupon_price_to_book_gate,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert!(stock_issue_state.eligible_for_double_tranche_issue);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reads_company_market_metrics_from_annual_finance_reader() {
|
fn reads_company_market_metrics_from_annual_finance_reader() {
|
||||||
let current_issue_calendar_word = 0x0101_0726;
|
let current_issue_calendar_word = 0x0101_0726;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use crate::{
|
use crate::{
|
||||||
CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state,
|
CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state,
|
||||||
runtime_company_annual_deep_distress_state, runtime_company_annual_finance_state,
|
runtime_company_annual_deep_distress_state, runtime_company_annual_finance_state,
|
||||||
runtime_company_annual_stock_repurchase_state, runtime_company_unassigned_share_pool,
|
runtime_company_annual_stock_issue_state, runtime_company_annual_stock_repurchase_state,
|
||||||
|
runtime_company_unassigned_share_pool,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn raw_u32_to_f32_text(raw: u32) -> String {
|
fn raw_u32_to_f32_text(raw: u32) -> String {
|
||||||
|
|
@ -124,6 +125,26 @@ pub struct RuntimeSummary {
|
||||||
pub selected_company_stock_repurchase_affordability_cash_floor: Option<i64>,
|
pub selected_company_stock_repurchase_affordability_cash_floor: Option<i64>,
|
||||||
pub selected_company_stock_repurchase_unassigned_share_pool: Option<u32>,
|
pub selected_company_stock_repurchase_unassigned_share_pool: Option<u32>,
|
||||||
pub selected_company_stock_repurchase_eligible_for_single_batch: Option<bool>,
|
pub selected_company_stock_repurchase_eligible_for_single_batch: Option<bool>,
|
||||||
|
pub selected_company_stock_issue_live_bond_count: Option<u8>,
|
||||||
|
pub selected_company_stock_issue_initial_batch_size: Option<u32>,
|
||||||
|
pub selected_company_stock_issue_trimmed_batch_size: Option<u32>,
|
||||||
|
pub selected_company_stock_issue_share_pressure_basis_points: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_pressured_share_price_scalar: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_pressured_proceeds: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_book_value_per_share_floor_applied: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_price_to_book_ratio_basis_points: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_current_cash: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_highest_coupon_live_bond_principal: Option<u32>,
|
||||||
|
pub selected_company_stock_issue_highest_coupon_live_bond_rate_basis_points: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_current_issue_age_absolute_counter_delta: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_current_issue_cooldown_floor: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_minimum_price_to_book_ratio_basis_points: Option<i64>,
|
||||||
|
pub selected_company_stock_issue_passes_share_price_floor: Option<bool>,
|
||||||
|
pub selected_company_stock_issue_passes_proceeds_floor: Option<bool>,
|
||||||
|
pub selected_company_stock_issue_passes_cash_gate: Option<bool>,
|
||||||
|
pub selected_company_stock_issue_passes_issue_cooldown_gate: Option<bool>,
|
||||||
|
pub selected_company_stock_issue_passes_coupon_price_to_book_gate: Option<bool>,
|
||||||
|
pub selected_company_stock_issue_eligible_for_double_tranche: Option<bool>,
|
||||||
pub player_count: usize,
|
pub player_count: usize,
|
||||||
pub chairman_profile_count: usize,
|
pub chairman_profile_count: usize,
|
||||||
pub active_chairman_profile_count: usize,
|
pub active_chairman_profile_count: usize,
|
||||||
|
|
@ -224,6 +245,9 @@ impl RuntimeSummary {
|
||||||
state.selected_company_id.and_then(|company_id| {
|
state.selected_company_id.and_then(|company_id| {
|
||||||
runtime_company_annual_stock_repurchase_state(state, company_id)
|
runtime_company_annual_stock_repurchase_state(state, company_id)
|
||||||
});
|
});
|
||||||
|
let selected_company_stock_issue_state = state
|
||||||
|
.selected_company_id
|
||||||
|
.and_then(|company_id| runtime_company_annual_stock_issue_state(state, company_id));
|
||||||
Self {
|
Self {
|
||||||
calendar: state.calendar,
|
calendar: state.calendar,
|
||||||
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
||||||
|
|
@ -550,6 +574,81 @@ impl RuntimeSummary {
|
||||||
selected_company_stock_repurchase_state
|
selected_company_stock_repurchase_state
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|repurchase_state| repurchase_state.eligible_for_single_batch_repurchase),
|
.map(|repurchase_state| repurchase_state.eligible_for_single_batch_repurchase),
|
||||||
|
selected_company_stock_issue_live_bond_count: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.live_bond_count),
|
||||||
|
selected_company_stock_issue_initial_batch_size: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.initial_issue_batch_size),
|
||||||
|
selected_company_stock_issue_trimmed_batch_size: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.trimmed_issue_batch_size),
|
||||||
|
selected_company_stock_issue_share_pressure_basis_points:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.share_pressure_basis_points),
|
||||||
|
selected_company_stock_issue_pressured_share_price_scalar:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| {
|
||||||
|
issue_state.pressured_support_adjusted_share_price_scalar
|
||||||
|
}),
|
||||||
|
selected_company_stock_issue_pressured_proceeds: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.pressured_proceeds),
|
||||||
|
selected_company_stock_issue_book_value_per_share_floor_applied:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.book_value_per_share_floor_applied),
|
||||||
|
selected_company_stock_issue_price_to_book_ratio_basis_points:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.price_to_book_ratio_basis_points),
|
||||||
|
selected_company_stock_issue_current_cash: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.current_cash),
|
||||||
|
selected_company_stock_issue_highest_coupon_live_bond_principal:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.highest_coupon_live_bond_principal),
|
||||||
|
selected_company_stock_issue_highest_coupon_live_bond_rate_basis_points:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.highest_coupon_live_bond_rate_basis_points),
|
||||||
|
selected_company_stock_issue_current_issue_age_absolute_counter_delta:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.current_issue_age_absolute_counter_delta),
|
||||||
|
selected_company_stock_issue_current_issue_cooldown_floor:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.current_issue_cooldown_floor),
|
||||||
|
selected_company_stock_issue_minimum_price_to_book_ratio_basis_points:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.minimum_price_to_book_ratio_basis_points),
|
||||||
|
selected_company_stock_issue_passes_share_price_floor:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.passes_share_price_floor),
|
||||||
|
selected_company_stock_issue_passes_proceeds_floor: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.passes_proceeds_floor),
|
||||||
|
selected_company_stock_issue_passes_cash_gate: selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.passes_cash_gate),
|
||||||
|
selected_company_stock_issue_passes_issue_cooldown_gate:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.passes_issue_cooldown_gate),
|
||||||
|
selected_company_stock_issue_passes_coupon_price_to_book_gate:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|issue_state| issue_state.passes_coupon_price_to_book_gate),
|
||||||
|
selected_company_stock_issue_eligible_for_double_tranche:
|
||||||
|
selected_company_stock_issue_state
|
||||||
|
.as_ref()
|
||||||
|
.map(|issue_state| issue_state.eligible_for_double_tranche_issue),
|
||||||
player_count: state.players.len(),
|
player_count: state.players.len(),
|
||||||
chairman_profile_count: state.chairman_profiles.len(),
|
chairman_profile_count: state.chairman_profiles.len(),
|
||||||
active_chairman_profile_count: state
|
active_chairman_profile_count: state
|
||||||
|
|
@ -2811,4 +2910,216 @@ mod tests {
|
||||||
Some(false)
|
Some(false)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn summarizes_selected_company_stock_issue_state() {
|
||||||
|
let mut year_stat_family_qword_bits = vec![
|
||||||
|
0u64;
|
||||||
|
((crate::RUNTIME_COMPANY_STAT_SLOT_COUNT + 2)
|
||||||
|
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize
|
||||||
|
];
|
||||||
|
year_stat_family_qword_bits[(crate::RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH
|
||||||
|
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize] = 250_000.0f64.to_bits();
|
||||||
|
|
||||||
|
let state = RuntimeState {
|
||||||
|
calendar: CalendarPoint {
|
||||||
|
year: 1845,
|
||||||
|
month_slot: 0,
|
||||||
|
phase_slot: 0,
|
||||||
|
tick_slot: 0,
|
||||||
|
},
|
||||||
|
world_flags: BTreeMap::new(),
|
||||||
|
save_profile: RuntimeSaveProfileState::default(),
|
||||||
|
world_restore: RuntimeWorldRestoreState {
|
||||||
|
partial_year_progress_raw_u8: Some(0x0c),
|
||||||
|
stock_issue_and_buyback_policy_raw_u8: Some(0),
|
||||||
|
stock_issue_and_buyback_allowed: Some(true),
|
||||||
|
bond_issue_and_repayment_policy_raw_u8: Some(0),
|
||||||
|
bond_issue_and_repayment_allowed: Some(true),
|
||||||
|
issue_37_value: Some(2),
|
||||||
|
issue_37_multiplier_raw_u32: Some(1.0f32.to_bits()),
|
||||||
|
issue_37_multiplier_value_f32_text: Some("1.000000".to_string()),
|
||||||
|
absolute_counter_raw_u32: Some(885_911_040),
|
||||||
|
..RuntimeWorldRestoreState::default()
|
||||||
|
},
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
companies: vec![RuntimeCompany {
|
||||||
|
company_id: 14,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
controller_kind: crate::RuntimeCompanyControllerKind::Unknown,
|
||||||
|
linked_chairman_profile_id: Some(8),
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
}],
|
||||||
|
selected_company_id: Some(14),
|
||||||
|
players: Vec::new(),
|
||||||
|
selected_player_id: None,
|
||||||
|
chairman_profiles: vec![crate::RuntimeChairmanProfile {
|
||||||
|
profile_id: 8,
|
||||||
|
name: "Taylor".to_string(),
|
||||||
|
active: true,
|
||||||
|
current_cash: 200,
|
||||||
|
linked_company_id: Some(14),
|
||||||
|
company_holdings: BTreeMap::from([(14, 14_000)]),
|
||||||
|
holdings_value_total: 0,
|
||||||
|
net_worth_total: 0,
|
||||||
|
purchasing_power_total: 0,
|
||||||
|
}],
|
||||||
|
selected_chairman_profile_id: None,
|
||||||
|
trains: Vec::new(),
|
||||||
|
locomotive_catalog: Vec::new(),
|
||||||
|
cargo_catalog: Vec::new(),
|
||||||
|
territories: Vec::new(),
|
||||||
|
company_territory_track_piece_counts: Vec::new(),
|
||||||
|
company_territory_access: Vec::new(),
|
||||||
|
packed_event_collection: None,
|
||||||
|
event_runtime_records: Vec::new(),
|
||||||
|
candidate_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_cost: Vec::new().into_iter().collect(),
|
||||||
|
all_cargo_price_override: None,
|
||||||
|
named_cargo_price_overrides: BTreeMap::new(),
|
||||||
|
all_cargo_production_override: None,
|
||||||
|
factory_cargo_production_override: None,
|
||||||
|
farm_mine_cargo_production_override: None,
|
||||||
|
named_cargo_production_overrides: BTreeMap::new(),
|
||||||
|
cargo_production_overrides: BTreeMap::new(),
|
||||||
|
world_runtime_variables: BTreeMap::new(),
|
||||||
|
company_runtime_variables: BTreeMap::new(),
|
||||||
|
player_runtime_variables: BTreeMap::new(),
|
||||||
|
territory_runtime_variables: BTreeMap::new(),
|
||||||
|
world_scalar_overrides: BTreeMap::new(),
|
||||||
|
special_conditions: BTreeMap::new(),
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
world_issue_opinion_base_terms_raw_i32: vec![0; 0x3b],
|
||||||
|
chairman_personality_raw_u8: BTreeMap::from([(8, 20)]),
|
||||||
|
company_market_state: BTreeMap::from([(
|
||||||
|
14,
|
||||||
|
crate::RuntimeCompanyMarketState {
|
||||||
|
outstanding_shares: 20_000,
|
||||||
|
bond_count: 2,
|
||||||
|
highest_coupon_live_bond_principal: Some(300_000),
|
||||||
|
current_issue_calendar_word: 0x0101_0725,
|
||||||
|
current_issue_calendar_word_2: 0x0001_0001,
|
||||||
|
founding_year: 1840,
|
||||||
|
cached_share_price_raw_u32: 35.0f32.to_bits(),
|
||||||
|
recent_per_share_cache_absolute_counter: 885_911_040,
|
||||||
|
recent_per_share_cached_value_bits: 34.0f64.to_bits(),
|
||||||
|
live_bond_slots: vec![
|
||||||
|
crate::RuntimeCompanyBondSlot {
|
||||||
|
slot_index: 0,
|
||||||
|
principal: 300_000,
|
||||||
|
coupon_rate_raw_u32: 0.11f32.to_bits(),
|
||||||
|
},
|
||||||
|
crate::RuntimeCompanyBondSlot {
|
||||||
|
slot_index: 1,
|
||||||
|
principal: 200_000,
|
||||||
|
coupon_rate_raw_u32: 0.07f32.to_bits(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direct_control_transfer_float_fields_raw_u32: BTreeMap::from([(
|
||||||
|
0x32f,
|
||||||
|
30.0f32.to_bits(),
|
||||||
|
)]),
|
||||||
|
year_stat_family_qword_bits,
|
||||||
|
..crate::RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let summary = RuntimeSummary::from_state(&state);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_live_bond_count,
|
||||||
|
Some(2)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_initial_batch_size,
|
||||||
|
Some(2_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_trimmed_batch_size,
|
||||||
|
Some(2_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_share_pressure_basis_points,
|
||||||
|
Some(-1_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_pressured_share_price_scalar,
|
||||||
|
Some(35)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_pressured_proceeds,
|
||||||
|
Some(70_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_book_value_per_share_floor_applied,
|
||||||
|
Some(30)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_price_to_book_ratio_basis_points,
|
||||||
|
Some(11_667)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_current_cash,
|
||||||
|
Some(250_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_highest_coupon_live_bond_principal,
|
||||||
|
Some(300_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_highest_coupon_live_bond_rate_basis_points,
|
||||||
|
Some(1_100)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_current_issue_age_absolute_counter_delta,
|
||||||
|
Some(967_680)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_current_issue_cooldown_floor,
|
||||||
|
Some(483_840)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_minimum_price_to_book_ratio_basis_points,
|
||||||
|
Some(8_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_passes_share_price_floor,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_passes_proceeds_floor,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_passes_cash_gate,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_passes_issue_cooldown_gate,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_passes_coupon_price_to_book_gate,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_stock_issue_eligible_for_double_tranche,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,10 +134,12 @@ The highest-value next passes are now:
|
||||||
and last bankruptcy for later annual finance-policy rehosting; live bond-slot count now travels through that same owned annual-finance
|
and last bankruptcy for later annual finance-policy rehosting; live bond-slot count now travels through that same owned annual-finance
|
||||||
state for the stock-capital branch gate, and the grounded bond table now also contributes both
|
state for the stock-capital branch gate, and the grounded bond table now also contributes both
|
||||||
the largest live bond principal and the chosen highest-coupon live bond principal into that same
|
the largest live bond principal and the chosen highest-coupon live bond principal into that same
|
||||||
owner-state surface; the same fixed-world save block now also carries the raw stock, bond,
|
owner-state surface, plus the highest live coupon rate for the stock-capital approval ladder;
|
||||||
|
the same fixed-world save block now also carries the raw stock, bond,
|
||||||
bankruptcy, and dividend finance-policy bytes, and the first annual creditor-pressure branch now
|
bankruptcy, and dividend finance-policy bytes, and the first annual creditor-pressure branch now
|
||||||
executes as a pure runtime reader over that owner state instead of remaining atlas-only; the
|
executes as a pure runtime reader over that owner state instead of remaining atlas-only; the
|
||||||
later deep-distress bankruptcy fallback now runs on that same save-native cash and trailing-
|
later deep-distress bankruptcy fallback now runs on that same save-native cash and trailing-
|
||||||
|
profit seam; the annual stock-repurchase and stock-capital issue branches now do too
|
||||||
net-profit surface too; the same owner seam now also carries the fixed-world building-density
|
net-profit surface too; the same owner seam now also carries the fixed-world building-density
|
||||||
growth setting plus the linked chairman personality byte, which is enough to run the annual
|
growth setting plus the linked chairman personality byte, which is enough to run the annual
|
||||||
stock-repurchase gate headlessly as another pure reader
|
stock-repurchase gate headlessly as another pure reader
|
||||||
|
|
|
||||||
|
|
@ -224,8 +224,9 @@ annual finance-policy gates in the atlas. Live bond-slot count now also flows th
|
||||||
owned company market and annual-finance state, matching the stock-capital branch gate that needs
|
owned company market and annual-finance state, matching the stock-capital branch gate that needs
|
||||||
at least two live bonds. The same grounded bond table now also contributes both the largest live
|
at least two live bonds. The same grounded bond table now also contributes both the largest live
|
||||||
bond principal and the chosen highest-coupon live bond principal into owned company market and
|
bond principal and the chosen highest-coupon live bond principal into owned company market and
|
||||||
annual-finance state, so later stock-capital gates can extend a rehosted owner-state seam instead
|
annual-finance state, and now also exposes the highest live coupon rate, so the stock-capital
|
||||||
of guessing another finance leaf. The same fixed-world save block now also carries the raw stock,
|
price-to-book ladder can extend a rehosted owner-state seam instead of guessing another finance
|
||||||
|
leaf. The same fixed-world save block now also carries the raw stock,
|
||||||
bond, bankruptcy, and dividend finance-policy bytes, and the earliest annual creditor-pressure
|
bond, bankruptcy, and dividend finance-policy bytes, and the earliest annual creditor-pressure
|
||||||
bankruptcy branch now runs as a pure runtime reader over owned annual-finance state, support-
|
bankruptcy branch now runs as a pure runtime reader over owned annual-finance state, support-
|
||||||
adjusted share price, and those policy bytes rather than staying in atlas prose only. The later
|
adjusted share price, and those policy bytes rather than staying in atlas prose only. The later
|
||||||
|
|
@ -233,7 +234,9 @@ deep-distress bankruptcy fallback now rides the same owner-state seam too, using
|
||||||
cash reader plus the first three trailing net-profit years instead of a parallel raw-offset guess.
|
cash reader plus the first three trailing net-profit years instead of a parallel raw-offset guess.
|
||||||
That same seam now also carries the fixed-world building-density growth setting plus the linked
|
That same seam now also carries the fixed-world building-density growth setting plus the linked
|
||||||
chairman personality byte, which is enough to rehost the annual stock-repurchase gate on owned
|
chairman personality byte, which is enough to rehost the annual stock-repurchase gate on owned
|
||||||
save/runtime state instead of another threshold-only note.
|
save/runtime state instead of another threshold-only note. The stock-capital issue branch now
|
||||||
|
rides that same seam too, with share-pressure, cooldown, and price-to-book gate state exposed as
|
||||||
|
normal runtime readers.
|
||||||
|
|
||||||
## Why This Boundary
|
## Why This Boundary
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue