NB: This is the original design document for QuantLibAddin. The QuantLibAddin project is now live and can be found at the following link:

http://www.quantlibaddin.org/index.html

This QuEP is superseded by the QuantLibAddin design document:

http://www.quantlibaddin.org/design.html

QuEP 12: QuantLibAddin

Eric Ehlers

Abstract

There is a requirement to supplement QuantLib's existing C++ API with a high-level API, QuantLibAddin, which implements a procedural interface that can be deployed on any platform capable of linking C++ libraries. QuantLibAddin is less flexible than the native API but allows QuantLib functionality to be loaded directly to end-user environments such as spreadsheets.

Contents

1. Design
1.1 Classes
1.2 Functions
1.3 Addins
1.4 Clients
2. Implementation
2.1 Classes
2.2 Functions
2.3 Addins
2.4 Clients
2.4.1 C++
2.4.2 Excel
3. Support for C
4. Autogeneration
5. Notes
6. Feedback

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. All calls to QuantLibAddin - constructors and member functions of the Objects - are wrapped in a function layer.

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 Functions

QuantLibAddin exports a standard set of functions. Typically each function corresponds to a constructor or member variable of an Object class. The function returns the Property vector of the object which has been created / amended. An object returned from one function may be passed as a parameter to another.

1.3 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:

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.4 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

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 (module QuantLibAddin), or browsed on line.

2.1 Classes

objectoption.hpp

#include "objecthandler.hpp"

class ObjectStochastic : public Object {
public:
   ObjectStochastic(
       const Spread &dividendYield,
       const Rate &riskFreeRate,
       const Volatility &volatility,
       const Real &underlying,
       const Date &todaysDate,
       const Date &settlementDate);
   // destructor, copy constructor, assignment operator
    virtual boost::shared_ptr<void> getReference() const {
        return boost::static_pointer_cast<void>(stochasticProcess_);
    }
private:
   boost::shared_ptr<BlackScholesProcess> stochasticProcess_;
};

class ObjectOption : public Object {
public:
   ObjectOption(
      boost::shared_ptr<ObjectStochastic>,
      const string &typestr,
      const Real &strike,
      const Size &timeSteps,
      const Date &exerciseDate,
      const Date &settlementDate);
   // destructor, copy constructor, assignment operator
   void setEngine(
      const string &engineName,
      const Size &timeSteps);
    virtual boost::shared_ptr<void> getReference() const {
        return boost::static_pointer_cast<void>(vanillaOption_);
    }
private:
   boost::shared_ptr<VanillaOption> vanillaOption_;
};

objectoption.cpp

#include "objectoption.hpp"

ObjectStochastic::ObjectStochastic(inputs ...) {
   // use the inputs to construct term structures etc ..
   ...
   // construct the BlackSholesProcess & point blackScholesProcess_ at it
   ...
}

ObjectOption::ObjectOption(
      boost::shared_ptr<ObjectStochastic> objectStochastic,
      inputs ...) {
   // acquire a reference to the BlackScholesProcess object
   const boost::shared_ptr<BlackScholesProcess> stochasticProcess =
      boost::static_pointer_cast<BlackScholesProcess>
      (objectStochastic->getReference());

   // use the inputs to construct exercise / payoff / pricing engine.
   ...

   // construct the option
   vanillaOption_ = boost::shared_ptr(new VanillaOption(stochasticProcess, payoff,
      amExercise, pricingEngine));
   // populate the Property vector in the base class
   any_ptr anyNpv(new boost::any(vanillaOption_->NPV()));
   any_ptr anyEngine(new boost::any(string(BINOMIAL_JARROW_RUDD)));
   ObjectProperty propNpv(PROPERTY_NPV, anyNpv);
   ObjectProperty propEngine(PROPERTY_ENGINE, anyEngine);
   properties_.push_back(propNpv);
   properties_.push_back(propEngine);
}

void ObjectOption::setEngine(
      const string &engineName,
      const Size &timeSteps) {
   // call corresponding QuantLib function
   if (engineName.compare(BINOMIAL_JARROW_RUDD) == 0)    {
      boost::shared_ptr<PricingEngine> pricingEngine(
         new BinomialVanillaEngine<JarrowRudd>(timeSteps));
      vanillaOption_->setPricingEngine(pricingEngine);
   } else if (engineName.compare(XXX) == 0)     {
     ...
   }
   // update Property vector
   *properties_[IDX_NPV]() = vanillaOption_->NPV();
   *properties_[IDX_ENGINE]() = engineUpper;
}

2.2 Functions

options.hpp

Properties QL_BLACKSCHOLES(
   const string &handleStochastic,
   const double &dividendYield,
   const double &riskFreeRate,
   const double &volatility,
   const double &underlying,
   const long &todaysDate,
   const long &settlementDate);

Properties QL_OPTION(
   const string &handleOption,
   const string &handleStochastic,
   const string &type,
   const double &strike,
   const long &timeSteps,
   const long &exerciseDate,
   const long &settlementDate);

Properties QL_OPTION_SETENGINE(
   const string &handleOption,
   const string &engineName,
   const long &timeSteps);

options.cpp

#include "options.hpp"

Properties QL_BLACKSCHOLES(
      const std::string &handleStochastic,
      inputs ...) {
   obj_ptr objectStochastic(new ObjectStochastic(inputs ...));
   ObjectHandler::instance().storeObject(handleStochastic, objectStochastic);
   return objectStochastic->getProperties();
}

Properties QL_OPTION(
      const std::string &handleOption,
      const std::string &handleStochastic,
      inputs ...) {
   // retrieve stochastic object from ObjectHandler & use it to construct option object
   boost::shared_ptr<ObjectStochastic> objectStochastic =
      boost::dynamic_pointer_cast<ObjectStochastic>
      (ObjectHandler::instance().retrieveObject(handleStochastic));
   obj_ptr objectOption(new ObjectOption(objectStochastic, inputs ...));
   ObjectHandler::instance().storeObject(handleOption, objectOption);
   return objectOption->getProperties();
}

Properties QL_OPTION_SETENGINE(
      const std::string &handleOption,
      const std::string &engineName,
      const long &timeSteps) {
   boost::shared_ptr<ObjectOption> objectOption =
      boost::dynamic_pointer_cast<ObjectOption>
   (ObjectHandler::instance().retrieveObject(handleOption));
   objectOption->setEngine(engineName, timeSteps);
   return objectOption->getProperties();
}

2.3 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 pseudocode for an Excel addin function.

options.cpp

LPXLOPER QL_OPTION(
      char *handleStochastic_char,
      char *type,
      double *strike,
      long int *timeSteps,
      long int *exerciseDateNum,
      long int *settlementDateNum) {
   try {
      // set handle equal to address of calling cell
      string handle = getCaller();
      // convert Excel inputs to QL inputs
      string handleStochastic(handleStochastic_char);
      // call the QL function
      Properties properties = QL_OPTION(handle, handleStochastic, ...);
      // return the Property vector as an XLOPER array
      static XLOPER xRet;
      setValues(&xRet, properties, handle);
      return &xRet;
   } catch(const exception &e) {
      // error - do QuantLibAddin standard error handling ...
      QL_LOGMESSAGE(...);
      // ... now indicate an error to the host environment:
      return 0;	// display #NA in calling cell
   }
}

2.4 Clients

2.4.1 C++

Example of a standalone C++ client application.

qlademo.cpp

int main() {
   cout << "hi" << endl;

   Spread dividendYield = 0.00;
   Rate riskFreeRate = 0.06;
   Volatility volatility = 0.20;
   Real underlying = 36;
   Date todaysDate(15, May, 1998);
   Date settlementDate(17, May, 1998);

   QL_BLACKSCHOLES("my_blackscholes", dividendYield, riskFreeRate, volatility,
   	underlying, todaysDate, settlementDate);

   Real strike = 40;
   Size timeSteps = 801;
   Date exerciseDate(17, May, 1999);

   QL_OPTION("my_option", "my_blackscholes", "PUT", strike, timeSteps, exerciseDate, settlementDate);

   cout << "High-level interrogation: after QL_OPTION" << endl;
   // get object from handler and retrieve its properties -
   // (properties also returned by QL_OPTION)
   obj_ptr object = objectHandler.retrieveObject("my_option");
   Properties properties = object->getProperties();
   Properties::const_iterator i;
   for (i = properties.begin();
      i != properties.end(); i++) {
      ObjectProperty property = *i;
      any_ptr any = property();
      cout << "property = " << property.name() << "\tvalue = " <<
         AnyToString(any) << endl;
   }

   QL_OPTION_SETENGINE("my_option", "Additive Equiprobabilities", 801);

   cout << "High-level interrogation: after QL_OPTION_SETENGINE" << endl;
   for (i = properties.begin();
      i != properties.end(); i++) {
      ObjectProperty property = *i;
      any_ptr any = property();
      cout << "property = " << property.name() << "\tvalue = " <<
         AnyToString(any) << endl;
   }

   cout << "Low-level interrogation: NPV of underlying option object" << endl;
   boost::shared_ptr<ObjectOption> objectOption =
      boost::dynamic_pointer_cast<ObjectOption>
      (objectHandler.retrieveObject("my_option"));
   boost::shared_ptr<VanillaOption> const vanillaOption =
      boost::static_pointer_cast<VanillaOption>
      (objectOption->getReference());
   cout << "underlying option NPV() = " << vanillaOption->NPV() << endl;

   cout << "bye" << endl;
   return 1;
}

Output of the above program:

2.4.2 Excel

Same again with cell formulas instead of values:

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

3. Support for C

To support addins implemented in C, the functions in the main interface library are wrapped in a supplementary library compiled with C linkage. This library performs additional conversion of inputs/outputs, e.g.:

As C does not support try/catch, the C versions of the functions return a boolean to indicate success/failure, and the actual return value - the Varies struct corresponding to the object's property vector - is a function parameter.

The C linkage for the QL_OPTION function:

qladdin.h

extern "C" {

int QL_OPTION_C(
   const char *handle,
   const char* handleStochastic,
   const char* typeOption,
   const double strike,
   const long timeSteps,
   const long exerciseDate,
   const long settlementDate,
   VariesList *result);  // "real" return value

}  // extern "C"

qladdin_c.cpp

#include "qladdin.h"
#include "qladdin.hpp"

int QL_OPTION_C(
      const char *handle,
      const char* handleStochastic,
      const char* typeOption,
      const double strike,
      const long timeSteps,
      const long exerciseDate,
      const long settlementDate,
      VariesList *result) {
   try {
      // convert native datatypes to those expected by QL
      ...
      // call the interface function
      Properties properties = QL_OPTION(inputs ...);
      // convert Property vector to array of Varies structs
      propertiesToVaries(properties, result);
      return SUCCESS;
   } catch (const std::exception &e) {
      QL_LOGMESSAGE("QL_OPTION_C Error: " + std::string(e.what()));
      result = 0;
      return FAIL;
   }
}

4. Autogeneration

The code for the platform-specific addins consists of

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 script, containing one module for each target platform. The script is supplemented with XML files describing the metadata for each function in the QuantLibAddin interface, e.g.:

<function>
<name>QL_BLACKSCHOLES</name>
<codename>qlBlackScholes</codename>
<desc>construct and return a handle to a Black Scholes object</desc>
<handle>true</handle>
<parameters>
	<param>
		<name>dividendYield</name>
		<type>double</type>
		<desc>dividend yield</desc>
	</param>
	<param>
		<name>riskFreeRate</name>
		<type>double</type>
		<desc>risk free rate</desc>
	</param>

	...

</parameters>
<returnval>
	<type>propertyvector</type>
	<desc>vector of properties describing Black Scholes object</desc>
</returnval>
</function>

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. Auto generated source is booked in to CVS so that people who download QuantLibAddin source from CVS can recompile without necessarily regenerating the source.

5. Notes

6. Feedback

Feedback on this proposal should be posted to the QuantLib-dev mailing list.