Skip to content

zapatify/amortization_engine

Repository files navigation

🏷️ Tags

Ruby Finance Amortization Loan Calculator Payment Schedule

Amortization Engine

A comprehensive Ruby library for generating loan amortization schedules with advanced features including grace periods, interest-only payments, federal bank holidays, and multiple interest calculation methods.

Features

  • Multiple payment frequencies: Daily or weekly
  • Flexible loan terms: 6, 9, 12, 15, or 18 months
  • Interest calculation methods: Simple (accrued daily) or precomputed (spread evenly through payments)
  • Grace periods: With automatic interest capitalization
  • Interest-only periods: Configure initial interest-only payments
  • Fee handling: Origination fees and additional fees with multiple treatment options
  • Bank day calculations: Automatically skip weekends and US federal holidays
  • Multiple output formats: Console display or CSV export

Installation

Prerequisites

gem install holidays
gem install rspec  # for running tests

Usage

Just require the file in your project:

require_relative 'amortization_engine'

Quick Start

Basic Example

require_relative 'amortization_engine'

schedule = AmortizationSchedule.new(
  start_date: "2025-11-15",
  principal: 100000.00,
  term_months: 12,
  annual_rate: 17.75,
  frequency: :daily
)

# Display in console
schedule.generate

# Generate CSV file
schedule.generate(output: :csv, csv_path: "schedule.csv")

Advanced Example

schedule = AmortizationSchedule.new(
  start_date: "2025-11-15",
  principal: 100000.00,
  term_months: 12,
  annual_rate: 17.75,
  frequency: :daily,
  origination_fee: 10000.00,           # Added to principal
  additional_fee: 2500.00,             # Processing fee
  additional_fee_treatment: :distributed, # Options: :distributed, :add_to_principal, :separate_payment
  bank_days_only: true,                # Skip weekends & holidays
  interest_only_periods: 10,           # First 10 payments are interest-only
  grace_period_days: 3,                # 3-day grace period
  interest_method: :simple             # Options: :simple, :precomputed
)

schedule.generate

API Reference

Initialization Parameters

Parameter Type Required Options Description
start_date String/Date Yes YYYY-MM-DD Loan start date
principal Float Yes - Loan principal amount
term_months Integer Yes 6, 9, 12, 15, 18 Loan term in months
annual_rate Float Yes - Annual interest rate (%)
frequency Symbol Yes :daily, :weekly Payment frequency
origination_fee Float No Default: 0 Fee added to principal
additional_fee Float No Default: 0 Additional processing fee
additional_fee_treatment Symbol No :distributed, :add_to_principal, :separate_payment How to handle additional fee
bank_days_only Boolean No Default: false Skip weekends and holidays
interest_only_periods Integer No Default: 0 Number of interest-only payments
grace_period_days Integer No Default: 0 Days before first payment
interest_method Symbol No :simple, :precomputed Interest calculation method

Methods

generate(output: :console, csv_path: nil)

Generate the amortization schedule.

Parameters:

  • output: :console or :csv
  • csv_path: Required if output is :csv

Returns: nil (outputs to console or file)

Fee Treatment Options

1. Distributed (:distributed)

The fee is spread evenly across all payments.

additional_fee_treatment: :distributed

2. Add to Principal (:add_to_principal)

The fee is added to the loan principal upfront.

additional_fee_treatment: :add_to_principal

3. Separate Payment (:separate_payment)

The fee is collected as a separate first payment.

additional_fee_treatment: :separate_payment

Interest Methods

Simple Interest (:simple)

Interest accrues daily on the remaining balance. As you pay down the principal, interest payments decrease.

Best for: Traditional amortizing loans

interest_method: :simple

Precomputed Interest (:precomputed)

Total interest is calculated upfront and divided equally across all payments. Interest per payment stays constant.

Best for: Fixed payment structures

interest_method: :precomputed

Bank Holidays

When bank_days_only: true, the schedule automatically skips:

Weekends

  • Saturday
  • Sunday

US Federal Reserve Holidays

  • New Year's Day
  • Martin Luther King Jr. Birthday
  • Presidents' Day (Washington's Birthday)
  • Memorial Day
  • Juneteenth National Independence Day
  • Independence Day
  • Labor Day
  • Columbus Day
  • Veterans Day
  • Thanksgiving Day
  • Christmas Day

Observed dates handled automatically:

  • Holidays on Saturday → observed Friday
  • Holidays on Sunday → observed Monday

Interactive CLI

Run the engine directly for an interactive experience:

ruby amortization_engine.rb

Choose from:

  1. Run default examples (demonstrates simple vs precomputed interest)
  2. Enter parameters manually (interactive prompts)
  3. Quit

Testing

Run the comprehensive test suite:

rspec amortization_spec.rb

Test Coverage:

  • 29 test cases
  • 100+ assertions
  • Covers all features and edge cases

Examples

Example 1: Simple 6-Month Daily Loan

schedule = AmortizationSchedule.new(
  start_date: "2025-01-15",
  principal: 50000.00,
  term_months: 6,
  annual_rate: 12.0,
  frequency: :daily
)
schedule.generate

Example 2: Loan with Grace Period

schedule = AmortizationSchedule.new(
  start_date: "2025-01-15",
  principal: 50000.00,
  term_months: 12,
  annual_rate: 15.0,
  frequency: :daily,
  grace_period_days: 5  # Interest capitalizes during grace period
)
schedule.generate

Example 3: Interest-Only with Bank Days

schedule = AmortizationSchedule.new(
  start_date: "2025-01-15",
  principal: 100000.00,
  term_months: 18,
  annual_rate: 10.0,
  frequency: :weekly,
  interest_only_periods: 8,  # First 8 weeks interest-only
  bank_days_only: true        # Skip weekends and holidays
)
schedule.generate

Example 4: Complex Loan with All Features

schedule = AmortizationSchedule.new(
  start_date: "2025-01-15",
  principal: 100000.00,
  term_months: 12,
  annual_rate: 17.75,
  frequency: :daily,
  origination_fee: 10000.00,
  additional_fee: 2500.00,
  additional_fee_treatment: :distributed,
  bank_days_only: true,
  interest_only_periods: 10,
  grace_period_days: 3,
  interest_method: :precomputed
)
schedule.generate(output: :csv, csv_path: "complex_loan.csv")

Programmatic Access

Access schedule data directly for custom processing:

schedule = AmortizationSchedule.new(
  start_date: "2025-01-15",
  principal: 50000.00,
  term_months: 12,
  annual_rate: 10.0,
  frequency: :daily
)

# Get raw schedule data
schedule_data = schedule.send(:generate_schedule_data)

# Process each payment
schedule_data.each do |payment|
  puts "Payment #{payment[:payment_number]}"
  puts "  Date: #{payment[:date]}"
  puts "  Principal: $#{'%.2f' % payment[:principal_payment]}"
  puts "  Interest: $#{'%.2f' % payment[:interest_payment]}"
  puts "  Balance: $#{'%.2f' % payment[:principal_balance]}"
end

# Calculate totals
total_interest = schedule_data.sum { |row| row[:interest_payment] || 0 }
puts "Total interest: $#{'%.2f' % total_interest}"

Output Formats

Console Output

Formatted table with:

  • Payment number
  • Payment date
  • Days in period
  • Principal payment
  • Interest payment
  • Additional fee payment
  • Total payment
  • Principal balance
  • Accrued interest
  • Total balance
  • Payment type

CSV Output

Same data as console in spreadsheet-compatible format, suitable for:

  • Excel/Google Sheets
  • Further data analysis
  • Financial reporting
  • Record keeping

Requirements

  • Ruby 2.7 or higher
  • holidays gem (~> 8.0)

Dependencies

This library depends on the holidays gem for accurate US federal holiday detection. The gem is actively maintained and includes automatic weekend observation rules.

To update holiday data:

gem update holidays

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Here's how you can help:

Reporting Bugs

Please open an issue with:

  • A clear description of the problem
  • Steps to reproduce
  • Expected vs actual behavior
  • Your Ruby version and OS

Suggesting Features

Open an issue describing:

  • The feature you'd like to see
  • Why it would be useful
  • How it might work

Submitting Changes

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass (rspec amortization_spec.rb)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to your branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Code Style

  • Follow Ruby community style guidelines
  • Write clear, descriptive variable names
  • Add comments for complex logic
  • Keep methods focused and concise

Testing

  • All new features must include RSpec tests
  • Ensure all existing tests pass
  • Aim for high test coverage

Questions?

Feel free to open an issue for any questions or discussions.

Support

For issues or questions:

  1. Run the test suite to verify installation: rspec amortization_spec.rb
  2. Check the examples above
  3. Review the API reference

Changelog

Version 1.0.0

  • Initial release
  • Support for daily and weekly payment frequencies
  • Simple and precomputed interest methods
  • Grace period calculations
  • Interest-only periods
  • Bank holiday awareness
  • Multiple fee treatment options
  • CSV and console output
  • Comprehensive RSpec test suite

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages