/* -------------------------------------------------------------------------- */ /* crsp.prg program to read in crsp government bond data, do government budget accounting creates zero-coupon maturity structure, face structure, total market value, total face value, and attempts to measure surplus directly from bond transactions. Includes savings bonds but NOT monetary base. Does direct, but not return-based estimate of surplus. rettos.prg does return-based surplus calculation Requires: various data files. data matrices, xa2 from lookbnd.prg preferred; otherwise mbxa,xnew2 or mbx Produces and saves as fmts: rev1,rev2, total and private revenue (-s) rev2c, coupon component rev2d, chg q outstanding rev2m maturing bonds, bills rev2n, new bonds, bills face1,face2, total and private face value time series mkt1,mkt2, total and private market value time series matstr1,matstr2, total and private maturity structure -- zero coupon equiv. A matrix T long and N (30) wide; Bt(t+j) facestr1,facestr2, total and private, principal payments only. use lookbond.prg first to clean data NOTE: much of the program calculates monthly data series. The final version of "A cashless view of us inflation" only used annual data series, so use monthly options with caution, as I abandoned it and haven't tested it. Also, much of the program calculates surplus by tracking monthly revenue from bond sales. In the end, I emphasized the return-based calculation of surplus in rettos.prg */ /* -------------------------------------------------------------------------- */ library pgraph; outwidth 250; output file=crsp.out reset; annual = 1; /* 1: uses only december data */ /* NOTE 0 has not been used in a long time, may not work anymore. */ clean = 1; /* 1: use data cleaned by lookbond */ addsavb = 1 ; /* 1: include savings bonds? */ /* load bond data */ if annual and clean; load x = xa2; /* just the dec 31 observations from lookbond.prg*/ elseif annual and (not clean); load x = mbxa; /* load the dirty file from makefmt.prg*/ elseif (not annual) and clean; load x = xnew2; /* xnew2 is created by lookbond */ elseif (not annual) and (not clean); load x = mbx; endif; /* -------------------------------------------------------------------- Definitions of the data in x. See crsp bond data documentation too. jdate = x[.,1]; /* quotation date */ crspid = x[.,2]; /* crsp id YYYYMMDD.TCCCE T=Itype 1 = noncallable bond 2 = noncallable note 3 = cert of indebtedness 4 = treasury bill 5 = callable bond 6 = callable note 7 = tax anticipation cert of ind 8 = tax anticipation bill 9 = other (strange provisions) CCC = coupon E = uniqueness number (separates otherwise identical bonds) */ pric1r = x[.,3]; /* month end bid price */ pric2r = x[.,4]; /* month end ask price */ accint = x[.,5]; /* accrued interest */ pdint = x[.,6]; /* interest payable during month */ Itax = x[.,7]; /* taxability of interest */ Iflwr = x[.,8]; /* flower bond code */ ytm = x[.,9]; /* yield to maturity */ retadj = x[.,10]; /* 1 month holding period return */ duratn = x[.,11]; /* duration */ iout1r = x[.,12]; /* face value outstanding 0, -1 = unknown */ iout2r = x[.,13]; /* par value publicly held NA for bills, many others */ 14 I put translated coupon here in bondstr (not x) 15 I put cleaned price here in bondstr (not x) --------------------------------------------------------------------- */ format /RD 12,6; /* format for output */ /* ---------------------------------------------------------------------- to study maturity structure create the following matrices: matstr: date on rows, months (years) ahead on cols, dollars due incl. coupons facestr: date on rows, months (years) ahead on cols, only principal payments add up net issue/release for period from change in quantity outstanding, coupon reports ------------------------------------------------------------------------- */ /* -------------------------------------------------------------- */ /* Load fama-bliss data, create one year interest rates. These are used below to try to clean up revenue a bit by keeping track of when during the year revenue items are created */ /* -------------------------------------------------------------- */ load xfb[] = famablis.dat; xfb = reshape(xfb,rows(xfb)/6,6); indx = seqa(7,12,rows(xfb)/12+1); indx = selif(indx,indx .<= rows(xfb)); /* indices of end of year rows 1231xx */ xfb = xfb[indx,.]; p1 = xfb[.,2]; /* bond prices 1 year maturity */ r1 = (100*ones(rows(p1)-1,1))./p1[1:rows(p1)-1]; /* gross nom. return */ /* end of 53 (from 52 price) - 96 */ r1 = r1[1]|r1[1]|r1; /* use 53 value for 51,52 */ /* ------------------------------------ */ /* load data on bills in private hands. */ /* ------------------------------------ */ clear xfb; if annual; maxmt = 40; else; maxmt = 480; endif; /* size of maturity matrices */ i = 1; /* loops through rows of data matrix */ t = 1; /* time index, is incremented when date changes */ /* (there are many rows=bonds for any time) */ clear oldjdate,revenue,face,mkt,fmratio,oldbond; do while i <= rows(x); /* loop through rows of data matrix */ jdate = x[i,1] ; /* date in crsp format */ /* ------------------------------------------------------------------- */ /* if first bond of period, initialize maturity etc. for the period; initialize bondst matrix of bonds for this period. if not first bond of perioid, increment bondst matrix. */ /* ------------------------------------------------------------------- */ /* if first time ever through loop, do some initializations */ if i == 1; matstr1 = zeros(1,maxmt); /* initialize maturity and face structure*/ facestr1 = zeros(1,maxmt); /* 1,2: public and total q outstanding */ matstr2 = zeros(1,maxmt); facestr2 = zeros(1,maxmt); bondst = x[i,.]~0~0; /* start list of bonds at t=1*/ /* extra space for coupon, price */ /* after decoding */ face1 = 0; /* initialize time series of face, */ mkt1 = 0; /* market values, using total and */ face2 = 0; /* public */ mkt2 = 0; endif; if i > 1; if (jdate == x[i-1,1]); /* same date as last one */ bondst = bondst|(x[i,.]~0~0); /* increment list of bonds at t*/ else; /* first bond of new date */ output off; "date " jdate " row " i; /* watch program run */ output on; matstr1 = matstr1|zeros(1,maxmt); /* set up row for t */ facestr1 = facestr1|zeros(1,maxmt); matstr2 = matstr2|zeros(1,maxmt); facestr2 = facestr2|zeros(1,maxmt); face1 = face1|0; mkt1 = mkt1|0; face2 = face2|0; mkt2 = mkt2|0; oldbond = bondst; /* save last one */ bondst = x[i,.]~0~0; /* initialize bonds at t*/ endif; endif; /* --------------------------------------- */ /* update maturity structure etc. matrices */ /* --------------------------------------- */ /* find quantity outstanding today */ iout1r = x[i,12]; /* quantity outstanding */ iout2r = x[i,13]; /* quantity outstanding in public */ if iout1r < 0; /* treat NA as zero */ iout1r = 0; endif; if iout2r < 0; iout2r = 0; endif; bondst[rows(bondst),12] = iout1r; /* save cleaned iout in bondst. */ bondst[rows(bondst),13] = iout2r; /* note it's NOT in x */ /* find coupon level from crsp id */ crspid = x[i,2]; prinmat = datedif(crspid,jdate); /* principal maturity in months see datedif procedure defined below */ if annual; prinmat = (1+trunc((prinmat-1)/12)); /* maps 1->1 2->1 ... 12->1 13->2.. i.e. december 31, bonds maturing through next december are 1, 2 starts jan 1 */ endif; /* determine coupon rate if any */ coupon = 10*crspid - trunc(10*crspid); /* get rid of date, 1st # (type). have .CCCCE */ coupon = trunc(10000*coupon+1e-5)/100; /* FIND OUT what first # is */ if coupon < 0 or coupon > 20; "ERROR finding a coupon rate of " coupon; endif; /* units are percent, e.g. 8 not 0.08 */ bondst[rows(bondst),14] = coupon; /* save decoded coupon*/ /* put maturity date in maturity structure matrices */ matstr1[t,prinmat] = matstr1[t,prinmat]+iout1r; facestr1[t,prinmat] = facestr1[t,prinmat]+iout1r; matstr2[t,prinmat] = matstr2[t,prinmat]+iout2r; facestr2[t,prinmat] = facestr2[t,prinmat]+iout2r; /* put future coupon payments in maturity str. matrix */ if (coupon > 0.01) and annual; /*.01 elim. near zero numerical prob*/ matstr1[t,1:prinmat] = matstr1[t,1:prinmat] + iout1r * coupon/100; matstr2[t,1:prinmat] = matstr2[t,1:prinmat] + iout2r * coupon/100; endif; if (coupon > 0.01) and (not annual); coupdat = prinmat; do while coupdat[rows(coupdat)] > 0; coupdat = coupdat|(coupdat[rows(coupdat)]-6); /* returns column vector with (eg.) ...18 12 6 0' according to length to mat. of bond */ endo; if coupdat[rows(coupdat)] < 1; coupdat = coupdat[1:rows(coupdat)-1]; /* takes off last element if <1 */ endif; matstr1[t,coupdat] = matstr1[t,coupdat]+iout1r*coupon/200; matstr2[t,coupdat] = matstr2[t,coupdat]+iout2r*coupon/200; endif; /* find price of this bond */ bidpr = x[i,3]; askpr = x[i,4]; if (bidpr > 0) and (askpr > 0); price = (bidpr+askpr)/2; elseif (bidpr > 0); price = bidpr; elseif (askpr > 0); price = askpr; else; "Error: issue" x[i,2] " with Q" iout1r iout2r " no price, using 100"; price = 100; endif; bondst[rows(bondst),15] = price; /* save fixed price*/ /* increment total face and market value for this month */ if t /= rows(face1); "ERROR in computing face, mkt values "; endif; face1[t] = face1[t] + iout1r; mkt1[t] = mkt1[t] + iout1r*price/100; face2[t] = face2[t] + iout2r; mkt2[t] = mkt2[t] + iout2r*price/100; /* -------------------------------------------- */ /* if last one of this period, figure out revenue*/ /* -------------------------------------------- */ /* figure out if it's the last bond of the month or last row */ flg = 0; /* set flg */ if (i < rows(x)); /* to one if it's the last */ if jdate /= x[i+1,1]; /* entry for this date or the */ flg = 1; /* last row of x. Logic is */ /* weird because You cant test*/ endif; /* x[i+1] at i=rows(x) */ else; flg = 1; /* sum up at last date */ endif; /* if so, find revenue for this month */ if (flg == 1) and (t >1); /* start doing this at end of month 2 */ revt1 = 0; revt2 = 0; /* initialize total and private revenue */ rev2ct = 0; /* coupon payments */ rev2dt = 0; /* change in q outstanding bonds */ rev2mt = 0; /* maturing bonds, bills */ rev2nt = 0; /* new bonds, bills */ j = 1; do while j <= rows(oldbond); /* go through old bonds */ newbrow = (oldbond[j,2] .== bondst[.,2]); /* find new obs of this bond*/ /*vector bonst length, 0 but */ /* 1 where new obs of old bond*/ if maxc(newbrow) == 0; /* bond disappeared -> */ /* govt paid face+any coupon */ if annual; /* infer coupon from above */ revt1 = revt1 - oldbond[j,12]*(1+oldbond[j,14]/100); rev2mt = rev2mt + oldbond[j,13]*oldbond[j,15]/100*r1[t-1]; /* "sell" bond at end of previous year, bring proceeds forward at one period rate. r1[t-1] since t starts at 50 and r starts at 51; thus at t=2 =51 you want r1[1] = 50 to 51 rate. Do NOT include coupons, since those are "sold" with the price */ revt2 = revt2 - oldbond[j,13]*oldbond[j,15]/100*r1[t-1]; else; /* have actual interest paymnt*/ /* no need to worry about when in the MONTH the bond is sold */ revt1 = revt1 - oldbond[j,12]*(1+oldbond[j,6]/100); rev2mt = rev2mt + oldbond[j,13]*(1+oldbond[j,6]/100); revt2 = revt2 - oldbond[j,13]*(1+oldbond[j,6]/100); endif; elseif sumc(newbrow) /= 1; /* check to make sure just 1 today*/ "Error. two bonds look identical. newbrow" newbrow'; else; /* bond still there */ newb = selif(bondst,newbrow); /* selects row in bondst of this bond */ /* pay coupons */ if annual; /* infer coupon from above */ revt1 = revt1 - oldbond[j,12]*(oldbond[j,14]/100); rev2ct = rev2ct + oldbond[j,13]*(oldbond[j,14]/100); revt2 = revt2 - oldbond[j,13]*(oldbond[j,14]/100); else; /* have actual interest paymnt*/ revt1 = revt1 - oldbond[j,12]*(oldbond[j,6]/100); rev2ct = rev2ct + oldbond[j,13]*(oldbond[j,6]/100); revt2 = revt2 - oldbond[j,13]*(oldbond[j,6]/100); endif; /* pay ch in q outstanding */ /* at current price. */ revt1 = revt1 + (newb[12]-oldbond[j,12])*price/100; rev2dt =rev2dt + (newb[13]-oldbond[j,13])*price/100; revt2 = revt2 + (newb[13]-oldbond[j,13])*price/100; /* ?do we want to check here if revenue change is negative that the bond was callable? */ endif; j = j+1; endo; /* go through new bonds, add any new issues */ j = 1; do while j <= rows(bondst); oldbrow = (oldbond[.,2] .== bondst[j,2]); /* now compatible with oldbond, 1 where current bond was */ if maxc(oldbrow) == 0; /* this is a new bond */ revt1 = revt1 + bondst[j,12]*price/100; /* add revenu from*/ rev2nt = rev2nt + bondst[j,13]*price/100; /* new issue */ revt2 = revt2 + bondst[j,13]*price/100; /* new issue */ endif; j = j+1; endo; if t == 2; /* create, increment revenue time series */ rev1 = revt1; rev2 = revt2; rev2c = rev2ct; rev2d = rev2dt; rev2m = rev2mt; rev2n = rev2nt; elseif t > 2; rev1 = rev1|revt1; rev2 = rev2|revt2; rev2c = rev2c|rev2ct; /* coupon payments */ rev2d = rev2d|rev2dt; /* change in q outstanding bonds */ rev2m = rev2m|rev2mt; /* maturing bonds, bills */ rev2n = rev2n|rev2nt; /* new bonds, bills */ endif; endif; /* end t>1, flag =1 */ /* increment time period and set up row for processing next month */ if flg == 1; t = t+1; oldbond = bondst; /* last bond data */ endif; i = i+1; /* on to next bond? */ endo; /* report breakdown for paper table */ "dec 1996 values for table"; "Principal only:"; "public face value w/o bills in 0-0.99,1-4.9,5-9.99,10-19.99,20+ categories"; facestr2[rows(facestr2),1] sumc(facestr2[rows(facestr2),2:5]') sumc(facestr2[rows(facestr2),6:10]') sumc(facestr2[rows(facestr2),11:20]') sumc(facestr2[rows(facestr2),21:cols(facestr2)]') ; ""; "Zero coupon basis"; "public face value w/o bills in 0-0.99,1-4.9,5-9.99,10-19.99,20+ categories"; sumc(matstr2[rows(matstr2),1]') sumc(matstr2[rows(matstr2),2:5]') sumc(matstr2[rows(matstr2),6:10]') sumc(matstr2[rows(matstr2),11:20]') sumc(matstr2[rows(matstr2),21:cols(matstr2)]'); "Market value in private hands -- no bills" mkt2[rows(mkt2)]; /* add tbills and savings bonds privately held. new tbill numbers only affect privately held xxxx2 matrices */ load tbillsav[] = savtbill.dat; tbillsav = reshape(tbillsav,rows(tbillsav)/3,3); tbrev = tbillsav[2:rows(tbillsav),2] - tbillsav[1:rows(tbillsav)-1,2]; savrev = tbillsav[2:rows(tbillsav),3] - tbillsav[1:rows(tbillsav)-1,3]; /* note: we don't have prices, so ignore interest rates. Most are 3 month bills anyway, so treating as cash (redeemed Jan1, issued Dec 30) is not so bad */ if addsavb; /* include savings bonds? */ rev2 = rev2 + tbrev + savrev; rev2n = rev2n + tbrev + savrev; face2 = face2 + tbillsav[.,2] + tbillsav[.,3]; mkt2 = mkt2 + tbillsav[.,2] + tbillsav[.,3]; matstr2[.,1] = matstr2[.,1] + tbillsav[.,2] + tbillsav[.,3]; facestr2[.,1] = facestr2[.,1] + tbillsav[.,2] + tbillsav[.,3]; rev1 = rev1 + savrev; face1 = face1 + tbillsav[.,3]; mkt1 = mkt1 + tbillsav[.,3]; matstr1[.,1] = matstr1[.,1]+ tbillsav[.,3]; facestr1[.,1] = facestr1[.,1]+ tbillsav[.,3]; else; rev2 = rev2 + tbrev ; rev2n = rev2n + tbrev; face2 = face2 + tbillsav[.,2] ; mkt2 = mkt2 + tbillsav[.,2] ; matstr2[.,1] = matstr2[.,1] + tbillsav[.,2] ; facestr2[.,1] = facestr2[.,1] + tbillsav[.,2] ; endif; "private market value including bills, savings bonds, no monetary base"; mkt2[rows(mkt2)]; save rev1,rev2,face1,face2,mkt1,mkt2,matstr1,matstr2,facestr1,facestr2, rev2c,rev2d,rev2m,rev2n; /* proc datedif finds difference between a column 2 date and a column 1 date */ /* returns date in months if annual = 0, in years if annual = 1 */ /* deletes days, so date 0131 due 0204 is a 1 month maturity */ proc(1) = datedif(date2,date1); local date2yr,date1yr,date2mo,date1mo,ddif; date2 = trunc(date2); /* get rid of identifier past zero */ date1 = date1+19000000; date2 = trunc(date2/100); date1 = trunc(date1/100); /* eliminate day info */ date2yr = trunc(date2/100); date1yr = trunc(date1/100); date2mo = date2-date2yr*100; date1mo = date1-date1yr*100; ddif = 12*date2yr+date2mo-12*date1yr-date1mo; retp(ddif); endp; output off;