Appendix

Quantlab types and C++

This table shows the types currently in the Quantlab language. The “Type” column presents the type name in Quantlab. The “Identifier” column shows the string used for declaring a parameter or a return value using the arg_spec function. The “Representation” column is how the type is practically represented when used as parameters and return values in your code. The “Test” column presents the testing function to see if a value is valid or not, used when elements are extracted from series, vectors and so on. Strings and handles are pointers that will be Null if they are not valid, so the only specific Quantlab testing functions are valid and valid_date, all others are tested with the common if statement.

Type

Identifier for arg_spec

Representation

Test

number

number

double

valid

date

date

double

valid_date

logical

logical

double

valid

algorithm

algorithm

handle

if

asset_swap_result

asset_swap_result

handle

if

calendar

calendar

handle

if

curve

curve

handl

if

date_range

date_range

handle

if

fit_result

fit_result

handle

if

instr_def

instr_def

handle

if

instrument

instrument

handle

if

lable_date

lable_date

handle

if

lable_number

lable_number

handle

if

model

model

handle

if

number_range

number_range

handle

if

point_date

point_date

handle

if

point_number

point_number

handle

if

weight_scheme

weight_scheme

handle

if

asset_swap_type

asset_swap_type

string

if

curve_name

curve_name

string

if

day_count_method

day_count_method

string

if

instr_class_name

instr_class_name

string

if

instrument_name

instrument_name

string

if

quote_side

quote_side

string

if

rate_type

rate_type

string

if

string

string

string

if

vector of <type>

vector(<type>)

handle

if

matrix of <type>

matrix(<type>)

handle

if

series of <type>

series(<type>)

handle

if

series of vectors of <type>

series(vector(<type>))

handle

if

series of matrices of <type>

series(matrix(<type>))

handle

if

Interface

Return types

QL type

C++ type

QL api function

Example

void

void

logical

QL::logical_t

integer

QL::int_t

number

QL::number_t

date

QL::date_t

boost::gregorian::date d; return d.is_not_a_date() ? QL::null_date() : QL::date(d.year(), d.month(), d.day());

timestamp

QL::timestamp_t

boost::posix_time::ptime t; return t.is_not_a_date_time() ? QL::null_timestamp : QL::timestamp( t->date().year(), t->date().month(), t->date().day(), t->t ime_of_day().hours(), t->tim e_of_day().minutes(), t->tim e_of_day().seconds(), 0);

enum

QL::enum_t

string

QL::handle

QL::create_string(const wchar_t *) or QL::create_string(const char *)

return QL::create_string(L"HELLO")

object

QL::handle_t<>

return my_space::my::create()

vector(logical)

QL::handle

QL::create_v_l(size) and QL::set_v_l(object&, ix, logical_t)

return QL::set_v_t(*vec_obj, ix, true)

vector(integer)

QL::handle

QL::create_v_i(size) and QL::set_v_i(object&,ix, int_t)

return QL::set_v_t(*vec_obj, ix, 45)

vector(number)

QL::handle

QL::create_v_n(size) and QL::set_v_n(object&, ix, number_t)

return QL::set_v_t(*vec_obj, ix, 45.345345)

vector(date)

QL::handle

QL::create_v_d(size) and QL::set_v_d(object&, ix, date_t)

return QL::set_v_t(*vec_obj,ix, NR::date_t(y, m, d).nr())

vector(timestamp)

QL::handle

QL::create_v_t(size) and QL::set_v_t(object&, ix, timestamp_t)

return QL::set_v_t(*vec_obj, ix, NR::timestamp_t(NR::date_t(y, m, d), hh, mm, ss, ms).nr())

vector(string)

QL::handle

QL::create_v_s(size) and QL::set_v_s(object&, ix, object *)

return QL::set_v_s(*vec_obj, ix, &(*QL::create_string(L"HELLO")))

vector(object)

QL::handle

QL::create_v_o(size) and QL::set_v_o(object&, ix, object *)

QL::set_v_o(*vec_obj,ix, my_obj.get())

Argument Types

QLtype

C++ type (normal argument)

C++ type (output argument)

C++ type (nullable argument) (Also works for normal arguments)

Comments

logical

Q L::logical_t

QL::logical_t &

QL::logical_t

integer

QL::int_t

QL::int_t &

QL::int_t

number

QL::number_t

QL::number_t &

QL::number_t

date

QL::date_t

QL::date_t &

QL::date_t

timestamp

QL::timestamp_t

QL::timestamp_t &

QL::timestamp_t

enum

QL::enum_t

QL::enum_t &

QL::enum_t

string

const QL::object &

QL::handle &

const QL::object *

const can only be used when the object will not be updated or reference counted in your QLL

object

const QL::object &

QL::handle &

const QL::object *

const can only be used when the object will not be updated or reference counted in your QLL

vector(...)

const QL::object &

QL::handle &

const QL::object *

const can only be used when the object will not be updated or reference counted in your QLL

Converting to C++ string

const char* QL::get_string(const QL::object&) // does a checked_cast to string
const char* QL::get_string_0(const QL::object*) // returns null pointer if null

Converting argument vector to C++ vector

Get the number of elements, to create the corresponding C++ vector:

int_t get_v_size(const object&)

Extract the elements using one of the following functions, where all functions have arguments (const object&, int_t); the first argument being the vector and the second the index.

Function

Return type

QL type

get_v_i

QL::int_t

integer

get_v_n

QL::number_t

number

get_v_l

QL::logical_t

logical

get_v_d

QL::date_t

date

get_v_m

QL::month_t

month

get_v_t

QL::timestamp_t

timestamp

get_v_e

QL::enum_t

enum

get_v_s

QL::handle

string

get_v_o

QL::handle

object

get_v_f

QL::handle

function

Example (vector of strings):

int n = QL::get_v_size(val);
vector<string> val_vec(n);

for (int i = 0; i < n; ++i)
{
   QL::handle string_h = QL::get_v_s(val, i);

   if (string_h)
   {
       // Ignore null strings, since std:string cannot be null
       // Had we used char\* instead of std:string, we could have called
       // QL::get_string_0 and gotten a null pointer if string was null.

       val_vec[i] = QL::get_string(*string_h);
   }
}

Functions and member functions

 QL::arg_spec ret;
 QL::arg_spec args[<size>]; // the Array must be big enough to handle all your function declarations

 void my_func(<arg1>, <arg2>...)

 r  = QL::arg_spec("<QLang argument type>");  // e.g QL::arg_spec("void")

 args[<index number>] = QL::arg_spec("<QLang argument type>", "<Argument name in QLang>");
// e.g QL::arg_spec("string", "name")

 QL::add_func(ctxt,
              reinterpret_cast<QL::function_t*>(my_func),
              "my_ql_function",
              category,
              ret,
              <number of arguments>,
              args);

void my_member_func(myspace::my &m, <arg1>, <arg2>...)

 r  = QL::arg_spec("<QLang argument type>");  // e.g QL::arg_spec("void")

 args[<index number>] = QL::arg_spec("<QLang argument type>",
                                     "<Argument name in QLang>"); // e.g QL::arg_spec("string", "name")

 QL::add_mem_func(ctxt,
                  reinterpret_cast<QL::function_t*>(my_member_func),
                  "my_ql_obj",
                  "my_ql_function",
                  category,
                  ret,
                  <number of arguments>,
                  args);

Exceptions

Very important to catch all exceptions generated in your QLL or from libraries that you have used (e.g. C++ Windows Standard Libary) or Quantlab will crash.

You must not though catch std::bad_alloc since Quantlab will handle that

Example code

my.h

#pragma once

#include <QL/api/ql.h>

namespace myspace {
class my;
     typedef QL::handle_t<my> my_p;
 }

 class myspace::my : public QL::object
 {
     private:
         explicit my() {}
     public:
         ~my() {}
     private:
         my(const my&);
         my &operator=(const my &);
     public:
         static my_p create()
         {
             return my_p(new my());
         }

         void set_name(const char *s)
         {
             name.assign(s);
         }
     private:
         std::string name;
 };

my.cpp

#include "my.h"

static const char category[] = "My Utilities";

static myspace::my_p create_my()
{
    try
    {
        myspace::my_p m = myspace::my::create();
        return m;
    }
    catch (const std::bad_alloc &)
    {
        throw;
    }
    catch (const std::exception &e)
    {
        throw QL::error(QL::E_UNSPECIFIC, e.what());
    }
}

QL::handle my_hello(myspace::my &m)
{
    try
    {
        return QL::create_string("HELLO WORLD");
    }
    catch (const std::bad_alloc &)
    {
        throw;
    }
    catch (const std::exception &e)
    {
        throw QL::error(QL::E_UNSPECIFIC, e.what());
    }
}

void my_set_name(myspace::my &m, QL::object &name)
{
    if (strlen(QL::get_string(name)) > 50)
       throw QL::error(QL::E_INVALID_ARG, "Too long name");

    m.set_name(QL::get_string(name));
}

__declspec(dllexport) void ql_init(QL::symtab &s)
{
    QL::symtab &ctxt = QL::add_module(s, "my_util");

    QL::arg_spec r;
    QL::arg_spec a[12];

    QL::add_object_type(ctxt, "my", 0, category);

    r  = QL::arg_spec("my");
    QL::add_func(ctxt,
                 reinterpret_cast<QL::function_t*>(create_my),
                 "my",
                 category,
                 r,
                 0,
                 a);

    r  = QL::arg_spec("string");

    QL::add_mem_func(ctxt,
                     reinterpret_cast<QL::function_t*>(my_hello),
                     "my",
                     "hello",
                     r,
                     0,
                     a);

    r  = QL::arg_spec("void");

    a[0] = QL::arg_spec("string");

    QL::add_mem_func(ctxt,
                     reinterpret_cast<QL::function_t*>(my_set_name),
                     "my",
                     "set_name",
                     r,
                     1,
                     a);
}

API specific functions in ql_base.h

This table presents the functions in the header file ql_base, as these are mostly only available through the Quantlab API and cannot be accessed using QLang.

API function

Header filer

Description

valid

ql_base

Tests if a number or logical is valid.

invalid_number

ql_base

Returns an invalid number.

throw_error

ql_base

Throws an error that is caught by Quantlab.

get_v_size

ql_base

Returns the size of a vector.

get_m_size

ql_base

Returns the number of rows and columns of a matrix.

get_s_dims

ql_base

Returns the number of ranges on which a series is based.

get_s_x0

ql_base

Returns the starting number, date or timestamp of one of the ranges of a series.

get_s_dx

ql_base

Returns the step length of one of the ranges of a series.

get_s_size

ql_base

Returns the number of elements of one of the ranges of a series.

get_s_range_number

ql_base

Tests if a range is a number range.

get_s_range_date

ql_base

Tests if a range is a date range.

get_s_range_timestamp

ql_base

Tests if a range is a timestamp range.

get_s_total_size

ql_base

Returns the total number of elements of a series, a multiplication of the sizes of all ranges.

get_s_x

ql_base

Returns the range number, date or timestamp corresponding to one element of a series.

get_sv_v_size

ql_base

Returns the size of each vector in a series of vectors.

get_sm_m_size

ql_base

Returns the number of rows and columns of each matrix in a series of matrices.

get_v_num

ql_base

Returns one element of a vector of numbers.

get_v_obj

ql_base

Returns one element of a vector of objects.

get_m_num

ql_base

Returns one element of a matrix of numbers.

get_m_obj

ql_base

Returns one element of a matrix of objects.

get_s_num

ql_base

Returns one element of a series of numbers.

get_s_obj

ql_base

Returns one element of a series of objects.

get_sv_num

ql_base

Returns one element of a series of vectors of numbers.

get_sv_obj

ql_base

Returns one element of a series of vectors of objects.

get_sm_num

ql_base

Returns one element of a series of matrices of numbers.

get_sm_obj

ql_base

Returns one element of a series of matrices of objects.

create_v_num

ql_base

Creates a vector of numbers.

create_v_obj

ql_base

Creates a vector of objects.

create_m_num

ql_base

Creates a matrix of numbers.

create_m_obj

ql_base

Creates a matrix of objects.

create_num_range

ql_base

Creates a number range.

create_date_range

ql_base

Creates a date range.

create_timestamp_range

ql_base

Creates a timestamp range.

create_s_num

ql_base

Creates a series of numbers.

create_s_obj

ql_base

Creates a series of objects.

create_sv_num

ql_base

Creates a series of vectors of numbers.

create_sv_obj

ql_base

Creates a series of vectors of objects.

create_sm_num

ql_base

Creates a series of matrices of numbers.

create_sm_obj

ql_base

Creates a series of matrices of objects.

set_v_num

ql_base

Sets one element of a vector of numbers.

set_v_obj

ql_base

Sets one element of a vector of objects.

set_m_num

ql_base

Sets one element of a matrix of numbers.

set_m_obj

ql_base

Sets one element of a matrix of objects.

set_s_num

ql_base

Sets one element of a series of numbers.

set_s_obj

ql_base

Sets one element of a series of objects.

set_sv_num

ql_base

Sets one element of a series of vectors of numbers.

set_sv_obj

ql_base

Sets one element of a series of vectors of objects.

set_sm_num

ql_base

Sets one element of a series of matrices of numbers.

set_sm_obj

ql_base

Sets one element of a series of matrices of objects.

Example “pair”: Adding an object type and its member functions

This is an example showing how to add the object type “pair” to Quantlab with a creation function and member functions. The “pair” object is simply a container of two numbers, created in Quantlab using the pair function. The two components can be extracted using the member functions first and last on the pair object.

#include "ql.h"

namespace
{

   class pair;
   typedef QL::handle_t<const pair> pair_p;

   // Declaring the pair class from the basic QL object class.

   class pair : public QL::object
   {
       // Declaring a creation function.
       public:
           static pair_p create(double first, double last)

       {
           return pair_p(new pair(first, last));
       }

       // Hiding the object creation to avoid reference problems.
       private:
           pair(double first, double last) : first(first), last(last) {}

       public:
           double first;
           double last;
       };
   }

   /* Creates a pair object. */
   static void create_pair(pair_p &result, double first, double last)
   {
       result = pair::create(first, last);
   }

   /* Extracts the first element of a pair. */
   static double pair_first(const pair_p &p)
   {
       return p->first;
   }

   /* Extracts the last element of a pair. */
   static double pair_last(const pair_p &p)
   {
       return p->last;
   }

   /*
   * Adds the pair object type, a pair creation function and two member functions
   * to the Quantlab context.
   */

   __declspec(dllexport) void ql_init(QL::symtab &ctxt)
   {
       QL::arg_spec r;
       QL::arg_spec a[12];

       QL::add_object_type(ctxt, "pair");

       r = QL::arg_spec("pair");

       a[0] = QL::arg_spec("number", "first");
       a[1] = QL::arg_spec("number", "last");

       QL::add_func(ctxt,
                    reinterpret_cast<QL::function_t*>(create_pair),
                    "pair",
                    "Pairs of numbers",
                    r,
                    2,
                    a);

       r = QL::arg_spec("number");

       QL::add_mem_func(ctxt,
                        reinterpret_cast<QL::function_t*>(pair_first),
                        "pair",
                        "first",
                        r,
                        0,
                        a);

       QL::add_mem_func(ctxt,
                        reinterpret_cast<QL::function_t*>(pair_last),
                        "pair",
                        "last",
                        r,
                        0,
                        a);

}

A series example

This is an example of how to access and use a series. The function my_average will calculate the average of a given series.

#include "ql.h"

static const char category[] = "Series example";

static double average(const QL::handle &s)
{
   int size = QL::get_s_total_size(s);

   int count = 0;
   double sum = 0;

   for (int i = 0 ; i < size ; i++)
   {
       double s_i = QL::get_s_num(s, i);

       if (QL::valid(s_i))
       {

       sum += s_i;

       ++count;
       }
   }
      return sum / count;
   }

__declspec(dllexport) void ql_init(QL::symtab &ctxt)
{
   QL::arg_spec r;
   QL::arg_spec a[12];

   r = QL::arg_spec("number");
   a[0] = QL::arg_spec("series(number)", "s");

   QL::add_func(ctxt,
                reinterpret_cast<QL::function_t*>(average),
                "my_average",
                category,
                r,
                1,
                a);
}

Blending functions

This is an example of how to blend yield curves. In a typical case you want to combine deposit rates, short-term futures or FRA:s (below, both are called FRA:s) and swap rates to form a complete yield curve. The number of deposit rates will vary depending on the time to the fixing of the first FRA. In the transition from deposit to FRA:s the standard method is to interpolate between the two nearest deposit rates in order to construct a loan that matures on the settlement date of the FRA. This is done by calling the Quantlab function curve_chop_long_interp. In this example, four different functions on this theme are created.

As an alternative, these functions can be created in the Quantlab language directly, which is shown in Blending functions: Corresponding QLang implementation.

#include "ql.h"

static const char category[] = "Curve blending";

static double min_maturity(const QL::handle &curve)
{
   QL::handle v = QL::curve_instruments(curve);
   int        n = QL::get_v_size(v);
   return QL::instr_maturity(QL::get_v_obj(v, 0));
}

static double max_maturity(const QL::handle &curve)
{
   QL::handle v = QL::curve_instruments(curve);
   int        n = QL::get_v_size(v);
   return QL::instr_maturity(QL::get_v_obj(v, n - 1));
}

static double min_settle(const QL::handle &curve)
{
   QL::handle v = QL::curve_instruments(curve);
   int n = QL::get_v_size(v);

   double min, tmp;
   min = QL::instr_settle_date(QL::get_v_obj(v, 0));

   for (int i = 1; i < n ; i++)
   {
       tmp = QL::instr_settle_date(QL::get_v_obj(v, i));

       if (min > tmp)
         min = tmp;
   }
   return min;
}

static void blend_depo_swap(QL::handle &result,
                            const QL::handle &depo_curve,
                            const QL::handle &swap_curve,
                            const QL::handle &ctxt)
{
   if (QL::curve_empty(swap_curve))
       result = depo_curve;
   else
   {
       double d = min_maturity(swap_curve) - 1;

       QL::handle c_short = QL::curve_chop_long(depo_curve, d);

       result = QL::curve_merge(c_short, swap_curve);
   }
}

static void blend_depo_fra(QL::handle &result,
                           const QL::handle &depo_curve,
                           const QL::handle &fra_curve,
                           const QL::handle &ctxt)
{
   if (QL::curve_empty(fra_curve))
     result = depo_curve;
   else
   {
       double d = min_settle(fra_curve);

       QL::handle c_short = QL::curve_chop_long_interp(depo_curve, d, ctxt);

       result = QL::curve_merge(c_short, fra_curve);
   }
}

static void blend_fra_prio(QL::handle &result,
                           const QL::handle &depo_curve,
                           const QL::handle &fra_curve,
                           const QL::handle &swap_curve,
                           const QL::handle &ctxt)
{
   if (QL::curve_empty(fra_curve))
     blend_depo_swap(result, depo_curve, swap_curve, ctxt);
   else
   {
     double d;

     QL::handle c_short, c_long;

     d = min_settle(fra_curve);

     c_short = QL::curve_chop_long_interp(depo_curve, d, ctxt);
     c_short = QL::curve_merge(c_short, fra_curve);

     d = max_maturity(c_short) + 1;

     c_long = QL::curve_chop_short(swap_curve, d);
     result = QL::curve_merge(c_short, c_long);
   }
}

static void blend_swap_prio(QL::handle &result,
                            const QL::handle &depo_curve,
                            const QL::handle &fra_curve,
                            const QL::handle &swap_curve,
                            const QL::handle &ctxt)
{
   if (QL::curve_empty(swap_curve))
       blend_depo_fra(result, depo_curve, swap_curve, ctxt);
   else
   {
       double d;

       QL::handle c_mid, c_short;

       d = min_maturity(swap_curve) - 1;

       c_mid = QL::curve_chop_long(fra_curve, d);

       if (QL::curve_empty(c_mid))
           blend_depo_swap(result, depo_curve, swap_curve, ctxt);
       else
       {
           d = min_settle(c_mid);

           c_short = QL::curve_chop_long_interp(depo_curve, d, ctxt);

           c_short = QL::curve_merge(c_short, c_mid);

           result = QL::curve_merge(c_short, swap_curve);
       }
   }
}

__declspec(dllexport) void ql_init(QL::symtab &ctxt)
{

   QL::arg_spec r;
   QL::arg_spec a[12];

   r = QL::arg_spec("curve");

   a[0] = QL::arg_spec("curve", "depo_curve");
   a[1] = QL::arg_spec("curve", "swap_curve");
   a[2] = QL::ctxt_arg();

   QL::add_func(ctxt,
                reinterpret_cast<QL::function_t*>(blend_depo_swap),
                "blend_curves_depo_swap",
                category,
                r,
                3,
                a);

   r = QL::arg_spec("curve");

   a[0] = QL::arg_spec("curve", "depo_curve");
   a[1] = QL::arg_spec("curve", "fra_curve");
   a[2] = QL::ctxt_arg();

   QL::add_func(ctxt,
                reinterpret_cast<QL::function_t*>(blend_depo_fra),
                "blend_curves_depo_fra",
                category,
                r,
                3,
                a);

   r = QL::arg_spec("curve");

   a[0] = QL::arg_spec("curve", "depo_curve");
   a[1] = QL::arg_spec("curve", "fra_curve");
   a[2] = QL::arg_spec("curve", "swap_curve");
   a[3] = QL::ctxt_arg();

   QL::add_func(ctxt,
                reinterpret_cast<QL::function_t*>(blend_fra_prio),
                "blend_curves_fra_prio",
                category,
                r,
                4,
                a);

   QL::add_func(ctxt,
                reinterpret_cast<QL::function_t*>(blend_swap_prio),
                "blend_curves_swap_prio",
                category,
                r,
                4,
                a);

}

Blending functions: Corresponding QLang implementation

This is an example of how to write the blending functions in Blending functions, using the Quantlab language only. It is stated here for comparison to the code in Blending functions.

curve blend_curves_depo_swap(curve depo_c, curve swap_c)
  option(category: 'Curve blending')
{
   if (swap_c.empty())
      return depo_c;
   else
   {
     depo_c = depo_c.chop_long(v_min(swap_c.instruments.maturity) - 1);

     return merge(depo_c, swap_c);
   }
}

curve blend_curves_depo_fra(curve depo_c, curve fra_c)
 option(category: 'Curve blending')
{
   if (fra_c.empty())
     return depo_c;
   else
   {
       depo_c = depo_c.chop_long_interp(v_min(fra_c.instruments.settle_date));

       return merge(depo_c, fra_c);
   }
}

curve blend_curves_fra_prio(curve depo_c, curve fra_c, curve swap_c)
  option(category: 'Curve blending')
{

   if (fra_c.empty())
       return blend_curves_depo_swap(depo_c, swap_c);
   else
   {
       date chop_d;
       curve short_c;

       chop_d = v_min(fra_c.instruments.settle_date);
       depo_c = depo_c.chop_long_interp(chop_d);
       short_c = merge(depo_c, fra_c);
       chop_d = v_max(short_c.instruments.maturity) + 1;
       swap_c = swap_c.chop_short(chop_d);

       return merge(short_c, swap_c);
   }
}

curve blend_curves_swap_prio(curve depo_c, curve fra_c, curve swap_c)
 option(category: 'Curve blending')
{
   if (swap_c.empty())
       return blend_curves_depo_fra(depo_c, fra_c);
   else
   {
       fra_c = fra_c.chop_long(v_min(swap_c.instruments.maturity) - 1);

       if (fra_c.empty())
           return blend_curves_depo_swap(depo_c, swap_c);
       else
       {
           date chop_d;

           chop_d = v_min(fra_c.instruments.settle_date);
           depo_c = depo_c.chop_long_interp(chop_d);

           return merge(depo_c, merge(fra_c, swap_c));
       }
   }
}

Exponentially weighted series functions

This is an example of how to calculate exponentially weighted versions of the series functions variance, covariance and correlation. It is also an example of how to make the difference between the Quantlab interface functions and the normal C++ functions not using QLang types. The source code can be viewed by opening the exponential_roll project, and it is not printed here due to its length.

The functions added to QLang by the dll all take one or more series plus a decay factor and an optional tolerance level. The first step is to check that the series are one-dimensional and that they are based on the same range. After that, the series are translated into vectors (or matrices) defined for this project in order to move away from the QLang environment. In the translation process the “holes” (null values) in the series are taken care of, making the resulting vectors tested and clean. The weight vector is also filled with exponential weights at this step. The final step is to call the QLang-independent function covariance_weighted, with the translated and tested input data. Covariance_weighted returns the covariance between two series, and using this information the QLang return objects are filled with appropriate data for transport back to the Quantlab environment.

This implementation ignores the index (“day”) of a set of series where there is one or more “holes”, removing them in the translation process. It will also cut off the calculations when the weight of the remaining indices becomes smaller than the tolerance level. For example, with a tolerance level of 1% (0.01, the default) and a decay factor of 0.97, calculations are based on the last 151 elements of the translated series as the weights of 152 to infinity accounts for less than 1% of the total weight.

The exponential weight vector is constructed from the end. Starting at the ending element with the weight of the decay factor, the element one step towards the beginning of the vector has this weight multiplied by the decay factor. This continues for every element until the start of the weight vector. The main mathematical advantage of this weight scheme is that if one value has been calculated for a series, it is extremely easy to calculate the value of the series with one more element added. This advantage has not been used in this implementation, so there’s plenty of room for performance improvement!

User-defined yield curve models

By using the QL::model_create_custom() it is possible to define your own yield curve model, which can be fitted to a term structure by using the fit() function just as you would with any of the built in models.

In the following example a new model custom_ns() is created to implement the standard Nelson-Siegel model in the same way as the built in ns() model. This is done by inheriting from the class QL::custom_model and implementing the four mandatory virtual functions:

#include <ql.h>
#include <math.h>

namespace
{

   class custom_ns : public QL::custom_model
   {

   public:

       /* Indices for the param vector. */
       enum { B0, B1, B2, T, N_PARAMS };

       static QL::custom_model_p create()
       {
           return QL::custom_model_p(new custom_ns);
       }

       virtual int n_params() const
       {
           return N_PARAMS;
       }

       virtual double default_param_guess(int p) const
       {
           switch (p)
           {
               case B0:
                   return 0.04;
               case B1:
                   return 0.01;
               case B2:
                   return -0.03;
               case T:
                   return 3;
           }
       }

       /* We need a lower bound on beta_0 for stability */
       virtual double default_param_min(int p) const
       {
           switch (p)
           {
               case B0:
                   return 0.001;
               default:
                   return custom_model::default_param_min(p);
           }
       }

       /* Dito upper bound on T */

       virtual double default_param_max(int p) const
       {
           switch (p)
           {
               case T:
                   return 30;
               default:
                   return custom_model::default_param_max(p);
           }
       }

       virtual void disc_fact(const double *p,
                              int n,
                              const double *t,
                              double *df)
       {
           int i = 0;

           /*
           * t[] is guaranteed to be sorted; we skip possible
           * 0 maturities to avoid division by zero below.
           */

           while (i < n && t[i] == 0)
               df[i++] = 1;

           while (i < n)
           {
               double t_i = t[i];
               double x_t = t_i / p[T];
               double e = exp(-x_t);

               double r;

               r = p[B0] + (p[B1] + p[B2]) * (1 - e) / x_t - p[B2] * e;

               df[i++] = exp(-r * t_i);
           }
       }

   private:
       explicit custom_ns()
       {}
       ~custom_ns()
       {}
   };

   }

   static void create_custom_ns(QL::handle &h)
   {
       h = QL::model_create_custom(custom_ns::create());
   }

   __declspec(dllexport) void ql_init(QL::symtab &ctxt)
   {
       QL::arg_spec r;

       r = QL::arg_spec("model");

       QL::add_func(ctxt, reinterpret_cast<QL::function_t*>(create_custom_ns),

       "custom_ns", "Yield curve models", r, 0, 0);
}