Source code for bamengine.events.bankruptcy

"""
Bankruptcy events for insolvency detection and agent replacement.

This module defines the bankruptcy phase events that execute at the end of each
period. Firms update net worth with retained earnings, insolvent agents are
detected and removed, and replacement agents are spawned to maintain population.

Event Sequence
--------------
The bankruptcy events execute in this order:

1. FirmsUpdateNetWorth - Add retained profits/losses to net worth
2. MarkBankruptFirms - Detect insolvent firms (A < 0 or Y = 0)
3. MarkBankruptBanks - Detect insolvent banks (E < 0)
4. SpawnReplacementFirms - Create new firms to replace bankrupt ones
5. SpawnReplacementBanks - Create new banks to replace bankrupt ones

Design Notes
------------
- Bankruptcy criteria: firms (A < 0 or Y = 0), banks (E < 0)
- Bankrupt firms: fire all workers, purge loans
- Bankrupt banks: purge all loans
- Replacement firms: inherit trimmed mean of survivors × scale factor
- Replacement banks: clone random surviving bank equity
- Population constant: n_firms, n_banks unchanged

Examples
--------
Execute bankruptcy events:

>>> import bamengine as be
>>> sim = be.Simulation.init(n_firms=100, n_banks=10, seed=42)
>>> # Bankruptcy events run as part of default pipeline
>>> sim.step()

Check bankruptcies:

>>> sim.ec.n_firm_failures  # doctest: +SKIP
2
>>> sim.ec.n_bank_failures  # doctest: +SKIP
0

See Also
--------
bamengine.events._internal.bankruptcy : System function implementations
Economy : Tracks bankruptcy counts
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from bamengine.core.decorators import event

if TYPE_CHECKING:  # pragma: no cover
    from bamengine.simulation import Simulation


[docs] @event class FirmsUpdateNetWorth: """ Update firm net worth with retained profits/losses. Net worth accumulates retained earnings from each period. Negative net worth (insolvency) leads to bankruptcy in the next event. Algorithm --------- For each firm i: .. math:: A_i \\leftarrow A_i + RP_i \\text{total\\_funds}_i = \\max(0, A_i) where :math:`A_i` = net_worth, :math:`RP_i` = retained_profit. Examples -------- >>> import bamengine as be >>> sim = be.Simulation.init(n_firms=100, seed=42) >>> event = sim.get_event("firms_update_net_worth") >>> event.execute(sim) See Also -------- FirmsPayDividends : Calculates retained_profit bamengine.events._internal.bankruptcy.firms_update_net_worth : Implementation """
[docs] def execute(self, sim: Simulation) -> None: from bamengine.events._internal.bankruptcy import firms_update_net_worth firms_update_net_worth(sim.bor)
[docs] @event class MarkBankruptFirms: """ Detect insolvent firms and remove them from the economy. Firms are bankrupt if net_worth < 0 or production_prev = 0 (ghost firm rule). Bankrupt firms fire all workers and have all loans purged from LoanBook. Note: We check ``production_prev`` (not ``production``) because ``production`` is zeroed at the start of each period's planning phase. ``production_prev`` holds the previous period's actual production. Examples -------- >>> import bamengine as be >>> sim = be.Simulation.init(n_firms=100, seed=42) >>> event = sim.get_event("mark_bankrupt_firms") >>> event.execute(sim) >>> sim.ec.n_firm_failures # doctest: +SKIP 2 See Also -------- SpawnReplacementFirms : Creates replacements bamengine.events._internal.bankruptcy.mark_bankrupt_firms : Implementation """
[docs] def execute(self, sim: Simulation) -> None: from bamengine.events._internal.bankruptcy import mark_bankrupt_firms mark_bankrupt_firms( sim.ec, sim.emp, sim.bor, sim.prod, sim.wrk, sim.lb, )
[docs] @event class MarkBankruptBanks: """ Detect insolvent banks and remove them from the economy. Banks are bankrupt if equity_base < 0. Bankrupt banks have all loans purged. Examples -------- >>> import bamengine as be >>> sim = be.Simulation.init(n_banks=10, seed=42) >>> event = sim.get_event("mark_bankrupt_banks") >>> event.execute(sim) >>> sim.ec.n_bank_failures # doctest: +SKIP 0 See Also -------- SpawnReplacementBanks : Creates replacements bamengine.events._internal.bankruptcy.mark_bankrupt_banks : Implementation """
[docs] def execute(self, sim: Simulation) -> None: from bamengine.events._internal.bankruptcy import mark_bankrupt_banks mark_bankrupt_banks(sim.ec, sim.lend, sim.lb)
[docs] @event class SpawnReplacementFirms: """ Create new firms to replace bankrupt ones. Replacement firms inherit attributes (price, wage, net worth) from trimmed mean of survivors × scale factor (smaller than average). For production fields: - ``production_prev = mean_prod × new_firm_production_factor`` (planning signal) - ``production = production_prev`` (seeds current-period output) Examples -------- >>> import bamengine as be >>> sim = be.Simulation.init(n_firms=100, seed=42) >>> event = sim.get_event("spawn_replacement_firms") >>> event.execute(sim) See Also -------- MarkBankruptFirms : Detects bankruptcies bamengine.events._internal.bankruptcy.spawn_replacement_firms : Implementation """
[docs] def execute(self, sim: Simulation) -> None: from bamengine.events._internal.bankruptcy import spawn_replacement_firms spawn_replacement_firms( sim.ec, sim.prod, sim.emp, sim.bor, sim.wrk, new_firm_size_factor=sim.config.new_firm_size_factor, new_firm_production_factor=sim.config.new_firm_production_factor, new_firm_wage_factor=sim.config.new_firm_wage_factor, new_firm_price_markup=sim.config.new_firm_price_markup, rng=sim.rng, )
[docs] @event class SpawnReplacementBanks: """ Create new banks to replace bankrupt ones. Replacement banks clone equity from random surviving bank. If no banks survive, simulation terminates (systemic collapse). Examples -------- >>> import bamengine as be >>> sim = be.Simulation.init(n_banks=10, seed=42) >>> event = sim.get_event("spawn_replacement_banks") >>> event.execute(sim) See Also -------- MarkBankruptBanks : Detects bankruptcies bamengine.events._internal.bankruptcy.spawn_replacement_banks : Implementation """
[docs] def execute(self, sim: Simulation) -> None: from bamengine.events._internal.bankruptcy import spawn_replacement_banks spawn_replacement_banks( sim.ec, sim.lend, rng=sim.rng, )