CHAPTER 3

image

C++ Programming Techniques in Finance

The C++ language was created as an extension of C, which means that any program written in C is also a valid C++ program. However, experienced programmers typically make use of a set of high-level features made available exclusively in C++ as a way to control program complexity. This is an especially important consideration for financial software development, where we want to create fast and expressive applications.

In this chapter we explore a few fundamental techniques that financial programmers have used over the years to write better C++ code with less effort. These techniques have been selected among the many features provided by C++ as the most effective in improving the quality and expressiveness of code. Such features include the following:

  • Templates: a feature that allows the creation of generic software, with classes and functions that can be applied over a set of possibly unrelated types that satisfy the set of requirements for a desired operation.
  • Shared pointers: a programming technique that reduces the need for direct manipulation of pointers. With shared pointers, you can avoid a big source of mistakes inherent to the way C++ programs manage memory and other resources.
  • Operator overloading: With overloading, you can apply standard operators already available in the language to your own classes and structures.
  • C++11 features: The latest iteration of the C++ standard has introduced many new features that help control the complexity of programs. These features, which can be easily used for the creation of financial software, include shared pointers and automatic type detection.

In the next sections, you will see a few selected programming examples that explore some of these C++ features in the context of financial applications.

Calculating Interest Rates for Investment Instruments

Interest rates are a fundamental concept for fixed-income investors. Design a C++ solution to return the annual interest rate, given a generic instrument class that provides methods such as getMonthlyPayment and getPrincipal.

Solution

The foregoing problem is frequently used in the design of interest rate calculation engines. You can create a solution using a number of strategies such as class hierarchies, but for performance and design considerations, the use of templates is the most indicated method of combining interest rate data from unrelated classes that represent investment instruments.

A template is a mechanism, along with a special syntax, used to create code that works with different underlying data types. Using templates, one can create functions, member functions, and classes that are able to support different types using the same code. The code generated using template-based programming techniques is said to be generic, since it can be used with different types (either fundamental types such as int or double, or user-defined classes and structures). Generic functions and types are instantiated using the name of the target type(s) between angle brackets, which indicates a particular version of the desired function or type.

The creation and use of generic code is possible because when the compiler finds a template, it does not generate code immediately. Instead, code is generated only at the point where the template object or function is instantiated. When that happens, the compiler detects the types involved in the expression, and the template is instantiated. Only then the traditional compilation steps such as syntactic analysis and code generation are performed on the instantiated code, and any resulting errors will be detected and reported to the programmer.

To solve the interest rate calculation problem, you can use templates to implement an interest rate engine class, called IntRateEngine. This class is defined in such a way that you can apply it to any class implementing the methods getMonthlyPayment and getPrincipal. I have included two sample classes that implement these methods, the classes BondInstrument and MortgateInstrument. However, the big advantage of using templates is that you don’t need to derive such classes from a particular base class, for example. You can use a class supplying these same methods and the compiler will do the hard work of combining these classes. This means that there is no coupling between investment instruments and the interest rate calculation engine. In fact, if you look at the files for IntRateEngine, you will not find any reference to the investment instrument classes.

Here is a quick look at the relevant parts of the BondInstrument class.

class BondInstrument {
public:
    double getMonthlyPayment();
    double getPrincipal();

    // other methods here...
};

With this class, one could instantiate the template that calculates the annual interest rate. The template class that performs the calculation is defined in the following way:

template <class T>
class IntRateEngine {
public:
    void setInstrument(T &inv);
    double getAnnualIntRate();
    // other methods here ...
private:
    T m_instrument;
};

Notice that the type of instrument is left unspecified as a type argument T. This is the parameterization that allows different classes to be used with the same template. Similarly, you can see the implementation of the getAnnualIntRate method.

template <class T>
double IntRateEngine<T>::getAnnualIntRate()
{
    double payment = m_instrument.getMonthlyPayment();
    double principal = m_instrument.getPrincipal();
    return (12 *payment) / principal;
}

Notice that the method only requires the parameter T to be offer the getMonthlyPayment and getPrincipal methods. Any type that supports these two methods can be used by IntRateEngine without problems.

Complete Code

The algorithm described previously has been implemented in the classes BondInstrument, MortgageInstrument, and IntRateEngine, as displayed in Listing 3-1.

Listing 3-1. InvestmentInstrument.h

//
//  InvestmentInstrument.h

#ifndef __FinancialSamples__InvestmentInstrument__
#define __FinancialSamples__InvestmentInstrument__

#include <iostream>

class BondInstrument {
public:
    BondInstrument() {}
    BondInstrument(double principal, double monthlyPayment);
    ~BondInstrument();
    BondInstrument(const BondInstrument &a);
    BondInstrument &operator =(const BondInstrument &a);

    double getMonthlyPayment();
    double getPrincipal();

    // other methods here...
private:
    double
        m_monthlyPay,
        m_principal;
};

class MortgageInstrument {
public:
    MortgageInstrument() {}
    MortgageInstrument(double monthlyPay, double propertyValue, double downpayment);
    ~MortgageInstrument();
    MortgageInstrument(const MortgageInstrument &a);
    MortgageInstrument &operator =(const MortgageInstrument &a);

    double getMonthlyPayment();
    double getPrincipal();

    // other methods here...
private:
    double
        m_monthlyPay,
        m_propertyValue,
        m_downPayment;
};

#endif /* defined(__FinancialSamples__InvestmentInstrument__) */

//
//  InvestmentInstrument.cpp

#include "InvestmentInstrument.h"

BondInstrument::BondInstrument(double principal, double monthlyPayment)
: m_principal(principal),
m_monthlyPay(monthlyPayment)
{

}

BondInstrument::~BondInstrument()
{

}

BondInstrument::BondInstrument(const BondInstrument &a)
: m_monthlyPay(a.m_monthlyPay),
m_principal(a.m_principal)
{

}

BondInstrument &BondInstrument::operator =(const BondInstrument &a)
{
    if (this != &a)
    {
        m_principal = a.m_principal;
        m_monthlyPay = a.m_monthlyPay;
    }
    return *this;
}

double BondInstrument::getMonthlyPayment()
{
    return m_monthlyPay;
}

double BondInstrument::getPrincipal()
{
    return m_principal;
}

/////////////

MortgageInstrument::MortgageInstrument(double monthlyPay, double propertyValue, double downpayment)
: m_monthlyPay(monthlyPay),
m_propertyValue(propertyValue),
m_downPayment(downpayment)
{

}

MortgageInstrument::~MortgageInstrument()
{

}

MortgageInstrument::MortgageInstrument(const MortgageInstrument &a)
: m_downPayment(a.m_downPayment),
m_propertyValue(a.m_propertyValue),
m_monthlyPay(a.m_monthlyPay)
{

}

MortgageInstrument &MortgageInstrument::operator =(const MortgageInstrument &a)
{
    if (this != &a)
    {
        m_downPayment = a.m_downPayment;
        m_propertyValue = a.m_propertyValue;
        m_monthlyPay = a.m_monthlyPay;
    }
    return *this;
}

double MortgageInstrument::getMonthlyPayment()
{
    return m_monthlyPay;
}

double MortgageInstrument::getPrincipal()
{
    return m_propertyValue - m_downPayment;
}

//
//  IntRateEngine.h

#ifndef __FinancialSamples__IntRateEngine__
#define __FinancialSamples__IntRateEngine__

#include <vector>

template <class T>
class IntRateEngine {
public:
    IntRateEngine();
    ~IntRateEngine();
    IntRateEngine(const IntRateEngine<T> &a);
    IntRateEngine<T> &operator =(const IntRateEngine<T> &a);

    void setInstrument(T &inv);
    double getAnnualIntRate();
private:
    T m_instrument;
};

template <class T>
IntRateEngine<T>::IntRateEngine()
{

}

template <class T>
IntRateEngine<T>::~IntRateEngine()
{

}

template <class T>
IntRateEngine<T>::IntRateEngine(const IntRateEngine<T> &a)
: m_instrument(a.m_instrument)
{

}

template <class T>
IntRateEngine<T> &IntRateEngine<T>::operator =(const IntRateEngine<T> &a)
{
    if (this != &a)
    {
        m_instrument = a.m_instrument;
    }
    return *this;
}

template <class T>
void IntRateEngine<T>::setInstrument(T &inv)
{
    m_instrument = inv;
}

template <class T>
double IntRateEngine<T>::getAnnualIntRate()
{
    double payment = m_instrument.getMonthlyPayment();
    double principal = m_instrument.getPrincipal();
    return (12 *payment) / principal;
}

#endif /* defined(__FinancialSamples__IntRateEngine__) */

//
//  main.cpp

#include "InvestmentInstrument.h"
#include "IntRateEngine.h"

#include <iostream>

int main()
{
    IntRateEngine<BondInstrument> engineA;
    IntRateEngine<MortgageInstrument> engineB;

    BondInstrument bond(40000, 250);
    MortgageInstrument mortgage(250, 50000, 5000);
    engineA.setInstrument(bond);
    engineB.setInstrument(mortgage);

    std::cout << " bond annual int rate: " << engineA.getAnnualIntRate()*100 << "%" << std::endl;
    std::cout << " mortgage annual int rate: " << engineB.getAnnualIntRate()*100 << "%" << std::endl;

    return 0;
}

Running the Code

You can compile the code presented in Listing 3-1 with any standards-compliant compiler. For example, with gcc you can use the following command line:

> gcc -o intRate main.cpp InvestmentInstrument.cpp IntRateEngine.cpp

You can use the resulting test program without any parameters.

> ./intRate
 bond annual int rate: 7.5%
 mortgage annual int rate: 6.66667%

Creating Financial Statement Objects

Create a class that computes a financial statement and returns it to the calling client. Do this while avoiding potential memory leaks of the returned data.

Solution

To solve this problem, you will create a simple financial statement class and a function that returns a pointer to the financial statement. The main issue that you may want to avoid in the solution is the possibility of memory leaks occurring when the financial statement is returned. For this purpose, you will learn about how to use auto pointers to manage memory. Before we explain that, however, you need to understand the concept of smart pointers.

Smart Pointers

The C language popularized the concept of pointers, and it innovated when it provided a simple notation for direct memory access on a high-level language. Pointers allow the manipulation of memory addresses in a way that complies with the underlying data type of the data. For example, it is possible to use pointers to refer to successive addresses using increment and decrement operators. For instance, you may have

int numbers[200];
// initialize numbers
int *parray = numbers;
for (int i=0; i<200; ++i) {
        std::cout << "value is " << *parray << " ";
        parray++;
}

In the previous example, parray is a pointer to an integer. It can be used to refer to the location of any integer value in memory. In particular, you can use it to hold the address of the first element of the numbers array. The code in the for loop is then used to print the current value pointed by parray, and to update its address using the increment operator, which moves the pointer to the next integer location. Notice that this is possible even though the number of bytes per integer is greater than one. In fact, the increment operator is aware of the pointer type used, and will change the address to point to the exact location of the next value for the declared type.

While the notation for pointers is very powerful, and fully supported by C++, programmers should avoid the use of pointers in C++ code whenever possible. Pointers have over the years been linked to poor programming practices that lead to potential resource leaks, memory corruption, and security-related bugs. Because pointers allow indiscriminate access to the computer memory, it is relatively easy to misuse them, resulting in bugs that are difficult to fix.

Some C++ classes and templates provide a great alternative to pointers, with little overhead and many of the same features. The main technique to avoid traditional pointers is to use smart pointer templates. Such smart pointers are simple templates that can be used to store addresses in a safer way. For example, a smart pointer of the type std::shared_ptr is a template-type object that allows the same address to be used by two or more parts of the code.

The main advantage of a smart pointer over a traditional C++ pointer is that the smart pointer knows how to clean up itself when it is no longer needed. Thus, a large class of problems that occur when a programmer doesn’t dispose of the pointer is avoided. The cleanup mechanism is defined according to the rules of the RAII (Resource Acquisition Is Initialization) idiom: resources contained in a smart pointer are initialized in the constructor of the template object, and released during destruction, which typically happens when the object in question goes out of scope.

There are different types of smart pointers, each one designed for a particular use case. The most commonly used smart pointers in C++ code are auto pointers and shared pointers.

An auto pointer (of template type auto_ptr) provides a wrapper for a traditional C++ pointer. The template, however, defines the semantic of object ownership, so that other references to the pointer are not valid after the transfer of ownership occurs. An auto pointer can be used in situations in which the receiver will take full control of the pointed object, as well as any associated resource. Therefore, a pointer passed to an auto_ptr object should not be referenced again in contexts other than the one where the auto_ptr is used.

A shared pointer (of template type shared_ptr) is a template that can be used to wrap an existing C++ native pointer. Unlike an auto pointer, a shared pointer can be used by two or more parts of the code. Internally, shared pointers maintain a counter that can be used to determine how many references to the original pointer have been created. This type of mechanism is referred to as reference counting. Every time a shared pointer object is destroyed, it checks its internal reference counter. If the counter is greater than zero, the internal object is not deleted. However, if the reference counter reaches zero, then the referred object is deleted and its destructor is activated.

In this section, you will see the details of using an auto pointer. In the next section, I will introduce shared pointers.

Using Auto Pointers

The solution to our memory management problem involves the use of auto pointers. An auto pointer is implemented using the standard class std::auto_ptr and, like other smart pointers, can be used to provide automated cleanup and resource ownership.

The policy used by std::auto_ptr ensures that once it has been assigned the ownership of the pointer, the memory stored in the pointer is not owned by anyone else. Therefore, the semantic of an std::auto_ptr involves the automatic destruction of the associated data as soon as the object goes out of scope. If the owner of the auto pointer doesn’t want to destroy the memory, it has the option of assigning it to another auto pointer, in a process of transferring the ownership to another object. This is how I solved the issue of managing the memory associated to the returned FinancialStatement.

First, consider the definition of the FinancialStatement class.

 class FinancialStatement {
public:
    FinancialStatement();
    ~FinancialStatement();
    FinancialStatement(const FinancialStatement&);
    FinancialStatement &operator=(FinancialStatement &);

    double getReturn();
    void addTransaction(const std::string &security, double val);

private:
    double m_return;
    std::vector<std::pair<std::string,double> > m_transactions;
};

There is also a function that is used to create a sample financial statement.

std::auto_ptr<FinancialStatement> getSampleStatement();

This function returns an auto pointer to a sample statement. This means that the caller of the code owns the returned memory, since the caller will have an auto pointer that has been initialized with a pointer to the resulting object. The implementation of getSampleStatement is

std::auto_ptr<FinancialStatement> getSampleStatement()
{
    std::auto_ptr<FinancialStatement> fs(new FinancialStatement);
    fs->addTransaction("IBM", 102.2);
    fs->addTransaction("AAPL", 523.0);
    return fs;
}

After the FinancialStatement object has been allocated and used to create an auto pointer, it is initialized and finally returned to the caller. Since the return statement transfers the ownership of the pointer to the returned object, the original FinancialStatement object is not destructed. Instead, it is now owned by the caller of the getSampleStatement function.

Complete Code

Listing 3-2 presents the code for the FinancialStatement class, which is divided into a header file and an implementation file.

Listing 3-2. FinancialStatement.h

//
//  FinancialStatement.h

#ifndef __FinancialSamples__FinancialStatement__
#define __FinancialSamples__FinancialStatement__

#include <string>
#include <vector>

class FinancialStatement {
public:
    FinancialStatement();
    ~FinancialStatement();
    FinancialStatement(const FinancialStatement&);
    FinancialStatement &operator=(FinancialStatement &);

    double getReturn();
    void addTransaction(const std::string &security, double val);

private:
    double m_return;
    std::vector<std::pair<std::string,double> > m_transactions;
};

std::auto_ptr<FinancialStatement> getSampleStatement();

void transferFinancialStatement(std::auto_ptr<FinancialStatement> statement);

#endif /* defined(__FinancialSamples__FinancialStatement__) */

//
//  FinancialStatement.cpp

#include "FinancialStatement.h"

FinancialStatement::FinancialStatement()
: m_return(0)
{

}

FinancialStatement::~FinancialStatement()
{

}

FinancialStatement::FinancialStatement(const FinancialStatement &v)
: m_return(v.m_return),
m_transactions(v.m_transactions)
{

}

FinancialStatement &FinancialStatement::operator=(FinancialStatement &v)
{
    if (this != &v)
    {
        m_return = v.m_return;
        m_transactions = v.m_transactions;
    }
    return *this;
}

double FinancialStatement::getReturn()
{
    return m_return;
}

void FinancialStatement::addTransaction(const std::string &security, double val)
{
    m_transactions.push_back(std::make_pair(security, val));
}

// returns a sample statement that includes a few common stocks
std::auto_ptr<FinancialStatement> getSampleStatement()
{
    std::auto_ptr<FinancialStatement> fs(new FinancialStatement);
    fs->addTransaction("IBM", 102.2);
    fs->addTransaction("AAPL", 523.0);
    return fs;
}

void transferFinancialStatement(std::auto_ptr<FinancialStatement> statement)
{
    // perform transfer here
    // ...
    // statement is still valid
    std::cout << statement->getReturn() << std::endl;
    // statement is released here
}

//
//  main.cpp

#include "FinancialStatement.h"

#include <iostream>

int main()
{
    std::auto_ptr<FinancialStatement> fs = getSampleStatement();
    // do some real work here...
    return 0;
    // the autopointer is released at the end of the scope...
}

Transferring Ownership

In the previous example, you saw how to use auto pointers to transfer the of objects to the caller of a function. Another important use of these smart pointers is to tell to a caller that the called function is taking ownership of the passed object.

For example, consider the function transferFinancialStatement, which is defined in the following way:

void transferFinancialStatement(std::auto_ptr<FinancialStatement> statement);

The parameter statement is an auto pointer to a FinancialStatement object, which means that once it receives a parameter of that particular type, it will become the owner of the object. Thus, the transferFinancialStatement can use the statement object knowing that it is the sole owner of its contents. Depending on the operations necessary to perform on the object, this may be an important advantage.

The sample implementation of the function reads as follows:

void transferFinancialStatement(std::auto_ptr<FinancialStatement> statement)
{
    // perform transfer here
    // ...
    // statement is still valid
    std::cout << statement->getReturn() << std::endl;
    // statement is released here
}

The important thing to understand here is that the pointed object is destroyed at the end of the transferFinancialStatement, since the parameter goes out of scope at the end of the function block.

Pitfalls of Auto Pointers

Due to the semantics of auto pointers, which require the ownership of the referenced data, a few errors can occur when programmers try to access data stored in this kind of smart pointer. For example, a common source of errors is the use of functions that take ownership of the pointed object. You can see this class of errors by using the function transferFinancialStatement, which I showed previously. When an auto pointer object is passed to a function that takes ownership of the memory, the auto pointer is not valid anymore, and may cause a crash the next time it is accessed.

int main()
{
    std::auto_ptr<FinancialStatement> fs = getSampleStatement();
    transferFinancialStatement(fs);

    // the auto_ptr object is invalid here, the next access can crash the program
    std::cout << fs->getReturn() << " ";

    return 0;
}

This example shows how easy it is to crash a program after an auto pointer has transferred the ownership of the object it points to. This is done as you call the transferFinancialStatement function. The next line tries to access the return value, which generates an invalid access. This results in a segmentation fault in many platforms. To avoid such invalid accesses, a programmer needs to be careful about calling functions that accept an auto pointer, versus a native pointer.

Another pitfall of auto pointers is their lack of support for STL containers. For example, it is not possible to have auto pointers as members of an std::vector. This happens because most containers work by copying their elements by value, which in practice invalidates the data stored in the existing auto pointer. Moreover, many algorithms for STL containers perform internal copies of the data they contain. This means that such algorithms can destroy the original elements without previous notice: a very awkward situation. For these reasons it is recommended that you avoid using auto pointers when dealing with STL containers.

To avoid the problems with std::auto_ptr, another smart pointer type has been introduced: the std::shared_ptr template provides the semantics of pointers that can be shared by different owners. In the next section, you will see an example of how to use such shared pointer objects.

Determining Credit Ratings

Create a class to determine the credit ratings of a given security.

Solution

Credit risk ratings are defined by accredited rating agencies and used to determine the risk of institutions. With this information, investors can determine the risk level of a particular bond or stock holding and control the level of risk they are willing to assume. For example, risk-averse investors such as pension funds and insurance companies typically shy away from investing in any institution that is not certified as top grade (typically AAA). There are three main credit rating agencies used by financial institutions all over the world: Moody’s, S&P, and Fitch. They define the risk grade not only for companies but also for local-, state-, and national-level governments.

To model credit ratings in C++, I created a separate class that encapsulates the fundamental concepts behind credit risk rating. The class CreditRisk has a member function called getRating, which returns the prevailing risk rating for a particular stock or bond. A second class, named RiskCalculator, is used to perform simple analysis of the risk associated with a particular portfolio, given a set of risk ratings for each component of the portfolio.

Using Shared Pointers

Since the RiskCalculator class needs to maintain a set of credit ratings, it makes sense to have a container to hold this information. However, we would like to avoid making copies of the data. Since the CreditRisk class is so simple, it doesn’t make much difference if you make copies or not. However, classes can become more complex, and in a large application these memory requirements start to add up. Therefore, a better design is to avoid making copies of CreditRisk objects as we add them to collections. We have seen, however, that std::auto_ptr is not suitable for inclusion in collections, which leaves us with the requirement of storing traditional pointers for objects.

A better solution is to use the std::shared_ptr class to handle the memory associated with CreditRisk objects. A shared pointer not only knows how to clean up the data referenced by a pointer but is also able to share the reference with other objects. This way, the object will not be destroyed until the last reference is also destroyed.

Shared pointers achieve this behavior through the use of a reference counting mechanism. A counter is maintained by the shared pointer object, which determines how many copies exist for the referenced object. When the shared pointer is destroyed, it checks this counter to determine if other references exist. If the counter is positive, the pointed object is not destroyed. The counter is also updated when a new copy of the shared pointer is created. The counter is incremented to indicate that another copy of the object reference exists. In this way, several copies of the same shared pointer can live in memory, and they will all manage the underlying data correctly, until the last one is destroyed and the original object is removed from memory.

I used shared pointers to handle the memory requirements of the RiskCalculator class. The set of CreditRisk objects is maintained as a vector of shared pointers, declared as follows:

std::vector<std::shared_ptr<CreditRisk> > m_creditRisks;

In this declaration, each element of the vector is a shared pointer. Since shared pointers know how to copy themselves, they can be used effectively as members of a vector or any other container, unlike auto pointers.

Working with shared pointers is easy because they automatically perform the required cleanup actions. For example, copying auto pointers is done simply with the use of the assignment operator. New elements to the m_creditRisks vector are added by the addCreditRisk member function.

void RiskCalculator::addCreditRisk(std::shared_ptr<CreditRisk> risk)
{
    m_creditRisks.push_back(risk);
}

Complete Code

In Listing 3-3 you have the complete code for the example described in the previous section. The class is called CreditRisk, which is contained in a header and an implementation file.

Listing 3-3. CreditRisk.h

//
//  CreditRisk.h

#ifndef __FinancialSamples__CreditRisk__
#define __FinancialSamples__CreditRisk__

// A simple class representing a credit risk assessment
class CreditRisk {
public:
    // these are risk grades, as determined by rating agencies
    enum RiskType {
        AAA,
        AAPlus,
        AA,
        APlus,
        A,
        BPlus,
        B,
        CPlus,
        C
    };

    // other methods here ...
};

#endif /* defined(__FinancialSamples__CreditRisk__) */

//
//  RiskCalculator.h

#ifndef __FinancialSamples__RiskCalculator__
#define __FinancialSamples__RiskCalculator__

#include "CreditRisk.h"

#include <memory>
#include <vector>

// calculates the risk associated to a portfolio
class RiskCalculator {
public:
    RiskCalculator();
    ~RiskCalculator();
    RiskCalculator(const RiskCalculator &);
    RiskCalculator &operator =(const RiskCalculator &);

    void addCreditRisk(std::shared_ptr<CreditRisk> risk);

    CreditRisk::RiskType portfolioMaxRisk();
    CreditRisk::RiskType portfolioMinRisk();
private:
    std::vector<std::shared_ptr<CreditRisk> > m_creditRisks;
};

#endif /* defined(__FinancialSamples__RiskCalculator__) */

//
//  RiskCalculator.cpp

#include "RiskCalculator.h"

RiskCalculator::RiskCalculator()
{

}

RiskCalculator::~RiskCalculator()
{

}

RiskCalculator::RiskCalculator(const RiskCalculator &v)
: m_creditRisks(v.m_creditRisks)
{

}

RiskCalculator &RiskCalculator::operator =(const RiskCalculator &v)
{
    if (this != &v)
    {
        m_creditRisks = v.m_creditRisks;
    }
    return *this;
}

void RiskCalculator::addCreditRisk(std::shared_ptr<CreditRisk> risk)
{
    m_creditRisks.push_back(risk);
}

CreditRisk::RiskType RiskCalculator::portfolioMaxRisk()
{
    CreditRisk::RiskType risk = CreditRisk::RiskType::AAA;

    for (int i=0; i<m_creditRisks.size(); ++i)
    {
        if (m_creditRisks[i]->getRating() < risk)
        {
            risk = m_creditRisks[i]->getRating();
        }
    }
    return risk;
}

CreditRisk::RiskType RiskCalculator::portfolioMinRisk()
{
    CreditRisk::RiskType risk = CreditRisk::RiskType::C;

    for (int i=0; i<m_creditRisks.size(); ++i)
    {
        if (m_creditRisks[i]->getRating() > risk)
        {
            risk = m_creditRisks[i]->getRating();
        }
    }
    return risk;
}

Using the auto Keyword

Among the many additions to C++ introduced by the C++11 standard, the auto keyword is one of the most practical and easy to understand. The basic idea behind this extension is that the compiler can perform the job of type detection for many categories of variables and expressions. Whenever this is possible, the programmer can use the auto keyword in the variable declaration, instead of entering the full type of the desired object. This way, the programmer can decide to let the compiler do the type detection automatically, while using the type name only when a visual labeling is desired and convenient.

The auto keyword has a few advantages over other forms of type declaration. It

  • Reduces the amount of manual work done by programmers, since it uses the compiler itself to analyze an expression and determine the exact type for a particular variable or expression.
  • Adds uniformity to the code, as most variables are declared using the same style.
  • Leaves intact the ability of programmers to enter the exact type as desired, so that any conflicts can be avoided.
  • Simplifies the use of templates, because data types can be automatically detected during compilation at each instantiation of the template.

As an example of the use of the auto keyword, we can modify some of the member functions in the RiskCalculator class to perform automatic detection of variable types. This is useful to simplify some of the expressions that are so common when using template collections. Following, for instance, is the portfolioMaxRisk member function:

CreditRisk::RiskType RiskCalculator::portfolioMaxRisk()
{
auto risk = CreditRisk::RiskType::AAA;
for (auto p = m_creditRisks.begin(); p != m_creditRisks.end(); ++p)
{
    if ((*p)->getRating() < risk)
    {
        risk = (*p)->getRating();
    }
}
return risk;
}

This code snippet shows how the type of the local variable risk can be automatically detected, so you don’t need to enter the redundant name of the type CreditRisk::RiskType. Similarly, the next line shows how to iterate through a collection of objects using the auto keyword to determine the right type of the iterator. Seasoned STL programmers know that using iterators may introduce a lot of extra types to a piece of code, which frequently obfuscates the original intent of the program. For comparison, notice that if it weren’t for the auto keyword, the foregoing for loop would need to be rendered as

for (std::vector<std::shared_ptr<CreditRisk> >::iterator p = m_creditRisks.begin();
     p != m_creditRisks.end(); ++p)
{
     // ...
}

Not only is this harder to enter manually, but it also makes the code harder to understand and modify.

Collecting Transaction Data

Create a solution to the problem of handling transaction orders, including BUY, SELL, or SELL SHORT, stored in a single file. The solution must correctly handle programming exceptions.

Solution

To solve this problem, we created a class that can handle trading transactions and perform the necessary operations. The class is responsible for receiving a file name and executing the instructions stored in the file. The class is also responsible for handling any error happening during this process, including errors from reading the file as well as incorrect trading requests sent to the application.

In this coding example, the operations allowed are simple and include only buy, sell, and sell short. Therefore, I concentrate on the problem of processing the file and handling unexpected errors in the program. We follow the best practices of using the exception handling mechanism offered by C++. Therefore, we create a new class, called TransactionHandler, which is able to read data from a file and perform the necessary actions in the member function handleTransactions. The resulting code is able to execute the trading actions stored in the file, but it handles possible exceptions using the try/catch/throw mechanism supplied by the C++ language as described in the next section.

Exception Handling

One of the basic problems faced by programmers is detecting and recovering from errors. While we try to avoid self-inflicted errors, there are many extraordinary situations that need to be handled even by correct programs. For example, what should your code do when the file system is full and there is no more space to save the current file? What can be done when a network connection is closed and the server is not available to complete a download? Programmers need to decide on how to respond to such exceptional situations, and a few strategies have been devised over the years in order to respond to such conditions.

C++ uses an exception-based model to deal with unexpected conditions occurring during program execution. This model uses a standard try/catch block to contain the code that you may want to protect. When an exception happens, the compiler throws an object to indicate the unexpected condition. As a result the enclosing blocks of code will also destroy all local objects that have been created in that particular context, to avoid resource leaks.

The other aspect of exception handling in C++ is the use of exception objects that inform programmers about the class of error that occurred. These objects are created using the throw keyword and caught using the catch block, which receives a reference to the exception object and uses it to understand and possibly recover from an unexpected state. Applications are free to create new classes of exceptions as a way to provide additional information about the error that triggered the exception.

In this example, I created two exception classes. They both derive from std::runtime_error, which provides the basic behavior for runtime exceptions. The first class, FileError, is used to flag any error occurred during the process of reading the file. Following is its definition:

class FileError :public std::runtime_error {
public:
    FileError (const std::string &description);
};

The second exception class, TransactionTypeError, is thrown when an unknown transaction type is found in the file, other than TRANSACTION_SELL, TRANSACTION_BUY, or TRANSACTION_SHORT. The definition is similar to what you saw with FileError. These classes are then used on the test code to determine the type of error encountered and how to proceed. In the test application we just print a descriptive error message using the string returned by the what() member function before terminating the program.

try
{
    TransactionHandler handler(fileName);
}
catch (FileError &e)
{
    std::cerr << "received a file error: " << e.what() << std::endl;
}
catch (TransactionTypeError &e)
{
    std::cerr << "received a transaction error: " << e.what() << std::endl;
}
catch (...)
{
    std::cerr << "received an unknown error ";
}

Complete Code

Listing 3-4 provides a complete example of exception handling. The classes presented here demonstrate how to handle exceptions while reading a transaction file. A sample main() function, provided at the end, shows how these classes work together.

Listing 3-4. TransactionHandler.h

//
//  TransactionHandler.h

#ifndef __FinancialSamples__TransactionHandler__
#define __FinancialSamples__TransactionHandler__

#include <iostream>

enum TransactionType {
    TRANSACTION_SELL,
    TRANSACTION_BUY,
    TRANSACTION_SHORT,
};

class FileError :public std::runtime_error {
public:
    FileError(const std::string &s);
};

class TransactionTypeError :public std::runtime_error {
public:
    TransactionTypeError(const std::string &s);
};

class TransactionHandler {
public:
    static const std::string SELL_OP;
    static const std::string BUY_OP;
    static const std::string SHORT_OP;

    TransactionHandler(const std::string &fileName);
    TransactionHandler(const TransactionHandler &);
    ~TransactionHandler();
    TransactionHandler &operator=(const TransactionHandler&);

    void handleTransactions();
private:
    std::string m_fileName;
};

#endif /* defined(__FinancialSamples__TransactionHandler__) */

//
//  TransactionHandler.cpp

#include "TransactionHandler.h"

#include <fstream>

FileError::FileError(const std::string &s)
: std::runtime_error(s)
{
}

TransactionTypeError::TransactionTypeError(const std::string &s)
: std::runtime_error(s)
{

}

const std::string TransactionHandler::SELL_OP = "SELL";
const std::string TransactionHandler::BUY_OP = "BUY";
const std::string TransactionHandler::SHORT_OP = "SHORT";

TransactionHandler::TransactionHandler(const std::string &fileName)
: m_fileName(fileName)
{

}

TransactionHandler::TransactionHandler(const TransactionHandler &a)
: m_fileName(a.m_fileName)
{

}

TransactionHandler::~TransactionHandler()
{
}

TransactionHandler &TransactionHandler::operator=(const TransactionHandler&a)
{
    if (this != &a)
    {
        m_fileName = a.m_fileName;
    }
    return *this;
}

void TransactionHandler::handleTransactions()
{
    std::ifstream file;
    file.open(m_fileName, std::ifstream::in);
    if (file.fail())
    {
        throw new FileError(std::string("error opening file ") + m_fileName);
    }

    std::string op;
    file >> op;
    while (file.good() && !file.eof())
    {
        if (op != SELL_OP && op != BUY_OP && op !=  SHORT_OP)
        {
            throw new TransactionTypeError(std::string("unknown transaction ") + op);
        }

        // process remaining transaction data...
    }
}

//
//  main.cpp

#include "TransactionHandler.h"

#include <iostream>

int main(int argc, const char **argv)
{
    if (argc < 2)
    {
        std::cerr << "usage: <progName> <fileName> ";
        return 1;
    }

    std::string fileName = argv[1];
    try
    {
        TransactionHandler handler(fileName);
    }
    catch (FileError &e)
    {
        std::cerr << "received a file error: " << e.what() << std::endl;
    }
    catch (TransactionTypeError &e)
    {
        std::cerr << "received a transaction error: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "received an unknown error ";
    }
    return 0;
}

Implementing Vector Operations

Implement the common addition and multiplication operators defined on numerical vectors.

Solution

This problem can be easily solved using the C++ facilities for operator overloading. I first give a general introduction to this programming technique, and subsequently show how to use it to implement numerical vector operations.

Operator Overloading

Operators are used in most programming languages to provide a simpler syntax for common operations. For example, the + operator is used to implement the addition of numbers without the need for a function called sum. So, one can type the following expression,

int total = a + b  + c + d;

instead of the less convenient version

int total = sum(a, sum(b, sum(c, d)));

Similarly, other operators perform comparable tasks for other primitive operations, such as subtraction, multiplication, logical comparison, and pointer arithmetic.

While operators are available in most modern programming languages, C++ is one of the few languages that allow programmers to redefine the meaning of existing operators to adapt them to the most natural usage in the target domain. For example, in an application where vectors of numbers are a common data structure, it makes sense to redefine the + operator to perform the sum of vectors in addition to the traditional usage of adding numeric (scalar) values.

C++ allows the definition of operators for each new declared type. Operators can be defined as part of a class (as a member function) or as a freestanding function. For example, consider the class Complex that redefines the + operator. The declaration for the operator can be written as

class complex {
public:
   // ... other methods here
   complex &operator +(const complex &v);
};

Another way of writing the same operator is using a free function.

Complex &operator +(const complex &a, const complex &b);

The difference between these two declarations is that the latter declares a function that receives two parameters, while the member function version requires only one additional parameter (the first parameter is the object itself). Similarly, you can declare new versions of most C++ operators, including math, logical, and pointer operators.

To solve the problem posed, we created a new class called NumVector, a simple numerical vector that can be used to store double numbers. To provide operations that can be applied to a vector in a natural way, we use operators that are declared as free functions.

NumVector operator +(const NumVector &a, const NumVector &b);
NumVector operator -(const NumVector &a, const NumVector &b);
NumVector operator *(const NumVector &a, const NumVector &b);

The class also provides a few member functions that are used in the implementation of these operations. In particular, you will want to have the member functions add, which adds a new element to the vector, removeLast, which removes the last element, get, which returns one of the elements given a position (index), and finally a size member function, which returns the size of the vector.

All operators are implemented in a similar way: they all check that the two parameters have the same size, and then perform a loop that is used to perform the required operation: addition, subtraction, or multiplication.

Complete Code

Listing 3-5 provides a sample implementation of a numeric vector. You can use this class to create numerical vectors and perform common vector operations.

Listing 3-5. NumVector.h

//
//  NumVector.h

#ifndef __FinancialSamples__NumVector__
#define __FinancialSamples__NumVector__

#include <vector>

class NumVector {
public:
    NumVector();
    ~NumVector();
    NumVector(const NumVector &);
    NumVector &operator =(const NumVector &);

    void add(double val);
    void removeLast();
    double get(int pos) const;
    size_t size() const;
private:
    std::vector<double> m_values;
};

NumVector operator +(const NumVector &a, const NumVector &b);
NumVector operator -(const NumVector &a, const NumVector &b);
NumVector operator *(const NumVector &a, const NumVector &b);

#endif /* defined(__FinancialSamples__NumVector__) */

//
//  NumVector.cpp

#include "NumVector.h"

#include <iostream>

NumVector::NumVector()
{

}

NumVector::~NumVector()
{

}

NumVector::NumVector(const NumVector &v)
: m_values(v.m_values)
{

}

NumVector &NumVector::operator=(const NumVector &v)
{
    if (this != &v)
    {
        m_values = v.m_values;
    }
    return *this;
}

size_t NumVector::size() const
{
    return m_values.size();
}

double NumVector::get(int pos) const
{
    return m_values[pos];
}

void NumVector::add(double val)
{
    m_values.push_back(val);
}

void NumVector::removeLast()
{
    m_values.pop_back();
}

NumVector operator +(const NumVector &a, const NumVector &b)
{
    if (a.size() != b.size())
    {
        throw new std::runtime_error("vectors must have the same size");
    }
    NumVector result;
    for (int i=0; i<a.size(); ++i)
    {
        result.add(a.get(i) + b.get(i));
    }
    return result;
}

NumVector operator -(const NumVector &a, const NumVector &b)
{
    if (a.size() != b.size())
    {
        throw new std::runtime_error("vectors must have the same size");
    }
    NumVector result;
    for (int i=0; i<a.size(); ++i)
    {
        result.add(a.get(i) - b.get(i));
    }
    return result;
}

NumVector operator *(const NumVector &a, const NumVector &b)
{
    if (a.size() != b.size())
    {
        throw new std::runtime_error("vectors must have the same size");
    }
    NumVector result;
    for (int i=0; i<a.size(); ++i)
    {
        result.add(a.get(i) * b.get(i));
    }
    return result;
}

Conclusion

C++ is a complex language, which provides mechanisms for the creation of software using one or more among several paradigms, such as structured, object-oriented, and functional programming. As a result, it is necessary to develop a set of techniques that are more appropriate for the development of financial software, while avoiding unproductive practices that obfuscate programs and hinder our ability to modify them. Over the years, financial engineers have successfully used a number of C++ idioms that make it easier to use the speed and abstraction facilities of the language.

In this chapter, I discussed a few programming examples that introduced and reviewed some of these useful C++ programming techniques, which are commonly employed in the development of financial applications. First, I reviewed the concept of templates, which let programmers write generic code that can be applied to several classes. In the first code sample, you learned how to design an interest rate calculation engine that is independent of the definition of interest rate classes. This type of design is very useful in the creation of large-scale financial applications.

Next, you learned how to define financial statement objects that can be sent to other parts of the application while reducing the occurrence of memory leaks. For this purpose, I explained the use of smart pointers as a way of making automatic decisions about the lifetime of objects. In particular, you learned about the std::auto_ptr template, which implements the semantics of auto-released, self-owned pointers.

The next section dealt again with memory management issues, this time in the context of determining credit ratings from rating agencies. In this case, you learned how to share rating information in such a way that the memory would be automatically destroyed even when several users had copies of the object. For this purpose, you can learn about the std::shared_ptr template, which uses a reference counting mechanism to determine the correct moment to delete memory, therefore avoiding memory leaks. You have also seen the use of the auto keyword to simplify type detection with STL containers. Both shared_ptr and the auto keyword are new features introduced with the C++11 standard, which is currently implemented by all modern C++ compilers.

Another important technique in C++ involves the handling of unexpected conditions. The exception-based mechanism provided by C++ allows programmers to deal with infrequent conditions in a clean way. You have seen an example of such policies applied to the problem of processing trading operations stored in a data file.

Finally, this chapter has considered the problem of implementing mathematical operations applied to a sequence of numbers. For this purpose, we created a new class called NumVector, which stores numbers in a sequence. To implement the addition, subtraction, and multiplication of vectors in a natural way, we used the operator overloading mechanism provided by C++. In this way, an application can perform vector operations using operators already present in the language, redefined so that they can be applied to your own types.

In this chapter, you learned about general C++ techniques that have been successfully used in financial applications. In the next chapter I explain how to use libraries that extend the language and offer useful functionality for financial software developers. These libraries include facilities such as new data containers and algorithms, as well as more advanced techniques for memory management, as well as time and event handling.

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

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