Design
NB: This document is out of date. The QuantLibAddin implementation still reflects the high-level ideas in this document but many of the details have changed. For further information please refer to the QuantLibAddin source code or send email to quant.nosp@m.lib-.nosp@m.users.nosp@m.@lis.nosp@m.ts.sf.nosp@m..net.

Abstract

QuantLibAddin implements an interface supporting a subset of QuantLib functionality. Constructor, member and utility functions are defined in XML metadata from which a Python application generates source code for supported platforms. The ObjectHandler repository is used to provide an object oriented environment even on procedural platforms such as spreadsheets - QuantLib objects may be constructed, interrogated, passed as arguments to other functions, and destructed. Polymorphism is supported, for example function qlNPV returns the NPV of an Instrument and can be invoked on an instance of any derived class - Swap, Bond, etc.

Contents

  1. Design
1.1 Classes
1.2 Addins
1.3 Clients
2 Implementation
2.1 Classes
2.2 Addins
2.3 Clients
2.3.1 C++
2.3.2 Excel
  1. Autogeneration

1. Design

QuantLibAddin uses ObjectHandler to allow the user to build up a repository of QuantLib objects. Each QuantLib class to be available in QuantLibAddin is wrapped in a class descended from Object.

The core QuantLibAddin library can be loaded directly into standalone C++ programs. For other platforms, QuantLibAddin is wrapped in an additional layer providing platform-specific functionality. QuantLibAddin has the same interface on all platforms.

1.1 Classes

The class for each QuantLib object to be stored in ObjectHandler is wrapped in a class derived from Object. The constructor of the derived Object class calls the constructor of the corresponding QuantLib class, and the resulting QuantLib object is stored as a member variable of the derived Object. The Propery vector in the base Object class is populated appropriately.

1.2 Addins

The core QuantLibAddin binary can be linked directly into end-user C++ applications. For other platforms, QuantLibAddin is linked into a platform-specific library which:

  • retrieves inputs in native format from host application
  • converts inputs to QuantLib format
  • invokes the QuantLibAddin function and captures its return value
  • converts the return value to native format and returns it to the host application

The above is wrapped in try/catch. All Addins use standard calls to log exceptions to the QuantLibAddin log file, and may perform addition platform-specific error handling e.g. throwing a new exception for the host application.

1.3 Clients

The client application loads QuantLibAddin, instantiating a single global instance of ObjectHandler. QuantLibAddin functions allow objects to be constructed, interrogated, modified, and passed as input parameters to constructors / member functions of other objects. Objects can be deleted explicitly, otherwise ObjectHandler destroys all objects in its repository when it is destroyed.

2 Implementation

The example below illustrates an implementation of QuantLibAddin and clients, including three QuantLibAddin functions which

  • construct a BlackScholesProcess from native datatypes
  • construct a VanillaOption from the BlackScholesProcess and native datatypes
  • change the Pricing Engine of the VanillaOption.

The example demonstrates how objects may be created, passed as inputs to constructors of other objects, manipulated, and destroyed. The example includes pseudocode, the latest development code can be checked out from the QuantLib CVS repository (module QuantLibAddin) or browsed on line.

2.1 Classes

qla/processes.hpp

namespace QuantLibAddin {
class BlackScholesProcess : public ObjHandler::Object {
public:
BlackScholesProcess(
const std::string &handleBlackVol,
const double &underlying,
const std::string &dayCounterID,
const long &settlementDateLong,
const double &riskFreeRate,
const double &dividendYield);
virtual boost::shared_ptr<void> getReference() const {
return boost::static_pointer_cast<void>(blackScholesProcess_);
}
private:
boost::shared_ptr<QuantLib::BlackScholesProcess>
blackScholesProcess_;
};
}

qla/processes.cpp

namespace QuantLibAddin {
BlackScholesProcess::BlackScholesProcess(
const std::string &handleBlackVol,
const double &underlying,
const std::string &dayCounterID,
const long &settlementDateLong,
const double &riskFreeRate,
const double &dividendYield) {
// convert input values to QuantLib datatypes
...
// retrieve BlackVolTermStructure from ObjectHandler
boost::shared_ptr<BlackVolTermStructure> blackVolTermStructure =
OH_GET_OBJECT(BlackVolTermStructure, handleBlackVol);
boost::shared_ptr<QuantLib::BlackVolTermStructure>
blackVolTermStructureP = OH_GET_REFERENCE(
QuantLib::BlackVolTermStructure,
blackVolTermStructure);
blackVolTermStructureH(blackVolTermStructureP);
// construct new BlackScholesProcess object
blackScholesProcess_ =
boost::shared_ptr<QuantLib::BlackScholesProcess>(
new QuantLib::BlackScholesProcess(
underlyingH,
flatDividendTS,
flatTermStructure,
blackVolTermStructureH));
}
}

qla/vanillaoption.hpp

namespace QuantLibAddin {
class VanillaOption : public Instrument {
public:
const std::string &handleBlackScholes,
const std::string &optionTypeID,
const std::string &payoffID,
const double &strike,
const std::string &exerciseID,
const long &exerciseDate,
const long &settlementDate,
const std::string &engineID,
const long &timeSteps);
std::string setEngine(
const std::string &engineName,
const long &timeSteps);
};
}

qla/vanillaoption.cpp

namespace QuantLibAddin {
const std::string &handleBlackScholes,
const std::string &optionTypeID,
const std::string &payoffID,
const double &strike,
const std::string &exerciseID,
const long &exerciseDate,
const long &settlementDate,
const std::string &engineID,
const long &timeSteps) {
// retrieve BlackVolTermStructure from ObjectHandler
boost::shared_ptr<BlackScholesProcess> blackScholesProcess =
OH_GET_OBJECT(BlackScholesProcess, handleBlackScholes);
const boost::shared_ptr<QuantLib::BlackScholesProcess>
blackScholesProcessQL = OH_GET_REFERENCE(
QuantLib::BlackScholesProcess,
blackScholesProcess);
// format inputs to QuantLib::VanillaOption constructor
...
// construct new VanillaOption object
mInstrument = boost::shared_ptr<QuantLib::VanillaOption>(
new QuantLib::VanillaOption(
blackScholesProcessQL,
payoff,
exercise,
pricingEngine));
// populate object's property vector
createProperty(FIELD_NPV, mInstrument->NPV());
createProperty(FIELD_ENGINE, engineID);
}
std::string VanillaOption::setEngine(
const std::string &engineID,
const long &timeSteps) {
boost::shared_ptr<QuantLib::PricingEngine> pricingEngine =
Create<boost::shared_ptr<QuantLib::PricingEngine> >()(
engineID, timeSteps);
mInstrument->setPricingEngine(pricingEngine);
updateProperty(IDX_NPV, mInstrument->NPV());
updateProperty(IDX_ENGINE, engineID);
return engineID;
}
}

2.2 Addins

The QuantLibAddin library can be linked directly into an end-user C++ application. For other platforms, QuantLibAddin is wrapped in a further addin providing platform specific functionality. Below is the code for an Excel addin function.

Addins/Excel/options.cpp

DLLEXPORT char *qlVanillaOption(
char *handle,
char *handleBlackScholes,
char *optionType,
char *payoff,
double *strike,
char *exercise,
long *exerciseDate,
long *settlementDate,
char *engine,
long *timeSteps) {
ObjHandler::obj_ptr objectPointer(new QuantLibAddin::VanillaOption(
handleBlackScholes,
optionType,
payoff,
*strike,
exercise,
*exerciseDate,
*settlementDate,
engine,
*timeSteps));
std::string returnValue =
ObjHandler::storeObject(handle, objectPointer);
static char ret[XL_MAX_STR_LEN];
ObjHandler::stringToChar(ret, returnValue);
return ret;
}
DLLEXPORT char *qlOptionSetEngine(
char *handle,
char *engineName,
long *timeSteps) {
boost::shared_ptr < QuantLibAddin::VanillaOption > objectPointer =
OH_GET_OBJECT(QuantLibAddin::VanillaOption, handle);
static std::string returnValue;
returnValue = objectPointer->setEngine(
engineName,
*timeSteps);
static char ret[XL_MAX_STR_LEN];
ObjHandler::stringToChar(ret, returnValue);
return ret;
}

2.3 Clients

2.3.1 C++

Example of a standalone C++ client application.

Clients/C++/qlademo.cpp

int main() {
double dividendYield = 0.00;
double riskFreeRate = 0.06;
double volatility = 0.20;
double underlying = 36;
double strike = 40;
long timeSteps = 801;
Date exerciseDate(13, March, 2020);
Date settlementDate(13, March, 2019);
obj_ptr blackConstantVol(new QuantLibAddin::BlackConstantVol(
settlementDate.serialNumber(), // settlement date as long
volatility, // volatility
"Actual360")); // daycount convention
storeObject("my_blackconstantvol", blackConstantVol);
obj_ptr blackScholesProcess(new QuantLibAddin::BlackScholesProcess(
"my_blackconstantvol", // black constant vol handle
underlying, // underlying
"Actual360", // daycount convention
settlementDate.serialNumber(), // settlement date as long
riskFreeRate, // risk free rate
dividendYield)); // dividend yield
storeObject("my_blackscholes", blackScholesProcess);
obj_ptr vanillaOption(new QuantLibAddin::VanillaOption(
"my_blackscholes", // stochastic process handle
"Put", // option type
"Vanilla", // payoff type
strike, // strike price
"American", // exercise type
exerciseDate.serialNumber(), // exercise date
settlementDate.serialNumber(), // settlement date
"JR", // engine type (jarrow rudd)
timeSteps)); // time steps
storeObject("my_option", vanillaOption);
logMessage("High-level interrogation of VanillaOption");
logObject("my_option");
boost::shared_ptr<QuantLibAddin::VanillaOption> vanillaOptionQLA =
OH_GET_OBJECT(QuantLibAddin::VanillaOption, "my_option");
if (!vanillaOptionQLA)
QL_FAIL("Error retrieving object my_option");
vanillaOptionQLA->setEngine(
"AEQPB", // AdditiveEQPBinomialTree
timeSteps);
logMessage("High-level interrogation: after setting engine");
logObject("my_option");
logMessage("Low-level interrogation: NPV of underlying option object");
boost::shared_ptr<QuantLib::VanillaOption> vanillaOptionQL =
OH_GET_REFERENCE(QuantLib::VanillaOption, vanillaOptionQLA);
ostringstream s;
s << "underlying option NPV() = " << vanillaOptionQL->NPV();
logMessage(s.str());
logMessage("end example program");
return 0;
}

Output of the above program:

2.3.2 Excel

Same again with cell formulas instead of values:

A description of each section of the spreadsheet:

  • Construct an object from native datatypes: Cell formula qlBlackScholesProcess is called to construct a Black Scholes object from numeric values. The formula accepts a 'handle stub' argument which is automatically decorated to create a unique Handle for the object in the ObjectHandler repository.
  • Construct an object from native datatypes & other objects: The Black Scholes object created above is passed as an input to qlVanillaOption() which creates an option. Note that only a reference to the cell containing the Black Scholes object - C12 - is passed to qlVanillaOption(). The Addin autmatically accesses cell C12, retrieves the Handle of the Black Scholes object ("stoch1~_0000a"), retrieves the Black Scholes object from ObjectHandler, and passes the object as an argument to the Vanilla Option constructor. qlVanillaOption() returns the decorated Handle of the new Vanilla Option object, which is passed to formula qlNPV() to display the NPV of the option.
  • interrogate an existing object with no prior knowledge of its structure: ohObjectPropertyNames() is called on the Handle returned from qlVanillaOption() to get the Object's Property vector.
  • Invoke a method of an existing object: The Handle of the option is passed to qlOptionSetEngine() which changes the option's pricing engine, reprices the option, and returns the changed state. Note that qlOptionSetEngine() acts on the option stored above in cell C25 - the option object is amended in situ in ObjectHandler.

The interfaces for other spreadsheet Addins are the same and spreadsheets can be shared verbatim between the various products.

3. Autogeneration

The code for the platform-specific addins consists of

  • structure - This refers to registration with the host application etc.
  • business functionality - The platform-specific implementation of each QuantLibAddin function

The code for each addin's structure is relatively static, this code is written manually when the Addin is created. The code for the business functionality is completely dependent upon the interface defined for QuantLibAddin, this code is generated automatically each time the interface is enhanced.

Autogeneration is accomplished with a Python application, containing one module for each target platform. The application is supplemented with XML files describing the metadata for each function in the QuantLibAddin interface, e.g.:

<Constructor name='qlVanillaOption'>
<description>construct and return a handle to
a Vanilla Option object</description>
<libraryFunction>VanillaOption</libraryFunction>
<functionCategory>QuantLib</functionCategory>
<Parameters>
    <Parameter name='handleBlackScholes' libraryClass='BlackScholesProcess'>
        <type>string</type>
        <tensorRank>scalar</tensorRank>
        <description>handle of a Black Scholes Process object</description>
    </Parameter>
    <Parameter name='optionType'>
        <type>string</type>
        <tensorRank>scalar</tensorRank>
        <description>option type</description>
    </Parameter>
    ...
</Parameters>
</Constructor>

The autogeneration script loads the metadata from the XML files and passes this to each platform-specific module which automatically generates the business functionality for that Addin, which is then recompiled.

Definition: basketlossmodels.hpp:32
Definition: volatilities.hpp:43
Definition: vanillaoption.hpp:33
Definition: abcd.hpp:38
VanillaOption(const boost::shared_ptr< ObjectHandler::ValueObject > &properties, const boost::shared_ptr< QuantLib::StrikedTypePayoff > &payoff, const boost::shared_ptr< QuantLib::Exercise > &exercise, bool permanent)