caplet +=0.25 * max( 0, scen[0].libors[0] ‐ STRIKE) * scen[0].disc[0] / scen.numeraire |
while reading the simulated data directly in working memory. All the expensive accounting and look‐up, allocation, and matching maturities to working memory (what we call indexing) were moved to processing time so that only fast arithmetic operations are performed at simulation time, with direct memory access.
Pre‐processing is not limited to indexing simulated data. All variables involved in a script are also pre‐indexed so they are random accessed in memory at simulation time. A statement like
product pays vAlive * vPayoff |
is pre‐processed into something like
V[2] += (V[0] * V[1]) / scen.numeraire |
where the variables are random accessed at run time in some type of array
Pre‐processing is critical to performance. Indexing and other pre‐processing steps enable the evaluation of scripts with speed similar to hard‐coded payoffs. Pre‐processing is facilitated by the framework we develop in part I, with the parsing of scripts into expression trees and the implementation of visitor objects that traverse the expression trees, gathering information, performing actions and maybe modifying scripts, all while maintaining their own internal state.
1.5 VISITORS
Valuation and pre‐processing are two ways in which we visit scripts. Other types of visits include queries, which provide information related to the cash flows, for instance the identification of non‐linearities; or transformations, like the aggregation and compression of schedules of cash‐flow; or decorations, which complement the description of the cash‐flows with the payoff of some value adjustment, as explained in part V. And there are many, many others. There is a visitor for everything.
Visitors are all the objects, like the evaluator and the pre‐processors, that traverse scripts and conduct calculations or actions when visiting its different pieces, while maintaining an internal state. Their internal state is what makes visitors so powerful. It is through their state that visitors accumulate and process information while traversing scripts. Internal state does not mean that visitors cannot be invoked concurrently. In fact, parallel scripting is easily implemented with multiple visitor instances working in parallel in multiple concurrent threads. The scripting library is thread safe as long as common‐sense rules are respected; for example, do not perform parallel work with the same instance of a visitor class.
To make visits possible, we make our scripts visitable. This means that we parse them into a data structure that is designed to be traversed in flexible ways. That data structure is the expression tree discussed in detail, in words and code, in chapter 2. Trees and visitors are the main building blocks of our scripting library.
One key benefit of the visitor pattern is that it facilitates the support of processes that we haven't even thought of yet. Our visitor‐based design caters for needs that will emerge in the future, and enables future development.
For example, we in 2016 worked out an algorithm to automatically smooth all the discontinuities in a transaction in order to stabilize its risk management. This algorithm is based on fuzzy logic and described in detail in part IV. It so happens that an implementation of this algorithm requires the determination of the value domain of all the conditional cash‐flows, which means, for every condition of the type
As another example, we were able to significantly improve the performance of LSM regressions for large xVA calculations by pre‐computing the entire dependency graph of the many thousands of variables involved in the aggregated script for a large netting set. That allowed us to selectively evaluate, during pre‐simulations, only those events that affected the target variable, saving many unnecessary evaluations. And it was relatively straightforward to design a visitor to produce that complete dependency graph.
The visitor‐based design provides a framework for the seamless development of any kind of visitor, now and in the future. The visitor‐based class designed in section 3.1 takes care of the traversal and visit logic so that a concrete visitor is developed simply by specifying what it does when it visits different nodes in the tree. For instance, the variable indexer developed in section 3.3 takes less than ten lines of code despite the apparent complexity of its work. This visitor effectively counts the variables in a script and matches them to an index in an array.
1.6 MODERN IMPLEMENTATION IN C++
Our library, discussed in part I and provided in our source repository, is designed to facilitate the visit of scripted cash‐flows with a convenient internal representation of scripts and a framework for visiting these representations. It revolves around two concepts:
1 Expression trees are our internal representation of the scripts in visitable data structures, produced from text scripts through parsing.
2 Visitors are the objects that traverse the expression trees, maintaining their own state during traversal, extracting information, performing calculations, or even modifying the trees along the way.
Expression trees are covered in chapter 2. Visitors are covered in chapter 3, including the evaluator in section 3.6 and some important pre‐processors in 3.3 and 3.4. Parsing, which turns scripts into expression trees, is covered in the appendix to part I.
We develop our scripting library in self‐contained C++. Other implementations attempted to reuse existing programming languages, like Python, Visual Basic, or C#, to code payoffs instead. In this context, the model, typically written in C++, would generate scenarios and delegate the evaluation of payoffs in given scenarios to code written by users in a simpler language. This is attractive at first sight: it saves developers the trouble of implementing a scripting library, and users the trouble of learning it. It offers the power and versatility of a general purpose programming language for the computation of payoffs, providing maximum flexibility in return for a somewhat increased operational risk. The main problem, however, is that it only