Skip to main content

The Heart of the Engine

The Simulation Core is a discrete, step-by-step annual processing pipeline. For each year of a projection, the engine executes a sequence of modules, each responsible for a specific aspect of ESOP administration.
This strict order of operations ensures that legal obligations are met before discretionary actions are taken.

Annual Processing Cycle

Processing Steps Overview

Optional predictive moduleUses statistical models to forecast which employees will terminate in the current year.Inputs:
  • Participant age, tenure, compensation
  • Historical turnover rates
  • Industry benchmarks
Output:
  • List of projected termination events with probabilities
Calculate share price and prepare for annual processingInitialize the annual processing cycle by calculating current share price and preparing state variables.Actions:
  • Calculate per-share value from company equity and outstanding shares
  • Apply annual growth rate to company equity value
  • Initialize security-specific prices in multi-class mode
  • Set up year-specific state variables
Calculate shares available for allocationCritical step that implements loan-by-loan share release mechanics.Sources:
  • New company contributions (stock)
  • Released suspense shares from ESOP loans
  • Reallocated forfeitures
See: Loan-by-Loan Mechanics
Distribute shares to participant accountsApply allocation formula to credit shares to individual accounts.Formula Options:
  • Pro-rata by compensation
  • Pro-rata by hours
  • Integrated (Social Security-adjusted)
Determine annual company contributionBased on contribution_policy in OperatingAssumptions.Policy Types:
  • Fixed amount
  • Percentage of payroll
  • Discretionary formula
  • Loan payment-based
Calculate vested balances and potential forfeituresApply the plan’s vesting schedule to determine vested vs unvested portions of participant accounts.Actions:
  • Apply vesting schedule based on years of service
  • Calculate vested percentages for each participant
  • Identify non-vested amounts subject to forfeiture
  • Track vesting per security in multi-class mode
  • Apply vesting to both shares and cash balances
Handle statutory diversificationProcess elections from eligible participants (age 55+ with 10+ years).Actions:
  • Identify eligible participants
  • Process diversification elections
  • Calculate amounts (25% or 50% of account)
  • Move funds to diversified investments
Execute share repurchasesRepurchase shares from terminated participants using the Funding Waterfall.See: Funding Waterfall
Finalize annual results and prepare for next yearRoll account balances forward, evolve employee data, and capture year-end snapshots.Actions:
  • Move allocated/diversified amounts to opening balances
  • Age employees by 1 year and increment service years
  • Apply compensation growth rates
  • Remove fully distributed participants
  • Capture year-end state snapshots and KPIs

Key Logic Modules

Loan-by-Loan Share Release

For leveraged ESOPs with multiple debt tranches, the engine implements precise loan-by-loan accounting.
Critical: Each ESOPLoan object directly owns the shares that collateralize it. This prevents cross-contamination of suspense accounts.

How It Works

1

Iterate Through Loans

for loan in esop_loans:
    if loan.principal_balance > 0:
        process_loan_payment(loan)
2

Calculate Payment

Determine principal and interest for current year based on loan terms
3

Release Shares

# Release shares proportional to principal paid
shares_to_release = (
    loan.suspense_shares * 
    (principal_payment / original_loan_amount)
)

loan.suspense_shares -= shares_to_release
share_pool += shares_to_release
4

Update Loan Balance

loan.principal_balance -= principal_payment

Example: Multi-Loan Scenario

# Year 2025 Processing

# Loan 1: Original $3M loan from 2020
loan_1 = ESOPLoan(
    loan_id="LOAN_2020",
    principal_balance=2_000_000,
    suspense_shares=20_000,
    annual_payment=400_000  # $300K principal + $100K interest
)

# Loan 2: New $2M loan from 2023
loan_2 = ESOPLoan(
    loan_id="LOAN_2023",
    principal_balance=1_800_000,
    suspense_shares=15_000,
    annual_payment=300_000  # $200K principal + $100K interest
)

# Process Loan 1
shares_released_loan_1 = 20_000 * (300_000 / 3_000_000) = 2_000 shares
loan_1.suspense_shares = 20_000 - 2_000 = 18_000

# Process Loan 2
shares_released_loan_2 = 15_000 * (200_000 / 2_000_000) = 1_500 shares
loan_2.suspense_shares = 15_000 - 1_500 = 13_500

# Total shares available for allocation
share_pool = 2_000 + 1_500 = 3_500 shares
Why This Matters: Without loan-by-loan tracking, shares from one loan could incorrectly be released when paying down another loan, violating ERISA requirements and creating audit risk.

Funding Waterfall

The repurchase processing module implements a strict, rules-based sequence for drawing funds from the trust’s cash accounts.

The Waterfall Sequence

The engine follows PlanRules.cash_usage_policy to draw funds in the specified order:
cash_usage_policy = [
    "unallocated_company_contributions",  # 1st priority
    "unallocated_forfeiture_cash",        # 2nd priority
    "participant_cash_accounts"           # 3rd priority
]

Processing Algorithm

1

Calculate Total Repurchase Need

total_repurchase_obligation = sum(
    participant.vested_shares * current_share_price
    for participant in terminated_participants
)
2

Apply Waterfall

remaining_need = total_repurchase_obligation

for cash_source in cash_usage_policy:
    if remaining_need <= 0:
        break
        
    available = trust_cash_ledger[cash_source]
    amount_to_use = min(available, remaining_need)
    
    trust_cash_ledger[cash_source] -= amount_to_use
    remaining_need -= amount_to_use
    
    log_transaction(cash_source, amount_to_use)
3

Handle Shortfall

if remaining_need > 0:
    # Unfunded repurchase obligation
    defer_to_next_year(remaining_need)
    # OR trigger company loan/contribution

Example: Waterfall in Action

Repurchase Need: $500,000

Trust Cash Ledger:
├─ unallocated_company_contributions: $200,000
├─ unallocated_forfeiture_cash: $150,000
└─ participant_cash_accounts: $300,000

Waterfall Execution:
1. Draw $200,000 from unallocated_contributions → $300,000 remaining
2. Draw $150,000 from unallocated_forfeitures → $150,000 remaining
3. Draw $150,000 from participant_cash → $0 remaining

Result: ✅ Fully funded
Legal Compliance: The order matters! For example, forfeiture cash often has restrictions on use. The waterfall ensures compliance with plan document rules and ERISA regulations.

State Capture

At the end of each annual cycle, the engine captures complete snapshots:
{
  "simulation_run_id": "run_123",
  "year": 2025,
  "revenue": 10_000_000,
  "ebitda": 2_200_000,
  "total_payroll": 3_500_000,
  "esop_contribution": 500_000,
  "source_type": "simulated"
}

Error Handling & Validation

The engine performs extensive validation at each step:

Input Validation

  • Schema compliance
  • Business rule checks
  • Data completeness
  • Referential integrity

Processing Checks

  • Share count reconciliation
  • Cash balance validation
  • Loan payment calculations
  • Legal compliance flags

Output Verification

  • Total shares consistency
  • Cash flow balance
  • Participant account totals
  • Year-over-year deltas

Audit Logging

  • Every transaction logged
  • Decision points captured
  • Assumption tracking
  • Error breadcrumbs

Performance Optimizations

Participant-level calculations use NumPy for efficient batch processing.
Historical snapshots loaded on-demand, not preloaded into memory.
Frequently accessed reference data (e.g., plan rules) cached per simulation run.
Independent scenario runs can execute in parallel for sensitivity analysis.

Next Steps