#!/usr/bin/perl -w use strict; my $Cur= '$'; # Currency symbol to use. my $Fmt= '%.2f'; # Format to output currency with. my $Cents= 100; # Fractions of $Cur to round to. my $Comma= ','; # Thousands separator. Main( @ARGV ); exit( 0 ); sub Usage { die "Usage: $0 [-v] 10000 8% /12 5.5\n", "Computes payment amount for a ${Cur}10,000 loan at 8%\n", "with 12 payments per year over 5.5 years.\n"; } sub PmtRatio { my( $periodic, $count )= @_; my $mult= $periodic ** $count; my $ratio= ($mult-1)/($periodic-1); return wantarray ? ( $ratio, $mult ) # "large payment ratio", "principle mu +ltiplier" : $ratio/$mult; # "small payment ratio" } =head1 Derivation P= principle (starting amount of loan) i= annual interest rate (0.10 == 10%) p= amount of periodic payment (to be calculated) f= payment frequency (12 for monthly payments and monthly compound +ing) N= total number of payments (N/f equals number of years) If we owe R, then after one period, R*i/f in interest will accrue so we will owe R+R*i/f or R*(1+i/f) r= periodic rate of growth of principle = (1+i/f) Payments Owe 0 P | 1 P*r - p ||||||| 2 [ P*r - p ]*r - p 2 P*r^2 - p*r - p 2 P*r^2 - p*( r + 1 ) ||||||||||||||||||| 3 [ P*r^2 - p*( r + 1 ) ]*r - p 3 P*r^3 - p*[ r^2 + r + 1 ] N P*r^N - p*sum<j=0..N-1>( r^j ) L= sum<j=0..N-1>( r^j ) L= 1+r+..+r^(N-1) r*L - L = r*( 1+r+..+r^(N-2)+r^(N-1) ) - ( 1+r+..+r^(N-1) ) r*L - L = ( r+r^2+..+r^(N-1)+r^N ) - ( 1+r+..+r^(N-1) ) r*L - L = r+r^2+..+r^(N-1) + r^N - ( 1 + r+..+r^(N-1) ) r*L - L = r+r^2+..+r^(N-1) + r^N - 1 - ( r+..+r^(N-1) ) ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ r*L - L = r^N - 1 (r - 1)*L = r^N - 1 L = ( r^N - 1 ) / ( r - 1 ) After payment N, we owe nothing so: 0 = P*r^N - p*L 0 = P*r^N - p*(r^N-1)/(r-1) p*(r^N-1)/(r-1) = P*r^N p = P * r^N / [ (r^N-1) / (r-1) ] ^^^ ^^^^^^^^^^^^^^^ M L We'll call M the "multiplier". It is the ratio of ending principle ov +er starting principle if you never make any payments. It is useful for computing how your savings will grow based on different interest compounding schemes. We'll call L the "large payment ratio". It is the ratio of P*M (endin +g priciple if no payments) over payment size. It will be somewhat large +r than N (your payments will be somewhat smaller than P*M/N). We'll call S=L/M the "small payment ratio". It is the ratio of P (starting principle) over payment size. It will be somewhat smaller than N (your payments will be somewhat larger than P/N). =cut sub Round { my( $amt )= @_; return int( $amt*$Cents + 0.5 ) / $Cents; } sub readLine { my $line= <STDIN>; die "Can't read STDIN: $!\n" unless defined $line; chomp $line; return $line; } sub getParams { my $params= @_ ? "@_" : ""; my $reAmt= '\d+[.]?\d*'; my( $P, $i, $f, $y ); if( $params =~ /\S/ ) { Usage() unless $params =~ s[ ^\s* ($reAmt) (\s|$) ][]x; $P= $1; } if( ! defined $P || "" eq $P ) { while( 1 ) { print "Enter current value [amount of loan]: "; $params= readLine(); if( $params =~ s[ ^\s* ($reAmt) (\s|$) ][]x ) { $P= $1; last; } warn "Invalid amount. Please try again.\n"; } } if( $params =~ /\S/ ) { warn "Ignoring garbage after principle ($params).\n" unless $params =~ s[ ^\s* ($reAmt)\s*% ([\s/]|$) ][$2]x; $i= $1; } if( ! defined $i || "" eq $i ) { while( 1 ) { print "Enter interest annual percentage rate: "; $params= readLine(); if( $params =~ s[ ^\s* ($reAmt)\s*%? ([\s/]|$) ][$2]x ) +{ $i= $1; last; } warn "Invalid amount. Please try again.\n"; } } if( $params =~ /\S/ ) { warn "Ignoring garbage after interest rate ($params).\n" unless $params =~ s[ ^\s* /\s*(\d+) (\s|$) ][]x; $f= $1; } if( ! defined $f || "" eq $f ) { while( 1 ) { print qq{Enter payment frequency [periods per "year"]: }; $params= readLine(); if( $params =~ s[ ^\s* (?:/\s*)?(\d+) (\s|$) ][]x ) { $f= $1; last; } warn "Invalid amount. Please try again.\n"; } } if( $params =~ /\S/ ) { warn "Ignoring garbage after frequency ($params).\n" unless $params =~ s[ ^\s* ($reAmt) (\s|$) ][]x; $y= $1; } if( ! defined $y || "" eq $y ) { while( 1 ) { print qq{Enter loan duration [years]: }; $params= readLine(); if( $params =~ s[ ^\s* ($reAmt) (\s|$) ][]x ) { $y= $1; last; } warn "Invalid amount. Please try again.\n"; } } my $N= int( $y * $f + 0.5); $y= $N/$f; $P= Round( $P ); return( $P, $i, $f, $y, $N ); } sub Main { my $verbose= 0; if( @_ && $_[0] eq "-v" ) { shift(@_); $verbose= 1; } my( $P, $i, $f, $y, $N )= getParams( @_ ); $i /= 100; my( $rat, $mult )= PmtRatio( 1+$i/$f, $N ); my $p= $P*$mult/$rat; my $amt= $Cur . $P; 0 while $amt =~ s/(\d)(\d\d\d(\D|$))/$1$Comma$2/; $p= Round( $p ); print "\n$amt at ", $i*100, qq{%/$f per period, requires $N payments of $Cur$p ($y "years" +)\n}; if( $verbose ) { print "\npayment_number +interest_accrued -payment_made =still +_owe\n"; } my $pi; my $ti= 0; my $yi= 0; for( 1..$N ) { $pi= Round( $P * $i / $f ); $yi= Round( $yi + $pi ); $ti= Round( $ti + $pi ); $P += $pi - $p; if( $verbose and 1 == $_ % $f || 0 == $_ % $f ) { printf "%3d +$Fmt -$Fmt =$Fmt", $_, $pi, $p, $P; if( 0 == $_ % $f ) { printf " ($Cur$Fmt int)", $yi; $yi= 0; } print "\n"; } } print "\n" if $verbose; $ti= sprintf $Fmt, $ti; 0 while $ti =~ s/(\d)(\d\d\d(\D|$))/$1$Comma$2/; printf "Final payment=$Cur$Fmt, Total interest=$Cur%s, APR=%f%%\n" +, $p+$P, $ti, 100*( (1+$i/$f)**$f - 1 ); }
In reply to Payments calculator by tye
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |