CHAPTER 16

image

Using C++ with R and Maxima

One of the advantages of code implemented in C++ is that it can be used as part of native libraries or stand-alone applications and also integrated as a component of other development and modeling environments. In the financial industry, for instance, it is common to have lower-level modules implemented in C++, with high-level analysis being performed in more user-oriented environments such as Excel, Mathematica, Matlab, Maxima, R, and Octave.

When using a high-level data analysis environments, it is crucial to have numeric results that are identical to the ones achieved in native code and to access the same underlying libraries that are already coded in C++. For this reason, an important skill for programmers working in the financial industry is to be able to integrate existing code with one or more of the analytical applications used by analysts and mathematicians.

In this chapter we show you how to incorporate financial libraries developed in C++ into two well-known simulation and modeling environments for financial analysis: R and Maxima. These are open source applications that are freely available on multiple platforms. However, the examples you see in this chapter demonstrate principles that can be applied to other commercial tools in the areas of statistics, simulation, engineering, and mathematics.

Following are a few topics that you will learn about in this chapter:

  • Integrating C++ with R: users of the R language have created a rich ecosystem of statistical libraries and applications. However, it is sometimes necessary to integrate C++ code as part of the analysis performed in R. You will see in this chapter how to easily embed C++ classes into this system, both for increased performance as well as for consistency with other applications deployed in C++.
  • Integrating C++ with Maxima: the Maxima Computer Algebra System is used to develop precise mathematical models with a simple, high-level language. It is also used for its visualization facilities. You can easily integrate existing C++ libraries into Maxima using the shared library mechanism supported by the language.

Integrating C++ with R

Create a C++ class to calculate the present value of a set of payments and that can be called from the R interpreter.

Solution

R is a programming environment that was created to perform statistical analysis of large data sets. Due to its easy-to-use and advanced statistical abilities, R has become the most used environment for data analysis and is the de facto standard in some areas such as data mining. A growing number of statisticians and engineers use R daily to study the properties of large data sets.

R is available for the most common operating systems and computer architectures. You can download it for free from the official web site at http://www.r-project.org. After running the required installation method for your operating system, you will be able to start the iterative interpreter for the language. The standard R environment is able to run R scripts and single commands. You can use these tools to perform quick data analysis and create plots based on existing data. You can see in Figure 16-1 what the main application window for the R console looks like.

9781430267157_Fig16-01.jpg

Figure 16-1. R Console window on Mac OS X

R is typically used to perform a large range of mathematical algorithms and data analysis tasks. For example, a common use of R is to run standard statistical procedures, such as mean squared error and other types of statistical regression. R has also been used to implement statistical tests tailored for financial data sets. As a consequence, it can be very useful to be able to load C++ code into the dynamic environment provided in the R console.

To make it possible to use C++ classes in R, you need to employ the R extension application programming interface (API). The extension API consists of a set of C-based functions that interact with the R runtime. For example, you can use the API to retrieve and convert values from R. Similarly, you can use the API for common tasks such as calling mathematical functions and random number generators, among others.

To create C++ functions and classes that access the R extension library, you need to include the header file R.h. This header is the main C file that coordinates access to the many API declarations exported by the R runtime.

For example, suppose that you need a fast way to determine the present value of a set of future payments. You can do this by creating your own C++ solution and using the R extension mechanism to export this solution to R. To understand the general idea of present-value calculations, you can refer to Chapter 1, where I discussed tools for fixed income analysis.

The main functionality is encoded in the RExtension class. The two main methods in this class are addCashPayment, which can be used to add a new cash flow that will be later considered by the algorithm, and presentValue, which calculates the present value of all cash payments added up to this point. The calculation of present value uses the following formula:

Eqn16-01.jpg

In this equation, Vi is the value of the i-th cash flow, Ti is the time of the i-th cash flow, and R is the interest rate.

The real entry point for the R interpreter is the presentValue function, which is declared in the following way:

extern "C" {
void presentValue(int *, double *, int *, double *, double *);
}

The reason for the extern "C" statement in this declaration is to avoid the normal mangling of function names that is performed by the C++ compiler. Only the name of the function is affected, and the contents of the function can use any desired C++ features. By declaring the function in such a way, the presentValue name will be maintained in the library without modifications, so that the R interpreter can view and access it.

The definition of the presentValue function is not unusual. The only difference to normal C++ code is that all parameters are passed as pointers. This is the way in which the R runtime allows data to be shared between the interpreter and the C++ code. Using pointers, the called function can both read and modify the passed arguments, if necessary. The implementation of the function uses the information passed in the parameters, which include the number of elements in the cash flow vector, the interest rate, then a vector with time indication, followed by a vector of cash flows. The last parameter is a pointer to the result value.

Complete Code

Listing 16-1 shows the implementation of class RExtension. The main parts are the definition of the class, which contains the functionality to calculate the present value of a set of cash flows, and the presentValue function, which can be directly accessed from the R runtime.

Listing 16-1. Code for the R Extension Library

//
// RExtension.h

#ifndef __FinancialSamples__RExtension__
#define __FinancialSamples__RExtension__

#include <vector>

class RExtension {
public:
    RExtension(double rate);
    RExtension(const RExtension &p);
    ~RExtension();
    RExtension &operator=(const RExtension &p);

    void addCashPayment(double value, int timePeriod);
    double presentValue();
private:
    std::vector<double> m_cashPayments;
    std::vector<int> m_timePeriods;
    double m_rate;
    double presentValue(double futureValue, int timePeriod);
};

#endif /* defined(__FinancialSamples__RExtension__) */

//
// RExtension.cpp

#include "RExtension.h"

#include <iostream>
#include <cmath>

using std::cout;
using std::endl;

extern "C" {
void presentValue(int *, double *, int *, double *, double *);
}

void presentValue(int *numPayments, double *intRate,
                  int *timePeriods, double *payments, double *result)
{
    int n = *numPayments;
    RExtension re(*intRate);
    for (int i=0; i<n; ++i)
    {
        re.addCashPayment(payments[i], timePeriods[i]);
    }
    *result = re.presentValue();
}

RExtension::RExtension(double rate)
: m_rate(rate)
{
}

RExtension::RExtension(const RExtension &v)
: m_rate(v.m_rate)
{

}

RExtension::~RExtension()
{

}

RExtension &RExtension::operator =(const RExtension &v)
{
    if (this != &v)
    {
        this->m_cashPayments = v.m_cashPayments;
        this->m_timePeriods = v.m_timePeriods;
        this->m_rate = v.m_rate;
    }
    return *this;
}

void RExtension::addCashPayment(double value, int timePeriod)
{
    m_cashPayments.push_back(value);
    m_timePeriods.push_back(timePeriod);
}

double RExtension::presentValue(double futureValue, int timePeriod)
{
    double pValue = futureValue / pow(1+m_rate, timePeriod);
    cout << " value " << pValue << endl;
    return pValue;
}

double RExtension::presentValue()
{
    double total = 0;
    for (unsigned i=0; i<m_cashPayments.size(); ++i)
    {
        total += presentValue(m_cashPayments[i], m_timePeriods[i]);
    }
    return total;
}

Running the Code

The code presented in the previous section can be built using any standards-compliant C++ compiler. However, the R interpreter makes it very easy to create an extension library using the CMD option of the R command. This build technique allows you to quickly create a shared object file that contains all the necessary C++ code, in a way that can be readily imported into the R runtime. Using the CMD option of the R interpreter, you also don’t need to worry about the correct compiler, the right location of header files, and link libraries, as well as other common compilation parameters.

Here is how I generated a binary object from the given source file (this was run on a Mac OS X system, but the result should be similar in other platforms).

$ R CMD SHLIB code/FinancialSamples/FinancialSamples/RExtension.cpp
g++ -arch x86_64 -I/Library/Frameworks/R.framework/Resources/include -DNDEBUG  -I/usr/local/include    -fPIC  -mtune=core2 -g -O2  -c code/FinancialSamples/FinancialSamples/RExtension.cpp -o code/FinancialSamples/FinancialSamples/RExtension.o
g++ -arch x86_64 -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup -single_module -multiply_defined suppress -L/usr/local/lib -L/usr/local/lib -o code/FinancialSamples/FinancialSamples/RExtension.so code/FinancialSamples/FinancialSamples/RExtension.o -F/Library/Frameworks/R.framework/.. -framework R -Wl,-framework -Wl,CoreFoundation

Once the shared object has been created (this is a file with a .so extension on UNIX, or .dll extension on Windows), you can load it into the R interpreter using the dyn.load function with the name of the file as the single parameter. After that, you just need to use the .C function to call the compiled C or C++ function. For this to work, you need to provide the function name as the first argument, followed by the arguments to the function. You need to ensure, however, that the values passed to the function are marked with the right parameter types (using functions such as as.integer and as.double). After the .C function is executed, the resulting values are printed in the interpreter window. Here is a sample session, where I import and use the RExtension module.

$ R
> dyn.load("RExtension.so")
> .C("presentValue", n=as.integer(4), r=as.double(0.05), t=as.integer(c(1,2,3,4)), p=as.double(c(3,4,5,6)), res=as.double(0))
 value 2.85714
 value 3.62812
 value 4.31919
 value 4.93621
$n
[1] 4

$r
[1] 0.05

$t
[1] 1 2 3 4

$p
[1] 3 4 5 6

$res
[1] 15.74066

>

The desired result is printed as the content of the variable res, which in this case is 15.74066.

Integrating with the Maxima CAS

Implement a class to compute option probabilities that can be accessed using the Maxima computer algebra system.

Solution

The R environment is an example of a successful application that has been used in the statistical analysis of data sets. Another class of mathematical applications that is commonly employed in data analysis is a computer algebra system (CAS). Financial analysts use such applications to perform algebraic transformations on mathematical functions and expressions. For example, such systems can be used to perform tasks such as solving equations, finding derivatives and integrals, or factoring polynomials. Well-known applications in this category include Mathematica, Maple, and Maxima.

In this section, you will learn how to interact with Maxima, an open source CAS that can be used to assist in the development of mathematical models in finance. You will also understand how to incorporate new or existing C++ code into Maxima, so that you can run iterative experiments with the code while using the Maxima interpreter for visualization purposes.

The Maxima CAS is an open source application that can be freely downloaded and installed from its Internet repository. The main web site for the project is located at http://maxima.sourceforge.net. Once installed, Maxima can be run using one of the existing front ends that are installed by default. The most commonly used front end to Maxima is wxMaxima, a cross platform application available for the Windows, Mac OS X, and Linux operating systems. You can also download wxMaxima for free: the latest version is available on the developer’s web site at http://wxmaxima.sourceforge.net/. Figure 16-2 shows the main window of the wxMaxima application.

9781430267157_Fig16-02.jpg

Figure 16-2. Main window for the wxMaxima application, a front end for the Maxima CAS

The wxMaxima application is an ideal environment for loading data and performing data analysis, including graphs, summary tables, and simple charts. This functionality can be used to perform quick studies on financial data, based on the results of algorithms such as the ones we discussed in the previous chapters. To integrate existing C++ code with the Maxima environment, you need to create a library that is compatible with the conventions stated on Maxima documentation. In this section you will learn how to do so with a sample class that calculates options probabilities.

The first step in integrating C++ with Maxima is to create a shared object library that can be loaded by the application. Creating such a library can be easily done with most compilers and integrated development environments (IDEs). I will show how this can be done in Windows with the MingW gcc compiler. Other environments have similar features and most of these instructions will be similar.

In Windows, the gcc compiler can be used to create dll files from C++ code. The contents of the dll will include the class OptionsProbabilities, which was explained in Chapter 14. For ease of reference, I include the header file for the class in Listing 16-2. The main operations of the class OptionsProbabilities are used to calculate specific probabilities, such as the probability that an option will be above or below the strike price, as calculated with probFinishAboveStrike and probFinishBelowStrike. You can also compute the probability of finishing between prices using the member function probFinalPriceBetweenPrices. Then, it is necessary to create the glue code in file OptionProbabilityExportedFunctions.cpp. This file declares functions that can be viewed by clients that import the dll.

Consider, for example, the function optionProbFinishAboveStrike. The extern "C" part of the declaration says the function name should not be modified by the C++ compiler, so that it can be found at runtime.  The __declspec(dllexport) declaration tells the compiler and linker that this function should be exported in the resulting dll. Everything else is normal C++ code that instantiates an object of class OptionsProbabilities and calls the desired function.

The next part of the problem that you need to solve is how to tell Maxima to find these external functions. This is done using a simple lisp file that can be loaded by Maxima. Lisp is the internal programming language used by Maxima to implement all its functionality. To extend Maxima using lower-level code you frequently have to create some lisp functions. In this case, however, we will use only two lisp functions that create all the necessary C++ code connecting to the dll created earlier.

The lisp file is named optionProbabilities.l and is shown in Listing 16-2. There are two parts in this file: the first part is a clines function that contains C code that will be compiled and used by Maxima. The second part is a set of lisp declarations for the desired functions. The code inside clines has to be between quotes, and for this reason it needs to escape any quotes (and backslash characters) using a backslash. Other than that, you can type any normal C statement. You will find that there are four functions that load the desired code from the dll.

The first function in optionProbabilities.l, loadLibrary, is responsible for loading the dll if this has not been done already. This is done using two Windows API functions: LoadLibraryA and GetProcAddress. The function LoadLibraryA takes the name of the library as parameter and returns a reference to it, if the load was successful. The function GetProcAddress, on the other hand, retrieves a pointer to the function named as in its second parameter. Once loadLibrary has completed its work, you will have pointers to the three functions exported in the file optprob.dll.

The next three functions in optionProbabilities.l are then used to call each of the desired functions in the dll. The file ends with three declarations that tell Maxima to accept these functions as top-level operations, along with the desired types.

Complete Code

In Listing 16-2 you can find the complete code for using the OptionsProbabilities class from the Maxima CAS. After the C++ code you can also see the lines of lisp code necessary to import the class into the Maxima environment.

Listing 16-2. Class OptionsProbabilities and Associated Maxima Code

//
// OptionsProbabilities.h

#ifndef __FinancialSamples__OptionsProbabilities__
#define __FinancialSamples__OptionsProbabilities__

#include <vector>

class OptionsProbabilities {
public:
    OptionsProbabilities(double initialPrice, double strike, double avgStep, int nDays);
    OptionsProbabilities(const OptionsProbabilities &p);
    ~OptionsProbabilities();
    OptionsProbabilities &operator=(const OptionsProbabilities &p);

    void setNumIterations(int n);

    double probFinishAboveStrike();
    double probFinishBelowStrike();
    double probFinalPriceBetweenPrices(double lowPrice, double highPrice);
    std::vector<double> getWalk();
private:
    double m_initialPrice;
    double m_strike;
    double m_avgStep;
    int m_numDays;
    int m_numIterations;

    double gaussianValue(double mean, double sigma);
    double getLastPriceOfWalk();
};

#endif /* defined(__FinancialSamples__OptionsProbabilities__) */

//
// OptionProbabilityExportedFunctions.cpp

#include "OptionsProbabilities.h"

extern "C" double __declspec(dllexport) optionProbFinishAboveStrike(double initialPrice,
        double strike, double avgStep, int nDays) {
        OptionsProbabilities optP(initialPrice, strike, avgStep, nDays);
        return optP.probFinishAboveStrike();
}

extern "C" double __declspec(dllexport) optionProbFinishBelowStrike(double initialPrice,
        double strike, double avgStep, int nDays) {
        OptionsProbabilities optP(initialPrice, strike, avgStep, nDays);
        return optP.probFinishBelowStrike();
}

extern "C" double __declspec(dllexport) optionProbFinishBetweenPrices(double initialPrice,
        double strike, double avgStep, int nDays, double lowPrice, double highPrice) {
        OptionsProbabilities optP(initialPrice, strike, avgStep, nDays);
        return optP.probFinalPriceBetweenPrices(lowPrice, highPrice);
}

;;
;; file  optionProbabilities.l
;;
(lisp:clines "
static double (*optionProbFinishAboveStrike_)(double,double,double,int) = NULL;
static double (*optionProbFinishBelowStrike_)(double,double,double,int) = NULL;
static double (*optionProbFinishBetweenPrices_)(double,double,double,int,double,double) = NULL;
__declspec (dllimport) void *__stdcall LoadLibraryA(const char *);
void *__stdcall GetProcAddress(void *,const char *);
__declspec (dllimport) unsigned int __stdcall GetLastError(void);

static int libraryLoaded = 0;
static const char *libName = "optprob.dll";

static int loadLibrary() {
   void *lib = LoadLibraryA(libName);
   if (!lib) return 0;
   optionProbFinishAboveStrike_ = GetProcAddress(lib, "optionProbFinishAboveStrike");
   optionProbFinishBelowStrike_ = GetProcAddress(lib, "optionProbFinishBellowStrike");
   optionProbFinishBetweenPrices_ = GetProcAddress(lib, "optionProbFinishBetweenPrices");
   libraryLoaded = 1;
   return 1;
}

double l_optionProbFinishAboveStrike(double a,double b,double c,int d) {
   if (!libraryLoaded && !loadLibrary()) return -1; /* error code */
   if (!optionProbFinishAboveStrike_) return -2;
   return optionProbFinishAboveStrike_(a, b, c, d);

}
double l_optionProbFinishBelowStrike(double a,double b,double c,int d) {
   if (!libraryLoaded && !loadLibrary()) return -1; /* error code */
   return optionProbFinishAboveStrike_(a, b, c, d);
}
double l_optionProbFinishBetweenPrices(double a,double b,double c,int d,double e,double f) {
   if (!libraryLoaded && !loadLibrary()) return -1; /* error code */
   return optionProbFinishBetweenPrices_(a, b, c, d, e, f);
}
")

(lisp:defentry $optionProbFinishAboveStrike  (lisp:double lisp:double lisp:double lisp:int)
   (lisp:double "l_optionProbFinishAboveStrike"))

(lisp:defentry $optionProbFinishBelowStrike (lisp:double lisp:double lisp:double lisp:int)
   (lisp:double "l_optionProbFinishBelowStrike"))

(lisp:defentry $optionProbFinishBetweenPrices(lisp:double lisp:double lisp:double lisp:int lisp:double lisp:double)
   (lisp:double "l_optionProbFinishBetweenPrices"))

Running the Code

Once you have created the code described in Listing 16-2, the next step is to build and run it using the Maxima CAS. First, you need to create the dll using the MingW gcc compiler. Here is the command line I used (you may need to adjust this to use the library paths in your system).

g++ "-IC:\bin\boost_1_55_0\" -O0 -g3 -Wall -c -fmessage-length=0 -o  "src\OptionProbabilityExportedFunctions.o" "..\src\OptionProbabilityExportedFunctions.cpp"
g++ "-IC:\bin\boost_1_55_0\" -O0 -g3 -Wall -c -fmessage-length=0 -o
"src\OptionsProbabilities.o" "..\src\OptionsProbabilities.cpp"
g++ -shared -o optprob.dll "src\TestClass.o" "src\OptionsProbabilities.o"  "src\OptionProbabilityExportedFunctions.o"

Once the dll has been created, you can now use Maxima to load it. For these instructions to work you need to make sure that Maxima is using GCL (Gnu Common Lisp) as the underlying lisp engine (you can determine this when downloading or building Maxima). The following lines tell you this information when Maxima is started:

Maxima 5.31.2 http://maxima.sourceforge.net
using Lisp GNU Common Lisp (GCL) GCL 2.6.8 (a.k.a. GCL)
Distributed under the GNU Public License. See the file COPYING.

Following is a transcript of a session with Maxima that shows how this work:

/* [wxMaxima: input   start ] */
(%i28) :lisp (compile-file
  "c:/MaximaCode/optionProbabilities.l" :c-file t :h-file t)

(%o28) Compiling c:/MaximaCode/optionProbabilities.l.
End of Pass 1.
End of Pass 2.
OPTIMIZE levels: Safety=2, Space=3, Speed=3
Finished compiling c:/MaximaCode/optionProbabilities.l.
#pc:/MaximaCode/optionProbabilities.o

(%i29) :lisp (load "c:/MaximaCode/optionProbabilities.o");
(%o29) OK

(%i30) optionprobfinishbellowstrike(30.0, 35.0, 0.01,  800);
(%o30)                               0.246

In this example, the first command starting with :lisp is used to compile the lisp file displayed. Once this is done, the combination of lisp and C code is saved as an object file, which is then named optionProbabilities.o. The second command starting with :lisp is used to load the resulting object file into the system. After this is finished, the functions typed in optionProbabilities.l will become available to Maxima.

The last step shows how to invoke the desired functions. For example, you can call the function optionprobfinishbelowstrike with a set of parameters that define the price of the underlying, the strike, the volatility, and the number of time periods.

Conclusion

This chapter contains programming examples that show how to integrate existing C++ classes with two open source mathematical applications: R and Maxima. The number of users for environments like these is growing due to the ease of programming with such a dynamic language, along with the ability to see immediate results from computations. Although these mathematical applications already contain a lot of functionality, most financial developers also need to access existing C++ code when making more complex analyses.

The examples in this chapter show you how to interface your C++ code with these two popular open source mathematical applications. First, you learned how C++ integration can be achieved with the R programming environment. You have seen an example where a set of payments are received as input, and the code calculates the present value of these payments.

The second C++ example shows how to export an existing class to the Maxima CAS. With Maxima you have access to a large number of mathematical tools to analyze and display data. The process of exporting C++ libraries to Maxima involves the creation of a dll and the use of some glue code written in C. Once you have these tools it is possible to access any C++ code from Maxima.

One of the secrets of creating useful financial software is to employ computational resources as efficiently as possible. In this way, it is possible to learn even more from existing investment data while making faster and more accurate decisions. In the next chapter, you will learn about multithreading, a programming technique that is frequently used to improve the performance of numerical and networking code used in financial applications. Because of the growing use of multicore processors in servers and even desktop machines, the use of multithreading has become a necessity for modern code written in C++. You can access such multiprocessing features from C++ using a few standard libraries, as you will see in Chapter 17.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.187.210