//This simple C++ program illustrates object oriented techniques for finance. //Financial events are CLASSES with 5 members: Present Value, Future Value, //Payment, Interest Rate and Number of Periods. The Present Value PV is the $ //amount deposited at Interest Rate R in order to obtain the Future Value FV //while depositing a fixed payment PMT for the Number of Periods N. //This class is best suited to perform calculation for an IRA type of //scenario. For a simpler class that deals with CD type of deposits, take a //look at the special case, and offspring class, Class0Pmt where payment =0. // #include <iostream.h> //cin and cout are tamer and better than printf, scanf #include <math.h> //need this for power, log and exp #include <iomanip.h> //need this for all the cool formatting of cout #define MAXP 51 //max number of periods 50 years X 12 months + initial investment #define NPAYS 26 //26 paychecks a year #define MAXCF 51 //max number of cash flows for npv & irr #define ZERO 10e-10 //tiny number for log and exp #define EMPTY -1 //nothing assigned to a value that needs to be calc'ed //financial events are classes class Financial { protected: double PV; //present value double FV; //future value these guys are accessed only inside double IntRate; //interest rate double NPer; //number of periods double Pmt; //periodic payment public: Financial(double InPV, double InFV, double InIntRate, double InNPer, double InPmt); double GetPV() { if (PV==EMPTY) PV=FV/pow(1+IntRate,NPer)-Pmt/IntRate+Pmt/(IntRate*pow(1+IntRate,NPer)); return PV; } double GetFV() { if (FV==EMPTY) FV=(PV+Pmt/IntRate-Pmt/(IntRate*pow(1+IntRate,NPer)))*pow(1+IntRate,NPer); return FV; } double GetNPer() {return NPer;} double GetIntRate() {return IntRate;} double GetPmt() { if (Pmt==EMPTY) Pmt=fabs(PV-FV/pow(1+IntRate,NPer))*(IntRate*pow(1+IntRate,NPer)/ (1-pow(1+IntRate,NPer))); return Pmt; } //function to display all members //because of inheritance, this function can be used with all offspring classes void DispFin() { cout <<setiosflags(0x0100) //always show decimal point <<setiosflags(0x1000) //always display in FIXED format <<setprecision(2) //2 decimals and line'm up. <<setfill(' ') //blanks for filler <<setw(18)<<"Present Value:" <<setw(11)<<fabs(GetPV())<<endl <<setw(18)<<"Future Value:" <<setw(11)<<fabs(GetFV())<<endl <<setw(18)<<"Interest Rate:" <<setw(11)<<fabs(GetIntRate()*100)<<endl <<setw(18)<<"Number of Periods:"<<setw(11)<<fabs(GetNPer())<<endl <<setw(18)<<"Payment:" <<setw(11)<<fabs(GetPmt())<<endl; } }; //construct class by initializing with parameter values //the member functions calculate and return their own values //if value is EMPTY then it needs to be calcualted, else it's given Financial::Financial(double InPV, double InFV, double InIntRate, double InNPer, double InPmt) { PV=InPV; FV=InFV; IntRate=InIntRate; NPer=InNPer; Pmt=InPmt; } //This is a special case of the Financial class where PMT is 0. //The most obvious application is the calculation of CD rates. In this case, //financial events are CLASSES with 4 members: Present Value, Future Value, //Interest Rate and Number of Periods. The Present Value PV is the $ amount //deposited at Interest Rate R in order to obtain the Future Value FV after the //Number of Periods N. class Class0Pmt :public Financial { public: Class0Pmt(double InPV, double InFV, double InIntRate, double InNPer); double GetPV() {if (PV==EMPTY) PV=FV/pow(1+IntRate,NPer); return PV;} double GetFV() {if (FV==EMPTY) FV=PV*pow(1+IntRate,NPer); return FV;} //The number of periods and rate can be deduced algebraicly for this class double GetNPer() { if (NPer==EMPTY) NPer=(log(FV)-log(PV))/log(1+IntRate); return NPer; } double GetIntRate() { if (IntRate==EMPTY) IntRate=exp((log(FV)-log(PV))/NPer)-1; return IntRate; } void DispFin() { cout <<setiosflags(0x0100) //always show decimal point <<setiosflags(0x1000) //always display in FIXED format <<setprecision(2) //2 decimals and line'm up. <<setfill(' ') //blanks for filler <<setw(18)<<"Present Value:" <<setw(11)<<fabs(GetPV())<<endl <<setw(18)<<"Future Value:" <<setw(11)<<fabs(GetFV())<<endl <<setw(18)<<"Interest Rate:" <<setw(11)<<fabs(GetIntRate()*100)<<endl <<setw(18)<<"Number of Periods:"<<setw(11)<<fabs(GetNPer())<<endl; } }; Class0Pmt::Class0Pmt(double InPV, double InFV, double InIntRate, double InNPer) :Financial(InPV, InFV, InIntRate, InNPer, 0) { PV=InPV; FV=InFV; IntRate=InIntRate; NPer=InNPer; }; //calculate the future value of retirement plans if the periodic //payments increase like in an RSP based on 6% or 9% of salary. double CalcRSP(double pmt[], double rsp[], double r, double gr, int numy) { int i; rsp[0]=pmt[0]; //rsp[0] is the initial investment for (i=1;i<=numy;i++) { if (i>1) pmt[i]=pmt[i-1]+(pmt[i-1]*gr/100); //deposit 6% or 9% of paycheck every payday (26 a year) Financial Retirement(rsp[i-1],-1,(r/100)/NPAYS,NPAYS,pmt[i]/NPAYS); rsp[i]=Retirement.GetFV(); } return rsp[numy]; } //oh yeah. some recursion and pointers and passing by reference. //function to calculate the rate of return of the RSP to acheive a target value double CalcR(double pmt[], double rsp[], double *r, double *inc, double gr, int numy, double rspt) { double tol,d1,d2; tol=fabs(pmt[0]/10000); //this will grant precision past 3rd dec d1=rspt-CalcRSP(pmt,rsp,*r+*inc,gr,numy); cout<<setprecision(3)<<"@"<<*r+*inc <<"% the RSP="<<rspt-d1<<endl; if (*r+*inc>200) { cout<<"\nSorry - no solution between 0% and 200%\n"; return EMPTY; } else { if (fabs(d1)<=tol) return *r+*inc; else { d2=rspt-CalcRSP(pmt,rsp,*r,gr,numy); if (d2*d1<0) *inc /= 10; else *r +=*inc; return CalcR(pmt,rsp,r,inc,gr,numy,rspt); } } } //get stats calculate RSP and rates, display results int RSP() { int numy, //number of years left to retirement i; double r, //investment rate spct, //% salary that goes to RSP gr, //salary growth rate rspt, //amount of rsp at end of time periods tpmt=0, //total payments inc, //increments for recursive r calculation pmt[MAXP],//array of annual payments (they grow with the pay) rsp[MAXP];//value of rsp in any period, rsp[0] is initial investment cout<<"Number of years left 'till retirement (max "<<MAXP-1<<") : "; cin >>numy; if (numy>MAXP||numy<=0) { cout<<"You are a funny guy\n"; return 1; } cout<<"Expected Rate of Return of the Retirement Plan : "; cin >>r; cout<<"Amount of the Startup Deposit (0 if you are starting now) : "; cin >>pmt[0]; if (pmt[0]==0) pmt[0]=ZERO; if (pmt[0]<0) pmt[0] *= -1; cout<<"Current annual pay before taxes (gross income): "; cin>>pmt[1]; //this will be divided by rsp rate if (pmt[1]<=0) { cout<<"You are a funny guy\n"; return 1; } cout<<"Percent of gross income dedicated to RSP (usually 6 or 9%) : "; cin>>spct; pmt[1] =pmt[1]*(spct/100); cout<<"Expected growth rate of your gross income (enter 5 for 5% annual raises): "; cin>>gr; rspt=CalcRSP(pmt,rsp,r,gr,numy); cout<<setiosflags(0x0100)<<setiosflags(0x1000) <<setfill(' ')<<setprecision(2)<<"\nYear Payment RSP\n\n"; for (i=0;i<=numy;i++) { cout<<setw(3)<<i<<setw(12)<<pmt[i]<<setw(12)<<rsp[i]<<endl; tpmt +=pmt[i]; } cout<<setw(27)<<setfill('-')<<"-"<<endl //print --------- <<setfill(' ')<<setw(3)<<" "<<setw(12)<<tpmt<<setw(12)<<rspt<<endl <<setw(27)<<setfill('=')<<"="<<endl; //print ========= cout<<"The amount of your RSP at retirement will be "<<rspt<<endl <<"Enter a target amount and I'll calculate the required rate of return."<<endl <<"Enter target amount : "; cin>>ws>>rspt; inc=10;r=ZERO-inc; if (CalcR(pmt,rsp,&r,&inc,gr,numy,rspt)==EMPTY) cout<<"Can't get to what you want with a rate between 0% and 200%\n"; else cout<<"To achieve an RSP="<<setprecision(2)<<rspt <<" the required Rate of Return is "<<setprecision(3)<<r<<endl; return 1; } //calculate Net Present Value of a series of cash flows double CalcNPV(double cf[], double pv[], double r, int numcf) { double npv; int i; npv=pv[0]=cf[0]; //cf[0] is the initial investment and doesn't discount for (i=1;i<=numcf;i++) { Class0Pmt CashFlow(EMPTY,cf[i],r/100,i); pv[i]=CashFlow.GetPV(); npv= npv+pv[i]; } return npv; } //calculate Internal Rate of Return of a series of cash flows //OOOOOOOoooooo recursion and passing by reference. double CalcIRR(double cf[], double pv[], double *r, double *inc, int numcf) { double tol,d1,d2; tol=fabs(cf[0]/10000); //this will grant precision past 3rd decimal d1=CalcNPV(cf,pv,*r + *inc,numcf); cout<<setprecision(3)<<"@"<<*r+*inc <<"% Initial Investment - NPV of Cash Flows="<<d1<<endl; if (((*r+*inc)<0) || ((*r+*inc)>200)) { cout<<"\nSorry - no solution between 0% and 200%\n"; return EMPTY; } else { if (fabs(d1)<=tol) return *r+*inc; else { d2=CalcNPV(cf,pv,*r,numcf); if (d2*d1<0) *inc /= 10; else *r +=*inc; return CalcIRR(cf,pv,r,inc,numcf); } } } //get cash flows, calculate NPV and IRR, display results int NpvIrr() { char yn; int numcf,i; //cash flow[0] is the intial investment double r,npv,tcf,inc,irr, cf[MAXCF],pv[MAXCF]; //cash flows 1 .. 50 get discounted cout<<"Enter number of cash flows (exclude Initial Investment and max " <<MAXCF-1<<") : "; cin >>numcf; if (numcf>MAXCF) { cout<<"You weren't paying attention\n"; return 1; } cout<<"Enter the interest rate : "; cin >>r; cout<<"Enter the amount of the Initial Investment (outflow): "; cin >>cf[0]; if (cf[0]>0) cf[0] *= -1; //Initial Investment is ALWAYS an outflow tcf=cf[0]; for (i=1;i<=numcf;i++) { cout<<"Enter Cash Flow["<<i<<"] : "; cin >>cf[i]; tcf +=cf[i]; } cin.ignore(1); npv=CalcNPV(cf,pv,r,numcf); cout<<setiosflags(0x0100)<<setiosflags(0x1000) <<setfill(' ')<<setprecision(2)<<"\nPer# Cash Flow NPV\n\n"; for (i=0;i<=numcf;i++) cout<<setw(3)<<i<<setw(12)<<cf[i]<<setw(12)<<pv[i]<<endl; cout<<setw(27)<<setfill('-')<<"-"<<endl //print --------- <<setfill(' ')<<setw(3)<<" "<<setw(12)<<tcf<<setw(12)<<npv<<endl <<setw(27)<<setfill('=')<<"="<<endl; //print ========= cout<<"Would you like to calculate the Internal Rate of Return (Y/N) : "; cin>>ws>>yn; cin.ignore(1); if (yn=='y' || yn=='Y') { r=0;inc=10;irr=CalcIRR(cf,pv,&r,&inc,numcf); if (irr!=EMPTY) cout<<"\nInternal Rate of Return="<<setprecision(3)<<irr<<endl; } return 1; } //give some help void HelpMsg() { cout <<"\nThis simple C++ program illustrates object oriented programming techniques for" <<"\nfinance. Financial events are CLASSES with 5 members: Present Value, Future" <<"\nValue, Interest Rate, Number of Periods and Payment. Idiot proofing and" <<"\nfeatures have been kept to a minimum to avoid obfuscating the code. Please feel" <<"\nfree to make any changes you want. If you have suggestions or improvements," <<"\nwrite me a note: Eddy Vasile, PO Box 71313, LA, CA 90071 or" <<"\n70451,3333@CompuServe." <<"\n\nThe Present Value PV is the $ amount deposited at Interest Rate R in order to" <<"\nobtain the Future Value FV after the Number of Periods N while paying Payment" <<"\nPMT. In order to calculate any member, the other 4 must be known in order to " <<"\nsatisfy the formula PV=FV/(1+R)^N-PMT/R+PMT/(R*(1+R)^N). Example: you start an" <<"\nIRA with an initial deposit of $8,000 with a fixed interest rate of 7% for 30" <<"\nyears and you make regular annual deposits of $1,500. Choose the Future Value" <<"\ncalculation to see the value of your savings at the end of the 30 years. You" <<"\nwill receive the prompt \"Enter PV R N and PMT:\" Respond by entering 8000 7 30" <<"\n1500. The result is $202,589. You were hoping that it would be around $500,000." <<"\nSince you can't change the interest rate or the annual deposits, you'd like to" <<"\nsee what the initial deposit amount (PV) should be in order to have the" <<"\n$500,000 FV. Choose the Present Value calculation and you'll see that you need" <<"\n$47,070. You still want $500,000 but you can only deposit $8,000. Choose the" <<"\nPayment Calculation and you'll see that you need to change your annual payments" <<"\nto $4,649.\nPress return to continue..";cin.get(); cout <<"\nThis is very steep. However, if you have a retirement savings plan" <<"\n(RSP) at work, you know that the regular payment is a percentage (6% - 9%) of" <<"\nyour income before taxes. Thus, the payments increase in size along with your" <<"\nsalary. Choose the option \"Retirement Plan With Annual Raises.\" Your Present" <<"\nValue or Initial Investment is still $8,000, the number of years to retirement" <<"\nis 30 and you expect the Rate of Return of the Plan to be around 7%. If you" <<"\nintend to contribute 6% of your $50,000 annual income to the RSP and you expect" <<"\nyour salary to grow at 5%, after 30 years, your RSP will be worth $594,343." <<"\nThe program assumes that you get paid 26 times a year and it compounds as such." <<"\n\nSuppose you expected around $1,000,000. When the program prompts for the" <<"\nexpected value, enter 1000000. The calculation will show that your plan needs" <<"\nto perform at a 9.729% Rate of Return to achieve that amount." <<"\nThe equation PV=FV/(1+R)^N-PMT/R+PMT/(R*(1+R)^N) has important special cases." <<"\nIf the Future Value is zero, then the Present Value is the amount of a loan" <<"\n(principal) and the payments are the amortization payments. If you intend to" <<"\ncalculate an annual loan payment, choose the Payment Calculation option and" <<"\nenter ZERO for the future value. If you want to calculate the monthly payment," <<"\nenter the the interest divided by 12 (monthly interest) and the number of years" <<"\nmultiplied by 12. Another important special case is when the payment=0. This is" <<"\nencountered in the case of a CD when one makes a deposit at an interest rate" <<"\nfor a number of years without subsequent payments. The equation is now:" <<"\nFV=PV*(1+R)^N.\nPress return to continue..";cin.get(); cout <<"\nConsider now the special case where there is NO periodic payment." <<"\nThis offspring CLASS has 4 members: Present Value, Future Value," <<"\nInterest Rate and Number of Periods. The Present Value PV is the $ amount" <<"\ndeposited at Interest Rate R in order to obtain the Future Value FV after the" <<"\nNumber of Periods N. In order to calculate any member, the other 3 must be" <<"\nknown in order to satisfy the formula FV-PV(1+R)^N=0." <<"\nR and I are derived by exponentiating (1+R)^N=FV/PV. Choose the calculation" <<"\nyou want and enter the requested values. Example: choose the Future Value" <<"\nCalculation. You will be prompted: 'Enter PV R and N :' Enter 5000 6.75 3 to" <<"\ncalculate the Future Value of a $5,000 deposit @6.75% for 3 years. The result" <<"\nis $6,082.38. To compound interest monthly, enter N as 36 (3 X 12) and R as" <<"\n0.563 (monthly interest = 6.75/12). FV is now $6,119.92. This is useful if you" <<"\nwant to calculate the YIELD of a CD. Now that you know the monthly compounded" <<"\nFV, choose the Interest Calculation and enter 5000 6119.92 3. The YIELD is" <<"\n6.97%. Modify the source code to compound weekly or daily." <<"\nPress return to continue..";cin.get(); cout <<"\nNet Present Value compares the value of an investment to a straight deposit." <<"\nAll payments (outflows of cash) must be entered as negative numbers. Cash" <<"\ninflows (payments to you) are entered as positive numbers. Example: You could" <<"\nbuy a rental property for 160,000 and manage it for 5 years or deposit the" <<"\nmoney for 5 years at 8.5%. Choose the NPV calculation and enter 5 for the" <<"\nnumber of cash flows and 8.5 for interest. Enter -160000 for the initial" <<"\ninvestment followed by the forecasted 5 year cash flows: -5000 1st year (a" <<"\nloss due to repairs and vacancy), 5500 2nd (a gain), 6500 3rd, 4500 4th and" <<"\nyou sell it in the 5th for 240000. The NPV of these cash flows is 8,010.60." <<"\nThus, investing in the rental brings in MORE money than depositing it at 8.5%." <<"\nIn other words, the 5 cash flows achieve at least the targeted return of 8.5%." <<"\nThe break-even interest rate is called the Internal Rate of Return. Rates" <<"\nhigher higher than IRR would mean that depositing the money is a better" <<"\ninvestment. Try the same NPV at 12%. You'll see that the cash flows do not" <<"\nachieve that return. The IRR can be calculated through iterations. My" <<"\nalgorithm searches the solution by trying incremental rates and then refining" <<"\nthe increments until a suitable solution is found between 0% and 200%." <<"\nPress return to continue..";cin.get(); } //menu of choices int menu() { //keep it simple or obfuscate C++ code double //also makes it portable p=EMPTY, //pv | When you start, all are unknown, or f=EMPTY, //fv | -1. Thus, if you miss initializing r=EMPTY, //interest rate | the program crashes(ln(EMPTY)) without n=EMPTY, //number of periods | returning bogus nums. Put in checks t=EMPTY; //payment | yourself char choice; cout <<endl <<"Calculations for the general financial class (IRA or RSP)\n" <<"=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=Eddy's Tools=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=|\n" <<"|1 PV Present Value Calculation |2 FV Future Value Calculation |\n" <<"|3 PMT- Payment Calculation |4 Retirement Plan With Annual Raises |\n" <<"|-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n" <<"\nCalculations for special case where PMT=0 (CD)\n" <<"=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=Eddy's Tools=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=|\n" <<"|5 PV Present Value Calc |6 FV Future Value Calculation |\n" <<"|7 N Number of Periods Calc |8 R Interest Rate Calculation |\n" <<"|9 NPV/IRR Internal Rate/Net Pres Value| |\n" <<"|0 Exit the program |? Help Brief Tutorial |\n" <<"=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n"; cout <<"Choose one of the above : "; cin >> ws >> choice; cin.ignore(1); //ws sucks out blanks switch (choice) { case '1': cout<<"Present Value Calculation; Enter FV R N and PMT: "; cin >> f >> r >> n >>t; cin.ignore(1); f=fabs(f);n=fabs(n);t=fabs(t); break; case '2': cout<<"Future Value Calculation; Enter PV R N and PMT: "; cin >> p >> r >> n >>t; cin.ignore(1); p=fabs(p);n=fabs(n);t=fabs(t); break; case '3': cout<<"Periodic Payment Calculation; Enter PV FV R and N: "; cin >> p >> f >> r >>n; cin.ignore(1); p=fabs(p);f=fabs(f);n=fabs(n); break; case '4': RSP();cin.ignore(1);break; case '5': cout<<"Present Value Calculation; Enter FV R and N : "; cin >> f >> r >> n; cin.ignore(1); break; case '6': cout<<"Future Value Calculation; Enter PV R and N : "; cin >> p >> r >> n; cin.ignore(1); break; case '7': cout<<"Number of Periods Calculation; Enter PV FV and R : "; cin >> p >> f >> r; cin.ignore(1); break; case '8': cout<<"Interest Rate Calculation; Enter PV FV and N : "; cin >> p >> f >> n; cin.ignore(1); break; case '9': NpvIrr();break; case '?': HelpMsg();return 1; case '0': cout<<"Bye\n";return 0; default : cout<<"Pardon?";choice='?';break; } if (choice<'4') { if (p==0) p=ZERO; // in case they check a mortgage or if (f==0) f=ZERO; // current value of an investment (modified pv) if (t==0) t=ZERO; // r=fabs(r/100); Financial Calculation(p,f,r,n,t); Calculation.DispFin(); return 1; } if (choice>'4' && choice<'9') { if (r!=EMPTY) r=r/100; if ((choice=='7'||choice=='8') && (p<=0||f<=0)) { cout<<"This algorithm is based on exponentiation. You may not have present or future\n" <<"values < or = 0. DO NOT make the present value negative to show outflows\n"; return 1; } Class0Pmt Calc0Pmt(p,f,r,n); Calc0Pmt.DispFin(); return 1; } return 1; } void main() { while (menu()); }