HotXLS beräknar Excels finansfunktioner för obligationer och statspapper direkt i Delphi och C++Builder: PRICE, YIELD, DURATION, MDURATION, de sex kupongdatumsprimitiven, statsskuldväxeltrion, diskonteringsfamiljen och de franska avskrivningsfunktionerna. De beräknas inuti bibliotekets egen formelmotor, så en arbetsbok som prissätter en obligationsportfölj omberäknas på en server utan att vare sig Excel eller tillägget Analysis ToolPak syns till någonstans
Dessa funktioner bodde tidigare i Analysis ToolPak, vilket är varför de fortfarande har rykte om sig att vara besvärliga. Sedan Excel 2007 har de varit en del av standardfunktionsuppsättningen och skrivs rent i filformatet, utan det _xlfn.-prefix som genuint nyare funktioner bär. En arbetsbok som använder dem är en helt vanlig arbetsbok; den enda frågan är om det som omberäknar den känner till funktionerna
Vad kupongdatummotorn måste få rätt
Varje obligationsberäkning vilar på samma primitiv: hur många dagar sedan senaste kupong, hur många dagar i den aktuella kupongperioden, hur många dagar till nästa kupong, när dessa kuponger infaller, och hur många som återstår. Det är COUPDAYBS, COUPDAYS, COUPDAYSNC, COUPPCD, COUPNCD och COUPNUM, och varje pris- och avkastningsfunktion anropar dem
Det som gör dem icke-triviala är basargumentet. Fem dagräkningskonventioner är i spel — 30/360 US, verklig/verklig, verklig/360, verklig/365 och 30/360 europeisk — och de är oense om frågor som vad som händer när ett likviddatum infaller på den 31:a i en månad, eller hur ett februarikupongdatum beter sig ett skottår. Att få en konvention rätt och approximera de andra ger resultat som ser rimliga ut och är fel med några punkter, vilket på en stor position är riktiga pengar
HotXLS implementerar alla fem i en självständig, nästlad kupongdatummotor som delas av varje funktion som behöver den, så PRICE och DURATION kan aldrig vara oense om var kupongdatumen ligger
Prissättning och avkastning i en arbetsbok
Inget med den anropande koden är speciellt: skriv formlerna, omberäkna, läs värdena. Det är poängen — arbetsboken är modellen, och din Delphi-applikation är räknaren:
uses
lxHandleX;
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Bond');
Sheet.Cells[1, 1].Value := EncodeDate(2026, 3, 15); // likviddag
Sheet.Cells[2, 1].Value := EncodeDate(2034, 11, 15); // förfallodag
Sheet.Cells[3, 1].Value := 0.0425; // kupongränta
Sheet.Cells[4, 1].Value := 0.0391; // begärd avkastning
Sheet.Cells[5, 1].Value := 100; // inlösenvärde
Sheet.Cells[6, 1].Value := 2; // frekvens
Sheet.Cells[7, 1].Value := 1; // bas: verklig/verklig
Sheet.Cells[9, 1].Formula := '=PRICE(A1,A2,A3,A4,A5,A6,A7)';
Sheet.Cells[10, 1].Formula := '=DURATION(A1,A2,A3,A4,A6,A7)';
Sheet.Cells[11, 1].Formula := '=MDURATION(A1,A2,A3,A4,A6,A7)';
Sheet.Cells[12, 1].Formula := '=COUPNUM(A1,A2,A6,A7)';
Book.Recalculate;
Writeln(Format('price %.6f Macaulay %.6f modified %.6f coupons %d',
[Double(Sheet.Cells[9, 1].Value), Double(Sheet.Cells[10, 1].Value),
Double(Sheet.Cells[11, 1].Value),
Integer(Sheet.Cells[12, 1].Value)]));
finally
Book.Free;
end;
end;
Datum matas in som riktiga datumvärden snarare än som strängar. Ett textdatum fungerar i Excel eftersom Excel tvångskonverterar det, och tvångskonvertering beror på lokalinställning, vilket är exakt den typen av beroende en serverside-omberäkning inte bör ha. Datumsystemreglerna som ligger under täcks i datumserienummer och 1904-systemet
Avkastning saknar sluten form, och det får konsekvenser
PRICE är en direkt beräkning: diskontera kassaflödena, summera dem. YIELD är dess invers och saknar en sluten lösning för en obligation med mer än en återstående kupong, så den måste lösas numeriskt. HotXLS använder en halveringsmetod (bisektion), som konvergerar tillförlitligt snarare än snabbt
Två praktiska konsekvenser följer. Avkastning är mätbart dyrare att beräkna än pris, så en portföljomvärdering som beräknar avkastning för tio tusen positioner kostar märkbart mer än en som beräknar priser, och det är värt att veta vilken av de två din modell faktiskt behöver. Och en avkastning som inte kan inringas — eftersom indata beskriver en obligation som ingen avkastning kan prissätta till det angivna värdet — returnerar ett fel snarare än ett felaktigt tal, vilket är det korrekta beteendet och det som är värt att testa för
Statsskuldväxlar är en egen familj
Statsskuldväxlar är diskonteringsinstrument utan kuponger, så de använder TBILLPRICE, TBILLYIELD och TBILLEQ snarare än obligationsfunktionerna. TBILLEQ konverterar en diskonteringsränta till en obligationsekvivalent avkastning, och den har en dokumenterad egenhet värd att känna till: för löptider över 182 dagar är konverteringen kvadratisk snarare än linjär, eftersom värdepapperet sträcker sig över mer än en halvårsperiod
Att istället mata en statsskuldväxel in i PRICE, med noll kupong och en frekvens, ger inte samma tal. Diskonteringsinstrumentfamiljen — DISC, INTRATE, RECEIVED, PRICEDISC och YIELDDISC — finns just för att dessa instrument noteras och avvecklas annorlunda
Resten av uppsättningen, på ett ställe
Utöver obligationer och statsskuldväxlar täcker samma sats de funktioner som omger dem i verkliga modeller. PRICEMAT och YIELDMAT hanterar värdepapper som betalar ränta vid förfall. ACCRINTM beräknar upplupen ränta för dessa. EFFECT och NOMINAL konverterar mellan effektiv och nominell årsränta, vilket är den konvertering som oftast görs fel för hand. CUMIPMT och CUMPRINC ger kumulativ ränta och amortering över ett intervall av perioder. FVSCHEDULE tillämpar en serie olika räntor i följd, och tar antingen ett intervall eller ett skalärt värde
DOLLARDE och DOLLARFR konverterar mellan decimala och bråkdelsbaserade dollarnoteringar, vilket spelar roll för alla instrument som fortfarande noteras i trettioandelar. Och AMORDEGRC och AMORLINC implementerar de franska avskrivningsmetoderna, vilka krävs enligt fransk redovisningslagstiftning snarare än är valfria bekvämligheter
// En räntekonvertering folk rutinmässigt gör fel för hand
Sheet.Cells[1, 3].Formula := '=EFFECT(0.0425,12)'; // nominell till effektiv
Sheet.Cells[2, 3].Formula := '=NOMINAL(0.043338,12)'; // och tillbaka
// Kumulativ ränta under de första två åren av ett 25-årigt lån
Sheet.Cells[4, 3].Formula :=
'=CUMIPMT(0.0389/12,300,340000,1,24,0)';
// En prognos med rörlig ränta: 3 procent, sedan 3,5, sedan 4
Sheet.Cells[6, 3].Formula := '=FVSCHEDULE(10000,{0.03;0.035;0.04})';
Att validera en implementation du inte själv skrev
Finansfunktioner är det ena området där "den returnerar ett tal" inte är belägg för någonting. Talet är rimligt till sin konstruktion, och ett fel i en dagräkningskonvention förskjuter resultat med ett belopp för litet för att märkas och för stort för att accepteras
Testet som faktiskt fungerar är ett differentiellt. Bygg ett kalkylblad med representativa instrument — en obligation per dagräkningsbas, en som avvecklas vid en månads slut, en som sträcker sig över en skottårsfebruar, en statsskuldväxel under 182 dagar och en över — beräkna värdena i Excel en gång, lagra dem som förväntade resultat, och jämför mot dem efter varje biblioteksuppgradering. Positioner som spänner över ett kupongdatum och likvider på den 31:a är där implementationer skiljer sig åt, så inkludera båda medvetet
För själva arbetsböckerna är de två följeslagarna till den här funktionen beräkningsmotorn som avgör vad som ska omberäknas, beskriven i inkrementell omberäkning och beroendegrafen, och den statistiska familjen som täcks i statistiska fördelningsfunktioner. Tillsammans är de vad som låter en finansiell modell köra obevakad på en server, vilket var hela anledningen till att implementera dem direkt
Obligationsfinans, statistik, ingenjörsfunktioner och omberäkningsmotorn levereras i ett bibliotek för Delphi och C++Builder; den fullständiga funktionslistan finns på HotXLS Delphi kalkylbladskomponentsidan