Bonds
06-01-2015, 09:20 AM (This post was last modified: 06-01-2015 04:22 PM by salvomic.)
Post: #1
 salvomic Senior Member Posts: 1,366 Joined: Jan 2015
Bonds
hi,
I would present a program useful to calculate price and yield of a Bond.
The program treat Bonds with coupons paid annually or semi-annually (or other ends) and also "Zero bonds", both for "actual year" and "financial year" (30/360).

For some examples see HP 12C User Guide or here.

Some examples may differ from the calculations made by HP 12C but always below 0.1, due to different rounding for the numbers...

Enjoy and let me know if you have further improvement.

Salvo M.

Code:
 smenu(); EXPORT Bonds() BEGIN smenu(); END; EXPORT Price() // by Salvo Micciché 2015 // For Zero Coupon bonds input 0 in "Coupon" field BEGIN   local mesg, days1, days2, toReg, N, finAct;   local sd, sm, sy, md, mm, my, ann, nextReg;   local coupon, yi, years, price, inter, dayPer;   local m1, d1;   input ({ {sd,[0],{15,15,1}},    {sm,{"1","2","3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, {40,15,1}},    {sy,[0],{70,20,1}},   {md,[0],{15,15,2}},    {mm,{"1","2","3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, {40,15,2}},    {my,[0],{70,20,2}},   {coupon,[0],{40,15,4}}, {yi,[0], {75,15,4}},   {ann,[0], {40,15,5}}, {finAct,2,{85,2, 5}}   },    "Price of Bond (maturity 100)", {"sd", "sm", "sy", "md", "mm", "my",    "Coupon", "y%", "ann", "Financial"},    {"Settlement day", "Settlement month", "Settlement year (purchase)",    "Maturity day", "Maturity month", "Maturity year (regulation)",    "Coupon payment % (0 for zero coupon)", "yield %", "n. of annuality (i.e 2=semi)", "Financial year (360)"},    {1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, {1, 0, 2015, 1, 0, 2015, 6.75, 4.75, 2, 0} );   sd:=EVAL(sd); sm:=EVAL(sm);   md:=EVAL(md); mm:=EVAL(mm);   sy:=EVAL(sy); my:=EVAL(my);   days1 := sy+sm/100+sd/10000;   days2 := my+mm/100+md/10000;   toReg := sy+mm/100+md/10000; // days to the regulation   years := my-sy; // years to the maturity   IF years<1 THEN years:= 1; END;   IF (finAct==0) THEN   N:= DDAYS(days1, days2);   IF (days1>toReg) THEN nextReg:=DATEADD(toReg,IP(365/ann)); ELSE nextReg:=toReg; END;   toReg := DDAYS(days1, nextReg);   dayPer := IP(365/ann);   ELSE   N:= 30*12*years;   IF (days1>toReg) THEN nextReg:=DATEADD(toReg,IP(360/ann)); ELSE nextReg:=toReg; END;   m1:=IP(FP(nextReg)*100); d1:=FP(FP(nextReg)*100)*100;   toReg:= 30*(m1-sm)+d1-sd;   dayPer:= IP(360/ann);   END; // if   price := (coupon/ann)*( (1-1/(1+(yi/(ann*100)))^(ann*years)) / (yi/(ann*100)) ) + 100/(1+(yi/(ann*100)))^(ann*years);   inter := (coupon/ann)*((dayPer - toReg)/dayPer);   IF (coupon == 0) THEN // Zero Coupon   RECT_P();     TEXTOUT_P("Zero Coupon", 10, 50);     TEXTOUT_P("from " + sd + "-" + sm + "-" + sy + " to " + md + "-" + mm + "-" + my , 10, 65 );     TEXTOUT_P("Price of Bond: " + price, 10, 85);     TEXTOUT_P("Paid " + ann + " times per year", 10, 105);     TEXTOUT_P("Press ESC to continue", 10, 130);   ELSE   RECT_P();     TEXTOUT_P("Days: " + N + " to the end (" + years + " years)", 10, 50);    TEXTOUT_P("from " + sd + "-" + sm + "-" + sy + " to " + md + "-" + mm + "-" + my , 10, 65 );     TEXTOUT_P("Days to next regulation " + toReg, 10, 85);     TEXTOUT_P("Price of Bond: " + price, 10, 105);     TEXTOUT_P("Paid " + ann + " times per year", 10, 125);     TEXTOUT_P("Interest: " + inter, 10, 145);     TEXTOUT_P("paid for " + EVAL(dayPer-toReg) + " days", 10, 165);     TEXTOUT_P("Press ESC to continue", 10, 190);   END; //if   WAIT; smenu();   RETURN {ROUND(price, 3), ROUND(inter,3), ROUND(price+inter,3)}; END; EXPORT Yield() BEGIN   local mesg, days1, days2, toReg, N, finAct;   local sd, sm, sy, md, mm, my, years, yield;   local coupon, price, ann, inter, dayPer, nextReg;   local d1, m1;   input ({ {sd,[0],{15,15,1}},    {sm,{"1","2","3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, {40,15,1}},    {sy,[0],{70,20,1}},   {md,[0],{15,15,2}},    {mm,{"1","2","3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, {40,15,2}},    {my,[0],{70,20,2}},   {coupon,[0],{40,15,4}}, {price,[0], {75,15,4}},   {ann,[0], {40,15,5}}, {finAct,2,{85,2, 5}}   },    "Bond YTM: yield to maturity", {"sd", "sm", "sy", "md", "mm", "my",    "Coupon", "Price", "annu", "Financial"},    {"Settlement day", "Settlement month", "Settlement year (purchase)",    "Maturity day", "Maturity month", "Maturity year (regulation)",    "Coupon payment % (0 for zero coupon)", "Price now", "n. of annuality (i.e 2=semi)", "Financial year (360)"},    {1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, {1, 0, 2015, 1, 0, 2015, 6.75, 120, 2, 0} );   sd:=EVAL(sd); sm:=EVAL(sm);   md:=EVAL(md); mm:=EVAL(mm);   sy:=EVAL(sy); my:=EVAL(my);   days1 := sy+sm/100+sd/10000;   days2 := my+mm/100+md/10000;   toReg := sy+mm/100+md/10000; // days to the regulation   years := my-sy; // years to the maturity   IF years<1 THEN years:= 1; END;   IF (finAct==0) THEN   N:= DDAYS(days1, days2);   IF (days1>toReg) THEN nextReg:=DATEADD(toReg,IP(365/ann)); ELSE nextReg:=toReg; END;   toReg := DDAYS(days1, nextReg);   dayPer := IP(365/ann);   ELSE   N:= 30*12*years;   IF (days1>toReg) THEN nextReg:=DATEADD(toReg,IP(360/ann)); ELSE nextReg:=toReg; END;   m1:=IP(FP(nextReg)*100); d1:=FP(FP(nextReg)*100)*100;   toReg:= 30*(m1-sm)+d1-sd;   dayPer:= IP(360/ann);   END; // if   C:= coupon; A:= ann; Y:= years; P:= price;   // yield := Solve.SOLVE((C/A)*( (1-1/(1+(X/A))^(A*Y)) / (X/A) ) + 100/(1+(X/A))^(A*Y) = P, X);   yield := FNROOT((C/A)*( (1-1/(1+(X/A))^(A*Y)) / (X/A) ) + 100/(1+(X/A))^(A*Y) = P, X);   IF (coupon==0) THEN // Zero Coupon RECT_P();     TEXTOUT_P("Zero Coupon", 10, 50);     TEXTOUT_P("from " + sd + "-" + sm + "-" + sy + " to " + md + "-" + mm + "-" + my , 10, 65 );     TEXTOUT_P("Price of Bond: " + price, 10, 85);     TEXTOUT_P("paid " + ann + " times per year", 10, 105);     TEXTOUT_P("Yield to Maturity: " + ROUND(100*yield,3) + "%", 10, 125);     TEXTOUT_P("Press ESC to continue", 10, 150);   ELSE RECT_P();     TEXTOUT_P("Days: " + N + " to the end (" + years + " years)", 10, 50);     TEXTOUT_P("from " + sd + "-" + sm + "-" + sy + " to " + md + "-" + mm + "-" + my , 10, 65 );    TEXTOUT_P("Days to next regulation " + toReg, 10, 85);     TEXTOUT_P("Price of Bond: " + price, 10, 105);    TEXTOUT_P("paid " + ann + " times per year", 10, 125);     TEXTOUT_P("Yield to Maturity: " + ROUND(100*yield,3) + "%", 10, 145);     TEXTOUT_P("Press ESC to continue", 10, 170);   END; //if   WAIT;   smenu();   RETURN ROUND(100*yield,3); END; smenu() BEGIN   local ch;   CHOOSE(ch, "Bonds: price, YTM, interest", "Price", "Yield", "Quit");   CASE    IF ch==1 THEN Price(); END;    IF ch==2 THEN Yield(); END;    IF ch==3 THEN RETURN; END;   DEFAULT   END; // case END;

∫aL√0mic (IT9CLU), HP Prime 50g 41CX 71b 42s 12C 15C - DM42 WP34s :: Prime Soft. Lib
08-20-2017, 09:40 PM
Post: #2
 Randall Junior Member Posts: 24 Joined: Nov 2015
RE: Bonds
Salvo-

I am a new Prime user and an occasional buyer of US municipal bonds.

I have a few questions:

From section 4 page 82 of the HP12C manual the examples are:

Treasury Bonds (actual/actual) semi annual payments

28 April 2004 settle date
04 June 2018 mature date
6.75% coupon
4.75% desired yield at maturity
? what is the bond price

HP 12C answer: 120.38 123.07 (bond price + accrued interest)
Salvo Prime answer: 120.28 2.69 accrued interest = 122.97 (bond price + accrued interest)

Bond Yield Example:

28 April 2004 settle date
04 June 2018 mature date
6.75% coupon
122.125 = price what is yield?

hp12c yield = 4.60 Salvo Prime answer = 4.59

Section 16 Page 175 has an hp12c program for 30/360 bonds such as municipal bonds. In using your program I understand that checking the financial box causes 30/360 calculations.

Example:

28 August 2004 settle date
01 June 2008 mature date
5.5% coupon
desired yield = 4.75 what is the bond price?

hp12c answer: 102.55 accrued interest 1.33
Salvo Prime answer: 102.703 accrued interest 1.375

desired yield = 4.50 what is the bond price?

hp12c answer: 103.41 accrued interest 1.33
Salvo Prime answer: 103.62 accrued interest 1.375

Your writeup says your answers are within .1 of the hp12c manual. Except for the example directly above (102.55 versus 102.70 and 103.41 versus 103.62) that is the case.

From the examples have I used your program correctly?

If the financial box is checked are bond calculations made on a 30/360 basis?

08-21-2017, 09:48 AM (This post was last modified: 08-21-2017 03:16 PM by salvomic.)
Post: #3
 salvomic Senior Member Posts: 1,366 Joined: Jan 2015
RE: Bonds
dear Randal,
You're right, there are some (and not good) approximations in my program. I made it in 2015 and it seemed to work well then, but now I can see your observed differences.
I don't know if the problem stay in the new firmware or in my formulas. For example the use of solve() function like this code:
Code:
C:= coupon; A:= ann; Y:= years; P:= price;   // yield := Solve.SOLVE((C/A)*( (1-1/(1+(X/A))^(A*Y)) / (X/A) ) + 100/(1+(X/A))^(A*Y) = P, X);   yield := FNROOT((C/A)*( (1-1/(1+(X/A))^(A*Y)) / (X/A) ) + 100/(1+(X/A))^(A*Y) = P, X);

I hope someone could be so kind to help us to find the bug.

73, regards
salvo it9clu

EDIT (PS): maybe sometime HP could implement such function inside the standard APPs in the Prime. I'm looking forward :-) ;-)

∫aL√0mic (IT9CLU), HP Prime 50g 41CX 71b 42s 12C 15C - DM42 WP34s :: Prime Soft. Lib
 « Next Oldest | Next Newest »

User(s) browsing this thread: 1 Guest(s)