Travaux Pratiques n°5

pricing par arbre binomial

Instructions

Ces travaux pratiques sont à effectuer seul.

Les réponses aux exercices sont à envoyer à mon adresse mail avant la séance suivante.

Évaluation par arbre binomial des options sur action

Le but de ces travaux pratiques est de vous faire créer une classe permettant de manipuler un arbre binomial destiné à l'évaluation d'options sur action.

Cette classe sera ensuite transformée en bibliothèque dynamique (DLL) pouvant être utilisée dans une application externe comme par exemple, un tableur ou même un autre programme que vous auriez écrit.

Rappels

Un client désire prendre une option d'achat sur une action (un call). Un tel contrat assure au client le flux à l'échéance. Cela signifie que, sur la période de temps couvrant le call, si la valeur de l'action est montée plus haut que la valeur du prix d'exercice (le strike), alors le client sera bénéficiaire, mais si la valeur de l'action a baissée en dessous du prix d'exercice, le client ne perdra pas plus que la mise initiale.

0 temps prix de l'action en euro TN 2TN N-1TN T S0 Su Sd
Arbre binomial avec N périodes

Le vendeur veut donc s'assurer de vendre un produit qui lui permettra de ne pas perdre d'argent ; c'est pour cela qu'une telle option d'achat a un prix.

Ce dernier va dépendre de plusieurs facteurs qui sont :

Notre objectif ici est donc de déterminer ce prix de vente en fonction de tous les paramètres à notre disposition.

Pour cela nous allons construire un arbre binomial à N périodes sur une durée T (voir la figure de l'arbre binomial). Les valeurs initiales de cet arbre seront S0, u et d.

Les valeurs u et d sont choisies de telle manière à ce que, sur deux périodes consécutives données, une hausse puis une baisse de la valeur de l'action donne le même résultat qu'une baisse puis une hausse.

Nous avons donc affaire à une structure un peu particulière qui fait que, un arbre à N périodes possède exactement N+1 nœuds terminaux (les feuilles).

Dans la première étape de l'évaluation de cette option, l'objectif est de renseigner chaque nœud de l'arbre avec la valeur simulée, correspondant aux cheminements du prix de l'action dans les branches entre la racine et ce nœud.

Pour un nœud St donné, la valeur obtenue est : St = 1+uk 1+dN-k S0 s'il y a eu k hausses et N-k baisses.

Le prix demandé au client pour une telle option sera alors calculé en remontant l'arbre depuis ses feuilles jusqu'à sa racine et en calculant pour chaque nœud les valeurs du call avec la formule suivante

C C+ C-
C = 11+r qC++1-qC-

où :

Réalisation

La réalisation d'une telle classe va demander plusieurs étapes que nous allons essayer de détailler ci après.

Ce que nous allons présenter se base sur une réalisation qui, au final, va utiliser deux classes de notre cru. Ceci est une proposition et si vous avez une meilleure idée de réalisation, libre à vous de l'implanter.

Arbre ou pas arbre ?

La question est de savoir comment nous allons représenter cet arbre binomial.

Il faut garder à l'esprit que cet arbre doit être parcouru dans les deux sens : i) de la racine vers les feuilles pour simuler les variations du cours de l'action et ainsi renseigner chaque nœuds, ii) des feuilles vers la racine pour calculer ce que le banquier va facturer au client pour cette option.

La structure de données employées devra donc permettre ce parcours et le permettre facilement.

Il faut également bien prendre en compte la structure particulière de cet arbre qui, en fin de compte, ressemble plus à un graphe (certains nœuds étant confondus), pour éviter de dupliquer ces nœuds confondus et donc devoir mettre en place une gestion lourde et coûteuse de ces doublons.

Enfin, on peut légitimement se poser la question, puisque l'on peut déterminer directement les valeurs des feuilles de l'arbre sans avoir à le cronstruire, si une structure d'arbre est nécessaire. Peut-être que l'utilisation d'un simple vecteur suffira.

Exercice

Imaginez une modélisation de et arbre binomial et mettez la en place dans une classe de votre choix.

Calculs

Les calculs ne sont pas (ou en tout cas ne devraient pas être) dépendants de la représentation de l'arbre, nous aurons donc une classe séparée pour les réaliser.

Nous nous proposons de créer une classe binomial qui utilise la classe que vous avez créée pour représenter l'arbre et qui réponde aux problèmes posés en introduction.

Cette classe devra proposer au moins :

  1. Un constructeur permettant de passer les paramètres de création de l'arbre S0, u, d, N et T ;
  2. une méthode permettant de préciser le taux d'intérêt r ;
  3. une méthode permettant de spécifier la valeur du call strike k ;
  4. une méthode qui retourne le prix que doit facturer la banque à son client ;
  5. un destructeur qui va s'occuper de libérer correctement la mémoire qui aurait pu être allouée.

Le constructeur, en plus d'initialiser les différents attributs de la classe, devra lancer la création de l'arbre (c'est à dire instancier les structures de données nécessaires et les renseigner en procédant à un parcours de la racine vers les feuilles pour positionner les valeurs de chaque nœuds). Comme il est possible qu'il y ait plusieurs constructeurs, cette création devra sans doute se faire dans une fonction séparée, elle même invoquée par les différents constructeurs.

La parcours inverse de l'arbre devra se faire à chaque fois que l'on fait appel à la méthode permettant d'afficher le prix. En effet, celui-ci étant dépendant de la valeur du call strike et du taux d'intérêt, il doit être évalué si un de ces deux paramètres change.

Le destructeur devra s'assurer que toute la mémoire qui a été utilisée pour stocker l'arbre en mémoire pendant l'exécution du programme, sera correctement et pleinement libérée.

Exercice

Aidez-vous des indications ci-dessus pour coder une classe binomial qui réponde au problème. Vous devrez décomposer ce code en deux parties : binomial.h et binomial.cpp.

La fonction main() ne sera pas présente dans ces fichiers, mais sera plutôt décrite dans un autre fichier (par exemple main.cpp) pour faciliter la dernière partie de ces travaux pratiques.


Testez votre programme avec les données suivantes :

  • S0=100 ;
  • N=2 et T=2 ;
  • u=0,2 et d=-0,1 ;
  • r=0,05 et K=100.

La valeur finale pour C0 doit être de 13,61.

Dynamic Link Library

Nous désirons pouvoir utiliser notre programme pour effectuer le calcul de prix depuis une autre application. Pour cela, nous allons créer une DLL (Dynamic Link Library ou Bibliothèque de Liens Dynamiques) qui va contenir notre programme.

Généralités

Dans le monde Windows, une DLL est un fichier contenant des données ou des fonctions que l'on désire mettre à disposition d'autres programmes.

Une DLL est, comme son nom l'indique, chargée en mémoire dynamiquement (au moment où le programme en a besoin). Ce qui est important c'est qu'elle n'est chargée qu'une seule fois même si, au même moment, plusieurs programmes différents l'utilisent. Nous voyons donc ici immédiatement le bénéfice sur le gain d'espace mémoire.

Une DLL propose des service représentés par des fonctions. En général ces services ont un rapport entre eux (une même thématique), mais ce n'est pas une obligation.

Dans la DLL (dans le fichier) il existe une table des matières permettant d'associer un nom à chacune des fonctions disponibles. Ceci permet, aux programmes en ayant besoin, de les retrouver.

Remarque : Le nom de chacune des fonctions dans cette table des matières est choisi par le programmeur qui a créé la bibliothèque ; il n'est pas obligatoire de mettre le même nom que les fonctions telles qu'on les trouve effectivement dans notre classe.

Objet ou pas objet ?

Quand on crée une bibliothèque dynamique, il faut se poser la question de savoir à qui elle est destinée et surtout, quel est le langage de programmation qui va servir à invoquer les fonctions proposées.

Si nous prenons l'exemple d'un programme écrit en Visual Basic, C++, C#, .Net, ... cela va fonctionner assez facilement et le langage hôte va pouvoir créer des instances des classes que nous proposons dans la DLL et les manipuler comme ses propres objets.

Par contre, dans le cas de la suite Office de Microsoft, nous savons que le langage de programmation pour écrire les macros est le VBA. Or, ce langage n'est pas objet et de fait, ne pourra pas comprendre les paradigmes sous-jacents de notre classe.

Alors que faire ? La solution est de ne pas écrire la DLL en utilisant un langage objet. Mais ce n'est pas notre but, nous programmons en C++. La méthode alors généralement adoptée est la mise en place d'adaptateurs (wrappers, en anglais) qui vont masquer l'aspect objet de notre DLL.

Un autre aspect important à prendre en compte est le type des données manipulées. En C++ nous connaissons int, float, string, ... Mais, en VBA par exemple, il y a Byte, Single, String, ... et la taille en octet de chacun de ces types, même ceux qui se ressemblent, n'est pas forcement la même d'un langage à l'autre (je vous invite à lire i) la documentation de votre compilateur ii) la documentation du langage hôte que vous allez utiliser).

Ensuite, sur les ordinateurs de type x86, il existe plusieurs méthodes de passage de paramètres lors de l'appel d'une fonction. Grossièrement, cela détermine qui de la fonction appelante ou appelée, va nettoyer la pile avant le dépôt des paramètres et s'il va être possible d'utiliser des registres pour les stocker.

L'API Windows Win32 utilise la méthode de passage de paramètres stdcall alors que, nativement, le compilateur GCC utilise la méthode cdecl. Il est de fait nécessaire d'indiquer à GCC d'utiliser la méthode stdcall en préfixant le nom de chaque fonction exportée de la macro __stdcall.

Vous trouverez dans les fichiers main.cpp et main.h, la définition de ces wrappers que vous n'aurez qu'à reprendre tels quels (éventuellement, vous devrez modifier le nom des méthodes appelées pour l'adapter aux vôtres).

Enfin, comme évoqué au début de cette section, les compilateurs C++, lors de la phase de compilation se livrent à une redéfinition des intitulés de chaque méthode/fonction.

De fait, pour pouvoir retrouver des fonctions lors de l'utilisation de notre DLL, il est nécessaire de créer un fichier de correspondance entre le nom que l'on désire trouver dans la table des matières de la DLL et le nom que le compilateur donne.

Une tel fichier (dont le nom se termine en général par .def) doit être lié à la DLL au moment de l'édition de liens.

Les noms donnés par le compilateur dépendent de plusieurs facteurs tels que le nombre de fonctions dans le programme, le nombre d'arguments à ces fonctions, les positions relatives des fonctions les unes par rapport aux autres dans le code source, ... Il n'est pas aisé de les deviner. Il faut donc que, la première fois, Code::Blocks génère ce fichier pour vous (ce qui est fait par défaut) pour que vous puissiez ensuite le modifier et obtenir quelque chose qui ressemble à ceci :

EXPORTS
   CreationArbre=CreationArbre@32 @1
   DonnePrix=DonnePrix@4 @2
   LiberationArbre=LiberationArbre@4 @3
   ModifieStrike=ModifieStrike@12 @4
   ModifieTaux=ModifieTaux@12 @5

Une fois ce fichier généré et modifié par vos soins, il faut indiquer à Code::Blocks : i) qu'il le prenne en compte et ii) qu'il n'en génère pas de nouveau. Pour cela, il faut modifier deux options dans le menu Project :

  • Dans l'entrée Build Options ..., onglet Linker settings, ajoutez votre fichier à la liste Link Libraries ;
  • Dans l'entrée Properties ..., onglet Build targets, décochez la case Create .DEF exports file.

Vous devez ensuite recompiler votre bibliothèque.

Exercice

À l'aide des indications et fichiers disponibles ci-dessus, adaptez votre classe pour qu'elle puisse être transformée en DLL.

Pour faciliter votre travail et configurer correctement le compilateur, nous vous conseillons de créer un nouveau projet avec Code::Blocks. Mais cette fois, au lieu de demander un modèle Console Application, vous demanderez une Dynamic Link Library. Vous importerez ensuite vos 4 fichiers source (noeud.h, noeud.cpp, binomial.h et binomial.cpp) ainsi que les deux que je vous propose (main.h et main.cpp).

Utilisation en VBA/Excel

Voici un exemple d'utilisation de cette DLL dans une macro Excel :

Public Declare Function CreationArbre Lib "C:\CodeBlocks\Binomial.dll"
(ByVal S0 As Double, ByVal N As Byte, ByVal T As Byte,
ByVal u As Double, ByVal d As Double)
As Long
Public Declare Sub ModifieTaux Lib "C:\CodeBlocks\Binomial.dll"
(ByVal X As Long, ByVal r As Double)
Public Declare Sub ModifieStrike Lib "C:\CodeBlocks\Binomial.dll"
(ByVal X As Long, ByVal k As Double)
Public Declare Function DonnePrix Lib "C:\CodeBlocks\Binomial.dll"
(ByVal X As Long)
As Double
Public Declare Sub LiberationArbre Lib "C:\CodeBlocks\Binomial.dll"
(ByVal X As Long)


Sub MacroArbre()
Dim Res As Double
Dim A As Long

A = CreationArbre(100, 2, 2, 0.2, -0.1)
Call ModifieTaux(A, 0.05)
Call ModifieStrike(A, 100)
Res = DonnePrix(A)

Range("D16").Select
ActiveCell.FormulaR1C1 = Res
LiberationArbre (A)
End Sub

La première partie (de la ligne 1 à la ligne 13 permet d'importer les fonctions (Functions) et les procédures (Sub -- qui ne retournent pas de valeur) depuis notre DLL, spécifiée par son chemin.

Dans cette partie, nous voyons que chaque fonction est décrite, tant au niveau du nom, que des paramètres et du type de retour. Il est important de noter que tous les paramètres doivent êtres spécifiés avec le mot clé ByVal, ce qui signifie que l'on va converser avec notre DLL en utilisant un passage par valeur et non par références (par défaut), ce qui pourrait occasionner des comportements étranges.

Les types des arguments et valeurs de retour sont également à choisir avec soin pour qu'ils puissent contenir ce que la DLL va nous envoyer. Nous remarquons l'exception qui est faite pour les types adresse mémoire qui sont véhiculés comme des Long (4 octets, pour pouvoir stocker une adresse).

Enfin dans la seconde partie (de la ligne 16 à la ligne 28), la définition de la macro elle même est on ne peut plus classique, avec l'utilisation d'une variable de type Long qui va permettre de stocker la référence à l'instance de notre classe. Il faudra donc la passer en paramètre de chacun de nos adaptateurs.

Références