function [ Price, AccruedInt ] = bndprice(Yield,CouponRate,Settle,Maturity,Period,Basis,Face)
%BNDPRICE Price a fixed income security from yield to maturity.
%   Given NBONDS with SIA date parameters and semi-annual yields to
%   maturity, return the clean prices and the accrued interest due.
%         
%   [Price,AccruedInt] = bndprice(Yield, CouponRate, Settle, Maturity, ...
%       Period, Basis, Face)
%
% Inputs: 
%
%   Yield (required) - Yield to maturity on a semi-annual basis
%   CouponRate (required) - Decimal number indicating the annual percentage
%   rate used to determine the coupons payable on a bond.
%   Settle (required) - Settlement date in serial date format or date
%   string; Settle must be earlier than or equal to Maturity
%   Maturity (required) - Maturity date in serial date format or date
%   string

%
% Optional Inputs:
%   Period - Coupons payments per year; Allowed values are 0, 1, 
%   2 (default), 3, 4, 6, and 12
%   Basis - Day-count basis: 0 = actual/actual (default), 1 = 30/360 (SIA),
%   2= actual/360, 3 = actual/365, 4 = 30/360 (PSA), 5 = 30/360 (ISDA), 6 =
%   30/360 (European), 7 = actual/365 (Japanese)
%   Face - Face value of the bond; default is 100
%
% Outputs:
%   Price - Clean price of the bond. 
%   AccruedInt - Accrued interest payable at settlement.
%
% Notes:
%   The dirty price of the bond is the clean price plus the
%   accrued interest.  It is equal to the present value of the bond
%   cash flows.
%   
%   The Price and Yield are related by the formula:
%   Price + Accrued Interest = sum( Cash Flow*(1+Yield/2)^(-Time) )
%   where the sum is over the bond's cash flows and corresponding
%   times in units of semi-annual coupon periods.

%  Author(s): B. Blunk 02/11/05 bblunk@unlnotes.unl.edu

% Check input parameters
if (nargin < 4) 
     error('You must enter Yield, CouponRate, Settle, and Maturity');
end 

if (nargin < 5)
    Period = 2;
end

%Basis defaults to 0
if (nargin < 6)
    Basis = 0;
end

%Face defaults to 100
if (nargin < 7)
    Face = 100;
end

%Make sure only CouponRate is a vector
if (~(isscalar(Yield) && isscalar(Period) && isscalar(Basis) && isscalar(Face)))
    error('Only CouponRate may be a vector')
end
%Make sure CouponRate is in column vector form
CouponRate = CouponRate(:);

%Given the period, calculate the value of each coupon payment
couponPayment = CouponRate * Face / Period;

%Calculate the number of days in the current coupon, and use this to
%determine accrued interest, and the fractional amount of the next coupon
%to use for clean price

%Calculate the number of days remaining in the bond
remainingDaysinBond = daysbetween(Settle, Maturity, Basis);

%From the number of days, find the number of upcoming coupons
%Note this does not factor lear years into consideration, however a 30 year
%bond would have at most 8 leap years, and the extra 8 days will not cause
%overflow to another year
if(Basis == 0)
    daysinyear = 365;
else
    daysinyear = 360
end
numCoupons = ceil(remainingDaysinBond / (daysinyear / Period));

%Based on the number of coupons remaining, find the date of the upcoming
%coupon
couponDate = addtodate(datenum(Maturity),-12/Period * (numCoupons - 1), 'month');

%If the settle date is a coupon payment, assume the seller collected coupon
%And start the next coupon period
if(couponDate == datenum(Settle))
    numCoupons = numCoupons - 1;
    couponDate = addtodate(datenum(couponDate), 12/Period, 'month');
end

%Now that we know the coupon date, find the days remaining in the current
%coupong
couponDaysRemaining = daysbetween(Settle,couponDate,Basis);
%Find the number of days elapsed in the current coupon
couponDaysElapsed = daysbetween(addtodate(datenum(couponDate),-6,'month'), Settle, Basis);
%Find the total number of days in the current coupon period
totalCouponDays = daysbetween(addtodate(datenum(couponDate),-6,'month'),datenum(couponDate), Basis);

%Calculate Accrued Interest based on the number of days already earned
AccruedInt = couponPayment * couponDaysElapsed / totalCouponDays;
%And the fraction of the first period to use in the clean price
fractionalPeriod = couponDaysRemaining / totalCouponDays;

%Build a vector of cash flows
cfVector = repmat(couponPayment, 1, numCoupons);
cfVector(:,numCoupons) = couponPayment + Face;

%Build a vector of discount periods
periodsVector = fractionalPeriod:numCoupons-1+fractionalPeriod;

%Find the periodic yield
periodYield = Yield ./ Period;

%Get the PV of each PMT in the vector
discountsVec = repmat(((1 + periodYield).^periodsVector),size(cfVector,1),1);
pvVector = cfVector ./ discountsVec;

%The present value of the cash flows is the dirty price, subtract the
%accrued interest for the clean price
Price = sum(pvVector,2) - AccruedInt;