Getting Started with QuantLib and MinGW from Scratch

By: Terrence August (4/5/2005)


So, you don't know too much about QuantLib and you don't really want to use some sort of full blown IDE? If that is the case, as it was for me, you might opt to go with MinGW. MinGW, which is available here is basically a set of GNU tools ported to Windows. This includes the C++ compiler "g++". Of course, since you want to use QuantLib, you are probably intending to do your coding in C++.

So, go to MinGW's download area and download the latest "Candidate MinGW" release. When I wrote these instructions, the file was: MinGW-3.2.0-rc-3.exe. Run this file and install the program to: c:\MinGW. The installation program should add the following to your PATH environment variable at the beggining: c:\MinGW\bin;. Check for this and add it if it is missing. Great, this is all we need to do to setup MinGW.

Next, we need to install MSYS. MSYS is basically a minimal shell that will allow us to do the old "(1) configure, (2) make, (3) make install" combination that long time Unix and/or Linux users are so familiar with doing. Even if that is not you, no worries as everything will be explained here. MSYS is available here. Download the latest executable. For me, the file was: MSYS-1.0.10.exe. Run this file and install MSYS. This should create a directory similar to c:\msys\1.0. We are going to need to update our PATH environment variable again by adding: c:\msys\1.0\bin; anywhere on the path string.

Since we are going to build QuantLib from source, we are going to need a dependency it uses called Boost. Boost is another open source project which can be found here. You should download the latest executable from their download page. For me, the file was: boost_1_32_0.exe. Run this file, and extract to: c:\msys\1.0\home. This will create the directory structure: c:\msys\1.0\home\boost_1_32_0 upon completion.

Since QuantLib needs Boost, we are going to need to build Boost first. In order to do that, we will make use of a very nice build system called Boost.Jam. We can download this executable here. For me, the file was: boost-jam-3.1.10-1-ntx86.zip. Unzip this zip file and extract: bjam.exe into c:\msys\1.0\bin which we already placed on the search path.

Now, let's build Boost. Open a command window (go to Run and then type: cmd and hit enter). Change directory to: c:\msys\1.0\home\boost_1_32_0 and then type: bjam "-sTOOLS=mingw" install and sit back and relax. This will take awhile while it builds Boost. When it completes, we want to put the built files in the right place. Go to: c:\msys\1.0. Make a folder called: local. Note that after completing the build, you now have the folder: c:\Boost. Go there and cut the two folders: include and lib and paste them to the location: c:\msys\1.0\local. We will need to fix one thing here. Inside of: c:\msys\1.0\local\include you will see the directory structure: boost-1_32/boost. I don't know why they put this extra: boost-1_32 folder here. Basically just cut and paste the folder: boost up one level and get rid of: boost-1_32. So in the end you should have: c:\msys\1.0\local\include\boost. I hope that wasn't too confusing.

Great job so far! Next, we are ready to get involved with QuantLib. The project QuantLib is located here. We want to download the platform independent source so that we can build it manually. The download page is located here. Download the appropriate file. For me, the file was: QuantLib-0.3.8.zip. Download the file, open the zip, and extract it to: c:\msys\1.0\home.

Now that we have everything downloaded and ready to go, it is time to build QuantLib. We need to use our shell so go to: c:\msys\1.0 and run the file: msys.bat. This will launch the shell. Inside the shell, navigate to: /home/QuantLib-0.3.8. Before we can configure, we need to set to environment variables within the shell. First type: export CPPFLAGS="-I/local/include" and hit enter. Next, type: export LDFLAGS="-I/local/lib" and hit enter. Now that the environment is set, we are ready to configure. Type: configure and hit enter. This will take a long time. When it completes, type: make and hit enter. Again, wait a long time. Finally, type: make install and hit enter. When this completes, you have successfully built QuantLib on MinGW!!!!

Testing it out: Now to make sure that it works, we run a simple test. A scaled down version of their European Option with Black Scholes example goes as follows:

eco.cpp

         #include <iostream>
         #include <ql/quantlib.hpp>
         
         using namespace QuantLib;
         
         int main(int argc, char **argv) {
                 
         QL_IO_INIT
 
         std::cout << "Using " << QL_VERSION << std::endl << std::endl;
 
         // our option
         Option::Type type(Option::Call);
         Real underlying = 7;
         Real strike = 8;
         Spread dividendYield = 0.00;
         Rate riskFreeRate = 0.05;
 
         Date todaysDate(15, May, 1998);
         Date settlementDate(17, May, 1998);
         Settings::instance().setEvaluationDate(todaysDate);
 
         Date exerciseDate(17, May, 1999);
         DayCounter dayCounter = Actual365Fixed();
         Time maturity = dayCounter.yearFraction(settlementDate,
                                                 exerciseDate);
 
         Volatility volatility = 0.10;
         std::cout << "option type = "  << OptionTypeFormatter::toString(type)
                   << std::endl;
         std::cout << "Time to maturity = "        << maturity
                   << std::endl;
         std::cout << "Underlying price = "        << underlying
                   << std::endl;
         std::cout << "Strike = "                  << strike
                   << std::endl;
         std::cout << "Risk-free interest rate = " << riskFreeRate
                   << std::endl;
         std::cout << "dividend yield = " << dividendYield
                   << std::endl;
         std::cout << "Volatility = "              << volatility
                   << std::endl;
         std::cout << std::endl;
 
         Date midlifeDate(19, November, 1998);
         std::vector<Date> exDates(2);
         exDates[0]=midlifeDate;
         exDates[1]=exerciseDate;
 
         boost::shared_ptr<Exercise> exercise(
                                           new EuropeanExercise(exerciseDate));
         boost::shared_ptr<Exercise> amExercise(
                                           new AmericanExercise(settlementDate,
                                                                exerciseDate));
         boost::shared_ptr<Exercise> berExercise(new BermudanExercise(exDates));
 
 
         Handle<Quote> underlyingH(
             boost::shared_ptr<Quote>(new SimpleQuote(underlying)));
 
         // bootstrap the yield/dividend/vol curves
         Handle<YieldTermStructure> flatTermStructure(
             boost::shared_ptr<YieldTermStructure>(
                 new FlatForward(settlementDate, riskFreeRate, dayCounter)));
         Handle<YieldTermStructure> flatDividendTS(
             boost::shared_ptr<YieldTermStructure>(
                 new FlatForward(settlementDate, dividendYield, dayCounter)));
         Handle<BlackVolTermStructure> flatVolTS(
             boost::shared_ptr<BlackVolTermStructure>(
                 new BlackConstantVol(settlementDate, volatility, dayCounter)));
 
         std::vector<Date> dates(4);
         dates[0] = settlementDate + 1*Months;
         dates[1] = exerciseDate;
         dates[2] = exerciseDate + 6*Months;
         dates[3] = exerciseDate + 12*Months;
         std::vector<Real> strikes(4);
         strikes[0] = underlying*0.9;
         strikes[1] = underlying;
         strikes[2] = underlying*1.1;
         strikes[3] = underlying*1.2;
 
         Matrix vols(4,4);
         vols[0][0] = volatility*1.1;
                      vols[0][1] = volatility;
                                   vols[0][2] = volatility*0.9;
                                                vols[0][3] = volatility*0.8;
         vols[1][0] = volatility*1.1;
                      vols[1][1] = volatility;
                                   vols[1][2] = volatility*0.9;
                                                vols[1][3] = volatility*0.8;
         vols[2][0] = volatility*1.1;
                      vols[2][1] = volatility;
                                   vols[2][2] = volatility*0.9;
                                                vols[2][3] = volatility*0.8;
         vols[3][0] = volatility*1.1;
                      vols[3][1] = volatility;
                                   vols[3][2] = volatility*0.9;
                                                vols[3][3] = volatility*0.8;
 
         Handle<BlackVolTermStructure> blackSurface(
             boost::shared_ptr<BlackVolTermStructure>(
                 new BlackVarianceSurface(settlementDate, dates,
                                          strikes, vols, dayCounter)));
 
 
         boost::shared_ptr<StrikedTypePayoff> payoff(new
             PlainVanillaPayoff(type, strike));
 
         boost::shared_ptr<BlackScholesProcess> stochasticProcess(new
             BlackScholesProcess(underlyingH, flatDividendTS,
                                 flatTermStructure,
                                 //  blackSurface
                                 flatVolTS));
 
         EuropeanOption option(stochasticProcess, payoff, exercise);
 
 
         std::string method;
         Real value, discrepancy, rightValue, relativeDiscrepancy;
 
         std::cout << std::endl << std::endl;
 
         // write column headings
         std::cout << "Method\t\tValue\tEstimatedError\tDiscrepancy"
                      "\tRel. Discr." << std::endl;
 
         // method: Black-Scholes Engine
         method = "Black-Scholes";
         option.setPricingEngine(boost::shared_ptr<PricingEngine>(
             new AnalyticEuropeanEngine()));
         rightValue = value = option.NPV();
         discrepancy = QL_FABS(value-rightValue);
         relativeDiscrepancy = discrepancy/rightValue;
         std::cout << method << "\t"
              << DecimalFormatter::toString(value, 4) << "\t"
              << "N/A\t\t"
              << DecimalFormatter::toString(discrepancy, 6) << "\t"
              << DecimalFormatter::toString(relativeDiscrepancy, 6)
              << std::endl;
         }


Here, I have just extracted the relevant piece of code from the example posted on QuantLib's website. What you will need to do is compile, link, and execute this code. We will then compare the result to the Matlab result from the function: blsprice(). First, we can compile this code with the statement:

g++ -I "c:\msys\1.0\local\include" -c eco.cpp

We can then link it with the command:

g++ -o eco eco.o -lQuantLib -L "c:\msys\1.0\local\lib"

Finally, run the program: eco.exe

The output should be as follows:

Using 0.3.8

option type = call
Time to maturity = 1
Underlying price = 7
Strike = 8
Risk-free interest rate = 0.05
dividend yield = 0
Volatility = 0.1

Method Value EstimatedError Discrepancy Rel. Discr.
Black-Scholes 0.0823 N/A 0.000000 0.000000


Note that Black-Scholes price is 0.0823. Now, in Matlab, when u run the command:

[CallPrice, PutPrice] = blsprice(7, 8, 0.05, 1, 0.1, 0)

you will see that indeed the European call option prices is 0.0823. So, we at least know that something is working! :)