0% found this document useful (0 votes)
629 views62 pages

Learning Modern C++ For Finance (Early Release) - Daniel Hanson - 2022 - O'Reilly Media, Inc - 1098100794 - Anna's Archive

Learning Modern C++ for Finance by Daniel Hanson is an early release ebook that provides foundational knowledge for quantitative programming in C++. It discusses the significance of C++ in finance, debunks common myths about the language, and highlights modern features introduced in recent C++ standards that are beneficial for financial modeling. The book aims to equip readers with the skills to write, compile, and run C++ programs, particularly focusing on applications in quantitative finance.

Uploaded by

pitipongtoon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
629 views62 pages

Learning Modern C++ For Finance (Early Release) - Daniel Hanson - 2022 - O'Reilly Media, Inc - 1098100794 - Anna's Archive

Learning Modern C++ for Finance by Daniel Hanson is an early release ebook that provides foundational knowledge for quantitative programming in C++. It discusses the significance of C++ in finance, debunks common myths about the language, and highlights modern features introduced in recent C++ standards that are beneficial for financial modeling. The book aims to equip readers with the skills to write, compile, and run C++ programs, particularly focusing on applications in quantitative finance.

Uploaded by

pitipongtoon
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Learning Modern C++ for

Finance
Foundations for Quantitative Programming

With Early Release ebooks, you get books in their earliest form—the
author’s raw and unedited content as they write—so you can take
advantage of these technologies long before the official release of these
titles.

Daniel Hanson
Learning Modern C++ for Finance
by Daniel Hanson
Copyright © 2022 Daniel Hanson. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc. , 1005 Gravenstein Highway North,
Sebastopol, CA 95472.
O’Reilly books may be purchased for educational, business, or sales
promotional use. Online editions are also available for most titles (
http://oreilly.com ). For more information, contact our
corporate/institutional sales department: 800-998-9938 or
corporate@oreilly.com .

Editors: Jeff Bleiel and Amanda Quinn

Production Editor: Ashley Stussy

Interior Designer: David Futato

Cover Designer: Karen Montgomery

Illustrator: Kate Dullea

July 2022: First Edition

Revision History for the First Edition


2022-04-27: First Release

See http://oreilly.com/catalog/errata.csp?isbn=9781098100803 for release


details.
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc.
Learning Modern C++ for Finance, the cover image, and related trade dress
are trademarks of O’Reilly Media, Inc.
The views expressed in this work are those of the author(s), and do not
represent the publisher’s views. While the publisher and the author(s) have
used good faith efforts to ensure that the information and instructions
contained in this work are accurate, the publisher and the author(s) disclaim
all responsibility for errors or omissions, including without limitation
responsibility for damages resulting from the use of or reliance on this
work. Use of the information and instructions contained in this work is at
your own risk. If any code samples or other technology this work contains
or describes is subject to open source licenses or the intellectual property
rights of others, it is your responsibility to ensure that your use thereof
complies with such licenses and/or rights.
978-1-098-10254-8
Chapter 1. An Overview of C++

A NOTE FOR EARLY RELEASE READERS


With Early Release ebooks, you get books in their earliest form—the author’s raw and
unedited content as they write—so you can take advantage of these technologies long
before the official release of these titles.
This will be the 1st chapter of the final book. Please note that the GitHub repo will be made
active later on.
If you have comments about how we might improve the content and/or examples in this
book, or if you notice missing material within this chapter, please reach out to the author at
learnmodcppfinance@gmail.com.

Before launching into programming in C++, it will be useful to present a brief overview of the
language the C++ Standard Library, and the ways in which C++ continues to have a major
presence in quantitative finance.
You may have already felt intimidated by opinions and rumors claiming that C++ is
extraordinarily difficult to learn and fraught with minefields. So, in this chapter, we will try to
allay these fears by first debunking some of the common myths about C++, and then presenting
straightforward examples to help you get up and running.
Most of the content here is likely familiar for most readers, but the discussion here attempts to
extend some of the basics with points about quantitative programming and best practices that
often are not included in introductory books. We will also have our first look at C++20, namely
mathematical constants that have been added to the C++ Standard Library.
By the end of the chapter, you should be able to write, compile, and run simple C++ programs,
understand basic numerical types, and employ mathematical functions in the Standard Library
that are fundamental in just about any quantitative discipline, including finance.

C++ and Quantitative Finance


C++ started its rapid growth in the financial sector around the mid-1990’s. Many of us who
were in the industry around this time had been raised on FORTRAN, particularly for writing
numerical routines and scientific applications. While FORTRAN and its supporting libraries
were very well-developed in terms of mathematical and linear algebra support, it lacked
support for object-oriented programming.
Financial modeling in the abstract is naturally comprised of different components that interact
with each other. For example, to price even a simple derivative contract based on foreign
exchange and interest rates, one would typically require the following:
The term structure of interest rates for each currency
A market rate feed of live foreign exchange rate quotes
Volatility curves or surfaces for movements in FX rates and interest rates
A set of pricing methods, eg closed form, simulation, or other numerical approximations
Each of these components can be represented by an object, and C++ provided the means for
creating these objects and managing their relationships to each other.
Banks and other financial institutions also needed a way to calculate risk measures at both a
regional and global scale. This was a particular challenge for companies with trading operations
spread across the major financial centers of New York, London, and Tokyo, as well as other
capital markets. At the start of each trading day, risk reporting was required for a firm’s
headquarters in, say, New York that took into account the portfolios maintained both locally
and around the world. This could be a computationally intensive task, but the performance of
C++ made it possible and was yet another significant factor in its early adoption in the financial
industry.
Around the turn of the century, newer object-oriented languages, such as Java and C#, made
software development a relatively simpler and faster process, while more efficient processors
became less expensive. However, the same features in these languages that enabled quicker
deployment, such as built-in managed memory and intermediate compilation, could also
introduce overhead in terms of run-time performance. Management decisions on which
language to adopt often came down to a trade-off between more rapid development and run-
time efficiency. Even if one of these language alternatives was employed, computationally
intensive pricing models and risk calculations were -- and still are -- often delegated to existing
C++ libraries and called via an interface. It should also be noted that C++ also offers certain
compile-time optimizations that are not available in these other programming languages.

C++ 11: The Modern Era is Born


In 2011, the Standard C++ Foundation released a substantial revision that addressed long-
needed modernization and in particular provided some very welcome abstractions that are
immediately useful to quantitative developers. These include:
Random number generation from a variety of probability distributions
Lambda expressions that encapsulate mathematical functions that can also be passed as
arguments
Task-based concurrency that can parallelize computations without the need for manual
thread management
Smart pointers that prevent memory-related program crashes, without affecting
performance
These topics and more will be discussed in the chapters ahead. An excellent reference that
covers the history and evolution of C++ into the modern era is also available from O’Reilly:
C++ Today: The Beast is Back, by Jon Kalb and Gasper Azman [1]. It should also be noted that
with more attention to, and promotion of best practices[1] and guidelines[2] by the ISO C++
committee, cross-platform development is now a much easier task than in years past.
And following C++11, new releases with more and more modern features addressing the
demands of financial and data science industries are being rolled out on a threeyear cadence,
with the most recent release being C++20. This book will primarily cover developments
through C++20, particularly those that should be of interest to financial quant developers.
Proposals currently in the works for future standards are also mentioned where relevant.
Proprietary and high-frequency trading firms have been at the forefront of adopting the C++11
Standard and later, where the speed of acting on market and trading book signals in statistical
strategies can mean a profound difference in profit and loss. Modern C++ is also in keen
demand for derivatives pricing models utilized by traders and risk managers at investment
banks and hedge funds. The recent random number generation and concurrency features in the
Standard Library, for example, provide built-in support for efficient Monte Carlo simulation
that is a key component in both evaluating trading strategies and pricing complex exotic
options. These tasks used to require many more hours of distributional random number
generation code development and time-consuming integration of platform-dependent threading
libraries.

Open Source Mathematical Libraries


Another very welcome development over the past decade has been the proliferation of robust
open-source mathematical libraries written in standard C++ that therefore do not require the
time-consuming C-language interface gymnastics of the past. Primary among these are the
Boost libraries, the Eigen and Armadillo matrix algebra libraries, and machine learning
libraries such as TensorFlow and PyTorch. We will cover Boost and Eigen in more detail later
in the book.

Debunking Myths About C++


There are a multitude of myths about C++. Here are several of the more infamous beliefs, and
explanations which debunk them.
Knowledge of C is necessary for learning C++: While the C++ Standard retains most of
the C language, it is entirely possible to learn C++ without knowledge of C, as we shall
see. Clinging to C style can in fact hinder learning the powerful abstractions and potential
benefits of C++.
C++ is too difficult: There is no doubt that C++ is a rich language that provides plenty of
the proverbial rope with which one can hang oneself, but by leveraging _modern_ features
of the language while holding legacy issues in abeyance at the outset, it is entirely possible
to become very productive as a quantitative developer in C++ very quickly.
Memory leaks are always a problem in C++: With smart pointers available since C++11,
this no longer needs to be an issue in most financial model implementations, as we shall
see.

Compiled vs Interpreted Code


As alluded to above, C++ is a compiled language, where commands typed into a file by us
mere mortals are translated into binary instructions, or machine code, that a computer processor
will understand. This is in contrast to non-typed and interpreted quantitative languages such as
Python, R, and Matlab, where each line of code must be individually translated to machine
code at run-time, thus slowing down execution time for larger applications.
This is by no means a knock on these languages, as their power is evident in their popularity for
rapid implementations of models arising in quantitative fields such as finance, data science, and
biosciences, with their built-in mathematical and statistical functions are often compiled in C,
C++, or FORTRAN. However, the financial world at least is replete with stories where a model
would require days to run in an interpreted language, where run times could be reduced to a
matter of minutes when reimplemented in C++.
An effective approach is to use interpreted mathematical languages with C++ in a
complementary fashion. For example, when computationally intensive models code is written
in a C++ library, and then called either interactively or from an application in R, for example,
C++ efficiently takes care of the number crunching. The results can then be used inside
powerful plotting and other visualization tools in R that are not available in C++.
Another advantage is that the models code is written once and maintained in a C++ library that
can be deployed across many different departments, divisions, and even international
boundaries, and called via interfaces from applications in written in different front-end
languages, while ensuring consistent numerical results throughout the organization. This can be
particularly advantageous for regulatory compliance purposes.
Popular open-source C++ packages are available for both R and Python, namely Rcpp and
pybind11, respectively. Matlab also provides options for C++ interfaces.

The Components of C++


Standard C++ releases, at a high level, consist of two components: language features, and the
C++ Standard Library. A software library is essentially a set of functions and classes that are
not executable on their own but that are called by an application or system. Library
development -- both open source and commercial -- now dominates modern C++ development
compared to standalone applications that were popular in previous decades, and we will discuss
some of those later that are useful for computational work. The most important C++ library is
the Standard Library that is shipped with modern compilers.

C++ Language Features


C++ language features mostly overlap with the essential operators and constructs one would
find in other programming languages, such as:

Fundamental integer and floating-point numerical types


Conditional branching: if/else if/else statements and switch/case statements
Iterative constructs: for loops and while loops
Standard mathematical variable types: integer, double precision floating point, etc
Standard mathematical and logical operators for numerical types: addition, subtraction,
multiplication, division, modulus, and inequalities

In addition, C++ is not limited to object-oriented programming; rather, the language also
supports the other three major programming paradigms, namely procedural programming,
generic programming, and functional programming. Each of these will be discussed in
subsequent chapters.
C++ is a strongly-typed language, meaning that before we use a variable, we must declare it by
its type. The language provides a variety of numerical types; however, those that we will
primarily use are as follows:

Type Description Minimum Value Maximum Value

double Double Precision +/- 2.2e-308 +/- 1.8e308


int Integer -2,147,483,648 2,147,483,647

Others, such as unsigned and extended integer types, will be introduced later when we need
them.

The C++ Standard Library


As Nicolai Josuttis describes it in his indispensable text, The C++ Standard Library - A
Tutorial and Reference, 2nd Edition[3], the C++ Standard Library “enable(s) programmers to
use general components and a higher level of abstraction without losing portability rather than
having to develop all code from scratch.” Up through the latest C++20 release, highly useful
library features for quantitative model implementations include:

Array-style containers, particularly the venerable `vector` class


A wide set of standard algorithms that operate on these array containers, such as sorting,
searching, and efficiently applying functions to a range of elements in a container
Standard real-valued mathematical functions such as square root, exponential, and
trigonometric functions
Complex numbers and arithmetic
Random number generation from a set of standard probability distributions
Task-based concurrency that manages threads internally and safely
Smart pointers that abstract away the dangers associated with memory management
A class to store and manage character data
Streaming functions to take input from and display results to the console

Use of Standard Library components, however, requires the programmer to explicitly import
them into the code, as they reside in a separate library rather than within the core language. The
idea is similar to importing a NumPy array into a Python program or loading an external
package of functions into an R script. In C++, this is a two-step process, starting with loading
the file containing the Standard Library declarations of functions and classes we wish to use,
and then scoping these functions with the Standard Library namespace name, `std` (often
pronounced as “stood” by C++ developers).

Compilers and IDE’s


In order to get started with learning C++, you will need to obtain a compiler and a development
environment. The three major modern and freely available compilers, which ship with their
implementations of the C++ Standard Library, are:

The Microsoft Visual Studio 2019 compiler


Clang (LLVM Project)
GNU gcc compiler

There are also several integrated development environments (IDE’s) available, namely Visual
Studio, Apple’s Xcode (which ships with the Clang compiler), and CLion, a product that
typically requires purchase from JetBrains. For this book, Microsoft’s Visual Studio compiler
and IDE are highly recommended. They are user-friendly options to get up and running quickly
on C++, with very powerful debugging tools.
Furthermore, the Visual Studio option also includes a Clang option that allows a programmer to
switch between it and the Microsoft compiler, helping to ensure cross-platform compatibility.
Unfortunately, the Visual Studio option for C++ only exists for Windows, as the Mac version
does not ship with a C++ option. In this case, one might opt for downloading Apple’s Xcode,
which ships with the Clang compiler. Linux users will typically want to opt for the gcc or Clang
compiler.

Basic Review of C++


The following will be a quick review of C++ using some simple code examples. We will also
have our first look at a new feature in C++20, namely mathematical constants.

Good Old “Hello World!”


First, here is a “Hello World!” example to get started. The following code will return the
message to the screen, and then allow the user to input the name of someone to whom to say
hello:

#include <iostream>
#include <string>
int main()
{
std::cout << "Hello World!" << '\n';
std::string person;
std::cout << "To whom do you wish to say hello? ";
std::cin >> person;
std::cout << "Hello "<< person << "!" << '\n';
return 0;
}

If you want to say hello to your mother, then after compiling and running the code, the screen
would resemble the following:

Hello World!
To whom do you wish to say hello? Mom
Hello Mom!

The main review points here are

cout and cin, along with the string class, depend upon including the C++ Standard
Library declaration files iostream and string.
Members of the Standard Library need to be scoped by their namespace std. An
alternative is to put using statements with the namespace scopes at the top of the file,
indicating that anytime these elements appear in the code, they are understood to be
coming from the std namespace. Also, you may find it easier to type endl (end of line)
rather than '\n’:
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <string>
using std::string;
int main()
{
cout << "Hello World!" << endl;
string person;
cout << "To whom do you wish to say hello? ";
cin >> person;
cout << "Hello " << person << "!" << endl;
return 0;
}

Importing the std namespace into the global namespace with

using namespace std;

is sometimes used to replace the individual using statements; however, this is not
considered good practice, as it can result in naming clashes at compile time. The
motivation behind namespaces will be presented in Chapter 3.
Output to and input from the console is almost never used in production-level financial
programming. User input data will typically come from graphical user interfaces (GUIs)
or web applications, while market data usually comes from live feeds. Results are
typically displayed in the user interface and then stored in a database, such as when a trade
in executed.
We will use cout and cin to sometimes mimic these inputs, but they should be avoided
in production code.

Simple Procedural Programming in C++


The structure of a procedural program should be familiar, namely:
A `main()` function, which is called first in execution of a program, and
A set of user-defined functions that contain individual tasks that comprise the program.
In the simplest case, these can all be written in a single executable file containing `main()`.
We first declare each user-defined function in a function declaration statement, prior to the
start of program execution in the `main()` function. A function declaration states its name,
return type, and input argument types, followed by a semicolon.
The function implementations are written beneath `main()`, each containing a series of
commands within open and closed braces. User-defined function calls can then be made within
the `main` function, or from other user-defined functions.
Single line comments are indicated by two consecutive forward slashes. The high-level format
is shown here:

// Function declarations ("//" indicates a comment line)


return_type function_01(input arguments);
return_type function_02(input arguments);
return_type function_03(input arguments);
.
.
.
int main()
{
// Call each function
function_01(input arguments);
function_02(input arguments);
function_03(input arguments);
.
.
.
}
return_type function_01(input arguments)
{
// Do stuff
// Return something (or void return)
}
return_type function_02(input arguments)
{
// Do stuff
// Return something (or void return)
}
return_type function_03(input arguments)
{
// Do stuff
// Return something (or void return)
}
.
.
.

NOTE
For larger and more robust production applications, we will soon look at writing functions in separate modules,
using a new feature in C++20, in which the same method of declaring and implementing functions will carry over.
Further details on functions follow in the next two subsections.

Function declarations
C++ functions may or may not return a value; furthermore, they may or may not take input
arguments. A function that has no return value is indicated by a `void` return type. For
example, if we move our “Hello World” example into a separate function, it would simply
output a message to the screen without returning a value when called from the `main` function,
so it would be declared as a `void` function. In addition, it does not require any input
parameters, so its declaration would take on the form

void hello_world();

Next, suppose we want to write a real-valued function that takes in a single variable and returns
twice its value. In this case, our declaration will have a double precision floating type return,
indicated by `double`, and an input of the same type. If we name this function `twice_a_real`,
and the input variable `x`, our declaration would be written as

double twice_a_real(double x);

As a final example, as in other programming languages, a function can take in more than one
variable. Suppose we wish to add three integers in a function called `add_three_ints` and return
the sum of variables `i`, `j`, and `k`. Integer types are indicated by `int`, so our function
declaration would be

int add_three_ints(int i, int j, int k);

Function implementations
Function implementations, also called function definitions, are where we implement the actual
commands to display a message to the screen, calculate a mathematical result, or to perform
other tasks. The body of the function is placed inside braces, as shown here for the
`hello_world` function. We again need to indicate the `void` return type.

void hello_world()
{
std::cout << "Hello World!\n";
}

Next, we can write the implementations of our two simple mathematical functions. As in their
declarations, the `double` and `int` return types, respectively, as well as the types of their input
variables, must be included:

double twice_a_real(double x)
{
double y = 2.0 * x;
return y;
}
int add_three_ints(int i, int j, int k)
{
return i + j + k;
}

In the first case, we initialize a new `double` variable `y` and with the result of the calculation.
Because C++ is a strongly typed language, we need to indicate the type of a variable when it is
initialized. This variable is then returned to the `main` function with the result. In the second
function, we just put the sum operations in the return statement itself; this is also perfectly
legal.
Finally, we put this all together with a `main` function that is called when the program starts
and makes calls to our user-defined functions. It goes in between the user-defined function
declarations and their implementations below, as shown here:

#include <iostream>
// Maybe put in using statements here(?)
void hello_world();
double twice_a_real(double x);
int add_three_ints(int i, int j, int k);
int main()
{
hello_world();
double prod = twice_a_real(2.5);
std::cout << "2 x 2.5 = " << prod << std::endl;
std::cout << "1 + 2 + 3 = " << add_three_ints(1, 2, 3) << std::endl;
double r;
std::cout << "Enter a real number: ";
std::cin >> r;
std::cout << "2 x " << r << " = " << twice_a_real(r) << std::endl;
return 0;
}
void hello_world()
{
std::cout << "Hello World!\n";
}
double twice_a_real(double x)
{
double y = 2.0 * x;
return y;
}
int add_three_ints(int i, int j, int k)
{
return i + j + k;
}

C++ Syntax and Style Guidelines


In this section a review of essential C++ syntax is provided, along with guidelines on code
formatting and variable naming. The guidelines discussion might not be high on many people’s
priority list, but this topic is in fact quite important when writing critical production code in
financial systems, in a feature-rich language such as C++. Bugs, runtime errors, and program
crashes are much more easily avoided or addressed if the source code is written in a clean and
maintainable state.
We will review essential rules about C++ syntax. Even if you are familiar with some of it
already, a summary will be presented in one place that you may find useful.

Code Blocks in Braces


Function implementations, also called function definitions, are placed inside braces, as shown
in each of the function implementations in 6.2.2 above. When control reaches the closing brace,
the function terminates. This is also true for other code blocks such as in conditional
statements, loops, user-defined functions, and user-defined classes. When the closing brace is
encountered, non-static local variables and objects defined within the block are said to _go out
of scope_. That is, they are wiped from memory and no longer accessible. Pointers can be an
exception to this rule, but we will discuss this in more detail in Chapter XX.

Syntax Review
Commands and declarations in C++ terminate with a semicolon:

double y = 2.0 * x;

Again, as C++ is a strongly-typed language, numerical variable types should be indicated


before initialization.

double x1 = 10.6;
int k; // Defaults to zero
double y1 = twice_a_real(x1);

NOTE
C++11 introduced the `auto` keyword that can automatically deduce a variable or object type, as well as uniform
initialization (with braces). Varied opinions on their use exist, but many programmers still prefer to explicitly state
plain old data (POD) types such as `int` and `double` to avoid ambiguity. This will be the style followed in this
book. `auto` and uniform initialization will be discussed later within contexts where they tend to be more useful.

One-line comments are indicated with two forward slashes, eg,

// This is a comment

Multiple lines of comments in a block can also be commented out, as follows:

/*
Owl loved to rest quietly whilst no one was talking
Sitting on a fence one day, he was surprised when
suddenly a kangaroo ran close by.
*/
There is no difference to the compiler between a single space or multiple spaces; for example,
despite the variations in whitespace, the following code is legal:

int j = 1101;
int k= 603;
int sum = j + k;
std::cout << "j + k = " << sum << "\n";

A well-known mantra in programming, however, and particularly relevant to C++, is just


because you can do something, doesn’t mean you should. The above code will be more readable
and maintainable if written with clear and consistent spacing:

int j = 1101;
int k = 603;
int sum = j + k;
std::cout << "j + k = " << sum << "\n";

Again, for more realistic and complex code, this mantra should be kept in mind. It will be a
recurring theme throughout this book.
Code may also be continued onto multiple lines without the use of a continuation character, and
vertical spaces are ignored. Returning to our previous example, writing

int j = 1101;
int k =
603;

int sum = j + k;
std::cout << "j + k = "
<< sum
<< "\n";

would yield the same result. As before, the preceding example, with uniform spacing and each
command placed in a single line, would be preferable. However, it should be noted that, in
quantitative programming where complex and nested calculations are involved, it often
becomes highly advisable to split up formulae and algorithms on multiple lines for clarity and
code maintainability. We will see examples of this in subsequent chapters.
Finally, C++ syntax is case sensitive. For example, two `double` variables `x` and `X` would be
as different as two other variables `kirk` and `spock`. The same applies to function names. In
examples above, we used the Standard Library function `std::cout`. Attempting to write
`std::Cout` instead would trigger a compiler error.

Naming Conventions
Variable, function, and class names can be any contiguous combination of letters and numbers,
subject to the following conditions:

Names must begin with a letter or an underscore; leading numerals are not allowed.
Other than the underscore character, special characters, such as `@`, `=`, `$` etc are not
allowed.
Spaces are not allowed. Names must be contiguous.
Language keywords are not allowed in naming, such as `double`, `if`, `while`, etc. A
complete listing can be found on https://en.cppreference.com/w/cpp/keyword.

The maximum name length is compiler-dependent, and in at least one case – the GNU gcc
compiler – imposes no limitation; however, see the mantra discussed above.
Single letter variable and function names are fine for simple examples and plain mathematical
functions. However, for quantitative models, it will usually be better to pass function arguments
with more descriptive names. Function and class names as well should also provide some
indication of what they do.
Several naming styles have been common over the years, namely
Lower Camel case; eg, `optionDelta`, `riskFreeRate`, `efficientFrontier`: Letter of first
word in lower case, and following words capitalized
Upper Camel, aka Pascal case; eg, `OptionDelta`, `RiskFreeRate`, `EfficientFrontier`:
Letter of each word is in upper case
Snake case; eg, `option_delta`, `risk_free_rate`, `efficient_frontier`: Each word begins
with lower case, separated by an underscore character
Lower Camel and Snake cases are the most typical of what is found in C++ function and
variable names, and class names are usually in Upper Camel form. In recent years – likely
propelled by Google’s C++ Style Guide [5] – variable and function names have gravitated more
toward the snake case. As such, we will adopt this convention in this book, and use Upper
Camel for class names.
In cases where single characters are used for integral counting variables, it is still common to
use the FORTRAN convention of letters `i` through `n`, although this is not required. We will
also adopt this practice.

Mathematical Operators, Functions, and Constants in


C++
While the previous discussion was loads of fun, our focus in this book is on math and finance.
We have already used the mathematical operators for addition and multiplication of built-in
C++ numerical types above. These are language features in C++, and a comprehensive
discussion of these standard operators follows. Common mathematical functions, however --
such as cosine, exponential, etc -- are provided in the C++ Standard Library rather than in the
core language.
Standard Arithmetic Operators
As suggested in the examples above, addition, subtraction, multiplication, and division of
numerical types are provided in C++ with the operators `+`, `-`, `*`, and `/`, respectively, as
usually found in other programming languages. In addition, the modulus operator, `%`, is also
included. Examples are as follows:

// integers:
int i = 8;
int j = 5;
int k = i + 7;
int v = j - 3;
int u = i % j;
// double precision:
double x1 = 30.6;
double x2 = 8.74;
double y = x1 + x2;
double z = x1 - x2;
double twice_x2 = 2.0 * x2;

The order and precedence of arithmetic operators are the same as found in most other
programming languages, namely:

Order runs from left to right:

i + j - v

Using the above integer values would result in 8 + 5 - 2 = 11


Multiplication, division, and modulus take precedence over addition and subtraction:

x1 + twice_x2/x2

Using the above double precision values would result in 30.6 + 2.0 = 32.6

Use round brackets to change the precedence:

(x1 + twice_x2)/x2

This would yield


with the same double precision values.

Mathematical Functions in the Standard Library


Many of the usual mathematical functions one finds in other languages have the same or
similar syntax in C++. Functions commonly used in computational finance include the
following, where `x` and `y` are assumed to be double precision variables:

`cos(x)` cosine of x
`sin(x)` sine of x
`tan` tangent of x
`exp` exponential function ex
`log` natural logarithm ln(x)
`sqrt` square root of x
`cbrt` cube root of x
`pow` x raised to the power of y
`hypot`

computes for two numerical values x and y

As these are contained in the Standard Library rather than as language features. The `cmath`
header file should always be included, with the functions scoped by the `std::` prefix:

#include <cmath> // Put this at top of the file.


double trig_fcn(double theta, double phi)
{
return = std::sin(theta) + std::cos(phi);
}

Again, if you don’t feel like typing out `std::` all the time, putting `using` statements after the
`include` statement are also fine:

#include <cmath> // Put this at top of the file.


using std::sin;
using std::cos;
double trig_fcn(double theta, double phi)
{
return = sin(theta) + cos(phi);
}

We can also now write our first finance example. We want to price a zero coupon bond
Ae-rt
where
A = the face value of the bond,
r is the interest rate, and
t is the time to maturity as a year fraction.
In C++, we could then write

double zero_coupon_bond(double face_value, double int_rate, double year_fraction)


{
return face_value * std::exp(-int_rate * year_fraction);
}

For a more comprehensive list of Standard Library math functions, again see Josuttis, The C++
Standard Library (2E) [4], Section 17.3, or the listing available on the CppReference website
[6]. Both are indispensable references for any modern C++ developer and are highly
recommended advanced complementary resources for this book. Some additional guidance on
the use of Standard Library math functions follows in the next two sections.

There is No Power Operator in C++


Unlike other languages, where an exponent is typically indicated by a `^` or a `**` operator,
this does not exist as a C++ language feature. Instead, one needs to call the Standard Library
`std::pow` function in `cmath`. When computing polynomials, however, it is more efficient to
apply factoring per Horner’s Method and reduce the number of multiplicative operations[6].
For example, if we wish to implement a function
it would be preferable to write it in C++ as
f(x) = 8x4 + 7x3 + 4x2- 10x - 6

double f(double x)
{
return x * (x * (x * (8.0 * x + 7.0) + 4.0 * x) - 10.0) - 6.0;
}

rather than

double f(double x)
{
return 8.0 * std::pow(x, 4) + 7.0 * std::pow(x, 3) +
4.0 * std::pow(x, 2) + 10.0 * x - 6.0;
}

For the case of a non-integer exponent, say


g(x,y) = x-1.368x + 4.19y
then there is no alternative but to use `std::pow`:
double g(double x, double y)
{
return std::pow(x, -1.368 * x) + 4.19 * y;
}

` < cmath >` Ensures Consistency Across Compilers


It may be the case that you can use these math functions without `#include <cmath>`, but one
should adhere to including `cmath` and scoping the functions with `std::`. First, because C++ is
built upon C, some compilers retain the old math functions from C in what is called the global
namespace. Other compilers, however, might put `cmath` into the global namespace. As a
result, one might actually be calling old C functions rather than the ISO C++ Standard versions,
and this could cause unexpected or inconsistent behavior among different compilers.
Another example of inconsistencies that can arise is with the absolute value function. In C, and
on older C++ compilers, the `abs` function was only implemented for integer types. In order to
calculate the absolute value of a floating point number, one would need to use the `fabs`
function. However, `std::abs` is overloaded for both integer and floating point (eg `double`)
arguments and should be preferred.
This is unfortunately one of the quirks in C++ due to its long association with C; however, the
moral of the story is quite simple: to keep C++ code ISO-compliant, we should always put
`#include <cmath>`, and scope the math functions with `std::`. This will help ensure cross-
compatibility on different compilers and operating system platforms.

NOTE
Note: Regarding C headers and namespace std, this is clarified, for example, in the specifications for the gcc
compiler:
The standard specifies that if one includes the C-style header (<math.h> in this case), the symbols will be
available in the global namespace and perhaps in namespace std:: (but this is no longer a firm requirement.) On
the other hand, including the C++-style header (<cmath>) guarantees that the entities will be found in
namespace std and perhaps in the global namespace.[8]

Constants
In any type of quantitative programming, there is often a need to use constant values in
calculations. In C++, one can define a constant by simply appending the keyword `const` when
a value is assigned. Furthermore, beginning with C++20, a set of commonly used mathematical
constants is now available.

The `const` Keyword


If a variable doesn’t change value, it is safer to declare it as a constant type, by using the `const`
keyword. For example, we could use it to store an approximation of earth’s gravitational
acceleration constant:
const double grav_accel = 9.80665;

Then, if later within the same scope someone attempted to reassign it to a different value:

grav_accel = 1.625; // Gravitational constant for the moon

a compiler error would result, with a message indicating an attempt was made to modify the
value of a constant. Catching errors at compile time is better than chasing them at runtime and
tracking down the cause, especially in a live production environment.
`const` also has other important uses and interesting properties that we will cover later,
particularly in an object-oriented programming context.

Standard Library Mathematical Constants


A handy addition to the C++ 20 Standard Library is a set of commonly used mathematical
constants, such as the values of , e, , etc. Some of those that are convenient for
quantitative finance are shown in the following table.

C++ constant `e` `pi` `inv_pi` `inv_sqrt_pi` `sqrt2`

Definition e

To use these constants, one must first include the `numbers` header in the Standard Library. At
the time of this writing, each must be scoped with the `std::numbers` namespace. For example,
to implement the function

we could write

#include <cmath>
#include <numbers>
. . .
double some_fcn(double x, double y)
{
double math_inv_sqrt_two_pi =
std::numbers::inv_sqrtpi / std::numbers::sqrt2;
return math_inv_sqrt_two_pi*(std::sin(std::numbers::pi * x) +
std::cos(std::numbers::inv_pi*y));
}

This way, whenever is used in calculations for example, its value will be consistent
throughout the program, rather than leaving it up to different programmers on a project who
might use approximations out to varying precisions, resulting in possible consistencies in
numerical results.

In addition, the value of , which can crop up somewhat frequently in mathematical


calculations, does not have to be computed with

std::sqrt(2.0)

each time it is needed. The constant

std::numbers::sqrt2

holds the double precision approximation itself. While perhaps of trivial consequence in terms
of one-off performance, repeated calls to the `std::sqrt` function millions of times in
computationally intensive code could potentially have some effect.

NOTE
While not essential to know at this point, it is worth at least mentioning that these constants are set at compile time
rather than runtime, using a C++11 designation called `constexpr`. This ties in with the much broader and more
advanced subject of template metaprogramming, in which calculations of constant values to be used at runtime are
performed at compile time. [[Might return to this topic later, although it is of limited used in financial modeling
where the computations depend on data only available at runtime]].

As a closing note, it is somewhat curious that the set of mathematical constants provided in

C++20 include the value , but not or , despite the latter two being more
commonly present in statistical calculations. [[See later chapter on the Boost libraries – they are
included there]].

Conclusion
This concludes our whirlwind overview of C++. We emphasized quantitative programming,
along with the mathematical constants now included in C++20.
Our coverage of best practices with respect to coding style will be a consistent theme
throughout the book, as C++ is an extremely feature-rich language with plenty of the proverbial
rope with which to hang oneself. Adhering to best practices and consistent coding style is vital
to ensure code maintainability and reliability.
One other point to remember is that while we use a lot of screen output and input, this is not
how C++ is typically used in quantitative development. `std::cout`, and `std::cin` should be
thought as placeholders for real-world interfaces. We will continue to use them as devices to
check our results, but they will mostly be relegated to use within the test functions that are
called from `main()`, rather than within mathematical and models code itself where they should
be avoided in practice anyway.

References
[1] Kalb and Azman, C++ Today: The Beast is Back, available on
https://resources.jetbrains.com/storage/products/cpp/books/Cplusplus_Today.pdf (link)
[2] Guideline Support Library (ISO) (link)
[3] ISO C++ Coding Standards (link)
[4] Nicolai Josuttis, The C++ Standard Library (2E) (link)
[5] Google C++ Style Guide (https://google.github.io/styleguide/cppguide.html)
[6] cppreference.com
[7] Stepanov, Mathematics of Generic Programming (Horner’s Method)
[8] GNU gcc Compiler Documentation
(https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_headers.html
Chapter 2. Some Mechanics of
C++

A NOTE FOR EARLY RELEASE READERS


With Early Release ebooks, you get books in their earliest form—the
author’s raw and unedited content as they write—so you can take
advantage of these technologies long before the official release of these
titles.
This will be the 2nd chapter of the final book. Please note that the
GitHub repo will be made active later on.
If you have comments about how we might improve the content and/or
examples in this book, or if you notice missing material within this
chapter, please reach out to the author at
learnmodcppfinance@gmail.com.

Just about any programming language will have some form of an array
structure for storing collections of like types. In C++, there are a handful of
options for this purpose, but it is the Standard Library `vector` container
that is by far the most-used type. In this chapter, we will see how a `vector`
can conveniently represent a vector of real numbers in the mathematical
sense. We will also go through the basics of creating and using a `vector`
and its key member functions, as it will tie in well with iterative statements,
such as counted loops and `while` statements, also to be covered in this
chapter.
Control structures, include both iterative statements and conditional.
Conditional branching in C++ can be implemented in `if` statements,
similar to other languages, as well as in what are called `switch` statements.
A good complementary topic related to the `switch` statement is that of
enumerated types (enums), particularly the more modern enum classes that
were added in C++11. Enum classes are also well-suited for facilitating data
input to and output from financial models.
Finally, we will wrap up with a summary of aliases that can be used in C++,
and why they are important. This includes type aliases that can add clarity
to the code, in place of longer and sometimes more cryptic templated types.
References and pointers allow you to access and modify objects without the
overhead of object copy, although pointers have wider-ranging uses as well.

The `vector` Container


The `vector` container in the C++ Standard Library is the go-to choice for
storing and managing an indexed array of like types. It is particularly useful
for managing vectors of real numbers that are ubiquitous in quantitative
work, with `double` types representing the numerical values.

NOTE
Historical Note: A `vector` is more specifically part of what is called the Standard
Template Library (STL). The STL was developed independently of Bjarne Stroupstrup’s
early efforts in the 1980’s and 90’s to design and produce C++, by researcher Alexander
Stepanov. The history behind acceptance the STL and its acceptance into the C++
Standard is a very interesting one [[see Kalb/Azman]], but the upshot is the STL – based
on generic programming – was accepted into the theretofore object-oriented focused
C++ for its first ISO standard release in 1998.

Being a generic container, `std::vector` can hold elements of a common


arbitrary type, ranging from plain old data (POD) types such as `double`
and `int`, to objects of user-defined and library classes.

#include <vector>
using std::vector;
//. . .
vector <double> x; // Vector of real numbers
vector <BondTrade> bond_trades; // Vector of user-defined BondTrade
objects

The type to be held is indicated inside the angle brackets.

NOTE
The angle brackets indicate the template parameter. Templates are the means by which
C++ implements generic programming. This topic will be discussed in further detail in
Chapter 7.

Setting and Accessing Elements of a `vector`


An STL `vector` essentially encapsulates and manages a dynamic array,
meaning that elements can be appended to it or removed from it after it is
constructed. The `vector` also supports random access, meaning an element
can be accessed, and moreover modified, by the index of the element. Like
everything else in C++, a `vector` is zero-indexed, meaning that the index
of its first position is index 0, and its last position is index n - 1, if it holds n
elements.

Creating a `vector` and Using its Index


The following instruction will create a vector holding three real numbers.

vector <double> v(3); // Will hold three elements

The `vector` can be populated element by element as shown here. Note that
indexing starts with zero rather than one.

v[0] = 10.6; // Set the first element (index 0) and


assign to 10.6
v[1] = 58.63; // Set the second element (index 1) and
assign to 58.63
v[2] = 0.874; // Set the first element (index 2) and
assign to 0.874

The index is indicated by square brackets. We can also change the values by
simply reassigning an element to a new value; viz,
v[1] = 13.68;

It is also possible to initialize vectors using uniform initialization


introduced in C++11. Also known as braced initialization, use of the
assignment operator is optional:

vector <double> w{9.8, 36.8, 91.3, 104.7}; // No assignment


operator
vector <int> q = {4, 12, 15}; // With assignment operator

NOTE
The addition of uniform initialization to C++11 has had a significant impact on the
language, beyond simply initializing a vector. It has some interesting and convenient
properties that will be discussed in Chapter 4.

Member Functions
As `vector` is a class, it holds a number of public member functions,
including three: `at`, `size`, and `push_back`.
The `at` Function
The `at` function essentially performs the same roles as the square bracket
operator, namely access of an element for a given index, or to modify the
element.

double val = v.at(2); // val = 0.874


w.at(1) = val; // 36.8 in w[1] is replaced with
0.874

The difference between using the square bracket operator and the `at`
function is the latter performs bound checking. Two examples are
Attempting to access an element that exceeds the maximum index; eg,

double out_of_range = v.at(100); // 2 is the max index


for v

Attempting to use a negative index value

w.at(-3) = 19.28;

In each case, an exception will be thrown that can be used in error handling.
Otherwise, you can just think of `at` and `[.]` as the same.

The `size` Function


The name of this member function makes it fairly obvious what it does: it
returns the number of elements held by a `vector`:

auto num_elems_w = w.size(); // Returns 5


auto num_elems_q = q.size(); // Returns 3

You may notice this is the first time we have used the `auto` keyword. What
this does is automatically deduce the type returned from the `size` function.
We will see in future cases how useful `auto` can be, but here, it helps us
get around the fact that the maximum size of a `std::vector` container will
vary depending upon compiler settings and the platform you are using. The
type will be some form of an unsigned (non-negative) integer, of which
there are multiple sizes.
So as to not get into the weeds here, we don’t need to be concerned with the
specific unsigned type here, so we can mostly just use `auto` for the return
type of the `size` member function.
The `push_back` Function
This function will append elements to a `vector`; that is, new elements are
“pushed onto the back” of the container.
For example, we can append a new element to the `vector v` above, say
47.44:
v.push_back(47.44);

Now, `v` contains four values: 10.6, 58.63, 0.84, 47.44, with `v[3]` (fourth
element, using 0-indexing) equal to the new value.
We can also append values to an empty vector. At the outset, we defined

vector <double> x; // x.size() = 0

Now, if we append a value,

x.push_back(3.08); // x.size() = 1

`x` now contains the value 3.08 in its index 0 position and contains one
element. This can be repeated arbitrarily many times:

x.push_back(5.12); // x.size() = 2
x.push_back(7.32); // x.size() = 3
//. . . etc

To close the discussion on `push_back`, there is one potential gotcha to be


aware of,
Suppose we create a `vector` of integers with three elements:
`vector <int> ints(3);`
Now, each element will hold the default value of an `int` type: 0.
If we then apply the `push_back` function to append, say, 5:
`ints.push_back(5);`
this value will be appended as a new element following the third zero; ie,
the vector now contains four elements.
0 0 0 5
To put a value into any of the first three positions, you will need to use the
index explicitly; eg

ints[0] = 2;
ints.at(2) = 4;

Concluding Remarks on STL `vector`s


In the examples above, we only used plain old numerical types `double` and
`int`. Vectors of real numbers are of course fundamental for computational
work, but keep in mind an STL `vector` is generic, in that it can hold
elements of any valid type, including objects rather than just numerical data
types, as we shall see in more advanced contexts.
Also, as mentioned earlier, in real-world production level programming,
inputs are taken from function arguments that come from market and
product data, and user input, not hard-coded values as seen in the previous
examples. One might find vectors set with fixed numerical values in test
functions, but they should be avoided in production code.
The Standard Library contains additional STL containers, plus a large set of
STL algorithms that are now a core component of modern C++
programming. These will be discussed in greater detail in Chapter 7, and
becoming familiar with the basics of the `vector` container now will make
this material more accessible when we get to it.
Finally, to reiterate, prefer using an STL `vector` over a dynamic C-style
array using `new` and `delete`. There is no performance benefit to using the
latter, and memory management is all encapsulated inside the `vector` class,
freeing the developer from risks due to memory leaks.

Enum Constants and Classes


Enumerated constants, more commonly called enums for short, map text to
integers. Prior to C++11, enums were a great means of making it clearer for
us mere mortals to comprehend integer codes by representing them in
(contiguous) words. It was also far more efficient for the machine to
process integers rather than bulkier `std::string` objects that take up more
memory. And finally, errors caused by typos in quoted characters and stray
strings could be avoided.
The C++11 Standard improved on this further with enum classes. These
remove ambiguities that can occur with overlapping integer values when
using regular enum constants, while preserving the advantages.
We will discuss the motivation for preferring the more modern enum
classes over integer-based enums. In the next section we will see how they
can be used to our advantage in conditional statements. Later on, they will
prove useful in making data input and output with financial models more
robust.

Enum Constants
Enums allow us to pass around identifiers, classifications, indicators etc in
text representation, while behind the scenes, the compiler recognizes them
as integers.
As an example, we can create an enum called `OptionType` that will
indicate the types of option deals that are allowed in a simple trading
system, eg European, American, Bermudan, and Asian. The `enum` type is
declared; then, inside the braces, the allowable types are defined, separated
by commas. By default, each will be assigned an integer value starting at
zero and incremented by one (remember that indexing in C++ is zero-
based). The closing brace must be followed by a semicolon. In code, we
would write:

enum OptionType
{
European, // default integer value = 0
American, // default integer value = 1
Bermudan, // default integer value = 2
Asian // default integer value = 3
};

We can then verify that in place of each option type, its corresponding
integer value is given:

cout << " European = " << European << endl;


cout << " American = " << American << endl;
cout << " Bermudan = " << Bermudan << endl;
cout << " Asian = " << Asian << endl;
cout << endl;

Checking the output, we get:

European
American
Bermudan
Asian

So, we can see how the program treats the text representations as integers.
Note that these text labels are not enclosed in quotation marks, as they
ultimately represent integer types, not strings.

Potential Conflicts with Enums


As discussed at the outset, for any `enum` type, the default integer
assignments start at zero and then are incremented by one for each type
member. Therefore, it is possible that two enumerated constants from two
different types could be numerically equal. For example, suppose we define
two different `enum` types, called `Football` and `Baseball`, representing
the defensive positions in each sport. By default, the baseball positions start
with 0 for the pitcher and are incremented by one for each in the list. The
same goes for the football positions, starting with defensive tackle. The
integer constants are provided in the comments.

enum Baseball
{
Pitcher, // 0
Catcher, // 1
First_Baseman, // 2
Second_Baseman, // 3
Third_Baseman, // 4
Shortstop, // 5
Left_Field, // 6
Center_Field, // 7
Right_Field // 8
};
enum Football
{
Defensive_Tackle, // 0
Edge_Rusher, // 1
Defensive_End, // 2
Linebacker, // 3
Cornerback, // 4
Strong_Safety, // 5
Weak_Safety // 6
};

Then, we could compare `Defensive_End` and `First_Baseman`:

if (Defensive_End == First_Baseman)
{
cout << " Defensive_End == First_Baseman is true"
<< endl;
}
else
{
cout << " Defensive_End != First_Baseman is true"
<< endl;
}

Our result would be nonsense:

Defensive_End == First_Baseman is true

This is because both positions map to an integer value of 2.


A quick fix, and one that was often employed prior to C++11, would be to
reindex each set of enums; eg,

enum Baseball
{
Pitcher = 100,
Catcher, // 101
First_Baseman, // 102
. . .
};

enum Football
{
Defensive_Tackle = 200,
Edge_Rusher, // 201
Defensive_End, // 202
. . .
};

Now, if we compare `Defensive_End` and `First_Baseman`, they will no


longer be equal, because 202 ≠ 102. Still, in large code bases there might be
hundreds of enum definitions, so it would not be out of the question for an
overlap to slip in and cause errors. Enum classes, introduced in C++11,
eliminate this risk.

Enum Classes
A new and more robust way to avoid `enum` overlaps was introduced in
C++11 that eliminates the integer representation altogether. The other
benefits of enums, such as avoiding cryptic numerical codes and larger
string objects, still remain, but the conflicts are avoided by using what is
called an enum class. As an example, we can define bond and futures
contract categories within enum classes, as shown here:

enum class Bond


{
Government,
Corporate,
Municipal,
Convertible
};
enum class Futures_Contract
{
Gold,
Silver,
Oil,
Natural_Gas,
Wheat,
Corn
};
enum class Options_Contract
{
European,
American,
Bermudan,
Asian
};

Notice that we no longer need to manually set integer values to avoid


conflicts as we did with regular enums.
Attempting to compare members of two different enum classes -- such as a
`Bond` and a `Futures_Contract` position, will now result in a compiler
error. For example, the following will not even compile:

if(Bond::Corporate == Futures_Contract::Gold)
{
// . . .
}

This works to our advantage, as it is much better to catch an error at


compile time rather than runtime. Modern best practices now maintain that
we should prefer using enum classes rather than enumerated constants
[[refer to ISO Guidelines]].

Control Structures
Control structures consist of two categories:

Conditional branching, such as `if` statements


Iterative controls that repeat a set of commands in a loop
In C++, the code that pertains to a given condition or sequence is contained
in a block defined by braces. Similar to a function, variables declared
within a block will go out of scope when the block terminates. These
structures can also be nested within one another.
It was assumed in the previous section on enums and enum classes that you
are familiar with the basics of `if` conditions, but here you can read through
a more comprehensive review of conditional and iterative constructs that
will be utilized heavily from here on out. Both depend on logical operators
determining a true or false condition, so before launching into our tour of
control structures, a quick review of logical operators and Boolean types are
in order.
The C++ boolean type, represented represented by `bool`, can store a value
of either `true` or `false`. Behind the scenes, a `bool` type has a size of one
byte and may store only `1` for `true`, or `0` for `false`. Note that `true` and
`false` are not placed in quotations, as ;ole enums they are not character
types. They represent fixed integer values.
The C++ operators for equality and inequalities will return a `bool` type
based on whether the result is true or false. They are as follows:

`<, >` Strict inequality


`<=, >=` Inclusive inequality
`==` Equality
`!=` Not equals
And and Or operations are represented by `&&` and `||` respectively.

Examples will follow in the next section on conditional branching.

Conditional Branching
C++ supports both the usual `if` based logic found in most other languages,
and `switch`/`case` statements that offer a cleaner alternative to multiple
`else if` conditions in special cases.

`if` and Related Conditions


The usual conditional branching statements

`if (condition) then (action)`


`if (condition) then (action), else (default action)`
`if (condition 1) then (action 1),`
`else if (condition 2) then (action 2)`
`...`
`else if (condition n) then (action n)`
`else (default action)`

are represented by the following C++ syntax. Each condition, whether it be


`if`, `else if`, or `else`, the code that gets executed for a `true` condition is
contained within a separate body, indicated by open and closed braces.

// Simple if
if (condition)
{
// action
}
// if/else
if (condition)
{
// action
}
else
{
// default action
}
// if/else if.../else
if (condition 1)
{
// action 1
}
else if (condition 2)
{
// action 2
}
// ...
else if (condition n)
{
// action n
}
else
{
// default action
}
TIP
In conditional statements containing `else if`, it is a best practice to include a default
`else` block at the end. Without it, code may build without any complaints from the
compiler and run just fine, but its execution could very easily result in unexpected
behavior that can cause major headaches in larger and more realistic code bases.

Utilizing the inequality operators introduced above, we can then write some
simple examples with all three variations on the `if` statement theme:

int x = 1;
int y = 2;
int z = 3;

// Simple if
if (x > 0)
{
cout << x << " > 0" << endl;
}
// if/else
if (x >= y)
{
cout << x << " >= " << y << endl;
}
else
{
cout << x << " is less than " << y << endl;
}
// if/else if.../else
if (x == z)
{
cout << x << " == " << z << endl;
}
else if (x < z)
{
cout << x << " > " << z << endl;
}
else if (x > z)
{
cout << x << " < " << z << endl;
}
else
{
cout << "Default condition" << endl;
}

WARNING
Due to the nature of floating point numerical representation and arithmetic, one should
never test for exact equality between two `double` types, nor should floating point types
be compared identically to zero. These cases will be covered later in a separate context.

The operators for logical AND and OR can also be used within conditional
arguments. For example:

#include <cmath>
using std::abs;
using std::exp;

// Simple if
if (x > 0 || y < 1)
{
cout << x << " > 0 OR " << y << " < 1 " << endl;
}
// if/else if.../else
if (x > 0 && y < 1)
{
cout << x << " > 0 AND " << y << " < 1 " << endl;
}
else if (x <= 0 || y >= 1)
{
cout << x << " <= 0 OR " << y << " >= 1 " << endl;
}
else if (z <= 0 || (abs(x) > z && exp(y) < z))
{
cout << z << " <= 0 OR " << endl;
cout << abs(x) << " > " << z << " AND "
<< exp(y) << " < " << z << endl;
}
else
{
cout << "Default condition" << endl;
}

Note that in the last `else if` condition, we put the AND condition inside
round brackets, as OR takes precedence over AND. [cppreference.com]
Finally, we can assign logical conditions to `bool` variables, and used
within `if` conditions, as shown here:

bool cond1 = (x > 0 && y < 1);


bool cond2 = (z <= 0 || (abs(x) > z && abs(y) < z));
if (cond1)
{
cout << x << " > 0 AND " << y << " < 1 " << endl;
}
else if (!cond1)
{
cout << x << " <= 0 OR " << y << " >= 1 " << endl;
}
else if (cond2)
{
cout << z << " <= 0 OR " << endl;
cout << abs(x) << " > " << z << " AND "
<< abs(y) << " < " << z << endl;
}
else
{
cout << "Default condition" << endl;
}

Note that a boolean variable can be negated simply by preceding it with the
`!` operator, as shown in the first `else if` condition above.

WARNING
A common trap is to mistakenly use `=` to test equality instead of `==`. The former is
the assignment operator and will cause unexpected behavior in this case. Be sure to use
`==` when testing for equality.

Ternary `if` Statement


There is also a convenient one-line shortcut for short and sweet `if-else`
combinations. The syntax is as follows:
type `var = ` logical condition `? var_val_true`(if `true`) : `var_val_false`(if
`false`);
In English, this means if _logical condition_ is `true`, assign the value `var`
to `var_val_true`; otherwise, assign it to `var_val_false`.
A code example should make this clearer:

using std::sin;
using std::cos;
int j = 10;
int k = 20;
double theta = 3.14;
double result = j < k ? sin(theta) : cos(theta);

So, in this example, `result` would be assigned the value of sin(3.14), or


approximately zero.

The `switch`/`case` Statement


Also known as just a `switch` statement, this control sequence allows us to
eliminate some of the clutter that comes with multiple `else if` clauses, but
for the particular case of branching on the state of a single integer type, or
alternatively, either an enum that maps to an integer, or an enum class
member.
For each possible `case`, the command that follows the matching state is
executed. As with the `else` condition above, a `default` action should be
provided at the end to catch cases that do not fall into any of the given
categories, or handle the error if no other possibilities are admissible.
As a first example, consider a case where we pretend an integer condition
represents a type of option, and in place of each `cout`, the action would be
to call a corresponding pricing model. This will render our code more
readable and maintainable than using multiple `else if` statements.

void switch_statement(int x)
{
switch (x)
{
case 0:
cout << "European Option: Use Black-Scholes" <<
endl;
break;
case 1:
cout << "American Option: Use a lattice model" <<
endl;
break;
case 2:
cout << "Bermudan Option: Use Longstaff-Schwartz
Monte Carlo" << endl;
break;
case 3:
cout << "Asian Option: Calculate average of the
spot time series" << endl;
break;
default:
cout << "Option type unknown" << endl;
break;
}
}

After each case, the `break` statement instructs the program to exit the
`switch` statement once the corresponding code for a particular state is
executed. So if `x` is `1`, a lattice model would be called to price an
American option, and then control would pass out of the body of the
`switch` statement rather than checking if `x` is `2`.
There are also cases where one might want to drop down to the next step if
the same action is desired for multiple states. For example, in (American)
football, if a drive stalls, the offense punts the ball on fourth down and no
points are scored. If the team scores, however, it might have kicked a field
goal for three points, or scored a touchdown with three possible outcomes:
Miss the extra point(s) -- Result is six points
Kick the extra point -- Result is seven points
Score a two-point conversion -- Result is eight points

No matter how a team scores, it kicks the ball off to their opponent, so for
cases 3, 6, 7, and 8, we just drop down through each case until we hit the
kickoff. This quasi-Bayesian logic could then be implemented with the
following code:

void switch_football(int x)
{
switch (x)
{
case 0: // Drive stalls
cout << "Punt" << endl;
break;
case 3: // Kick field goal
case 6: // Score touchdown; miss extra point(s)
case 7: // Kick extra point
case 8: // Score two-point conversion
cout << "Kick off" << endl;
break;
default:
cout << "Are you at a tennis match?" << endl;
break;
}
}

An obvious pre-C++11 alternative for the `switch` on option pricing `case`s


would be to substitute in the corresponding enums for the integer codes,
thus making the logic even easier to understand for human consumption
(`cout` messages remain the same):

void switch_statement_enum(OptionType ot)


{
switch (ot)
{
case European: // = 0
cout << "European Option: Use Black-Scholes" <<
endl;
break;
case American: // = 1
. . .
case Bermudan: // = 2
. . .
case Asian: // = 3
. . .
default:
cout << "Option type unknown" << endl;
break;
}
}

However, modern ISO Guidelines now favor using enum classes, for the
reasons demonstrated above with integer conflicts. So, we just substitute the
`Options_Contract` enum class into the preceding example to get:

void switch_enum_class_member(Options_Contract oc)


{
switch (oc)
{
case Options_Contract::European:
cout << "European Option: Use Black-Scholes" <<
endl;
break;
case Options_Contract::American:
. . .
case Options_Contract::Bermudan:
. . .
case Options_Contract::Asian:
. . .
default:
cout << "Option type unknown" << endl;
break;
}
}

Iterative Statements
In C++, there are two built-in language features that enable looping logic
and iteration:

`while` and `do...while` loops


`for` loops (including range-based `for` loops)
These iterative commands will execute a repeated block of code over a set
of values or objects based on a fixed count, while a logical condition is true,
or over a range of elements held by a `vector`.

`while` and `do...while` Loops


The essential workflow behind a `while` loop is to repeat a block of code
while a logical expression is `true` (or alternatively, whilst `false`). The
following simple example demonstrates a simple `while` loop, where the
incremented value of an integer is output to the screen while its value
remains strictly less than some fixed maximum value:
int i = 0;
int max = 10;
while (i < max)
{
cout << i << ", ";
++i;
}

Our logical condition is for `i` to be strictly less than the value `max`. As
long as this condition holds, the value of `i` will be incremented
A `do...while` loop is similar, except that by placing the `while` condition at
the end, it guarantees that at least one iteration of the loop will be executed.
For example:

int i = 0;
int max = 10;
do
{
cout << i << ", ";
++i;
} while (i < max);

Note that even if `max` had been set to zero or less, there would still be one
trip through the `do...while` loop, as the maximum condition is not checked
until the end. This is the distinction that separates it from the simpler
`while` loop.
In time, we will see looping examples that involve more interesting
mathematics and financial applications.

The `for` Loop


This construct is another form of iteration over a countable range. The form
that is employed in C++ can be summarized in the following pseudocode
example:

for(initial expression executed only once;


exit condition executed at the beginning of every loop;
loop expression executed at the end of every loop)
{
DoSomeStuff;
}

The syntax here is important, namely the semicolons separating the three
expressions in the `for` argument. Breaking this down into parts a, b, and c,
we would have

for(a; b; c)

Each of these parts is typically dependent on some form of a counter, such


as an `int i` counter as seen in the `while` statement; however, we now
move this index into the argument of the `for` statement, which allows us to
remove the increment from the body of the loop. The (a) part determines
the starting value of the counter, (b) indicates where to stop, and (c)
enforces how the counter is increased or decreased.
For example, we could rewrite the `while` example above using a `for` loop
as follows:

int max = 10;


for(int i = 0; i < max; ++i)
{
cout << i << ", "; // we no longer need ++i in
the body
}

The results will be exactly the same as the those in the `while` loop
examples.
1. There technically is a difference between the pre- and post- increment
operator that can affect other uses, but either `++i` with `i++` in the
`for` will work identically. It is generally preferred to use `++i`
2. It is also legal to have a `for` loop where a decrement (`--`) is used to
decrease the index value down to some minimum value.

`break` and `continue`


In iterative loops, it is sometimes necessary to break out of a loop before a
maximum or minimum index value is attained, or before the specified
logical condition would otherwise terminate the iteration. A prime example
in computational finance is barrier option pricing using Monte Carlo
simulation. The simulation paths will typically have the same number of
time steps; however, in the case of an up-and-out barrier option, for
example, we would need to break out of the loop if the underlying asset
price rose above the barrier level.
This is accomplished by applying the same `break` command as used in
`switch` statements. A simple example is shown here, which also
demonstrates nesting an `if` condition inside a `for` block:

int max = 10;


for (int i = 0; i < 100; ++i)
{
cout << i << ", ";
if (i > max)
{
cout << "Passed i = " << max << "; I'm tired, so let's
go home."
<< endl;
break;
}
}

Once `i` is incremented to 11, the `if` statement is true, so the `break`
command is called, causing the program control to exit the `for` loop.
There is also the `continue` keyword that can be used to continue the
process of the loop, but since this is the default behavior of a loop anyway,
its usefulness is limited.

Nested Loops
In addition to nesting `if` conditions inside loops, it is also possible to nest
iterative blocks inside other blocks, whether they be `for` or `while` loop. In
quantitative programming, it is easy to find oneself writing double and
sometimes even triple nested loops when implementing common numerical
routines and financial models. This type of coding, however, can become
complex and error prone in a hurry, so one needs to take special
precautions, as well as consider alternatives we will take up later.

Range-Based `for` Loops


Prior to C++11, iterating through a `vector` would involve using the index
as the counter, up to the number its elements.

vector<double> v;
// Populate the vector v and then use below:
for(unsigned i = 0; i < v.size(); ++i)
{
// Do something with v[i] or v.at(i). . .
}

Range-based `for` loops, introduced in C++11, make this more functional


and elegant. Instead of explicitly using the `vector` index, a range-based for
loop simply says “for every element `elem` in `v`, do something with it”:

for(auto elem : v)
{
// Use elem, rather than v[i] or v.at(i)
}

As a trivial example, calculate the sum of the elements:

double sum = 0.0;


for(auto elem : v)
{
sum += elem;
}

And we are done. No worries about making a mistake with the index, there
is less to type, and the code more obviously expresses what it is doing. The
ISO Guidelines in fact tell us to prefer using range-based `for` loops with
`vector` objects, as well as other STL containers that will be discussed in
Chapter 7.

Aliases
Aliasing can take on several forms, the first being one of convenience,
namely type aliasing, where commonly used parameterized type names can
be assigned to a shorter and more descriptive alias names.
In addition, reference aliases help to avoid copies of objects being created
when they are passed into functions, often resulting in significant speedup
at runtime.
Pointers can also be considered as aliases, particularly useful for
representing an active object in class design (the `this` pointer in Chapter
4). Pointers (and now smart pointers) can also be used for allocating
memory that persists, but this is a separate and deeper discussion that will
be deferred until Chapter 6.
Both references and pointers can help facilitate the object-oriented
programming concepts of inheritance and composition that will be
presented in subsequent chapters.

Type Aliases
`std::vector<double>` objects are ubiquitous in quantitative code for fairly
obvious reasons. Because it is used so much, it is common to assign a type
alias to it, such as `RealVector`. This better expresses what it is
mathematically, plus we don’t need to bother with as much typing.
Using modern C++, we can define the alias `RealVector` by simply defining
it as follows:

using RealVector = vector<double>;

Then, we could just write, for example:

RealVector v = {3.19, 2.58, 1.06};


v.push_back(2.1);
v.push_back(1.7);
// etc...

As long as the alias is defined before it is used in the code, then it’s fair
game.
Prior to C++11, this application of the `using` command did not exist, so
type aliasing was accomplished by using the `typedef` command; eg,

typedef vector<double> RealVector;

This is also valid C++ and is still found in many modern code bases, but the
`using` form is preferable per the modern ISO Guidelines. The detailed
reason for this is outside the scope of this book, but the upshot is `using`
can be used to define aliases of generic templated types (eg, not just
`double` parameters as above), while `typedef` cannot.

References
A reference, put simply, provides an alias for a variable, rather than a type.
Once a reference is defined, then accessing or modifying it is exactly the
same as using the original variable. A reference is created by placing an
ampersand between the type name and the reference name before assigning
it to the original variable. For example:

int original = 15;


int& ref = original; // int& means "reference to an int"

At this point, both `original` and `ref` would return 15 if accessed in a


function or assigned to another variable. However, reassigning `original` to
12 would also mean `ref` now returns 12. Similarly, reassigning `ref` would
change the value held by `original`:

original = 12; // ref now = 12


ref = 4; // original also now = 4

It is important to note that a reference must be assigned at the same time it


is declared. For example,

int& ozone;

would be nonsense as there is nothing to which it refers, and the code would
fail to compile. Also, once a reference is defined, it cannot be reassigned to
another variable for the remainder of its lifetime.
Using a reference for a plain old numerical type is trivial, but they become
important when passing large objects into a function, so as to avoid object
copy that can decimate a program’s runtime performance.
Suppose we have a `std::vector` containing 2000 option contract objects?
By passing it as a reference into a function, the original object itself can be
accessed without copying it.
There is one caveat, however. Remember that if a reference is modified, so
is the original variable to which it refers. For this reason, one can just make
the reference argument `const`. Then, any attempt to modify the reference
will be prevented by the compiler.
For example, here are two functions that take in a `std::vector<int>` object
as a reference argument. The first one returns the sum of the elements, so
there is no modification of the elements attempted. The second one,
however, attempts to reset each element to twice its value and then sum the
elements. This will result in a compiler error – much better than a runtime
error – and prevent the operations from ever being executed:

// This is OK
using IntVector = std::vector<int>;
int sum_ints(const IntVector& v)
{
int sum = 0;
for (auto elem : v)
{
sum += elem;
}

return sum;
}
int sum_of_twice_the_ints(const IntVector& v)
{
// Will not compile! const prevents modification
// of the elements in the vector v.

int sum = 0;
for (auto elem : v)
{
elem = 2 * elem;
sum += elem;
}

return sum;
}

NOTE
It is also possible to pass a function argument as non-`const` reference, with the intent to
modify it in place. In this case, one would typically make the return type `void`, instead
of returning a modified variable. This is rarely justified anymore in modern C++ due to
return value optimization (RVO). With RVO, objects by default are returned “in place”
rather than as copies from functions. This is now a requirement for compilers per the
ISO standards, beginning with C++11.
One final point about references relates to managed languages such as Java and C#, in
that the default behavior is to pass objects by non-constant reference. In C++ the default
is to pass by value; hence, one must specifically instruct the compiler to expect a
function argument as a reference with the `&`. This is an adjustment that a programmer
needs to make if switching between C++ and a managed language.

Pointers
A pointer in C++ shares some similarities with a reference, in that it can
also be an alias to another variable, but rather than being permanently tied
to a variable throughout its lifetime, a pointer points to a memory address
containing the variable’s contents, and it can be redirected to another
memory address containing another variable.
This unfortunately can be confusing, as a memory address of a variable is
also indicated by the `&` operator, in addition to another operator `*` that is
used to declare a pointer. A simple example illustrates this. First, declare
and assign an integer variable:

int x = 42;

Next, declare a pointer to an integer, using the `*` operator:

int* xp;
This says to create a variable that will be a pointer to an `int` type, but don’t
point to anything specific yet; this comes in the next step:

xp = &x;

The `&` operator in this case means the address of `x`. `xp` now points at
the memory address that contains the contents of `x`, namely 42. Note that
this usage of `&` has a different meaning than declaring a reference.
We can now access the contents of this memory address by dereferencing
`xp` by applying the `*` operator. If we put

std::cout << *xp << std::endl;

the output would be 42, just as if we had applied `std::cout` to the variable
`x`. Note the `*` operator is used in a different context here, accessing the
contents of memory rather than declaring a pointer.
We can also means we can change the value of `x`. For example, putting

*xp = 25;

then both `*xp` and `x` will return the value 25, rather than 42.
It is also possible to reassign the pointer `xp` to a different memory address;
this is not possible to do with a reference. Suppose we have a different
integer variable `y` and we reassign `xp` to point to the address of `y`:

int y = 106;
xp = &y;

Now, `*xp` will return 106 rather than 25, but `x` is still equal to 25.

NOTE
`xp`, as opposed to `*xp`, will return the hexadecimal value that represents the address
of the first byte in memory containing the contents of `y`.
Similar to references, pointers can be used with objects. If we have a class
`SomeClass` with a member function, say `some_fcn`, then we can define a
pointer to a `SomeClass` object:

SomeClass sc;
auto SomeClass* ptr_sc = &sc;

As it’s obvious that `ptr_sc` will point to a `SomeClass` object, we can use
the `auto` keyword without obscuring its context.
Suppose also that `SomeClass` has a member function `some_fcn`. This
function can be invoked by dereferencing `ptr_sc` and then calling it in the
usual way:

(*ptr_sc).some_fcn();

More common, however, is to use the indirection operator, indicated by an


arrow:

ptr_sc->some_fcn();

This is all we will need to know about pointers for now. More specifically,
these examples take place in stack memory, and they are automatically
deleted when the function or control block in which they are defined
terminates. More advanced usage will be presented later.

NOTE
Pointers can also point to memory allocated in heap memory, which allows the value or
object to persist in memory outside the scope of a function or control block. This
becomes relevant in certain situations related to object-oriented programming and
requires and extra care. Moreover, C++11 introduced smart pointers into the Standard
Library. These topics will be presented in Chapter 5.

Function and Operator Overloading


A key feature of C++, as well as other modern programming languages, is
implementing different versions of the same function name, distinguished
by different sets of input arguments. This is known as function overloading.
A related feature that is very convenient to us as quantitative programmers
is operator overloading, where we can define an operation for specific
types, such as vector multiplication. Operator overloading is not supported
in as many languages as function overloading; for example, it exists in C++
and C#, but it is not an option in Java.

Function Overloading
To illustrate function overloading, let’s look at an example of two versions
of a `sum` function, one of which returns a `double` type, while the other
returns a `vector<double>`. The first version is trivial, just summing two
real numbers.

#include <vector>
// . . .
double sum(double x, double y)
{
return x + y;
}

The second version, however, will take in two `std::vector<double>` objects


and return a vector containing the sum of its elements.

std::vector<double> sum(const std::vector<double>& x, const


std::vector<double>& y)
{
// NOTE TO SELF: Can we do this with range-based for
loops(?!)
std::vector<double> vec_sum;
if(x.size() == y.size())
{
for (int i = 0; i < x.size(); ++i)
{
vec_sum.push_back(x.at(i) + y.at(i));
}
}
return vec_sum; // Empty if size of x and y do not
match
}

As we can see, the two functions perform two distinct tasks, and have
different return types, based on the types of arguments.
Overloaded functions can also be distinguished based on the number of
arguments of the same type, as well as return the same type. For example
(trivially), we could define a `sum` function that takes in three real
numbers:

double sum(double x, double y, double z)


{
return x + y + z;
}

Now, if we put in our `main()` function the following,

sum(5.31, 92.26);
sum(4.19, 41.9, 419.0);

the respective overloaded functions will be called.

Operator Overloading
C++ provides the standard mathematical operators for integer and floating
type numerical values. The Standard Library also provides the `+` operator
for `std::string` types, which will concatenate them. However, there are no
operators provided for `std::vector`, for example. So, if we want to compute
an element-by-element sum of two vectors, or calculate a dot product, we’re
on our own.
We could just use the `sum` overload for two vectors as shown above for
vector addition, and write a new function called `dot_product` for vector
multiplication. However, C++ provides us with a more naturally
mathematical approach, namely operator overloading.
For a vector sum, the addition operator replaces the `sum` overload as
shown below. The body of the function remains the same:
std::vector<double> operator + (const std::vector<double>& x, const
std::vector<double>& y)
{
std::vector<double> add_vec;
if (x.size() == y.size())
{
for (unsigned i = 0; i < x.size(); ++i)
{
add_vec.push_back(x.at(i) + y.at(i));
}
}
return add_vec; // Empty vector if x & y sizes not
identical
}

Similarly, for the dot product, which returns a scalar (`double`), overload
the `*` operator:

double operator * (const std::vector<double>& x, const


std::vector<double>& y)
{
double dot_prod = 0.0;
if (x.size() == y.size())
{
for (int i = 0; i < x.size(); ++i)
{
dot_prod += (x[i] * y[i]);
}
}
return dot_prod; // Return 0.0 if size of x and y do
not match
}

Then, for two vectors `x` and `y`, say

std::vector<double> x = {1.1, 2.2, 3.3};


std::vector<double> y = {0.1, 0.2, 0.3};

the overloaded operators would perform vector addition and multiplication:

auto v_sum = x + y; // ans: {1.2, 2.4, 3.6}


auto v_dot = x * y; // ans: 1.54
For `double` types, the compiler knows to apply the language-provided
operators

double s = 1.1 + 0.1; // s = 1.2


double p = 2.2 * 0.2; // p = 0.44

NOTE
1. For simultaneous iteration over two `vector` objects, at this stage we need to
revert to an indexed `for` loop. There are more elegant ways to do this that avoid
the index but require additional background that will be presented in Chapter 7.
2. For the error condition where `x.size() != y.size()`, for now we are simply
returning an empty vector for the vector sum, and 0 for the dot product.
Exceptions would be more appropriate for production code.

As for other examples, if we were to write a `Matrix` class, we would also


want to overload operators `+`, `-`, and `*`. For a `Date` class, we could
define `-` to return the number of days between two dates. Operator
overloading is thus very convenient for mathematical and financial
programming. We will utilize it in various contexts going forward.

Summary
This chapter has covered a fairly lengthy list of topics, starting with the
`std::vector` container class in the Standard Template Library (STL).
`std::vector` is ubiquitous in quantitative programming, for (good) reasons
that will be covered in Chapter 7, along with STL iterators and algorithms
that can make C++ code more elegant, reliable, and efficient. At this point,
however, the goal is to be familiar with `std::vector` as a dynamic array of
real numbers.
Aliases come in three different varieties: type aliases (`using`), references,
and pointers. `using` saves us from having to type out long type names,
such as `RealVector` in place of the oft-used `std::vector<double>`.
References in C++ are mostly used in passing `const` reference objects as
function arguments, avoiding object copying that can degrade performance,
while preventing the object from being modified inside the function.
Pointers have several important applications beyond being mere aliases that
will be presented in due course, along with smart pointers that were added
to the Standard Library beginning with C++11.
Function overloading is a natural fit for mathematical programming, and
operator overloading even more so for objects such as matrices and vectors
that are ubiquitous in quantitative programming. This is another topic that
will be extended in object-oriented programming in Chapter 4.

References
[1] CppReference:
`https://en.cppreference.com/w/cpp/language/operator_precedence]`
[2] Stroustrup 4E (not directly referenced)
About the Author(s)
John Doe does some interesting stuff...

You might also like