Monday, February 9, 2015

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.

Did I mention that the workshop "A Look at QuantLib Usage and Development" I recorded for Quants Hub last October is now available for purchase? Right, I did. But in case you missed it, you can go back and read my last post for links and details. Oh, and I'm not the only QuantLib developer that did that; Ferdinando has recorded one as well—although it has nothing to do with QuantLib...

Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the widgets for that are in the sidebar, at the top right of the page. Also, make sure to check my Training page.

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.
    class Settings : public Singleton<Settings> {
      private:
        class DateProxy : public ObservableValue<Date> {
            DateProxy();
            operator Date() const;
            ...
        };
        ... // more implementation details
      public:
        DateProxy& evaluationDate();
        const DateProxy& evaluationDate() const;
        boost::optional<bool>& includeTodaysCashFlows();
        boost::optional<bool> includeTodaysCashFlows() const;
        ...
    };
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.
    template <class T>
    class ObservableValue {
      public:
        // initialization and assignment
        ObservableValue(const T& t)
        : value(t), observable_(new Observable) {}
        ObservableValue<T>& operator=(const T& t)  {
          value_ = t;
          observable_->notifyObservers();
          return *this;
        }
        // implicit conversions
        operator T() const { return value_; }
        operator boost::shared_ptr<Observable>() const {
          return observable_;
        }
      private:
        T value_;
        boost::shared_ptr<Observable> observable_;
    };
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:
    registerWith(Settings::instance().evaluationDate());
on the other hand, it is possible to use the returned value just like a Date instance, as in:
    Date d2 = calendar.adjust(Settings::instance().evaluationDate());
which triggers an automatic conversion; and on the gripping hand, a simple assignment syntax can be used for setting the evaluation date, as in:
    Settings::instance().evaluationDate() = d;
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 \code{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
    termStructure->discount(t, context);
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.

Liked this post? Share it: