Swiss army knife MATLAB programs for quantitative finance

From LiteratePrograms
Jump to: navigation, search

MCQF

An efficiency use of Matlab in Quantitative finance requires to master some important topics:

Contents

[edit] Functional Object Programming in Matlab

Matlab functions are able to store embedded function (like the set_n function below), and to return a structure containing handles to those functions. Here is the simplest functional object programmed like this:

<<firstoo.m>>=
function z = firstoo( n)
% FIRSTOO - my first real matlab object
% z=firstoo(2)
% z.add_n(3)
% z.get_n()
% z.set_n(7)
% z.get_n()
this.n = n;
z = struct('get_n',@get_n,'add_n',@add_n, 'set_n', @set_n);
    function a = get_n()
        a=this.n;
    end
    function u = set_n( m)
        this.n = m;
    end
    function u = add_n(u)
        u = u +this.n;
    end
end

[edit] Optional Arguments

A good management of optional arguments is very useful, our goal here is to be able to call functions like Matlab built-in ones, id est:

my_function( mandatory_arg1, ..., mandatory_argN, ...
             optional_arg1_name, optional_arg1_value, ..., optional_argK_name, optional_argK_value);

for instance:

 my_function(100, 2, 'color', 'black', 'figure-handle', figure);

we will build a Matlab function to help us using such optional arguments in our Matlab functions. It is called options and will be used like this inside any of our other functions:

function z = any_function(mandatory_arg1, ..., mandatory_argN, varargin)
% ANY_FUNCTION - oneliner help

opt = options({optional_name1, default_value1, ..., optional_nameK, default_valueK}, varargin);

% to access an optional value:
my_value = opt.get(optional_name);

Here are the main elements of the options function:

  • an initialization step
  • a get method
  • a set method

The methods are published with the functional object method:

function_outputs = struct('get',@get_v,'set',@set_v);

The data are stored in the object into the variable named this.

<<options.m>>=
function z = options(default, overwrite)
% OPTIONS - sandbox for optionnal parameters management
%
% use:
% opt = options({'alpha',1,'beta',2},{'alpha',8,'gamma',3})
% opt.get('alpha')
% opt.set('alpha',3)
% opt.get('alpha')
% opt.get()

%%** Optional parameters management

%<* Initialization of the structure
overwrite parameters if needed
%< Internal variable
this = struct('value',{values});
%>
%< Publication of external methods
z    = struct('get',@get_v,'set',@set_v);
%>
%>*

%%** Internals

%<* Get a value
get value
%>*
%<* Set a value
set value
%>*
end

Overwriting of the parameters. The names and the default values for parameters are given to the function into a cellarray as first argument. It's second argument is another cellarray containing another list of parameter name, parameter value. If the parameter is into the default list, it's value is updated, otherwise the parameter and the associated value are created.

<<overwrite parameters if needed>>=
values = default;
if nargin>1
    for i=1:2:length(overwrite)-1
        idx = strmatch(overwrite{i},values(1:2:end-1),'exact');
        if isempty(idx)
            %< New parameter
            % Added to the list of parameters
            values{end+1} = overwrite{i};
            values{end+1} = overwrite{i+1};
            %>
        else
            %< Parameter exists
            % the new value replace the old one
            values{idx(1)*2} = overwrite{i+1};
            %>
        end
    end
end

The get method. Our options object will have a get method. It can be used in three ways:

  • simply to get the value of an existing parameter: opt.get('parameter-name'),
  • to get the value of a parameter if it exists, and a default value if it does not exist: opt.get(parameter-name, default_value)
  • to get the list of all parameters and thier value: opt.get().

The last use of options is usefull when you want to use your options to call another function:

tmp = my_parameters.get();
another_function(mandatory_parameters, tmp{:});
<<get value>>=
function [r, h] = get_v( name, def_value)
    h = 1:length(this.value);
    if nargin==0
        %< No argument
        % Return the internal memory
        r = this.value;
        %>
    else
        idx = strmatch(name, this.value(1:2:end-1),'exact');
        if isempty(idx) & nargin>1
            %< Default value
            % Unknown argument but default value is provided.
            r = def_value;
            h = 0;
            %>
        elseif isempty(idx) & nargin < 2
            %< Unknown argument
            error('options:get:unknown','parameter with name <%s> unknown',name);
            %>
        else
            %< Argument found
            % Its value (and the associate index) is returned
            r = this.value{idx(1)*2-1+1};
            h = idx(1)*2-1+1;
            %>
        end
    end
end
<<set value>>=
function set_v(name, value)
    [v,h] = get_v(name, []);
    if h>0
        this.value{h} = value;
    else
        this.value{end+1} = name;
        this.value{end+1} = value;
    end
end

[edit] Interacting with plots

Matlab plots are dedicated to industrial data plotting. For financial data, it's not so simple, mainly because of the Matlab date format which is not adapted to its use as xticks.

[edit] Store cumization data into figure axes

A good way to customize plots in Matlab is to use their UserData property. Once an axe is created, its UserData property can be access with the get and set Matlab unctions:

A matlab-generated Brownian Motion
<<simple figure>>=
figure;
plot(cumsum(randn(200,1)),'linewidth',2);
legend('A brownian motion');
get(gca,'UserData')

Its default value is empty.

Because the UserData property will possibly used but a lot of different programs to store a lot of different informations, we will use our options object to store heterogeneous data in it.

Using the myuserdata function, we can store and access to heterogeneous informations in this field:

<<myuserdata.m>>=
function z = myuserdata(handle, mode, name, value, def_value)
% MYUSERDATA - a function to access the UserData field
% use:
%  myuserdata(gca,'set','my-data', rand(10,2))
%  v = myuserdata(gca,'get','my-data')

ud = get(handle,'UserData');
switch lower(mode)
    case 'set'
        if isempty(ud)
            ud = options({name, value});
        else
            ud.set(name,value);
        end
        set(handle,'UserData',ud);
    case 'get'
        if isempty(ud)
            if nargin > 3
                z = value;
            else
                error('myuserdata:get:unknown','option <%s> not present in the UserData field',name);
            end
        else
            if nargin > 3
                z = ud.get(name, value);
            else
                z = ud.get(name);
            end
        end
    otherwise
        error('myuserdata:mode','mode <%s> unknown', mode);
end

An example of use is:

myuserdata(gca,'set','test',10);
myuserdata(gca,'set','test2',Inf);
myuserdata(gca,'get','test')
myuserdata(gca,'get','test2')

[edit] Real dateticks

The Matlab function datetick is not very efficient used on real dates. Try for instance:

<<matlab dateticks>>=
figure;
v=cumsum(randn(200,1));dt=today:today+200-1;
plot(dt,v,'linewidth',2) % step 1
datetick                 % step 2
% zoom manually            step 3
Step 1: a curve with dates
Step 2: a curve with dates
Step 3: a zoomed curve with dates

You can see that in step 3 all ticks are lost! (simply by zooming into the dotted rectangle)...

It is possible to manage your self your dateticks, using the graphical function text(x,y,'text',properties...) this way:

Simple self-made dateticks
<<simple self-made datetick>>=
figure; plot(dt,v,'linewidth',2);
ax = axis;
dtx=get(gca,'Xtick');
set(gca,'XtickLabel',[]);
text(dtx,repmat(ax(3),length(dtx),1),datestr(dtx,6),'Rotation',60,'HorizontalAlignment','Right')

Even if it does not look better at first glance, you can at least modify a lot of parameters of the ticks (using the usual properties of the text graphical component, and the options of the function datestr).

It's the main fetaure of this mydateticks function. It uses the options' function to store optional arguments (like datestr format or rotation, font size, font name of the labels).

<<mydateticks.m>>=
function z = mydateticks( varargin)
% MYDATETICKS - simple dateticks management

%%** MyDateTicks
opt = options({'axe-handle',gca, 'nb-steps',[], 'text-handle', [], ...
               'datestr-format',24, 'rotation', 60, 'fontsize',8, ...
               'fontname','Arial'},varargin);
%<* Initializations
this.options = opt;
redraw;
%>*
z    = struct('redraw', @redraw);

%%** Redraw
Redraw
end

This redraw function can be called after a zoom on the axe (using the returned handle):

<<Redraw>>=
function redraw
    %<* Get info
    % From internal state
    ah = this.options.get('axe-handle');
    th = this.options.get('text-handle');
    %>*
    %<* Cleaning
    % Select good axe, clean xtickslabels
    axes(ah);
    ax  = axis;
    dtx = get(ah,'XTick');
    set(ah,'XTickLabel',[]);
    if ~isempty(th)
        delete(th);
    end
    %>*
    %<* Nb of ticks
    % It's a function of the number of pixel on screen
    nb_steps = this.options.get('nb-steps');
    if isempty(nb_steps)
        posf     = get(gcf,'position');
        posa     = get(gca,'position');
        w        = posf(3)*posa(3);
        nb_steps = round(w/20);
    end
    dv  = ax(1):(ax(2)-ax(1))/nb_steps:ax(2);
    if abs(dv(end)-ax(2))<eps
        dv = [dv, ax(2)];
    end
    %>*
    th = text(dv,repmat(ax(3),length(dv),1), ...
              datestr(dv,this.options.get('datestr-format')), ...
              'Rotation',this.options.get('rotation'),...
              'HorizontalAlignment','Right', ...
              'fontsize',this.options.get('fontsize'), ...
              'fontname',this.options.get('fontname'));
    this.options.set('text-handle',th);
end

This function can be used to obtain very fine plots, here is an example in 3 steps:

<<example of mydatetick use>>=
figure
plot(dt,v,'linewidth',2)
z=mydateticks;                                % step 1
z.redraw('datestr-format', 12)                % step 2
z.redraw('datestr-format', 12, 'fontsize',6)  % step 3
Step 1: default dateticks
Step 2: dateticks with a specified format
Step 3: after a zoom: dateticks with adequate options

The important point is to notice that the returned handle (here stored in function z) can be used after a zoom to redraw the dateticks (changing the options if you want).

[edit] Structured data manipulations

In Quantitative Finance, there are some structured dataset. It is very important to keep them synchronized. The minimal structure to store such data is:

  • a title
  • a vector of dates (d x 1)
  • a matrix of value (d x c)
  • a cellarray of column names (1 x c)

It can be initialized like this:

<<creation of my first data structure>>=
data = struct('title','Randomized quotes','date',(today:today+199)', ...
              'value',100*cumprod(1+[randn(200,1)* 0.10, randn(200,1)* 0.21]/16), ...
              'names', {{'Ticker1', 'Ticker2'}})
Plot of a structured dataset
And then it can be plotted like this:
<<plot of my first data structure>>=
figure;
plot(data.date,data.value,'linewidth',2);
title(data.title); 
axis([min(data.date) max(data.date) min(data.value(:)) max(data.value(:))]);
z = mydateticks('datestr-format',25);
legend(data.names);

[edit] Simple manipulations of structured datasets

Now it's quite easy to use such a dataset.

For instance, to get only some rows preserving the correspondance between the columns:

<<extract rows>>=
data.value = data.value(idx,:);
data.date  = data.date(idx);

Or to get rows between two dates:

Zoom on the structured dataset
<<extract dates>>=
if iscell(dates)
    nate = datenum(dates{1},dformat);
    if length(dates)>1
        nate = [nate, datenum(dates{2},dformat)];
    end
    dates = nate;
end
if length(dates)<2
    dates = [dates, dates];
end
idx = find((data.date>=dates(1)) & (data.date<=dates(2)));
extract rows

Those two blocks can be used like this:

<<simple dates extraction>>=
dformat = 'dd/mm/yyyy';
dates = {'10/06/2006' '14/09/2006'};
extract dates
plot of my first data structure

A lot of dataset manipulations can be defined and groupped into a single function, let's call it the zdata function. Features to implement to obtain something usefull are:

  • read an excel datasheet and load it into a structured dataset
  • extract a column and return a structured dataset with only this column
  • get a column values
  • extract dates
  • save to a mat file
  • load from a mat file
<<example4data_structure.m>>=
creation of my first data structure
plot of my first data structure
simple dates extraction
plot of my first data structure

[edit] Complex manipulations of structured datasets

An interesting feature is to be able to apply functions on structured dataset. Functions that can be applied to a structured dataset are called hooks (like in GNU emacs Lisp). An hook is a functionnal object with a field and three methods:

  • names - which is the names of the columns to work on
  • func - which is an handle on the function to apply to columns, dates, and optionnal arguments
  • datefun - which is an handle on the function to apply to dates and columns
  • colfun - which is an handle on the function to apply on column names

Here is a simple function to build hooks:

<<build_hook.m>>=
function z = build_hook(varargin)
% BUILD_HOOK - build an hook
% a hook is a functionnal object with a field an 3 methods:
% - names  , which is the names of the columns to work on
% - func   , which is an handle on the function to apply to columns
% - datefun, which is an handle on the function to apply to dates
% - colfun , which is an handle on the function to apply on column names

opt = options({'names', '', 'func', @(v,d)(v), 'datefun', @(d,v)(d), 'colfun', @(x)(x)}, varargin);
if isempty(opt.get('names'))
    error('build_hook:names', 'I need names of columns to work on');
end

this.opt = opt;
z = @apply;

function data = apply(data, varargin)
    extract structured dataset from column names
    data.value = feval(this.opt.get('func'),data.value,data.date,varargin{:});
    data.date  = feval(this.opt.get('datefun'), data.date,data.value);
    data.names = feval(this.opt.get('colfun'), data.names);
end

end

The extraction of dataset according to column names should be implemented inside the zdata function; here is a block of code juste dedicated to hook building:

<<extract structured dataset from column names>>=
names = this.opt.get('names');
idx   = strmatch(names,data.names,'exact');
data.value = data.value(:,idx);
data.names = data.names(idx);
Plot of a 'hooked' structured dataset
Hooks can be used that way (the result are the returns of the Ticker2):
<<example of hook building.m>>=
my_hook = build_hook('names','Ticker2', ...
                     'func',@(v,d)(diff(v)./v(1:end-1)), ...
                     'datefun',@(d,v)(d(1:end-1)), ...
                     'colfun',@(x)(cellfun(@(n)(['returns of ' n]),x,'UniformOutput',false)))
data = my_hook(data)
plot of my first data structure

[edit] Simple Exponential Brownian Motion

[edit] Usefull tools

At first I need some usefull tools

<<tokenize.m>>=
function t = tokenize(str,sep)
% TOKENIZE - string into cellarray using a separator
if nargin<2
   sep = ';';
end
t = cellfun(@(x)(x{1}),regexp(str,sprintf('([^%s]+)%s',sep,sep),'tokens'),'Uniformoutput',false);

Which can be used as in:

>> tokenize(sprintf('v-%d;',1:4),';')
ans =
   'v-1'    'v-2'    'v-3'    'v-4'
<<plotstruct.m>>=
function h = plotstruct(data, varargin)
% PLOTSTRUCT - plot a structured dataset
% returns an handle on mydateticks
% use:
%  h = plotstruct(data, options)
% options:
% - dates    : false
% - figure   : []
% - linewidth: 2
opts = options({'dates', false, 'figure', [], 'linewidth', 2}, varargin);
h = [];
g = opts.get('figure');
if isempty(g)
   g = figure;
else
   g = figure(g);
end
plot(data.date, data.value, 'linewidth', opts.get('linewidth'));
legend(data.names);
title(data.title);
if opts.get('dates')
   h=mydateticks; 
end

[edit] Simulations

A matlab-generated Exponential Brownian Motion

This function (which uses the <<options>> and <<tokenize>> functions) simulate independant exponential brownian motions.

<<brownexp.m>>=
function data = brownexp(volatility, nb_points, varargin)
% BROWNEXP - simulation of a brownian exponential diffusion
%  use:
% > data = brownexp(volatility, nb_points, options)
% options:
% - drift         :   0
% - start-price   : 100
% - time-step     :  1/250
% - nb-simulations:   1
% - time-horizon  :  []
% if time-horizon not empty, it replaces nb_points
%
%  example:
% data = brownexp(.20, 250, 'nb-simulations', 5)
% plotstruct( data)

opt = options({'drift', 0, 'start-price', 100, 'time-step', 1/250, ...
               'nb-simulations', 1, 'time-horizon', []}, varargin);

if nargin < 2
    nb_points = 100;
end
time_step = opt.get('time-step');
if ~isempty(opt.get('time-horizon'))
    time_horizon = opt.get('time-horizon');
else
    time_horizon = time_step*(nb_points-1);
end

dates  = (0:time_step:time_horizon)';
values = opt.get('start-price')*cumprod(1+opt.get('drift') * ...
         time_step+volatility *sqrt(time_step)*randn(length(dates),opt.get('nb-simulations')));

data   = struct( 'title', 'simulations', 'value', values, 'date', dates, ...
                 'names', {tokenize(sprintf('sim-%d;',1:size(values,2)),';')});
Download code
hijacker
hijacker
hijacker
hijacker