Odds and ends: global settings
Hello everybody.
This week, a section from the book appendix; namely, the one on the
infamous Settings
class. Feedback would be particularly appreciated on
this one.
Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.
Odds and ends: global settings
The Settings
class, outlined in the listing below, is a singleton
(I’ll cover this pattern in a future post) that holds information
global to the whole library.
Most of its data are flags that you can look up in the official documentation, or that you can simply live without; the one piece of information that you’ll need to manage is the evaluation date, which defaults to today’s date and is used for the pricing of instruments and the fixing of any other quantity.
This poses a challenge: instruments whose value can depend on the
evaluation date must be notified when the latter changes. This is done
by returning the corresponding information indirectly, namely, wrapped
inside a proxy class; this can be seen from the signature of the
relevant methods. The proxy inherits from the ObservableValue
class
template (outlined below) which is implicitly convertible to
Observable
and overloads the assignment operator in order to notify
any changes. Finally, it allows automatic conversion of the proxy
class to the wrapped value.
This allows one to use the facility with a natural syntax. On the one hand, it is possible for an observer to register with the evaluation date, as in:
on the other hand, it is possible to use the returned value just like
a Date
instance, as in:
which triggers an automatic conversion; and on the gripping hand, a simple assignment syntax can be used for setting the evaluation date, as in:
which will cause all observers to be notified of the date change.
Of course, the elephant in the room is the fact that we have a global
evaluation date at all. The obvious drawback is that one can’t perform
two parallel calculations with two different evaluation dates, at
least in the default library configuration; but while this is true, it
is also a kind of red herring. On the one hand, there’s a compilation
flag that allows a program to have one distinct Settings
instance per thread (with a bit of work on the part of the user) but
as we’ll see, this doesn’t solve all the issues. On the other hand,
the global data may cause unpleasantness even in a single-threaded
program: even if one wanted to evaluate just an instrument on a
different date, the change will trigger recalculation for every other
instrument in the system when the evaluation date is set back to its
original value.
This clearly points (that is, quite a few smart people had the same idea when we talked about it) to some kind of context class that should replace the global settings. But how would one select a context for any given calculation?
It would be appealing to add a setContext
method to the Instrument
class, and to arrange things so that during calculation the instrument
propagates the context to its engine and in turn to any term
structures that need it. However, I don’t think this can be
implemented easily.
First, the instrument and its engine are not always aware of all the term structures that are involved in the calculation. For instance, a swap contains a number of coupons, any of which might or might not reference a forecast curve. We’re not going to reach them unless we add the relevant machinery to all the classes involved. I’m not sure that we want to set a context to a coupon.
Second, and more important, setting the context for an engine would be
a mutating operation. Leaving it to the instrument during calculations
would execute it at some point during the call to its NPV
method,
which is supposed to be const
. This would make it way too easy to
trigger a race condition; for instance with a harmless-looking
operation such as using the same discount curve for two instruments
and evaluating them at different dates. A user with a minimum of
experience in parallel programming wouldn’t dream of, say, relinking
the same handle in two concurrent threads; but when the mutation is
hidden inside a const
method, she might not be aware of it. (But
wait, you say. Aren’t there other mutating operations possibly being
done during the call to NPV
? Good catch: see the aside at the end of
this post.)
So it seems that we have to set up the context before starting the calculation. This rules out driving the whole thing from the instrument (because, again, we would be hiding the fact that setting a context to an instrument could undo the work done by another that shared a term structure with the first) and suggests that we’d have to set the context explicitly on the several term structures. On the plus side, we no longer run the risk of a race in which we unknowingly try to set the same context to the same object. The drawbacks are that our setup just got more complex, and that we’d have to duplicate curves if we want to use them concurrently in different contexts: two parallel calculations on different dates would mean, for instance, two copies of the overnight curve for discounting. And if we have to do this, we might as well manage with per-thread singletons.
Finally, I’m skipping over the scenario in which the context is passed but not saved. It would lead to method calls like
which would completely break caching, would cause discomfort to all parties involved, and if we wanted stuff like this we’d write in Haskell.
To summarize: I hate to close the section on a gloomy note, but all is not well. The global settings are a limitation, but I don’t have a solution; and what’s worse, the possible changes increase complexity. We would not only tell a first-time user looking for the Black-Scholes formula that she needs term structures, quotes, an instrument and an engine: we’d also put contexts in the mix. A little help here?
Aside: more mutations than in a B-movie.
Unfortunately, there are already a number of things that change during
a call to the supposedly const
method Instrument::NPV
.
To begin with, there are the arguments
and results
structures
inside the engine, which are read and written during calculation and
thus prevent the same engine to be used concurrently for different
instruments. This might be fixed by adding a lock to the engine (which
would serialize the calculations) or by changing the interface so that
the engine’s calculate
method takes the arguments
structure as a
parameter and returns the results
structure.
Then, there are the mutable data members of the instrument itself, which are written at the end of the calculation. Whether this is a problem depends on the kind of calculations one’s doing. I suppose that calculating the value of the instrument twice in concurrent threads might just result in the same values being written twice.
The last one that comes to mind is a hidden mutation, and it’s probably the most dangerous. Trying to use a term structure during the calculation might trigger its bootstrap, and two concurrent ones would trash each other’s calculations. Due to the recursive nature of the bootstrap, I’m not even sure how we could add a lock around it. So if you do decide to perform concurrent calculations (being careful, setting up everything beforehand and using the same evaluation date) be sure to trigger a full bootstrap of your curves before starting.