diff --git a/components/fpspreadsheet/examples/other/financemath.pas b/components/fpspreadsheet/examples/other/financemath.pas new file mode 100644 index 000000000..5dbe30121 --- /dev/null +++ b/components/fpspreadsheet/examples/other/financemath.pas @@ -0,0 +1,184 @@ +unit financemath; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +{ Cash flow equation: + + FV + PV * q^n + PMT (q^n - 1) / (q - 1) = 0 (1) + + with + + q = 1 + interest rate (RATE) + PV = present value of an investment + FV = future value of an investment + PMT = regular payment (per period) + n = NPER = number of payment periods + + This is valid for payments occuring at the end of each period. If payments + occur at the start of each period the payments are multiplied by a factor q. + This case is indicated by means of the parameter PaymentTime below. + + The interest rate is considered as "per period" - whatever that is. + If the period is 1 year then we use the "usual" interest rate. + If the period is 1 month then we use 1/12 of the yearly interest rate. + + Sign rules: + - Money that I receive is to a positive number + - Money that I pay is to a negative number. + + Example 1: Saving account + - A saving account has an initial balance of 1000 $ (PV). + I paid this money to the bank --> negative number + - I deposit 100$ regularly to this account (PMT): I pay this money --> negative number. + - At the end of the payments (NPER periods) I get the money back --> positive number. + This is the FV. + + Example 2: Loan + - I borrow 1000$ from the bank: I get money --> positive PV + - I pay 100$ back to the bank in regular intervals --> negative PMT + - At the end, all debts are paid --> FV = 0. + + The cash flow equation (1) contains 5 parameters (Rate, PV, FV, PMT, NPER). + The functions below solve this equation always for one of these parameters. + + References: + - http://en.wikipedia.org/wiki/Time_value_of_money + - https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_Derivation_of_Financial_Formulas +} + +type + TPaymentTime = (ptEndOfPeriod, ptStartOfPeriod); + +function FutureValue(ARate: Extended; NPeriods: Integer; + APayment, APresentValue: Extended; APaymentTime: TPaymentTime): Extended; + +function InterestRate(NPeriods: Integer; APayment, APresentValue, AFutureValue: Extended; + APaymentTime: TPaymentTime): Extended; + +function NumberOfPeriods(ARate, APayment, APresentValue, AFutureValue: Extended; + APaymentTime: TPaymentTime): Extended; + +function Payment(ARate: Extended; NPeriods: Integer; + APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; + +function PresentValue(ARate: Extended; NPeriods: Integer; + APayment, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; + + +implementation + +uses + math; + +function FutureValue(ARate: Extended; NPeriods: Integer; + APayment, APresentValue: Extended; APaymentTime: TPaymentTime): Extended; +var + q, qn, factor: Extended; +begin + if ARate = 0 then + Result := -APresentValue - APayment * NPeriods + else begin + q := 1.0 + ARate; + qn := power(q, NPeriods); + factor := (qn - 1) / (q - 1); + if APaymentTime = ptStartOfPeriod then + factor := factor * q; + Result := -(APresentValue * qn + APayment*factor); + end; +end; + +function InterestRate(NPeriods: Integer; APayment, APresentValue, AFutureValue: Extended; + APaymentTime: TPaymentTime): Extended; +{ The interest rate cannot be calculated analytically. We solve the equation + numerically by means of the Newton method: + - guess value for the interest reate + - calculate at which interest rate the tangent of the curve fv(rate) + (straight line!) has the requested future vale. + - use this rate for the next iteration. } +const + DELTA = 0.001; + EPS = 1E-9; // required precision of interest rate (after typ. 6 iterations) + MAXIT = 20; // max iteration count to protect agains non-convergence +var + r1, r2, dr: Extended; + fv1, fv2: Extended; + iteration: Integer; +begin + iteration := 0; + r1 := 0.05; // inital guess + repeat + r2 := r1 + DELTA; + fv1 := FutureValue(r1, NPeriods, APayment, APresentValue, APaymentTime); + fv2 := FutureValue(r2, NPeriods, APayment, APresentValue, APaymentTime); + dr := (AFutureValue - fv1) / (fv2 - fv1) * delta; // tangent at fv(r) + r1 := r1 + dr; // next guess + inc(iteration); + until (abs(dr) < EPS) or (iteration >= MAXIT); + Result := r1; +end; + +function NumberOfPeriods(ARate, APayment, APresentValue, AFutureValue: extended; + APaymentTime: TPaymentTime): extended; +{ Solve the cash flow equation (1) for q^n and take the logarithm } +var + q, x1, x2: Extended; +begin + if ARate = 0 then + Result := -(APresentValue + AFutureValue) / APayment + else begin + q := 1.0 + ARate; + if APaymentTime = ptStartOfPeriod then + APayment := APayment * q; + x1 := APayment - AFutureValue * ARate; + x2 := APayment + APresentValue * ARate; + if (x2 = 0) // we have to divide by x2 + or (sign(x1) * sign(x2) < 0) // the argument of the log is negative + then + Result := Infinity + else begin + Result := ln(x1/x2) / ln(q); + end; + end; +end; + +function Payment(ARate: Extended; NPeriods: Integer; + APresentValue, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; +var + q, qn, factor: Extended; +begin + if ARate = 0 then + Result := -(AFutureValue + APresentValue) / NPeriods + else begin + q := 1.0 + ARate; + qn := power(q, NPeriods); + factor := (qn - 1) / (q - 1); + if APaymentTime = ptStartOfPeriod then + factor := factor * q; + Result := -(AFutureValue + APresentValue * qn) / factor; + end; +end; + +function PresentValue(ARate: Extended; NPeriods: Integer; + APayment, AFutureValue: Extended; APaymentTime: TPaymentTime): Extended; +var + q, qn, factor: Extended; +begin + if ARate = 0.0 then + Result := -AFutureValue - APayment * NPeriods + else begin + q := 1.0 + ARate; + qn := power(q, NPeriods); + factor := (qn - 1) / (q - 1); + if APaymentTime = ptStartOfPeriod then + factor := factor * q; + Result := -(AFutureValue + APayment*factor) / qn; + end; +end; + +end. + diff --git a/components/fpspreadsheet/examples/other/test_formula_func.lpi b/components/fpspreadsheet/examples/other/test_formula_func.lpi index 7fe05078f..cf20902e9 100644 --- a/components/fpspreadsheet/examples/other/test_formula_func.lpi +++ b/components/fpspreadsheet/examples/other/test_formula_func.lpi @@ -60,12 +60,17 @@ - + + + + + + diff --git a/components/fpspreadsheet/examples/other/test_formula_func.lps b/components/fpspreadsheet/examples/other/test_formula_func.lps index 39ac60b35..5b85ae9d9 100644 --- a/components/fpspreadsheet/examples/other/test_formula_func.lps +++ b/components/fpspreadsheet/examples/other/test_formula_func.lps @@ -4,204 +4,211 @@ - + - - - + + + - + + + + + + + + + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + diff --git a/components/fpspreadsheet/examples/other/test_formula_func.pas b/components/fpspreadsheet/examples/other/test_formula_func.pas index 9412e09f9..ed10d0607 100644 --- a/components/fpspreadsheet/examples/other/test_formula_func.pas +++ b/components/fpspreadsheet/examples/other/test_formula_func.pas @@ -24,134 +24,29 @@ uses {$ENDIF}{$ENDIF} Classes, SysUtils, laz_fpspreadsheet { you can add units after this }, - math, fpspreadsheet, xlsbiff8, fpsfunc; + math, fpspreadsheet, xlsbiff8, fpsfunc, financemath; {------------------------------------------------------------------------------} -{ Basic implmentation of the four financial funtions } -{------------------------------------------------------------------------------} - -const - paymentAtEnd = 0; - paymentAtBegin = 1; - -{ Calculates the future value (FV) of an investment based on an interest rate and - a constant payment schedule: - - "interest_rate" (r) is the interest rate for the investment (as decimal, not percent) - - "number_periods" (n) is the number of payment periods, i.e. number of payments - for the annuity. - - "payment" (PMT) is the amount of the payment made each period - - "pv" is the present value of the payments. - - "payment_type" indicates when the payments are due (see paymentAtXXX constants) - - see: http://en.wikipedia.org/wiki/Time_value_of_money - or https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_Derivation_of_Financial_Formulas#PV.2C_FV.2C_PMT.2C_NPER.2C_RATE - - As in Excel's implementation the cash flow is a signed number: - - Positive cash flow means: "I get money" - - Negative cash flow means: "I pay money" - - With these conventions, the contributions (FV, PV, Payments) add up to 0: - FV + PV q^n + PMT (q^n - 1) / (q - 1) = 0 ( q = 1 + r ) -} -function FV(interest_rate: Double; number_periods: Integer; payment, pv: Double; - payment_type: integer): Double; -var - q, qn, factor: Double; -begin - q := 1.0 + interest_rate; - qn := power(q, number_periods); - factor := (qn - 1) / (q - 1); - if payment_type = paymentAtBegin then - factor := factor * q; - - Result := -(pv * qn + payment*factor); -end; - -{ Calculates the number of periods for an investment based on an interest rate - and a constant payment schedule. - Solve above formula for qn and then take the log to get n. - } -function NPER(interest_rate, payment, pv, fv: Double; - payment_type:Integer): double; -var - q, x1, x2, T: Double; -begin - if interest_rate = 0 then - Result := (pv + fv) / payment - else - q := 1.0 + interest_rate; - if payment_type = paymentAtBegin then - payment := payment * q; - x2 := pv * interest_rate + payment; - if x2 = 0 then - Result := Infinity - else begin - x1 := -fv * interest_rate + payment; - Result := ln(x1/x2) / ln(q); - end; -end; - -{ Calculates the regular payments for a loan based on an interest rate and a - constant payment schedule - Arguments as shown for FV(), in addition: - - "fv" is the future value of the payments. - see: http://en.wikipedia.org/wiki/Time_value_of_money - } -function PMT(interest_rate: Double; number_periods: Integer; pv, fv: Double; - payment_type: Integer): Double; -var - q, qn, factor: Double; -begin - q := 1.0 + interest_rate; - qn := power(q, number_periods); - factor := (qn - 1) / (q - 1); - if payment_type = paymentAtBegin then - factor := factor * q; - - Result := -(fv + pv * qn) / factor; -end; - -{ Calculates the present value of an investment based on an interest rate and - a constant payment schedule. - Arguments as shown for FV(), in addition: - - "fv" is the future value of the payments. - see: http://en.wikipedia.org/wiki/Time_value_of_money -} -function PV(interest_rate: Double; number_periods: Integer; payment, fv: Double; - payment_type: Integer): Double; -var - q, qn, factor: Double; -begin - q := 1.0 + interest_rate; - qn := power(q, number_periods); - factor := (qn - 1) / (q - 1); - if payment_type = paymentAtBegin then - factor := factor * q; - - Result := -(fv + payment*factor) / qn; -end; - - -{------------------------------------------------------------------------------} -{ Adaption for usage by fpspreadsheet } +{ Adaption of financial functions to usage by fpspreadsheet } +{ The functions are implemented in the unit "financemath.pas". } {------------------------------------------------------------------------------} function fpsFV(Args: TsArgumentStack; NumArgs: Integer): TsArgument; var data: TsArgNumberArray; begin - // Pop the argument off the stack. This can be done by means of PopNumberValues - // which brings the values back into the right order and reports an error + // Pop the argument from the stack. This can be done by means of PopNumberValues + // which brings the values back in the right order and reports an error // in case of non-numerical values. if Args.PopNumberValues(NumArgs, false, data, Result) then - // Call our FV function with the NumberValues of the arguments. - Result := CreateNumberArg(FV( + // Call the FutureValue function with the NumberValues of the arguments. + Result := CreateNumberArg(FutureValue( data[0], // interest rate round(data[1]), // number of payments data[2], // payment data[3], // present value - round(data[4]) // payment type + TPaymentTime(round(data[4])) // payment type )); end; @@ -160,12 +55,12 @@ var data: TsArgNumberArray; begin if Args.PopNumberValues(NumArgs, false, data, Result) then - Result := CreateNumberArg(PMT( + Result := CreateNumberArg(Payment( data[0], // interest rate round(data[1]), // number of payments data[2], // present value data[3], // future value - round(data[4]) // payment type + TPaymentTime(round(data[4])) // payment type )); end; @@ -174,12 +69,12 @@ var data: TsArgNumberArray; begin if Args.PopNumberValues(NumArgs, false, data, Result) then - Result := CreateNumberArg(PV( + Result := CreateNumberArg(PresentValue( data[0], // interest rate round(data[1]), // number of payments data[2], // payment data[3], // future value - round(data[4]) // payment type + TPaymentTime(round(data[4])) // payment type )); end; @@ -188,12 +83,26 @@ var data: TsArgNumberArray; begin if Args.PopNumberValues(NumArgs, false, data, Result) then - Result := CreateNumberArg(NPER( + Result := CreateNumberArg(NumberOfPeriods( data[0], // interest rate data[1], // payment data[2], // present value data[3], // future value - round(data[4]) // payment type + TPaymentTime(round(data[4])) // payment type + )); +end; + +function fpsRATE(Args: TsArgumentStack; NumArgs: Integer): TsArgument; +var + data: TsArgNumberArray; +begin + if Args.PopNumberValues(NumArgs, false, data, Result) then + Result := CreateNumberArg(InterestRate( + round(data[0]), // number of payment periods + data[1], // payment + data[2], // present value + data[3], // future value + TPaymentTime(round(data[4])) // payment type )); end; @@ -202,159 +111,192 @@ end; {------------------------------------------------------------------------------} procedure WriteFile(AFileName: String); const - INTEREST_RATE = 0.03; - NUMBER_PAYMENTS = 10; - PAYMENT = 1000; - PRESENT_VALUE = 10000; - PAYMENT_WHEN = paymentAtEnd; + INTEREST_RATE = 0.03; // interest rate per period + NUMBER_PAYMENTS = 10; // number of payment periods + REG_PAYMENT = 1000; // regular payment per period + PRESENT_VALUE = 10000; // present value of investment + PAYMENT_WHEN: TPaymentTime = ptEndOfPeriod; // when is the payment made var workbook: TsWorkbook; worksheet: TsWorksheet; - fval, pval, pmtval, nperval: Double; + fval, pval, pmtval, nperval, rateval: Double; begin - { We have to register our financial function in fpspreadsheet. Otherwise an - error code would be displayed in the reading part of this demo in these + { We have to register our financial functions in fpspreadsheet. Otherwise an + error code would be displayed in the reading part of this demo for these formula cells. } RegisterFormulaFunc(fekFV, @fpsFV); RegisterFormulaFunc(fekPMT, @fpsPMT); RegisterFormulaFunc(fekPV, @fpsPV); RegisterFormulaFunc(fekNPER, @fpsNPER); + RegisterFormulaFunc(fekRATE, @fpsRATE); workbook := TsWorkbook.Create; try worksheet := workbook.AddWorksheet('Financial'); worksheet.Options := worksheet.Options + [soCalcBeforeSaving]; worksheet.WriteColWidth(0, 40); + worksheet.WriteColWidth(1, 15); - worksheet.WriteUTF8Text(0, 0, 'Interest rate'); - worksheet.WriteNumber(0, 1, INTEREST_RATE, nfPercentage, 1); // B1 + worksheet.WriteUTF8Text(0, 0, 'INPUT DATA'); + worksheet.WriteFontStyle(0, 0, [fssBold]); - worksheet.WriteUTF8Text(1, 0, 'Number of payments'); - worksheet.WriteNumber(1, 1, NUMBER_PAYMENTS); // B2 + worksheet.WriteUTF8Text(1, 0, 'Interest rate'); + worksheet.WriteNumber(1, 1, INTEREST_RATE, nfPercentage, 1); // B2 - worksheet.WriteUTF8Text(2, 0, 'Payment'); - worksheet.WriteCurrency(2, 1, PAYMENT, nfCurrency, 2, '$'); // B3 + worksheet.WriteUTF8Text(2, 0, 'Number of payments'); + worksheet.WriteNumber(2, 1, NUMBER_PAYMENTS); // B3 - worksheet.WriteUTF8Text(3, 0, 'Present value'); - worksheet.WriteCurrency(3, 1, PRESENT_VALUE, nfCurrency, 2, '$'); // B4 + worksheet.WriteUTF8Text(3, 0, 'Payment'); + worksheet.WriteCurrency(3, 1, REG_PAYMENT, nfCurrency, 2, '$'); // B4 - worksheet.WriteUTF8Text(4, 0, 'Payment at end (0) or at begin (1)'); - worksheet.WriteNumber(4, 1, PAYMENT_WHEN); // B5 + worksheet.WriteUTF8Text(4, 0, 'Present value'); + worksheet.WriteCurrency(4, 1, PRESENT_VALUE, nfCurrency, 2, '$'); // B5 + + worksheet.WriteUTF8Text(5, 0, 'Payment at end (0) or at begin (1)'); + worksheet.WriteNumber(5, 1, ord(PAYMENT_WHEN)); // B6 // future value calculation - fval := FV(INTEREST_RATE, NUMBER_PAYMENTS, PAYMENT, PRESENT_VALUE, PAYMENT_WHEN); - worksheet.WriteUTF8Text(6, 0, 'Calculation of the future value'); - worksheet.WriteFontStyle(6, 0, [fssBold]); - worksheet.WriteUTF8Text(7, 0, 'Our calculation'); - worksheet.WriteCurrency(7, 1, fval, nfCurrency, 2, '$'); + fval := FutureValue(INTEREST_RATE, NUMBER_PAYMENTS, REG_PAYMENT, PRESENT_VALUE, PAYMENT_WHEN); + worksheet.WriteUTF8Text(7, 0, 'CALCULATION OF THE FUTURE VALUE'); + worksheet.WriteFontStyle(7, 0, [fssBold]); + worksheet.WriteUTF8Text(8, 0, 'Direct calculation'); + worksheet.WriteCurrency(8, 1, fval, nfCurrency, 2, '$'); - worksheet.WriteUTF8Text(8, 0, 'Excel''s calculation using constants'); - worksheet.WriteNumberFormat(8, 1, nfCurrency, 2, '$'); - worksheet.WriteRPNFormula(8, 1, CreateRPNFormula( // B9 + worksheet.WriteUTF8Text(9, 0, 'Worksheet calculation using constants'); + worksheet.WriteNumberFormat(9, 1, nfCurrency, 2, '$'); + worksheet.WriteRPNFormula(9, 1, CreateRPNFormula( RPNNumber(INTEREST_RATE, RPNNumber(NUMBER_PAYMENTS, - RPNNumber(PAYMENT, + RPNNumber(REG_PAYMENT, RPNNumber(PRESENT_VALUE, - RPNNumber(PAYMENT_WHEN, + RPNNumber(ord(PAYMENT_WHEN), RPNFunc(fekFV, 5, nil)))))))); - worksheet.WriteUTF8Text(9, 0, 'Excel''s calculation using cell values'); - worksheet.WriteNumberFormat(9, 1, nfCurrency, 2, '$'); - worksheet.WriteRPNFormula(9, 1, CreateRPNFormula( // B9 - RPNCellValue('B1', // interest rate - RPNCellValue('B2', // number of periods - RPNCellValue('B3', // payment - RPNCellValue('B4', // present value - RPNCellValue('B5', // payment at end or at start + worksheet.WriteUTF8Text(10, 0, 'Worksheet calculation using cell values'); + worksheet.WriteNumberFormat(10, 1, nfCurrency, 2, '$'); + worksheet.WriteRPNFormula(10, 1, CreateRPNFormula( + RPNCellValue('B2', // interest rate + RPNCellValue('B3', // number of periods + RPNCellValue('B4', // payment + RPNCellValue('B5', // present value + RPNCellValue('B6', // payment at end or at start RPNFunc(fekFV, 5, // Call Excel's FV formula nil)))))))); // present value calculation - pval := PV(INTEREST_RATE, NUMBER_PAYMENTS, PAYMENT, fval, PAYMENT_WHEN); - worksheet.WriteUTF8Text(11, 0, 'Calculation of the present value'); - worksheet.WriteFontStyle(11, 0, [fssBold]); - worksheet.WriteUTF8Text(12, 0, 'Our calculation'); - worksheet.WriteCurrency(12, 1, pval, nfCurrency, 2, '$'); + pval := PresentValue(INTEREST_RATE, NUMBER_PAYMENTS, REG_PAYMENT, fval, PAYMENT_WHEN); + worksheet.WriteUTF8Text(12, 0, 'CALCULATION OF THE PRESENT VALUE'); + worksheet.WriteFontStyle(12, 0, [fssBold]); + worksheet.WriteUTF8Text(13, 0, 'Direct calculation'); + worksheet.WriteCurrency(13, 1, pval, nfCurrency, 2, '$'); - worksheet.WriteUTF8Text(13, 0, 'Excel''s calculation using constants'); - worksheet.WriteNumberFormat(13, 1, nfCurrency, 2, '$'); - worksheet.WriteRPNFormula(13, 1, CreateRPNFormula( - RPNNumber(INTEREST_RATE, - RPNNumber(NUMBER_PAYMENTS, - RPNNumber(PAYMENT, - RPNNumber(fval, - RPNNumber(PAYMENT_WHEN, - RPNFunc(fekPV, 5, - nil)))))))); - Worksheet.WriteUTF8Text(14, 0, 'Excel''s calculation using cell values'); + worksheet.WriteUTF8Text(14, 0, 'Worksheet calculation using constants'); worksheet.WriteNumberFormat(14, 1, nfCurrency, 2, '$'); worksheet.WriteRPNFormula(14, 1, CreateRPNFormula( - RPNCellValue('B1', // interest rate - RPNCellValue('B2', // number of periods - RPNCellValue('B3', // payment - RPNCellValue('B10', // future value - RPNCellValue('B5', // payment at end or at start + RPNNumber(INTEREST_RATE, + RPNNumber(NUMBER_PAYMENTS, + RPNNumber(REG_PAYMENT, + RPNNumber(fval, + RPNNumber(ord(PAYMENT_WHEN), + RPNFunc(fekPV, 5, + nil)))))))); + Worksheet.WriteUTF8Text(15, 0, 'Worksheet calculation using cell values'); + worksheet.WriteNumberFormat(15, 1, nfCurrency, 2, '$'); + worksheet.WriteRPNFormula(15, 1, CreateRPNFormula( + RPNCellValue('B2', // interest rate + RPNCellValue('B3', // number of periods + RPNCellValue('B4', // payment + RPNCellValue('B11', // future value + RPNCellValue('B6', // payment at end or at start RPNFunc(fekPV, 5, // Call Excel's PV formula nil)))))))); // payments calculation - pmtval := PMT(INTEREST_RATE, NUMBER_PAYMENTS, PRESENT_VALUE, fval, PAYMENT_WHEN); - worksheet.WriteUTF8Text(16, 0, 'Calculation of the payment'); - worksheet.WriteFontStyle(16, 0, [fssBold]); - worksheet.WriteUTF8Text(17, 0, 'Our calculation'); - worksheet.WriteCurrency(17, 1, pmtval, nfCurrency, 2, '$'); + pmtval := Payment(INTEREST_RATE, NUMBER_PAYMENTS, PRESENT_VALUE, fval, PAYMENT_WHEN); + worksheet.WriteUTF8Text(17, 0, 'CALCULATION OF THE PAYMENT'); + worksheet.WriteFontStyle(17, 0, [fssBold]); + worksheet.WriteUTF8Text(18, 0, 'Direct calculation'); + worksheet.WriteCurrency(18, 1, pmtval, nfCurrency, 2, '$'); - worksheet.WriteUTF8Text(18, 0, 'Excel''s calculation using constants'); - worksheet.WriteNumberFormat(18, 1, nfCurrency, 2, '$'); - worksheet.WriteRPNFormula(18, 1, CreateRPNFormula( + worksheet.WriteUTF8Text(19, 0, 'Worksheet calculation using constants'); + worksheet.WriteNumberFormat(19, 1, nfCurrency, 2, '$'); + worksheet.WriteRPNFormula(19, 1, CreateRPNFormula( RPNNumber(INTEREST_RATE, RPNNumber(NUMBER_PAYMENTS, RPNNumber(PRESENT_VALUE, RPNNumber(fval, - RPNNumber(PAYMENT_WHEN, + RPNNumber(ord(PAYMENT_WHEN), RPNFunc(fekPMT, 5, nil)))))))); - Worksheet.WriteUTF8Text(19, 0, 'Excel''s calculation using cell values'); - worksheet.WriteNumberFormat(19, 1, nfCurrency, 2, '$'); - worksheet.WriteRPNFormula(19, 1, CreateRPNFormula( - RPNCellValue('B1', // interest rate - RPNCellValue('B2', // number of periods - RPNCellValue('B4', // present value - RPNCellValue('B10', // future value - RPNCellValue('B5', // payment at end or at start + Worksheet.WriteUTF8Text(20, 0, 'Worksheet calculation using cell values'); + worksheet.WriteNumberFormat(20, 1, nfCurrency, 2, '$'); + worksheet.WriteRPNFormula(20, 1, CreateRPNFormula( + RPNCellValue('B2', // interest rate + RPNCellValue('B3', // number of periods + RPNCellValue('B5', // present value + RPNCellValue('B11', // future value + RPNCellValue('B6', // payment at end or at start RPNFunc(fekPMT, 5, // Call Excel's PMT formula nil)))))))); // number of periods calculation - nperval := NPER(INTEREST_RATE, PAYMENT, PRESENT_VALUE, fval, PAYMENT_WHEN); - worksheet.WriteUTF8Text(21, 0, 'Calculation of the number of payment periods'); - worksheet.WriteFontStyle(21, 0, [fssBold]); - worksheet.WriteUTF8Text(22, 0, 'Our calculation'); - worksheet.WriteNumber(22, 1, nperval, nfFixed, 2); + nperval := NumberOfPeriods(INTEREST_RATE, REG_PAYMENT, PRESENT_VALUE, fval, PAYMENT_WHEN); + worksheet.WriteUTF8Text(22, 0, 'CALCULATION OF THE NUMBER OF PAYMENT PERIODS'); + worksheet.WriteFontStyle(22, 0, [fssBold]); + worksheet.WriteUTF8Text(23, 0, 'Direct calculation'); + worksheet.WriteNumber(23, 1, nperval, nfFixed, 2); - worksheet.WriteUTF8Text(23, 0, 'Excel''s calculation using constants'); - worksheet.WriteNumberFormat(23, 1, nfFixed, 2); - worksheet.WriteRPNFormula(23, 1, CreateRPNFormula( - RPNNumber(INTEREST_RATE, - RPNNumber(PAYMENT, - RPNNumber(PRESENT_VALUE, - RPNNumber(fval, - RPNNumber(PAYMENT_WHEN, - RPNFunc(fekNPER, 5, - nil)))))))); - Worksheet.WriteUTF8Text(24, 0, 'Excel''s calculation using cell values'); + worksheet.WriteUTF8Text(24, 0, 'Worksheet calculation using constants'); worksheet.WriteNumberFormat(24, 1, nfFixed, 2); worksheet.WriteRPNFormula(24, 1, CreateRPNFormula( - RPNCellValue('B1', // interest rate - RPNCellValue('B3', // payment - RPNCellValue('B4', // present value - RPNCellValue('B10', // future value - RPNCellValue('B5', // payment at end or at start + RPNNumber(INTEREST_RATE, + RPNNumber(REG_PAYMENT, + RPNNumber(PRESENT_VALUE, + RPNNumber(fval, + RPNNumber(ord(PAYMENT_WHEN), + RPNFunc(fekNPER, 5, + nil)))))))); + Worksheet.WriteUTF8Text(25, 0, 'Worksheet calculation using cell values'); + worksheet.WriteNumberFormat(25, 1, nfFixed, 2); + worksheet.WriteRPNFormula(25, 1, CreateRPNFormula( + RPNCellValue('B2', // interest rate + RPNCellValue('B4', // payment + RPNCellValue('B5', // present value + RPNCellValue('B11', // future value + RPNCellValue('B6', // payment at end or at start RPNFunc(fekNPER, 5, // Call Excel's PMT formula nil)))))))); + // interest rate calculation + rateval := InterestRate(NUMBER_PAYMENTS, REG_PAYMENT, PRESENT_VALUE, fval, PAYMENT_WHEN); + worksheet.WriteUTF8Text(27, 0, 'CALCULATION OF THE INTEREST RATE'); + worksheet.WriteFontStyle(27, 0, [fssBold]); + worksheet.WriteUTF8Text(28, 0, 'Direct calculation'); + worksheet.WriteNumber(28, 1, rateval, nfPercentage, 2); + + worksheet.WriteUTF8Text(29, 0, 'Worksheet calculation using constants'); + worksheet.WriteNumberFormat(29, 1, nfPercentage, 2); + worksheet.WriteRPNFormula(29, 1, CreateRPNFormula( + RPNNumber(NUMBER_PAYMENTS, + RPNNumber(REG_PAYMENT, + RPNNumber(PRESENT_VALUE, + RPNNumber(fval, + RPNNumber(ord(PAYMENT_WHEN), + RPNFunc(fekRATE, 5, + nil)))))))); + Worksheet.WriteUTF8Text(30, 0, 'Worksheet calculation using cell values'); + worksheet.WriteNumberFormat(30, 1, nfPercentage, 2); + worksheet.WriteRPNFormula(30, 1, CreateRPNFormula( + RPNCellValue('B3', // number of payments + RPNCellValue('B4', // payment + RPNCellValue('B5', // present value + RPNCellValue('B11', // future value + RPNCellValue('B6', // payment at end or at start + RPNFunc(fekRATE, 5, // Call Excel's PMT formula + nil)))))))); + workbook.WriteToFile(AFileName, sfExcel8, true); finally @@ -389,6 +331,9 @@ begin s2 := UTF8ToAnsi(worksheet.ReadAsUTF8Text(r, 1)); if s1 = '' then WriteLn + else + if s2 = '' then + WriteLn(s1) else WriteLn(s1+': ':50, s2); end;