Читать книгу Modern Computational Finance - Antoine Savine - Страница 12

1.4 PRE‐PROCESSING

Оглавление

Pre‐processing is the key optimization that makes the valuation of scripts (almost) as fast as hard‐coded payoffs. Where performance matters most is in the code repeated for a large number of simulations. Monte‐Carlo developers know that maximum performance is achieved by moving as much work as possible before simulations take place: pre‐allocation of working memory, transformation of data, pre‐calculation of quantities that don't directly depend on the simulated variables. We want to perform as much work as possible once, before simulations start, so that the subsequent work conducted repeatedly over scenarios is as limited and as efficient as possible.

Pre‐processing is not only about pre‐computing parts of subsequent evaluations. It is not so much about the optimization of the arithmetic calculations performed during simulations. It is mainly about moving most of the administrative, logistic overhead to processing time. CPUs perform mathematical calculations incredibly fast, but the access of data in memory may be slow if not carefully dealt with. With hard‐coded payoffs (payoffs coded in C++), it is the compiler that optimizes the code, putting all the right data in the right places in the interest of performance. With scripting, the payoff, as a function of the scenario, is built at run time. The compiler cannot optimize this function for us. This is our responsibility. This is where the pre‐processors come into play.

A special breed of pre‐processors called indexers arrange data for the fastest possible access during simulations, with a massive performance impact. For instance, when some cash‐flow is related to a given Libor rate of a given maturity, the indexer “informs” the evaluator, before simulations start, where in pre‐allocated memory that Libor will live during simulations, so that, at that point, the evaluator reads the simulated Libor there, directly, without any kind of expensive lookup.

Most noticeable benefits are achieved in the context of interest rates (and multiple assets) discussed in section III. The dates when coupons are fixed or paid and the maturities of the simulated data such as discount factors and Libors are independent of scenarios. The value of these simulated variables may be different in every scenario, but their specification, including maturity, is fixed. Pre‐processors identify what rates are required for what event dates, and perform memory allocation, indexing, and other logistics before simulations start. During simulations, where performance matters most, the model communicates the values of the simulated data in a pre‐indexed array for a fast, random access.

This is covered and clarified in part III, but we introduce an example straight away. Consider a script for a caplet:

01Jun2021 caplet pays 0.25 * max( 0, libor( 03Jun2021, 3m, act/360, L3) ‐ STRIKE) on 03Sep2021

The payoff of this caplet on 03Jun2021 is:


The responsibility of the model is to generate a number of joint scenarios for the Libor and discount.7 The responsibility of the evaluator is to compute the payoff above from these two values.

While this is all mathematically very clear, a practical implementation is more challenging. The product is scripted at run time, so we don't know, at compile time, what simulation data exactly the evaluator expects from the model. Dedicated data structures must be designed for that purpose, something like (in pseudo‐code):

Scenario := vector of SimulData per event date SimulData := numeraire, vector of libors, vector of discounts, …

In our simple example, we have one event date: 01Jun2021, and we need one Libor fixed on the event date for a loan starting two business days later on 03Jun2021, with coupon paid on 03Sep2021, as well as one discount factor for the payment date. We humans know that from reading the script. A pre‐processor figures that out by visiting the script at processing time. This allows not only to pre‐allocate the vectors of Libors and discounts but also to transform the payoff into something like:

0.25 * max( 0, scen[0].libors[0] ‐ STRIKE) * scen[0].disc[0] / scen.numeraire

The pre‐processor also “knows” that event date 0 is 01Jun2021, and that maturity 0 is 03Sep2021 for both Libors and discounts. That information may be communicated to the model before simulations start. The model then knows what it has to simulate and where in pre‐allocated memory it must write the simulated data. The evaluator doesn't know what that simulated data is; in particular, it doesn't know what are the maturities of the Libors and discounts. All it does is execute the expression:

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‐allocated at pre‐processing time, where variables are counted and their names are matched to an index in . Variable indexing is explained in words and code in section 3.3.

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.

Modern Computational Finance

Подняться наверх