fpspreadsheet: Add formula financial function NPER to user-defined formula demo.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3291 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-07-06 19:13:05 +00:00
parent 486e6b630c
commit 803c910ac6
2 changed files with 158 additions and 75 deletions

View File

@ -4,194 +4,204 @@
<PathDelim Value="\"/> <PathDelim Value="\"/>
<Version Value="9"/> <Version Value="9"/>
<BuildModes Active="Debug"/> <BuildModes Active="Debug"/>
<Units Count="7"> <Units Count="8">
<Unit0> <Unit0>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="test_formula_func"/> <UnitName Value="test_formula_func"/>
<IsVisibleTab Value="True"/> <IsVisibleTab Value="True"/>
<TopLine Value="295"/> <TopLine Value="373"/>
<CursorPos X="19" Y="324"/> <CursorPos X="30" Y="209"/>
<UsageCount Value="27"/> <UsageCount Value="37"/>
<Bookmarks Count="1"> <Bookmarks Count="1">
<Item0 X="21" Y="280" ID="1"/> <Item0 X="21" Y="325" ID="1"/>
</Bookmarks> </Bookmarks>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit0> </Unit0>
<Unit1> <Unit1>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="..\..\fpspreadsheet.pas"/>
<UnitName Value="fpspreadsheet"/> <UnitName Value="fpspreadsheet"/>
<EditorIndex Value="4"/> <EditorIndex Value="5"/>
<TopLine Value="1216"/> <TopLine Value="1216"/>
<CursorPos X="9" Y="1128"/> <CursorPos X="9" Y="1128"/>
<UsageCount Value="13"/> <UsageCount Value="18"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit1> </Unit1>
<Unit2> <Unit2>
<Filename Value="..\..\fpsfunc.pas"/> <Filename Value="..\..\fpsfunc.pas"/>
<UnitName Value="fpsfunc"/> <UnitName Value="fpsfunc"/>
<EditorIndex Value="3"/> <EditorIndex Value="4"/>
<TopLine Value="113"/> <TopLine Value="113"/>
<CursorPos X="10" Y="132"/> <CursorPos X="10" Y="132"/>
<UsageCount Value="13"/> <UsageCount Value="18"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit2> </Unit2>
<Unit3> <Unit3>
<Filename Value="..\..\fpsutils.pas"/> <Filename Value="..\..\fpsutils.pas"/>
<UnitName Value="fpsutils"/> <UnitName Value="fpsutils"/>
<EditorIndex Value="2"/> <EditorIndex Value="3"/>
<TopLine Value="876"/> <TopLine Value="876"/>
<CursorPos Y="894"/> <CursorPos Y="894"/>
<UsageCount Value="13"/> <UsageCount Value="18"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit3> </Unit3>
<Unit4> <Unit4>
<Filename Value="..\..\fpolebasic.pas"/> <Filename Value="..\..\fpolebasic.pas"/>
<UnitName Value="fpolebasic"/> <UnitName Value="fpolebasic"/>
<EditorIndex Value="1"/> <EditorIndex Value="2"/>
<TopLine Value="43"/> <TopLine Value="43"/>
<CursorPos X="29" Y="53"/> <CursorPos X="29" Y="53"/>
<UsageCount Value="13"/> <UsageCount Value="18"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit4> </Unit4>
<Unit5> <Unit5>
<Filename Value="..\..\xlsbiff8.pas"/> <Filename Value="..\..\xlsbiff8.pas"/>
<UnitName Value="xlsbiff8"/> <UnitName Value="xlsbiff8"/>
<EditorIndex Value="5"/> <EditorIndex Value="6"/>
<TopLine Value="63"/> <TopLine Value="63"/>
<CursorPos X="20" Y="83"/> <CursorPos X="20" Y="83"/>
<UsageCount Value="12"/> <UsageCount Value="17"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit5> </Unit5>
<Unit6> <Unit6>
<Filename Value="..\..\xlscommon.pas"/> <Filename Value="..\..\xlscommon.pas"/>
<UnitName Value="xlscommon"/> <UnitName Value="xlscommon"/>
<EditorIndex Value="6"/> <EditorIndex Value="7"/>
<TopLine Value="1084"/> <TopLine Value="1084"/>
<CursorPos Y="1103"/> <CursorPos Y="1103"/>
<UsageCount Value="12"/> <UsageCount Value="17"/>
<Loaded Value="True"/> <Loaded Value="True"/>
</Unit6> </Unit6>
<Unit7>
<Filename Value="d:\lazarus-svn\fpc\2.6.4\source\rtl\objpas\math.pp"/>
<UnitName Value="math"/>
<EditorIndex Value="1"/>
<TopLine Value="294"/>
<CursorPos X="10" Y="313"/>
<UsageCount Value="13"/>
<Loaded Value="True"/>
</Unit7>
</Units> </Units>
<JumpHistory Count="30" HistoryIndex="29"> <JumpHistory Count="30" HistoryIndex="29">
<Position1> <Position1>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1521" TopLine="1501"/> <Caret Line="66" Column="12" TopLine="53"/>
</Position1> </Position1>
<Position2> <Position2>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1522" TopLine="1501"/> <Caret Line="78" Column="28" TopLine="59"/>
</Position2> </Position2>
<Position3> <Position3>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1523" TopLine="1501"/> <Caret Line="70" TopLine="59"/>
</Position3> </Position3>
<Position4> <Position4>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1548" TopLine="1544"/> <Caret Line="71" TopLine="59"/>
</Position4> </Position4>
<Position5> <Position5>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1562" TopLine="1544"/> <Caret Line="73" TopLine="59"/>
</Position5> </Position5>
<Position6> <Position6>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1452" TopLine="1432"/> <Caret Line="74" TopLine="59"/>
</Position6> </Position6>
<Position7> <Position7>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1562" TopLine="1543"/> <Caret Line="77" TopLine="59"/>
</Position7> </Position7>
<Position8> <Position8>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1567" TopLine="1543"/> <Caret Line="78" TopLine="59"/>
</Position8> </Position8>
<Position9> <Position9>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1573" TopLine="1543"/> <Caret Line="96" Column="46" TopLine="59"/>
</Position9> </Position9>
<Position10> <Position10>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="125" TopLine="105"/> <Caret Line="362" TopLine="262"/>
</Position10> </Position10>
<Position11> <Position11>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="126" TopLine="105"/> <Caret Line="70" TopLine="51"/>
</Position11> </Position11>
<Position12> <Position12>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="128" TopLine="105"/> <Caret Line="71" TopLine="51"/>
</Position12> </Position12>
<Position13> <Position13>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="133" TopLine="105"/> <Caret Line="73" TopLine="51"/>
</Position13> </Position13>
<Position14> <Position14>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="130" TopLine="105"/> <Caret Line="74" TopLine="51"/>
</Position14> </Position14>
<Position15> <Position15>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="133" TopLine="105"/> <Caret Line="77" TopLine="51"/>
</Position15> </Position15>
<Position16> <Position16>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="131" TopLine="105"/> <Caret Line="78" TopLine="51"/>
</Position16> </Position16>
<Position17> <Position17>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="133" TopLine="105"/> <Caret Line="69" Column="42" TopLine="51"/>
</Position17> </Position17>
<Position18> <Position18>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="132" TopLine="105"/> <Caret Line="82" TopLine="63"/>
</Position18> </Position18>
<Position19> <Position19>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="133" TopLine="105"/> <Caret Line="83" Column="29" TopLine="63"/>
</Position19> </Position19>
<Position20> <Position20>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="129" TopLine="105"/> <Caret Line="86" Column="22" TopLine="63"/>
</Position20> </Position20>
<Position21> <Position21>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="133" TopLine="105"/> <Caret Line="96" Column="103" TopLine="78"/>
</Position21> </Position21>
<Position22> <Position22>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="90" TopLine="70"/> <Caret Line="99" TopLine="68"/>
</Position22> </Position22>
<Position23> <Position23>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="75" Column="25" TopLine="71"/> <Caret Line="97" Column="68" TopLine="69"/>
</Position23> </Position23>
<Position24> <Position24>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="127" Column="34" TopLine="102"/> <Caret Line="98" Column="13" TopLine="64"/>
</Position24> </Position24>
<Position25> <Position25>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="280" TopLine="261"/> <Caret Line="97" Column="3" TopLine="68"/>
</Position25> </Position25>
<Position26> <Position26>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1543" Column="32" TopLine="1543"/> <Caret Line="96" Column="3" TopLine="68"/>
</Position26> </Position26>
<Position27> <Position27>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="98" Column="13" TopLine="71"/>
</Position27> </Position27>
<Position28> <Position28>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="67" Column="34" TopLine="35"/> <Caret Line="97" Column="9" TopLine="71"/>
</Position28> </Position28>
<Position29> <Position29>
<Filename Value="..\..\fpspreadsheet.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="1121" Column="10" TopLine="1089"/> <Caret Line="92" Column="11" TopLine="71"/>
</Position29> </Position29>
<Position30> <Position30>
<Filename Value="test_formula_func.pas"/> <Filename Value="test_formula_func.pas"/>
<Caret Line="269" Column="29" TopLine="261"/> <Caret Line="367" Column="28" TopLine="346"/>
</Position30> </Position30>
</JumpHistory> </JumpHistory>
</ProjectSession> </ProjectSession>

View File

@ -3,9 +3,10 @@
calculation procedure. calculation procedure.
The example will show implementation of the some financial formulas: The example will show implementation of the some financial formulas:
- FV(...) (future value) - FV() (future value)
- PV(...) (present value) - PV() (present value)
- PMT(...) (payment) - PMT() (payment)
- NPER() (number of payment periods)
The demo writes an xls file which uses these formulas and then displays The demo writes an xls file which uses these formulas and then displays
the result in a console window. (Open the generated file in Excel or the result in a console window. (Open the generated file in Excel or
@ -27,25 +28,31 @@ uses
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
{ Basic implmentation of the three financial funtions } { Basic implmentation of the four financial funtions }
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
const const
paymentAtEnd = 0; paymentAtEnd = 0;
paymentAtBegin = 1; paymentAtBegin = 1;
{ Calculates the future value of an investment based on an interest rate and { Calculates the future value (FV) of an investment based on an interest rate and
a constant payment schedule: a constant payment schedule:
- "interest_rate" is the interest rate for the investment (as decimal, not percent) - "interest_rate" (r) is the interest rate for the investment (as decimal, not percent)
- "number_periods" is the number of payment periods, i.e. number of payments - "number_periods" (n) is the number of payment periods, i.e. number of payments
for the annuity. for the annuity.
- "payment" is the amount of the payment made each period - "payment" (PMT) is the amount of the payment made each period
- "pv" is the present value of the payments. - "pv" is the present value of the payments.
- "payment_type" indicates when the payments are due (see paymentAtXXX constants) - "payment_type" indicates when the payments are due (see paymentAtXXX constants)
see: http://en.wikipedia.org/wiki/Time_value_of_money
In Excel's implementation the payments and the FV add up to 0: see: http://en.wikipedia.org/wiki/Time_value_of_money
FV + PV q^n + PMT (q^n - 1) / (q - 1) = 0 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; function FV(interest_rate: Double; number_periods: Integer; payment, pv: Double;
payment_type: integer): Double; payment_type: integer): Double;
@ -61,6 +68,30 @@ begin
Result := -(pv * qn + payment*factor); Result := -(pv * qn + payment*factor);
end; 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 { Calculates the regular payments for a loan based on an interest rate and a
constant payment schedule constant payment schedule
Arguments as shown for FV(), in addition: Arguments as shown for FV(), in addition:
@ -152,6 +183,19 @@ begin
)); ));
end; end;
function fpsNPER(Args: TsArgumentStack; NumArgs: Integer): TsArgument;
var
data: TsArgNumberArray;
begin
if Args.PopNumberValues(NumArgs, false, data, Result) then
Result := CreateNumberArg(NPER(
data[0], // interest rate
data[1], // payment
data[2], // present value
data[3], // future value
round(data[4]) // payment type
));
end;
{------------------------------------------------------------------------------} {------------------------------------------------------------------------------}
{ Write xls file comparing our own calculations with Excel result } { Write xls file comparing our own calculations with Excel result }
@ -167,7 +211,7 @@ const
var var
workbook: TsWorkbook; workbook: TsWorkbook;
worksheet: TsWorksheet; worksheet: TsWorksheet;
fval, pval, pmtval: Double; fval, pval, pmtval, nperval: Double;
begin begin
{ We have to register our financial function in fpspreadsheet. Otherwise an { We have to register our financial function in fpspreadsheet. Otherwise an
@ -176,6 +220,7 @@ begin
RegisterFormulaFunc(fekFV, @fpsFV); RegisterFormulaFunc(fekFV, @fpsFV);
RegisterFormulaFunc(fekPMT, @fpsPMT); RegisterFormulaFunc(fekPMT, @fpsPMT);
RegisterFormulaFunc(fekPV, @fpsPV); RegisterFormulaFunc(fekPV, @fpsPV);
RegisterFormulaFunc(fekNPER, @fpsNPER);
workbook := TsWorkbook.Create; workbook := TsWorkbook.Create;
try try
@ -200,7 +245,7 @@ begin
// future value calculation // future value calculation
fval := FV(INTEREST_RATE, NUMBER_PAYMENTS, PAYMENT, PRESENT_VALUE, PAYMENT_WHEN); fval := FV(INTEREST_RATE, NUMBER_PAYMENTS, PAYMENT, PRESENT_VALUE, PAYMENT_WHEN);
worksheet.WriteUTF8Text(6, 0, 'Future value'); worksheet.WriteUTF8Text(6, 0, 'Calculation of the future value');
worksheet.WriteFontStyle(6, 0, [fssBold]); worksheet.WriteFontStyle(6, 0, [fssBold]);
worksheet.WriteUTF8Text(7, 0, 'Our calculation'); worksheet.WriteUTF8Text(7, 0, 'Our calculation');
worksheet.WriteCurrency(7, 1, fval, nfCurrency, 2, '$'); worksheet.WriteCurrency(7, 1, fval, nfCurrency, 2, '$');
@ -228,7 +273,7 @@ begin
// present value calculation // present value calculation
pval := PV(INTEREST_RATE, NUMBER_PAYMENTS, PAYMENT, fval, PAYMENT_WHEN); pval := PV(INTEREST_RATE, NUMBER_PAYMENTS, PAYMENT, fval, PAYMENT_WHEN);
worksheet.WriteUTF8Text(11, 0, 'Present value'); worksheet.WriteUTF8Text(11, 0, 'Calculation of the present value');
worksheet.WriteFontStyle(11, 0, [fssBold]); worksheet.WriteFontStyle(11, 0, [fssBold]);
worksheet.WriteUTF8Text(12, 0, 'Our calculation'); worksheet.WriteUTF8Text(12, 0, 'Our calculation');
worksheet.WriteCurrency(12, 1, pval, nfCurrency, 2, '$'); worksheet.WriteCurrency(12, 1, pval, nfCurrency, 2, '$');
@ -256,7 +301,7 @@ begin
// payments calculation // payments calculation
pmtval := PMT(INTEREST_RATE, NUMBER_PAYMENTS, PRESENT_VALUE, fval, PAYMENT_WHEN); pmtval := PMT(INTEREST_RATE, NUMBER_PAYMENTS, PRESENT_VALUE, fval, PAYMENT_WHEN);
worksheet.WriteUTF8Text(16, 0, 'Payment'); worksheet.WriteUTF8Text(16, 0, 'Calculation of the payment');
worksheet.WriteFontStyle(16, 0, [fssBold]); worksheet.WriteFontStyle(16, 0, [fssBold]);
worksheet.WriteUTF8Text(17, 0, 'Our calculation'); worksheet.WriteUTF8Text(17, 0, 'Our calculation');
worksheet.WriteCurrency(17, 1, pmtval, nfCurrency, 2, '$'); worksheet.WriteCurrency(17, 1, pmtval, nfCurrency, 2, '$');
@ -282,6 +327,34 @@ begin
RPNFunc(fekPMT, 5, // Call Excel's PMT formula RPNFunc(fekPMT, 5, // Call Excel's PMT formula
nil)))))))); 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);
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.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
RPNFunc(fekNPER, 5, // Call Excel's PMT formula
nil))))))));
workbook.WriteToFile(AFileName, sfExcel8, true); workbook.WriteToFile(AFileName, sfExcel8, true);
finally finally
@ -308,7 +381,7 @@ begin
// Write all cells with contents to the console // Write all cells with contents to the console
WriteLn(''); WriteLn('');
WriteLn('Contents of the first worksheet of the file:'); WriteLn('Contents of file "', AFileName, '"');
WriteLn(''); WriteLn('');
for r := 0 to worksheet.GetLastRowIndex do begin for r := 0 to worksheet.GetLastRowIndex do begin