私は、償却スケジュールを生成するプログラムの作成を任されています。これまでのところ、少しの調査しか行っていませんが、支払い、支払いごとの利子、および支払いごとの元本を計算する必要があります。これを理解するために正しい方向に私を向けることはできますか? 私はこれを RPG で書いていますが、他の人が将来このアルゴリズムを利用できると確信しています。
(更新) では、1 年を 365 日として計算するにはどうすればよいでしょうか。
私は、償却スケジュールを生成するプログラムの作成を任されています。これまでのところ、少しの調査しか行っていませんが、支払い、支払いごとの利子、および支払いごとの元本を計算する必要があります。これを理解するために正しい方向に私を向けることはできますか? 私はこれを RPG で書いていますが、他の人が将来このアルゴリズムを利用できると確信しています。
(更新) では、1 年を 365 日として計算するにはどうすればよいでしょうか。
最近、初めて家を購入したとき、個人用に JavaScript アプリを作成しました。
毎月の支払いを決定するための私のコードの変更されたスニペットは次のとおりです。
var balance = 200000; // for example
var periods = 360; // 30 years
var monthlyRate = (0.065)/12; // 0.065= APR of 6.5% as decimal
var monthyPayment = (monthlyRate /(1-(Math.pow((1+monthlyRate),-(periods)))))*balance;
for (var i=0; i<360; i++) {
var interestForMonth = balance * monthlyRate;
var principalForMonth = monthlyPayment - interestForMonth;
balance -= monthlyPayment; // probably should be -= principalForMonth see comments below
// output as necessary.
}
その後、償却表を作成するのはかなり簡単です。(他の人が言及しているように、丸め誤差に注意してください。実装ではこの問題に適切に対処していませんでした。)
これらの式を提供するサイトはたくさんあります。1つは以下です: http://www.math4finance.com/financial-formulas.php
数式は一般に非常に単純であるため、手の込んだアルゴリズムは必要ありません。
C# の償却スケジュールの式
numerator = i * principle * (Math.Pow(1 + i, n));
denominator = (Math.Pow(1 + i, n) - 1);
/* Calculation of Amortization Payment Amount */
payment = numerator / denominator;
これが私が作成したものです。テストプログラム全体を投稿しました。RPGで書かれていますが、他の言語では簡単に理解できるはずです。
H ActGrp(*caller) BndDir('MODULES') DftActGrp(*no)
//*********************************************************************
// Program . . . . . AMORT
//*********************************************************************
// Printer/Display Files
FAMORTDF CF E WORKSTN sfile(SFL01:rrn01)
//*********************************************************************
// Named Constants
/copy modules/qcopysrc,statuscopy
// Named Indicators
D indicatorPtr S * Inz(%Addr(*IN))
D DS Based(IndicatorPtr)
/copy modules/qcopysrc,scrncopy
// Subfile Fields
D rrn01 S 4P 0 inz(0)
//*********************************************************************
// Misc Fields
D* Monthly Payment
D m S 12P 2
D* Principal
D p S 12P 2
D* Interest
D i S 5P 3
D* Length (in Years)
D l S 3P 0
D* Monthly Interest
D j S 10P10
D* # of Months
D n S 5P 0
D* Current Monthly Int.
D h S 12P 2
D* Current Principal
D c S 12P 2
D* New Balance
D q S 12P 2
//*********************************************************************
// External Program Procedures
// Internal Subprocedures
D Init PR
D Main PR
D SubfileFilled PR N
D ClearScreen PR
D IsValidData PR N
D LoanPayment PR 12P 2
D principal 12P 2
D interest 5P 3
D loanPeriod 3P 0
D paymentsYear 3P 0
// External Subprocedures
///copy modules/qsrvsrc,p.string
//*********************************************************************
// Entry Parms
D AMORT PR extpgm('AMORT')
D AMORT PI
//*********************************************************************
/free
Init();
Main();
*inlr = *on;
/end-free
P*--------------------------------------------------
P* Procedure name: Init
P* Purpose:
P* Returns:
P*--------------------------------------------------
P Init B
D Init PI
/free
pgm = 'AMORT';
sflDsp = *off;
return;
/end-free
P Init E
P*--------------------------------------------------
P* Procedure name: Main
P* Purpose:
P* Returns:
P*--------------------------------------------------
P Main B
D Main PI
/free
dow (not F3) and (not F12);
write OVR01;
exfmt CTL01;
ClearScreen();
if (IsValidData()) and (not F3) and (not F12);
// Fill the header information
dPayment = LoanPayment(dLoanAmt:dIntRate:dLoanPrd:dPayYear);
dNumPaymnt = dLoanPrd * dPayYear;
m = dPayment + dExtraPay;
p = dLoanAmt;
q = p;
// Fill the table
if (SubfileFilled());
sflDsp = *on;
endif;
endif;
enddo;
return;
/end-free
P Main E
P*--------------------------------------------------
P* Procedure name: SubfileFilled
P* Purpose: Fill the subfile
P* Returns:
P*--------------------------------------------------
P SubfileFilled B
D SubfileFilled PI N
D isFilled S N
D x S 4P 0
D intCume S 12P 2
D extraPayCume S 12P 2
D payDate S D
D payment S 12P 2
D extraPayment S 12P 2
/free
isFilled = *on;
sflClear = *on;
write CTL01;
sflClear = *off;
rrn01 = 0;
x = 0;
// Setup the work fields
payment = dPayment;
extraPayment = dExtraPay;
payDate = dStartDate;
// Create records until there is a zero balance
dow (q > 0);
x += 1;
eval(h) h = p * j; // Monthly Interest
// Adjust for final payment
if (p < m);
m = p + h;
payment = p;
extraPayment = h;
endif;
// Calulate Principal
c = m - h;
// Calulate the new balance
q = p - c;
// Accumulate the interest and extra payments
intCume += h;
extraPayCume += extraPayment;
// Determine the next pay date
select;
when dTerms = '1'; //Yearly
payDate += %years(1);
when dTerms = '2'; //Semi-Annual
payDate += %months(6);
when dTerms = '3'; //Quarterly
payDate += %months(3);
when dTerms = '4'; //Monthly
payDate += %months(1);
when dTerms = '5'; //Bi-Weekly
payDate += %days(14);
endsl;
// Fill the subfile
sPayNum = x;
sPayDate = payDate;
sBegBal = p;
sSchedPay = payment;
sExtraPay = extraPayment;
sTotPay = m;
sInterest = h;
sPrincipal = c;
sEndBal = q;
sCumeInt = intCume;
// Move the End balance to the beginning balance
p = q;
rrn01 += 1;
write SFL01;
enddo;
// Return the calculated information to the header
dActPaymnt = x;
dTotInt = intCume;
dTotEPay = extraPayCume;
if (rrn01 < 1);
isFilled = *off;
endif;
return isFilled;
/end-free
P SubfileFilled E
P*--------------------------------------------------
P* Procedure name: ClearScreen
P* Purpose:
P* Returns:
P*--------------------------------------------------
P ClearScreen B
D ClearScreen PI
/free
c = 0;
h = 0;
i = 0;
j = 0;
l = 0;
m = 0;
n = 0;
p = 0;
q = 0;
dPayment = 0;
dNumPaymnt = 0;
dActPaymnt = 0;
dTotEPay = 0;
dTotInt = 0;
return;
/end-free
P ClearScreen E
P*--------------------------------------------------
P* Procedure name: IsValidData
P* Purpose: Validate the data on the screen
P* Returns: True or False
P*--------------------------------------------------
P IsValidData B
D IsValidData PI N
D isValid S N
/free
if (dLoanAmt <> 0) and (dIntRate <> 0) and (dLoanPrd <> 0) and
(dPayYear <> 0) and (dStartDate <> %date('0001-01-01'));
isValid = *on;
else;
isValid = *off;
endif;
return isValid;
/end-free
P IsValidData E
P*--------------------------------------------------
P* Procedure name: LoanPayment
P* Purpose: Calculates the payment
P* Returns:
P*--------------------------------------------------
P LoanPayment B
D LoanPayment PI 12P 2
D principal 12P 2
D interest 5P 3
D loanPeriod 3P 0
D paymentsYear 3P 0
D retMonthlyPayment...
D S 12P 2
/free
eval(h) n = loanPeriod * paymentsYear;
eval(h) j = interest / (paymentsYear * 100);
eval(h) m = principal * (j / (1 - (1 + j) ** -n));
return m;
/end-free
P LoanPayment E
このコードは、ローン計算機に対してテストされています。テスト対Excelの場合、$200,000のローンで約32セントオフでした。私はそれが丸めの問題に関係していると確信しています。
ブライアンは正しい。式は非常に単純です。
解を適切なものにしたい場合は、丸め誤差に注意を払う必要があります。丸め誤差を蓄積して伝播させるだけで、スケジュールの最後に数セントずれることがあります。慎重にプログラミングすることで、エラーを最小限に抑えることができます。
また、コンピュータに MS Excel がある場合は、MS オフィス オンラインから償却テンプレートをダウンロードできます。
var annuity = P * (i + i / (Math.pow(1+i,n) -1));
//P: principal, I: periodic interest rate, N: number of periods